blob: 2c8a27abed6006cc79977694138904729efb492c [file] [log] [blame]
The Android Open Source Project0c908882009-03-03 19:32:16 -08001/*
2 * Copyright (C) 2006 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.browser;
18
19import android.app.Activity;
Jeff Hamilton64144e42010-08-18 16:31:59 -050020import android.app.Fragment;
Jeff Hamilton84029622010-08-05 14:29:28 -050021import android.app.LoaderManager;
Dianne Hackborneb07b172010-08-26 22:18:14 -070022import android.content.ClipData;
Michael Kolbbef7a752010-08-25 17:11:35 -070023import android.content.ClipboardManager;
Jeff Hamilton84029622010-08-05 14:29:28 -050024import android.content.ContentUris;
Dianne Hackborn80f32622010-08-05 14:17:53 -070025import android.content.Context;
John Reck71efc2b2011-05-09 16:54:28 -070026import android.content.CursorLoader;
The Android Open Source Project0c908882009-03-03 19:32:16 -080027import android.content.Intent;
Jeff Hamilton84029622010-08-05 14:29:28 -050028import android.content.Loader;
John Reck4f61ee42011-08-22 16:31:14 -070029import android.content.SharedPreferences;
John Reckb3417f02011-01-14 11:01:05 -080030import android.content.res.Configuration;
31import android.content.res.Resources;
Jeff Hamilton84029622010-08-05 14:29:28 -050032import android.database.Cursor;
The Android Open Source Project0c908882009-03-03 19:32:16 -080033import android.graphics.Bitmap;
Jeff Hamilton84029622010-08-05 14:29:28 -050034import android.graphics.BitmapFactory;
35import android.net.Uri;
John Reckc8490812010-11-22 14:15:36 -080036import android.os.AsyncTask;
The Android Open Source Project0c908882009-03-03 19:32:16 -080037import android.os.Bundle;
Jeff Hamilton69bd7072010-08-17 12:38:22 -050038import android.provider.BrowserContract;
John Reck71efc2b2011-05-09 16:54:28 -070039import android.provider.BrowserContract.Accounts;
John Recke7c97de2011-05-27 14:42:43 -070040import android.view.ActionMode;
The Android Open Source Project0c908882009-03-03 19:32:16 -080041import android.view.ContextMenu;
Jeff Hamiltondb90aa82010-09-16 03:38:04 -050042import android.view.ContextMenu.ContextMenuInfo;
Jeff Hamilton64144e42010-08-18 16:31:59 -050043import android.view.LayoutInflater;
The Android Open Source Project0c908882009-03-03 19:32:16 -080044import android.view.MenuInflater;
45import android.view.MenuItem;
46import android.view.View;
Jeff Hamiltondb90aa82010-09-16 03:38:04 -050047import android.view.ViewGroup;
John Reck71efc2b2011-05-09 16:54:28 -070048import android.widget.ExpandableListView;
49import android.widget.ExpandableListView.OnChildClickListener;
John Recke7c97de2011-05-27 14:42:43 -070050import android.widget.ImageView;
51import android.widget.LinearLayout;
Jeff Hamilton84029622010-08-05 14:29:28 -050052import android.widget.Toast;
The Android Open Source Project0c908882009-03-03 19:32:16 -080053
John Recke7c97de2011-05-27 14:42:43 -070054import com.android.browser.BookmarkDragHandler.BookmarkDragController;
55import com.android.browser.BookmarkDragHandler.BookmarkDragState;
John Reck1e9815d2011-08-08 17:45:05 -070056import com.android.browser.provider.BrowserProvider2;
John Reck9db95292011-06-20 13:00:12 -070057import com.android.browser.view.BookmarkExpandableView;
58import com.android.browser.view.BookmarkExpandableView.BookmarkContextMenuInfo;
John Recke7c97de2011-05-27 14:42:43 -070059
John Reck4f61ee42011-08-22 16:31:14 -070060import org.json.JSONException;
61import org.json.JSONObject;
62
John Reck71efc2b2011-05-09 16:54:28 -070063import java.util.HashMap;
64
John Reck608baa72010-11-24 10:32:28 -080065interface BookmarksPageCallbacks {
66 // Return true if handled
67 boolean onBookmarkSelected(Cursor c, boolean isFolder);
68 // Return true if handled
John Reckd3e4d5b2011-07-13 15:48:43 -070069 boolean onOpenInNewWindow(String... urls);
John Reck608baa72010-11-24 10:32:28 -080070}
71
The Android Open Source Project0c908882009-03-03 19:32:16 -080072/**
73 * View showing the user's bookmarks in the browser.
74 */
Jeff Hamilton64144e42010-08-18 16:31:59 -050075public class BrowserBookmarksPage extends Fragment implements View.OnCreateContextMenuListener,
John Reck9db95292011-06-20 13:00:12 -070076 LoaderManager.LoaderCallbacks<Cursor>, BreadCrumbView.Controller,
John Reckd3e4d5b2011-07-13 15:48:43 -070077 OnChildClickListener {
The Android Open Source Project0c908882009-03-03 19:32:16 -080078
John Recke7c97de2011-05-27 14:42:43 -070079 public static class ExtraDragState {
80 public int childPosition;
81 public int groupPosition;
82 }
83
Jeff Hamilton84029622010-08-05 14:29:28 -050084 static final String LOGTAG = "browser";
Nicolas Catania095292f2010-03-15 09:00:14 -070085
John Reck71efc2b2011-05-09 16:54:28 -070086 static final int LOADER_ACCOUNTS = 1;
87 static final int LOADER_BOOKMARKS = 100;
Jeff Hamilton84029622010-08-05 14:29:28 -050088
Michael Kolb801ecb72010-08-25 12:57:38 -070089 static final String EXTRA_DISABLE_WINDOW = "disable_new_window";
John Reck4f61ee42011-08-22 16:31:14 -070090 static final String PREF_GROUP_STATE = "bbp_group_state";
Michael Kolb801ecb72010-08-25 12:57:38 -070091
John Reck71efc2b2011-05-09 16:54:28 -070092 static final String ACCOUNT_TYPE = "account_type";
93 static final String ACCOUNT_NAME = "account_name";
94
John Reck9db95292011-06-20 13:00:12 -070095 public static final int VIEW_THUMBNAILS = 1;
96 public static final int VIEW_LIST = 2;
Jeff Hamiltondb90aa82010-09-16 03:38:04 -050097
John Reck608baa72010-11-24 10:32:28 -080098 BookmarksPageCallbacks mCallbacks;
John Reck0ce8a942011-01-14 19:57:09 -080099 View mRoot;
John Reck9db95292011-06-20 13:00:12 -0700100 BookmarkExpandableView mGrid;
Jeff Hamilton84029622010-08-05 14:29:28 -0500101 boolean mDisableNewWindow;
John Reck608baa72010-11-24 10:32:28 -0800102 boolean mEnableContextMenu = true;
Jeff Hamilton84029622010-08-05 14:29:28 -0500103 View mEmptyView;
John Reck608baa72010-11-24 10:32:28 -0800104 View mHeader;
John Reck71efc2b2011-05-09 16:54:28 -0700105 HashMap<Integer, BrowserBookmarksAdapter> mBookmarkAdapters = new HashMap<Integer, BrowserBookmarksAdapter>();
John Reck66302e52011-05-18 17:12:33 -0700106 BookmarkDragHandler mDragHandler;
John Reck4f61ee42011-08-22 16:31:14 -0700107 JSONObject mState;
Michael Kolb370a4f32010-10-06 10:45:32 -0700108
Jeff Hamilton84029622010-08-05 14:29:28 -0500109 @Override
110 public Loader<Cursor> onCreateLoader(int id, Bundle args) {
John Reck71efc2b2011-05-09 16:54:28 -0700111 if (id == LOADER_ACCOUNTS) {
112 return new AccountsLoader(getActivity());
113 } else if (id >= LOADER_BOOKMARKS) {
114 String accountType = args.getString(ACCOUNT_TYPE);
115 String accountName = args.getString(ACCOUNT_NAME);
116 BookmarksLoader bl = new BookmarksLoader(getActivity(),
117 accountType, accountName);
118 return bl;
119 } else {
120 throw new UnsupportedOperationException("Unknown loader id " + id);
Jeff Hamilton84029622010-08-05 14:29:28 -0500121 }
Jeff Hamilton84029622010-08-05 14:29:28 -0500122 }
123
124 @Override
Jeff Hamiltondb90aa82010-09-16 03:38:04 -0500125 public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
John Reck71efc2b2011-05-09 16:54:28 -0700126 if (loader.getId() == LOADER_ACCOUNTS) {
127 LoaderManager lm = getLoaderManager();
128 int id = LOADER_BOOKMARKS;
129 while (cursor.moveToNext()) {
130 String accountName = cursor.getString(0);
131 String accountType = cursor.getString(1);
132 Bundle args = new Bundle();
133 args.putString(ACCOUNT_NAME, accountName);
134 args.putString(ACCOUNT_TYPE, accountType);
135 BrowserBookmarksAdapter adapter = new BrowserBookmarksAdapter(
John Reckd3e4d5b2011-07-13 15:48:43 -0700136 getActivity(), VIEW_THUMBNAILS);
John Reck71efc2b2011-05-09 16:54:28 -0700137 mBookmarkAdapters.put(id, adapter);
John Reck4f61ee42011-08-22 16:31:14 -0700138 boolean expand = true;
139 try {
140 expand = mState.getBoolean(accountName != null ? accountName
141 : BookmarkExpandableView.LOCAL_ACCOUNT_NAME);
142 } catch (JSONException e) {} // no state for accountName
143 mGrid.addAccount(accountName, adapter, expand);
John Reck71efc2b2011-05-09 16:54:28 -0700144 lm.restartLoader(id, args, this);
145 id++;
Jeff Hamiltondb90aa82010-09-16 03:38:04 -0500146 }
John Reck71efc2b2011-05-09 16:54:28 -0700147 // TODO: Figure out what a reload of these means
148 // Currently, a reload is triggered whenever bookmarks change
149 // This is less than ideal
150 // It also causes UI flickering as a new adapter is created
151 // instead of re-using an existing one when the account_name is the
152 // same.
153 // For now, this is a one-shot load
154 getLoaderManager().destroyLoader(LOADER_ACCOUNTS);
155 } else if (loader.getId() >= LOADER_BOOKMARKS) {
156 BrowserBookmarksAdapter adapter = mBookmarkAdapters.get(loader.getId());
157 adapter.changeCursor(cursor);
Jeff Hamiltondb90aa82010-09-16 03:38:04 -0500158 }
Jeff Hamilton84029622010-08-05 14:29:28 -0500159 }
160
John Reck522a4382011-01-26 13:23:47 -0800161 @Override
Dianne Hackborn39772c82010-12-16 00:43:54 -0800162 public void onLoaderReset(Loader<Cursor> loader) {
Dianne Hackborn39772c82010-12-16 00:43:54 -0800163 }
164
The Android Open Source Project0c908882009-03-03 19:32:16 -0800165 @Override
166 public boolean onContextItemSelected(MenuItem item) {
John Reck54ed3aa2011-08-19 15:16:05 -0700167 if (!(item.getMenuInfo() instanceof BookmarkContextMenuInfo)) {
168 return false;
169 }
170 BookmarkContextMenuInfo i = (BookmarkContextMenuInfo) item.getMenuInfo();
The Android Open Source Project0c908882009-03-03 19:32:16 -0800171 // If we have no menu info, we can't tell which item was selected.
172 if (i == null) {
Leon Scrogginsc8cae082010-11-03 18:51:46 -0400173 return false;
The Android Open Source Project0c908882009-03-03 19:32:16 -0800174 }
Nicolas Catania095292f2010-03-15 09:00:14 -0700175
John Recke7c97de2011-05-27 14:42:43 -0700176 if (handleContextItem(item.getItemId(), i.groupPosition, i.childPosition)) {
177 return true;
178 }
179 return super.onContextItemSelected(item);
180 }
181
182 public boolean handleContextItem(int itemId, int groupPosition,
183 int childPosition) {
184 final Activity activity = getActivity();
185 BrowserBookmarksAdapter adapter = getChildAdapter(groupPosition);
186
187 switch (itemId) {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800188 case R.id.open_context_menu_id:
John Recke7c97de2011-05-27 14:42:43 -0700189 loadUrl(adapter, childPosition);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800190 break;
191 case R.id.edit_context_menu_id:
John Recke7c97de2011-05-27 14:42:43 -0700192 editBookmark(adapter, childPosition);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800193 break;
194 case R.id.shortcut_context_menu_id:
John Recke7c97de2011-05-27 14:42:43 -0700195 Cursor c = adapter.getItem(childPosition);
John Reck608baa72010-11-24 10:32:28 -0800196 activity.sendBroadcast(createShortcutIntent(getActivity(), c));
The Android Open Source Project0c908882009-03-03 19:32:16 -0800197 break;
198 case R.id.delete_context_menu_id:
John Recke7c97de2011-05-27 14:42:43 -0700199 displayRemoveBookmarkDialog(adapter, childPosition);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800200 break;
201 case R.id.new_window_context_menu_id:
John Recke7c97de2011-05-27 14:42:43 -0700202 openInNewWindow(adapter, childPosition);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800203 break;
Jeff Hamilton84029622010-08-05 14:29:28 -0500204 case R.id.share_link_context_menu_id: {
John Recke7c97de2011-05-27 14:42:43 -0700205 Cursor cursor = adapter.getItem(childPosition);
Michael Kolb8233fac2010-10-26 16:08:53 -0700206 Controller.sharePage(activity,
Jeff Hamilton84029622010-08-05 14:29:28 -0500207 cursor.getString(BookmarksLoader.COLUMN_INDEX_TITLE),
208 cursor.getString(BookmarksLoader.COLUMN_INDEX_URL),
209 getBitmap(cursor, BookmarksLoader.COLUMN_INDEX_FAVICON),
210 getBitmap(cursor, BookmarksLoader.COLUMN_INDEX_THUMBNAIL));
The Android Open Source Project0c908882009-03-03 19:32:16 -0800211 break;
Jeff Hamilton84029622010-08-05 14:29:28 -0500212 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800213 case R.id.copy_url_context_menu_id:
John Recke7c97de2011-05-27 14:42:43 -0700214 copy(getUrl(adapter, childPosition));
Leon Scrogginsfeb941d2009-05-28 17:27:38 -0400215 break;
Jeff Hamilton84029622010-08-05 14:29:28 -0500216 case R.id.homepage_context_menu_id: {
John Recke7c97de2011-05-27 14:42:43 -0700217 BrowserSettings.getInstance().setHomePage(getUrl(adapter, childPosition));
Jeff Hamilton64144e42010-08-18 16:31:59 -0500218 Toast.makeText(activity, R.string.homepage_set, Toast.LENGTH_LONG).show();
Leon Scrogginsfeb941d2009-05-28 17:27:38 -0400219 break;
Jeff Hamilton84029622010-08-05 14:29:28 -0500220 }
Leon Scrogginsa5d669e2009-08-05 14:07:58 -0400221 // Only for the Most visited page
Jeff Hamilton84029622010-08-05 14:29:28 -0500222 case R.id.save_to_bookmarks_menu_id: {
John Recke7c97de2011-05-27 14:42:43 -0700223 Cursor cursor = adapter.getItem(childPosition);
Jeff Hamilton84029622010-08-05 14:29:28 -0500224 String name = cursor.getString(BookmarksLoader.COLUMN_INDEX_TITLE);
225 String url = cursor.getString(BookmarksLoader.COLUMN_INDEX_URL);
Leon Scrogginsa5d669e2009-08-05 14:07:58 -0400226 // If the site is bookmarked, the item becomes remove from
227 // bookmarks.
Jeff Hamilton64144e42010-08-18 16:31:59 -0500228 Bookmarks.removeFromBookmarks(activity, activity.getContentResolver(), url, name);
Leon Scrogginsa5d669e2009-08-05 14:07:58 -0400229 break;
Jeff Hamilton84029622010-08-05 14:29:28 -0500230 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800231 default:
John Recke7c97de2011-05-27 14:42:43 -0700232 return false;
The Android Open Source Project0c908882009-03-03 19:32:16 -0800233 }
234 return true;
235 }
236
John Reck608baa72010-11-24 10:32:28 -0800237 static Bitmap getBitmap(Cursor cursor, int columnIndex) {
Jeff Hamilton84029622010-08-05 14:29:28 -0500238 byte[] data = cursor.getBlob(columnIndex);
239 if (data == null) {
240 return null;
The Android Open Source Project0c908882009-03-03 19:32:16 -0800241 }
Jeff Hamilton84029622010-08-05 14:29:28 -0500242 return BitmapFactory.decodeByteArray(data, 0, data.length);
243 }
244
John Reckc8490812010-11-22 14:15:36 -0800245 private MenuItem.OnMenuItemClickListener mContextItemClickListener =
246 new MenuItem.OnMenuItemClickListener() {
247 @Override
248 public boolean onMenuItemClick(MenuItem item) {
249 return onContextItemSelected(item);
250 }
251 };
252
Jeff Hamilton84029622010-08-05 14:29:28 -0500253 @Override
254 public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
John Reck71efc2b2011-05-09 16:54:28 -0700255 BookmarkContextMenuInfo info = (BookmarkContextMenuInfo) menuInfo;
256 BrowserBookmarksAdapter adapter = getChildAdapter(info.groupPosition);
257 Cursor cursor = adapter.getItem(info.childPosition);
John Reck1cebb442010-12-10 12:02:16 -0800258 if (!canEdit(cursor)) {
259 return;
260 }
Leon Scroggins5e8a40f2010-09-27 17:13:35 -0400261 boolean isFolder
262 = cursor.getInt(BookmarksLoader.COLUMN_INDEX_IS_FOLDER) != 0;
Michael Kolb801ecb72010-08-25 12:57:38 -0700263
Jeff Hamilton64144e42010-08-18 16:31:59 -0500264 final Activity activity = getActivity();
265 MenuInflater inflater = activity.getMenuInflater();
Jeff Hamilton84029622010-08-05 14:29:28 -0500266 inflater.inflate(R.menu.bookmarkscontext, menu);
John Reckc8490812010-11-22 14:15:36 -0800267 if (isFolder) {
268 menu.setGroupVisible(R.id.FOLDER_CONTEXT_MENU, true);
269 } else {
270 menu.setGroupVisible(R.id.BOOKMARK_CONTEXT_MENU, true);
271 if (mDisableNewWindow) {
272 menu.findItem(R.id.new_window_context_menu_id).setVisible(false);
273 }
Jeff Hamilton84029622010-08-05 14:29:28 -0500274 }
John Reckc8490812010-11-22 14:15:36 -0800275 BookmarkItem header = new BookmarkItem(activity);
John Reck83c01512011-09-09 11:14:16 -0700276 header.setEnableScrolling(true);
John Reckc8490812010-11-22 14:15:36 -0800277 populateBookmarkItem(cursor, header, isFolder);
John Reckc8490812010-11-22 14:15:36 -0800278 menu.setHeaderView(header);
Jeff Hamilton84029622010-08-05 14:29:28 -0500279
John Reckc8490812010-11-22 14:15:36 -0800280 int count = menu.size();
281 for (int i = 0; i < count; i++) {
282 menu.getItem(i).setOnMenuItemClickListener(mContextItemClickListener);
Jeff Hamilton84029622010-08-05 14:29:28 -0500283 }
Jeff Hamilton84029622010-08-05 14:29:28 -0500284 }
285
John Reck1cebb442010-12-10 12:02:16 -0800286 boolean canEdit(Cursor c) {
John Reckd18ac4b2012-04-12 17:27:34 -0700287 int type = c.getInt(BookmarksLoader.COLUMN_INDEX_TYPE);
288 return type == BrowserContract.Bookmarks.BOOKMARK_TYPE_BOOKMARK
289 || type == BrowserContract.Bookmarks.BOOKMARK_TYPE_FOLDER;
John Reck1cebb442010-12-10 12:02:16 -0800290 }
291
John Reckc8490812010-11-22 14:15:36 -0800292 private void populateBookmarkItem(Cursor cursor, BookmarkItem item, boolean isFolder) {
Jeff Hamilton84029622010-08-05 14:29:28 -0500293 item.setName(cursor.getString(BookmarksLoader.COLUMN_INDEX_TITLE));
John Reckc8490812010-11-22 14:15:36 -0800294 if (isFolder) {
295 item.setUrl(null);
296 Bitmap bitmap =
Michael Kolb5a72f182011-01-13 20:35:06 -0800297 BitmapFactory.decodeResource(getResources(), R.drawable.ic_folder_holo_dark);
John Reckc8490812010-11-22 14:15:36 -0800298 item.setFavicon(bitmap);
John Reck60e33362010-12-02 17:52:38 -0800299 new LookupBookmarkCount(getActivity(), item)
300 .execute(cursor.getLong(BookmarksLoader.COLUMN_INDEX_ID));
John Reckc8490812010-11-22 14:15:36 -0800301 } else {
302 String url = cursor.getString(BookmarksLoader.COLUMN_INDEX_URL);
303 item.setUrl(url);
304 Bitmap bitmap = getBitmap(cursor, BookmarksLoader.COLUMN_INDEX_FAVICON);
John Reckc8490812010-11-22 14:15:36 -0800305 item.setFavicon(bitmap);
Jeff Hamilton84029622010-08-05 14:29:28 -0500306 }
Jeff Hamilton84029622010-08-05 14:29:28 -0500307 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800308
309 /**
310 * Create a new BrowserBookmarksPage.
Nicolas Catania095292f2010-03-15 09:00:14 -0700311 */
The Android Open Source Project0c908882009-03-03 19:32:16 -0800312 @Override
Jeff Hamilton64144e42010-08-18 16:31:59 -0500313 public void onCreate(Bundle icicle) {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800314 super.onCreate(icicle);
John Reck4f61ee42011-08-22 16:31:14 -0700315 SharedPreferences prefs = BrowserSettings.getInstance().getPreferences();
316 try {
317 mState = new JSONObject(prefs.getString(PREF_GROUP_STATE, "{}"));
318 } catch (JSONException e) {
319 // Parse failed, clear preference and start with empty state
320 prefs.edit().remove(PREF_GROUP_STATE).apply();
321 mState = new JSONObject();
322 }
Jeff Hamilton64144e42010-08-18 16:31:59 -0500323 Bundle args = getArguments();
Leon Scrogginsbc922852010-10-22 12:15:27 -0400324 mDisableNewWindow = args == null ? false : args.getBoolean(EXTRA_DISABLE_WINDOW, false);
John Reck8f295d12011-03-18 15:25:18 -0700325 setHasOptionsMenu(true);
John Reck2d963a22011-08-10 15:53:07 -0700326 if (mCallbacks == null && getActivity() instanceof CombinedBookmarksCallbacks) {
327 mCallbacks = new CombinedBookmarksCallbackWrapper(
328 (CombinedBookmarksCallbacks) getActivity());
329 }
Jeff Hamilton64144e42010-08-18 16:31:59 -0500330 }
Leon Scrogginsd87f85e2009-08-18 14:13:31 -0400331
John Reck4f61ee42011-08-22 16:31:14 -0700332 @Override
333 public void onPause() {
334 super.onPause();
335 try {
336 mState = mGrid.saveGroupState();
337 // Save state
338 SharedPreferences prefs = BrowserSettings.getInstance().getPreferences();
339 prefs.edit()
340 .putString(PREF_GROUP_STATE, mState.toString())
341 .apply();
342 } catch (JSONException e) {
343 // Not critical, ignore
344 }
345 }
346
John Reck2d963a22011-08-10 15:53:07 -0700347 private static class CombinedBookmarksCallbackWrapper
348 implements BookmarksPageCallbacks {
349
350 private CombinedBookmarksCallbacks mCombinedCallback;
351
352 private CombinedBookmarksCallbackWrapper(CombinedBookmarksCallbacks cb) {
353 mCombinedCallback = cb;
354 }
355
356 @Override
357 public boolean onOpenInNewWindow(String... urls) {
358 mCombinedCallback.openInNewTab(urls);
359 return true;
360 }
361
362 @Override
363 public boolean onBookmarkSelected(Cursor c, boolean isFolder) {
364 if (isFolder) {
365 return false;
366 }
367 mCombinedCallback.openUrl(BrowserBookmarksPage.getUrl(c));
368 return true;
369 }
370 };
371
Jeff Hamilton64144e42010-08-18 16:31:59 -0500372 @Override
Jeff Hamilton64144e42010-08-18 16:31:59 -0500373 public View onCreateView(LayoutInflater inflater, ViewGroup container,
374 Bundle savedInstanceState) {
John Reck0ce8a942011-01-14 19:57:09 -0800375 mRoot = inflater.inflate(R.layout.bookmarks, container, false);
376 mEmptyView = mRoot.findViewById(android.R.id.empty);
Leon Scrogginsd87f85e2009-08-18 14:13:31 -0400377
John Reck9db95292011-06-20 13:00:12 -0700378 mGrid = (BookmarkExpandableView) mRoot.findViewById(R.id.grid);
John Reck71efc2b2011-05-09 16:54:28 -0700379 mGrid.setOnChildClickListener(this);
380 mGrid.setColumnWidthFromLayout(R.layout.bookmark_thumbnail);
381 mGrid.setBreadcrumbController(this);
John Reck608baa72010-11-24 10:32:28 -0800382 setEnableContextMenu(mEnableContextMenu);
John Reck66302e52011-05-18 17:12:33 -0700383 mDragHandler = new BookmarkDragHandler(getActivity(), mDragController,
384 mGrid.getDragAdapter());
John Reck608baa72010-11-24 10:32:28 -0800385
Jeff Hamiltondb90aa82010-09-16 03:38:04 -0500386 // Start the loaders
387 LoaderManager lm = getLoaderManager();
John Reck71efc2b2011-05-09 16:54:28 -0700388 lm.restartLoader(LOADER_ACCOUNTS, null, this);
Jeff Hamilton84029622010-08-05 14:29:28 -0500389
John Reck0ce8a942011-01-14 19:57:09 -0800390 return mRoot;
Jeff Hamilton64144e42010-08-18 16:31:59 -0500391 }
Michael Kolb801ecb72010-08-25 12:57:38 -0700392
John Reck608baa72010-11-24 10:32:28 -0800393 @Override
394 public void onDestroyView() {
395 super.onDestroyView();
John Reck71efc2b2011-05-09 16:54:28 -0700396 mGrid.setBreadcrumbController(null);
John Reckc3c46d72011-09-06 16:17:34 -0700397 mGrid.clearAccounts();
John Reck71efc2b2011-05-09 16:54:28 -0700398 LoaderManager lm = getLoaderManager();
399 lm.destroyLoader(LOADER_ACCOUNTS);
400 for (int id : mBookmarkAdapters.keySet()) {
401 lm.destroyLoader(id);
John Reck608baa72010-11-24 10:32:28 -0800402 }
John Reck71efc2b2011-05-09 16:54:28 -0700403 mBookmarkAdapters.clear();
John Reck71efc2b2011-05-09 16:54:28 -0700404 }
405
406 private BrowserBookmarksAdapter getChildAdapter(int groupPosition) {
John Reck9db95292011-06-20 13:00:12 -0700407 return mGrid.getChildAdapter(groupPosition);
John Reck71efc2b2011-05-09 16:54:28 -0700408 }
409
410 private BreadCrumbView getBreadCrumbs(int groupPosition) {
John Reck9db95292011-06-20 13:00:12 -0700411 return mGrid.getBreadCrumbs(groupPosition);
Nicolas Catania095292f2010-03-15 09:00:14 -0700412 }
413
Jeff Hamilton84029622010-08-05 14:29:28 -0500414 @Override
John Reck71efc2b2011-05-09 16:54:28 -0700415 public boolean onChildClick(ExpandableListView parent, View v,
416 int groupPosition, int childPosition, long id) {
417 BrowserBookmarksAdapter adapter = getChildAdapter(groupPosition);
418 Cursor cursor = adapter.getItem(childPosition);
John Reck608baa72010-11-24 10:32:28 -0800419 boolean isFolder = cursor.getInt(BookmarksLoader.COLUMN_INDEX_IS_FOLDER) != 0;
420 if (mCallbacks != null &&
421 mCallbacks.onBookmarkSelected(cursor, isFolder)) {
John Reck71efc2b2011-05-09 16:54:28 -0700422 return true;
Ben Murdoch328ea872009-09-16 13:33:29 +0100423 }
424
John Reck608baa72010-11-24 10:32:28 -0800425 if (isFolder) {
Michael Kolb370a4f32010-10-06 10:45:32 -0700426 String title = cursor.getString(BookmarksLoader.COLUMN_INDEX_TITLE);
Jeff Hamilton84029622010-08-05 14:29:28 -0500427 Uri uri = ContentUris.withAppendedId(
428 BrowserContract.Bookmarks.CONTENT_URI_DEFAULT_FOLDER, id);
John Reck71efc2b2011-05-09 16:54:28 -0700429 BreadCrumbView crumbs = getBreadCrumbs(groupPosition);
430 if (crumbs != null) {
Michael Kolb370a4f32010-10-06 10:45:32 -0700431 // update crumbs
John Reck71efc2b2011-05-09 16:54:28 -0700432 crumbs.pushView(title, uri);
John Reckcece1fe2011-09-13 08:47:17 -0700433 crumbs.setVisibility(View.VISIBLE);
Michael Kolb370a4f32010-10-06 10:45:32 -0700434 }
John Reck71efc2b2011-05-09 16:54:28 -0700435 loadFolder(groupPosition, uri);
Leon Scrogginsa5d669e2009-08-05 14:07:58 -0400436 }
John Reck71efc2b2011-05-09 16:54:28 -0700437 return true;
The Android Open Source Project0c908882009-03-03 19:32:16 -0800438 }
439
John Reck608baa72010-11-24 10:32:28 -0800440 /* package */ static Intent createShortcutIntent(Context context, Cursor cursor) {
Jeff Hamilton84029622010-08-05 14:29:28 -0500441 String url = cursor.getString(BookmarksLoader.COLUMN_INDEX_URL);
Michael Kolbccaa8962010-10-20 17:03:10 -0700442 String title = cursor.getString(BookmarksLoader.COLUMN_INDEX_TITLE);
Jeff Hamilton84029622010-08-05 14:29:28 -0500443 Bitmap touchIcon = getBitmap(cursor, BookmarksLoader.COLUMN_INDEX_TOUCH_ICON);
444 Bitmap favicon = getBitmap(cursor, BookmarksLoader.COLUMN_INDEX_FAVICON);
John Reck608baa72010-11-24 10:32:28 -0800445 return BookmarkUtils.createAddToHomeIntent(context, url, title, touchIcon, favicon);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800446 }
447
John Reck71efc2b2011-05-09 16:54:28 -0700448 private void loadUrl(BrowserBookmarksAdapter adapter, int position) {
449 if (mCallbacks != null && adapter != null) {
450 mCallbacks.onBookmarkSelected(adapter.getItem(position), false);
John Reck608baa72010-11-24 10:32:28 -0800451 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800452 }
453
John Reck71efc2b2011-05-09 16:54:28 -0700454 private void openInNewWindow(BrowserBookmarksAdapter adapter, int position) {
John Reck608baa72010-11-24 10:32:28 -0800455 if (mCallbacks != null) {
John Reck71efc2b2011-05-09 16:54:28 -0700456 Cursor c = adapter.getItem(position);
John Reck1b3c66e2011-01-11 13:20:32 -0800457 boolean isFolder = c.getInt(BookmarksLoader.COLUMN_INDEX_IS_FOLDER) == 1;
458 if (isFolder) {
459 long id = c.getLong(BookmarksLoader.COLUMN_INDEX_ID);
460 new OpenAllInTabsTask(id).execute();
461 } else {
John Reckd3e4d5b2011-07-13 15:48:43 -0700462 mCallbacks.onOpenInNewWindow(BrowserBookmarksPage.getUrl(c));
John Reck1b3c66e2011-01-11 13:20:32 -0800463 }
John Reck608baa72010-11-24 10:32:28 -0800464 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800465 }
Nicolas Catania095292f2010-03-15 09:00:14 -0700466
John Reck1b3c66e2011-01-11 13:20:32 -0800467 class OpenAllInTabsTask extends AsyncTask<Void, Void, Cursor> {
468 long mFolderId;
469 public OpenAllInTabsTask(long id) {
470 mFolderId = id;
471 }
472
473 @Override
474 protected Cursor doInBackground(Void... params) {
475 Context c = getActivity();
476 if (c == null) return null;
477 return c.getContentResolver().query(BookmarkUtils.getBookmarksUri(c),
478 BookmarksLoader.PROJECTION, BrowserContract.Bookmarks.PARENT + "=?",
479 new String[] { Long.toString(mFolderId) }, null);
480 }
481
482 @Override
483 protected void onPostExecute(Cursor result) {
John Reckd3e4d5b2011-07-13 15:48:43 -0700484 if (mCallbacks != null && result.getCount() > 0) {
485 String[] urls = new String[result.getCount()];
486 int i = 0;
John Reck1b3c66e2011-01-11 13:20:32 -0800487 while (result.moveToNext()) {
John Reckd3e4d5b2011-07-13 15:48:43 -0700488 urls[i++] = BrowserBookmarksPage.getUrl(result);
John Reck1b3c66e2011-01-11 13:20:32 -0800489 }
John Reckd3e4d5b2011-07-13 15:48:43 -0700490 mCallbacks.onOpenInNewWindow(urls);
John Reck1b3c66e2011-01-11 13:20:32 -0800491 }
492 }
493
494 }
495
John Reck71efc2b2011-05-09 16:54:28 -0700496 private void editBookmark(BrowserBookmarksAdapter adapter, int position) {
Jeff Hamilton64144e42010-08-18 16:31:59 -0500497 Intent intent = new Intent(getActivity(), AddBookmarkPage.class);
John Reck71efc2b2011-05-09 16:54:28 -0700498 Cursor cursor = adapter.getItem(position);
Jeff Hamilton84029622010-08-05 14:29:28 -0500499 Bundle item = new Bundle();
Michael Kolbbef7a752010-08-25 17:11:35 -0700500 item.putString(BrowserContract.Bookmarks.TITLE,
Jeff Hamilton84029622010-08-05 14:29:28 -0500501 cursor.getString(BookmarksLoader.COLUMN_INDEX_TITLE));
Michael Kolbbef7a752010-08-25 17:11:35 -0700502 item.putString(BrowserContract.Bookmarks.URL,
Jeff Hamilton84029622010-08-05 14:29:28 -0500503 cursor.getString(BookmarksLoader.COLUMN_INDEX_URL));
504 byte[] data = cursor.getBlob(BookmarksLoader.COLUMN_INDEX_FAVICON);
505 if (data != null) {
Michael Kolbbef7a752010-08-25 17:11:35 -0700506 item.putParcelable(BrowserContract.Bookmarks.FAVICON,
Jeff Hamilton84029622010-08-05 14:29:28 -0500507 BitmapFactory.decodeByteArray(data, 0, data.length));
508 }
John Reckc8490812010-11-22 14:15:36 -0800509 item.putLong(BrowserContract.Bookmarks._ID,
510 cursor.getLong(BookmarksLoader.COLUMN_INDEX_ID));
Leon Scroggins III052ce662010-09-13 14:44:16 -0400511 item.putLong(BrowserContract.Bookmarks.PARENT,
512 cursor.getLong(BookmarksLoader.COLUMN_INDEX_PARENT));
John Reckc8490812010-11-22 14:15:36 -0800513 intent.putExtra(AddBookmarkPage.EXTRA_EDIT_BOOKMARK, item);
514 intent.putExtra(AddBookmarkPage.EXTRA_IS_FOLDER,
515 cursor.getInt(BookmarksLoader.COLUMN_INDEX_IS_FOLDER) == 1);
516 startActivity(intent);
Jeff Hamilton84029622010-08-05 14:29:28 -0500517 }
518
John Reck71efc2b2011-05-09 16:54:28 -0700519 private void displayRemoveBookmarkDialog(BrowserBookmarksAdapter adapter,
520 int position) {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800521 // Put up a dialog asking if the user really wants to
522 // delete the bookmark
John Reck71efc2b2011-05-09 16:54:28 -0700523 Cursor cursor = adapter.getItem(position);
Leon Scroggins75630672011-01-13 17:56:15 -0500524 long id = cursor.getLong(BookmarksLoader.COLUMN_INDEX_ID);
525 String title = cursor.getString(BookmarksLoader.COLUMN_INDEX_TITLE);
Jeff Hamilton64144e42010-08-18 16:31:59 -0500526 Context context = getActivity();
Leon Scroggins75630672011-01-13 17:56:15 -0500527 BookmarkUtils.displayRemoveBookmarkDialog(id, title, context, null);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800528 }
529
John Reck71efc2b2011-05-09 16:54:28 -0700530 private String getUrl(BrowserBookmarksAdapter adapter, int position) {
531 return getUrl(adapter.getItem(position));
John Reck608baa72010-11-24 10:32:28 -0800532 }
533
534 /* package */ static String getUrl(Cursor c) {
535 return c.getString(BookmarksLoader.COLUMN_INDEX_URL);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800536 }
Michael Kolb801ecb72010-08-25 12:57:38 -0700537
The Android Open Source Project0c908882009-03-03 19:32:16 -0800538 private void copy(CharSequence text) {
Jeff Hamilton64144e42010-08-18 16:31:59 -0500539 ClipboardManager cm = (ClipboardManager) getActivity().getSystemService(
540 Context.CLIPBOARD_SERVICE);
Dianne Hackbornbf1d10a2011-01-17 12:29:51 -0800541 cm.setPrimaryClip(ClipData.newRawUri(null, Uri.parse(text.toString())));
The Android Open Source Project0c908882009-03-03 19:32:16 -0800542 }
Michael Kolb370a4f32010-10-06 10:45:32 -0700543
John Reckb3417f02011-01-14 11:01:05 -0800544 @Override
John Reckb3417f02011-01-14 11:01:05 -0800545 public void onConfigurationChanged(Configuration newConfig) {
546 super.onConfigurationChanged(newConfig);
547 Resources res = getActivity().getResources();
John Reck71efc2b2011-05-09 16:54:28 -0700548 mGrid.setColumnWidthFromLayout(R.layout.bookmark_thumbnail);
John Reck0ce8a942011-01-14 19:57:09 -0800549 int paddingTop = (int) res.getDimension(R.dimen.combo_paddingTop);
John Reckf3828cd2011-06-14 17:21:55 -0700550 mRoot.setPadding(0, paddingTop, 0, 0);
John Reck9fdd9812011-05-17 14:16:38 -0700551 getActivity().invalidateOptionsMenu();
John Reckb3417f02011-01-14 11:01:05 -0800552 }
553
John Reck608baa72010-11-24 10:32:28 -0800554 /**
555 * BreadCrumb controller callback
556 */
557 @Override
John Reck71efc2b2011-05-09 16:54:28 -0700558 public void onTop(BreadCrumbView view, int level, Object data) {
559 int groupPosition = (Integer) view.getTag(R.id.group_position);
John Reck89f73c12010-12-01 10:10:14 -0800560 Uri uri = (Uri) data;
561 if (uri == null) {
562 // top level
563 uri = BrowserContract.Bookmarks.CONTENT_URI_DEFAULT_FOLDER;
564 }
John Reck71efc2b2011-05-09 16:54:28 -0700565 loadFolder(groupPosition, uri);
John Reckcece1fe2011-09-13 08:47:17 -0700566 if (level <= 1) {
567 view.setVisibility(View.GONE);
568 } else {
569 view.setVisibility(View.VISIBLE);
570 }
John Reck89f73c12010-12-01 10:10:14 -0800571 }
572
573 /**
574 * @param uri
575 */
John Reck71efc2b2011-05-09 16:54:28 -0700576 private void loadFolder(int groupPosition, Uri uri) {
John Reck89f73c12010-12-01 10:10:14 -0800577 LoaderManager manager = getLoaderManager();
John Reck71efc2b2011-05-09 16:54:28 -0700578 // This assumes groups are ordered the same as loaders
579 BookmarksLoader loader = (BookmarksLoader) ((Loader<?>)
580 manager.getLoader(LOADER_BOOKMARKS + groupPosition));
John Reck89f73c12010-12-01 10:10:14 -0800581 loader.setUri(uri);
582 loader.forceLoad();
John Reck608baa72010-11-24 10:32:28 -0800583 }
584
John Reck608baa72010-11-24 10:32:28 -0800585 public void setCallbackListener(BookmarksPageCallbacks callbackListener) {
586 mCallbacks = callbackListener;
587 }
588
589 public void setEnableContextMenu(boolean enable) {
590 mEnableContextMenu = enable;
591 if (mGrid != null) {
592 if (mEnableContextMenu) {
593 registerForContextMenu(mGrid);
594 } else {
595 unregisterForContextMenu(mGrid);
596 mGrid.setLongClickable(false);
597 }
598 }
John Reck608baa72010-11-24 10:32:28 -0800599 }
John Reckc8490812010-11-22 14:15:36 -0800600
John Reck66302e52011-05-18 17:12:33 -0700601 private BookmarkDragController mDragController = new BookmarkDragController() {
602
603 @Override
604 public boolean startDrag(Cursor item) {
605 return canEdit(item);
606 }
John Recke7c97de2011-05-27 14:42:43 -0700607
608 @Override
609 public ViewGroup getActionModeView(ActionMode mode,
610 BookmarkDragState state) {
611 LayoutInflater inflater = LayoutInflater.from(getActivity());
612 LinearLayout view = (LinearLayout) inflater.inflate(
613 R.layout.bookmarks_drag_actionmode, null);
614 view.setShowDividers(LinearLayout.SHOW_DIVIDER_MIDDLE);
615 ExtraDragState extraState = (ExtraDragState) state.extraState;
616 BrowserBookmarksAdapter adapter = getChildAdapter(extraState.groupPosition);
617 Cursor c = adapter.getItem(extraState.childPosition);
618 boolean isFolder = c.getInt(BookmarksLoader.COLUMN_INDEX_IS_FOLDER) != 0;
619 if (isFolder) {
620 view.findViewById(R.id.open_context_menu_id).setVisibility(View.GONE);
621 ImageView iv = (ImageView) view.findViewById(
622 R.id.new_window_context_menu_id);
623 iv.setImageResource(R.drawable.ic_windows_holo_dark);
624 }
625 return view;
626 }
627
628 @Override
629 public void actionItemClicked(View v, BookmarkDragState state) {
630 if (v.getId() == R.id.info) {
John Reck9db95292011-06-20 13:00:12 -0700631 mGrid.showContextMenuForState(state);
John Recke7c97de2011-05-27 14:42:43 -0700632 } else {
633 ExtraDragState extraState = (ExtraDragState) state.extraState;
634 handleContextItem(v.getId(), extraState.groupPosition,
635 extraState.childPosition);
636 }
637 }
John Reck66302e52011-05-18 17:12:33 -0700638 };
639
John Reckc8490812010-11-22 14:15:36 -0800640 private static class LookupBookmarkCount extends AsyncTask<Long, Void, Integer> {
641 Context mContext;
642 BookmarkItem mHeader;
643
644 public LookupBookmarkCount(Context context, BookmarkItem header) {
Ben Murdoch914c5592011-08-01 13:58:47 +0100645 mContext = context.getApplicationContext();
John Reckc8490812010-11-22 14:15:36 -0800646 mHeader = header;
647 }
648
649 @Override
650 protected Integer doInBackground(Long... params) {
651 if (params.length != 1) {
652 throw new IllegalArgumentException("Missing folder id!");
653 }
654 Uri uri = BookmarkUtils.getBookmarksUri(mContext);
655 Cursor c = mContext.getContentResolver().query(uri,
656 null, BrowserContract.Bookmarks.PARENT + "=?",
657 new String[] {params[0].toString()}, null);
658 return c.getCount();
659 }
660
661 @Override
662 protected void onPostExecute(Integer result) {
663 if (result > 0) {
664 mHeader.setUrl(mContext.getString(R.string.contextheader_folder_bookmarkcount,
665 result));
666 } else if (result == 0) {
667 mHeader.setUrl(mContext.getString(R.string.contextheader_folder_empty));
668 }
669 }
670 }
John Reck89f73c12010-12-01 10:10:14 -0800671
John Reck71efc2b2011-05-09 16:54:28 -0700672 static class AccountsLoader extends CursorLoader {
John Reck89f73c12010-12-01 10:10:14 -0800673
John Reck71efc2b2011-05-09 16:54:28 -0700674 static String[] ACCOUNTS_PROJECTION = new String[] {
675 Accounts.ACCOUNT_NAME,
676 Accounts.ACCOUNT_TYPE
677 };
John Reck89f73c12010-12-01 10:10:14 -0800678
John Reck71efc2b2011-05-09 16:54:28 -0700679 public AccountsLoader(Context context) {
John Reck1e9815d2011-08-08 17:45:05 -0700680 super(context, Accounts.CONTENT_URI
681 .buildUpon()
682 .appendQueryParameter(BrowserProvider2.PARAM_ALLOW_EMPTY_ACCOUNTS, "false")
683 .build(),
684 ACCOUNTS_PROJECTION, null, null, null);
John Reck89f73c12010-12-01 10:10:14 -0800685 }
John Reck522a4382011-01-26 13:23:47 -0800686
John Reck522a4382011-01-26 13:23:47 -0800687 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800688}