blob: b11162c7129a2a7fc76fcbb4c9d0bc3d8831589b [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;
John Reckf94abcf2011-10-10 15:33:48 -070035import android.graphics.BitmapFactory.Options;
Jeff Hamilton84029622010-08-05 14:29:28 -050036import android.net.Uri;
John Reckc8490812010-11-22 14:15:36 -080037import android.os.AsyncTask;
The Android Open Source Project0c908882009-03-03 19:32:16 -080038import android.os.Bundle;
Jeff Hamilton69bd7072010-08-17 12:38:22 -050039import android.provider.BrowserContract;
John Reck71efc2b2011-05-09 16:54:28 -070040import android.provider.BrowserContract.Accounts;
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;
Jeff Hamilton84029622010-08-05 14:29:28 -050050import android.widget.Toast;
The Android Open Source Project0c908882009-03-03 19:32:16 -080051
John Reck1e9815d2011-08-08 17:45:05 -070052import com.android.browser.provider.BrowserProvider2;
John Reck9db95292011-06-20 13:00:12 -070053import com.android.browser.view.BookmarkExpandableView;
54import com.android.browser.view.BookmarkExpandableView.BookmarkContextMenuInfo;
John Recke7c97de2011-05-27 14:42:43 -070055
John Reck4f61ee42011-08-22 16:31:14 -070056import org.json.JSONException;
57import org.json.JSONObject;
58
John Reck71efc2b2011-05-09 16:54:28 -070059import java.util.HashMap;
60
John Reck608baa72010-11-24 10:32:28 -080061interface BookmarksPageCallbacks {
62 // Return true if handled
63 boolean onBookmarkSelected(Cursor c, boolean isFolder);
64 // Return true if handled
John Reckd3e4d5b2011-07-13 15:48:43 -070065 boolean onOpenInNewWindow(String... urls);
John Reck608baa72010-11-24 10:32:28 -080066}
67
The Android Open Source Project0c908882009-03-03 19:32:16 -080068/**
69 * View showing the user's bookmarks in the browser.
70 */
Jeff Hamilton64144e42010-08-18 16:31:59 -050071public class BrowserBookmarksPage extends Fragment implements View.OnCreateContextMenuListener,
John Reck9db95292011-06-20 13:00:12 -070072 LoaderManager.LoaderCallbacks<Cursor>, BreadCrumbView.Controller,
John Reckd3e4d5b2011-07-13 15:48:43 -070073 OnChildClickListener {
The Android Open Source Project0c908882009-03-03 19:32:16 -080074
John Recke7c97de2011-05-27 14:42:43 -070075 public static class ExtraDragState {
76 public int childPosition;
77 public int groupPosition;
78 }
79
Jeff Hamilton84029622010-08-05 14:29:28 -050080 static final String LOGTAG = "browser";
Nicolas Catania095292f2010-03-15 09:00:14 -070081
John Reck71efc2b2011-05-09 16:54:28 -070082 static final int LOADER_ACCOUNTS = 1;
83 static final int LOADER_BOOKMARKS = 100;
Jeff Hamilton84029622010-08-05 14:29:28 -050084
Michael Kolb801ecb72010-08-25 12:57:38 -070085 static final String EXTRA_DISABLE_WINDOW = "disable_new_window";
John Reck4f61ee42011-08-22 16:31:14 -070086 static final String PREF_GROUP_STATE = "bbp_group_state";
Michael Kolb801ecb72010-08-25 12:57:38 -070087
John Reck71efc2b2011-05-09 16:54:28 -070088 static final String ACCOUNT_TYPE = "account_type";
89 static final String ACCOUNT_NAME = "account_name";
90
John Reck608baa72010-11-24 10:32:28 -080091 BookmarksPageCallbacks mCallbacks;
John Reck0ce8a942011-01-14 19:57:09 -080092 View mRoot;
John Reck9db95292011-06-20 13:00:12 -070093 BookmarkExpandableView mGrid;
Jeff Hamilton84029622010-08-05 14:29:28 -050094 boolean mDisableNewWindow;
John Reck608baa72010-11-24 10:32:28 -080095 boolean mEnableContextMenu = true;
Jeff Hamilton84029622010-08-05 14:29:28 -050096 View mEmptyView;
John Reck608baa72010-11-24 10:32:28 -080097 View mHeader;
John Reck71efc2b2011-05-09 16:54:28 -070098 HashMap<Integer, BrowserBookmarksAdapter> mBookmarkAdapters = new HashMap<Integer, BrowserBookmarksAdapter>();
John Reck4f61ee42011-08-22 16:31:14 -070099 JSONObject mState;
Michael Kolb370a4f32010-10-06 10:45:32 -0700100
Jeff Hamilton84029622010-08-05 14:29:28 -0500101 @Override
102 public Loader<Cursor> onCreateLoader(int id, Bundle args) {
John Reck71efc2b2011-05-09 16:54:28 -0700103 if (id == LOADER_ACCOUNTS) {
104 return new AccountsLoader(getActivity());
105 } else if (id >= LOADER_BOOKMARKS) {
106 String accountType = args.getString(ACCOUNT_TYPE);
107 String accountName = args.getString(ACCOUNT_NAME);
108 BookmarksLoader bl = new BookmarksLoader(getActivity(),
109 accountType, accountName);
110 return bl;
111 } else {
112 throw new UnsupportedOperationException("Unknown loader id " + id);
Jeff Hamilton84029622010-08-05 14:29:28 -0500113 }
Jeff Hamilton84029622010-08-05 14:29:28 -0500114 }
115
116 @Override
Jeff Hamiltondb90aa82010-09-16 03:38:04 -0500117 public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
John Reck71efc2b2011-05-09 16:54:28 -0700118 if (loader.getId() == LOADER_ACCOUNTS) {
119 LoaderManager lm = getLoaderManager();
120 int id = LOADER_BOOKMARKS;
121 while (cursor.moveToNext()) {
122 String accountName = cursor.getString(0);
123 String accountType = cursor.getString(1);
124 Bundle args = new Bundle();
125 args.putString(ACCOUNT_NAME, accountName);
126 args.putString(ACCOUNT_TYPE, accountType);
127 BrowserBookmarksAdapter adapter = new BrowserBookmarksAdapter(
John Reckaf991f92012-04-24 15:31:16 -0700128 getActivity());
John Reck71efc2b2011-05-09 16:54:28 -0700129 mBookmarkAdapters.put(id, adapter);
John Reck4f61ee42011-08-22 16:31:14 -0700130 boolean expand = true;
131 try {
132 expand = mState.getBoolean(accountName != null ? accountName
133 : BookmarkExpandableView.LOCAL_ACCOUNT_NAME);
134 } catch (JSONException e) {} // no state for accountName
135 mGrid.addAccount(accountName, adapter, expand);
John Reck71efc2b2011-05-09 16:54:28 -0700136 lm.restartLoader(id, args, this);
137 id++;
Jeff Hamiltondb90aa82010-09-16 03:38:04 -0500138 }
John Reck71efc2b2011-05-09 16:54:28 -0700139 // TODO: Figure out what a reload of these means
140 // Currently, a reload is triggered whenever bookmarks change
141 // This is less than ideal
142 // It also causes UI flickering as a new adapter is created
143 // instead of re-using an existing one when the account_name is the
144 // same.
145 // For now, this is a one-shot load
146 getLoaderManager().destroyLoader(LOADER_ACCOUNTS);
147 } else if (loader.getId() >= LOADER_BOOKMARKS) {
148 BrowserBookmarksAdapter adapter = mBookmarkAdapters.get(loader.getId());
149 adapter.changeCursor(cursor);
Jeff Hamiltondb90aa82010-09-16 03:38:04 -0500150 }
Jeff Hamilton84029622010-08-05 14:29:28 -0500151 }
152
John Reck522a4382011-01-26 13:23:47 -0800153 @Override
Dianne Hackborn39772c82010-12-16 00:43:54 -0800154 public void onLoaderReset(Loader<Cursor> loader) {
John Reck3ad8d5c2012-04-23 14:50:09 -0700155 if (loader.getId() >= LOADER_BOOKMARKS) {
156 BrowserBookmarksAdapter adapter = mBookmarkAdapters.get(loader.getId());
157 adapter.changeCursor(null);
158 }
Dianne Hackborn39772c82010-12-16 00:43:54 -0800159 }
160
The Android Open Source Project0c908882009-03-03 19:32:16 -0800161 @Override
162 public boolean onContextItemSelected(MenuItem item) {
John Reck54ed3aa2011-08-19 15:16:05 -0700163 if (!(item.getMenuInfo() instanceof BookmarkContextMenuInfo)) {
164 return false;
165 }
166 BookmarkContextMenuInfo i = (BookmarkContextMenuInfo) item.getMenuInfo();
The Android Open Source Project0c908882009-03-03 19:32:16 -0800167 // If we have no menu info, we can't tell which item was selected.
168 if (i == null) {
Leon Scrogginsc8cae082010-11-03 18:51:46 -0400169 return false;
The Android Open Source Project0c908882009-03-03 19:32:16 -0800170 }
Nicolas Catania095292f2010-03-15 09:00:14 -0700171
John Recke7c97de2011-05-27 14:42:43 -0700172 if (handleContextItem(item.getItemId(), i.groupPosition, i.childPosition)) {
173 return true;
174 }
175 return super.onContextItemSelected(item);
176 }
177
178 public boolean handleContextItem(int itemId, int groupPosition,
179 int childPosition) {
180 final Activity activity = getActivity();
181 BrowserBookmarksAdapter adapter = getChildAdapter(groupPosition);
182
183 switch (itemId) {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800184 case R.id.open_context_menu_id:
John Recke7c97de2011-05-27 14:42:43 -0700185 loadUrl(adapter, childPosition);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800186 break;
187 case R.id.edit_context_menu_id:
John Recke7c97de2011-05-27 14:42:43 -0700188 editBookmark(adapter, childPosition);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800189 break;
190 case R.id.shortcut_context_menu_id:
John Recke7c97de2011-05-27 14:42:43 -0700191 Cursor c = adapter.getItem(childPosition);
John Reck608baa72010-11-24 10:32:28 -0800192 activity.sendBroadcast(createShortcutIntent(getActivity(), c));
The Android Open Source Project0c908882009-03-03 19:32:16 -0800193 break;
194 case R.id.delete_context_menu_id:
John Recke7c97de2011-05-27 14:42:43 -0700195 displayRemoveBookmarkDialog(adapter, childPosition);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800196 break;
197 case R.id.new_window_context_menu_id:
John Recke7c97de2011-05-27 14:42:43 -0700198 openInNewWindow(adapter, childPosition);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800199 break;
Jeff Hamilton84029622010-08-05 14:29:28 -0500200 case R.id.share_link_context_menu_id: {
John Recke7c97de2011-05-27 14:42:43 -0700201 Cursor cursor = adapter.getItem(childPosition);
Michael Kolb8233fac2010-10-26 16:08:53 -0700202 Controller.sharePage(activity,
Jeff Hamilton84029622010-08-05 14:29:28 -0500203 cursor.getString(BookmarksLoader.COLUMN_INDEX_TITLE),
204 cursor.getString(BookmarksLoader.COLUMN_INDEX_URL),
205 getBitmap(cursor, BookmarksLoader.COLUMN_INDEX_FAVICON),
206 getBitmap(cursor, BookmarksLoader.COLUMN_INDEX_THUMBNAIL));
The Android Open Source Project0c908882009-03-03 19:32:16 -0800207 break;
Jeff Hamilton84029622010-08-05 14:29:28 -0500208 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800209 case R.id.copy_url_context_menu_id:
John Recke7c97de2011-05-27 14:42:43 -0700210 copy(getUrl(adapter, childPosition));
Leon Scrogginsfeb941d2009-05-28 17:27:38 -0400211 break;
Jeff Hamilton84029622010-08-05 14:29:28 -0500212 case R.id.homepage_context_menu_id: {
John Recke7c97de2011-05-27 14:42:43 -0700213 BrowserSettings.getInstance().setHomePage(getUrl(adapter, childPosition));
Jeff Hamilton64144e42010-08-18 16:31:59 -0500214 Toast.makeText(activity, R.string.homepage_set, Toast.LENGTH_LONG).show();
Leon Scrogginsfeb941d2009-05-28 17:27:38 -0400215 break;
Jeff Hamilton84029622010-08-05 14:29:28 -0500216 }
Leon Scrogginsa5d669e2009-08-05 14:07:58 -0400217 // Only for the Most visited page
Jeff Hamilton84029622010-08-05 14:29:28 -0500218 case R.id.save_to_bookmarks_menu_id: {
John Recke7c97de2011-05-27 14:42:43 -0700219 Cursor cursor = adapter.getItem(childPosition);
Jeff Hamilton84029622010-08-05 14:29:28 -0500220 String name = cursor.getString(BookmarksLoader.COLUMN_INDEX_TITLE);
221 String url = cursor.getString(BookmarksLoader.COLUMN_INDEX_URL);
Leon Scrogginsa5d669e2009-08-05 14:07:58 -0400222 // If the site is bookmarked, the item becomes remove from
223 // bookmarks.
Jeff Hamilton64144e42010-08-18 16:31:59 -0500224 Bookmarks.removeFromBookmarks(activity, activity.getContentResolver(), url, name);
Leon Scrogginsa5d669e2009-08-05 14:07:58 -0400225 break;
Jeff Hamilton84029622010-08-05 14:29:28 -0500226 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800227 default:
John Recke7c97de2011-05-27 14:42:43 -0700228 return false;
The Android Open Source Project0c908882009-03-03 19:32:16 -0800229 }
230 return true;
231 }
232
John Reck608baa72010-11-24 10:32:28 -0800233 static Bitmap getBitmap(Cursor cursor, int columnIndex) {
John Reckf94abcf2011-10-10 15:33:48 -0700234 return getBitmap(cursor, columnIndex, null);
235 }
236
237 static ThreadLocal<Options> sOptions = new ThreadLocal<Options>() {
238 @Override
239 protected Options initialValue() {
240 return new Options();
241 };
242 };
243 static Bitmap getBitmap(Cursor cursor, int columnIndex, Bitmap inBitmap) {
Jeff Hamilton84029622010-08-05 14:29:28 -0500244 byte[] data = cursor.getBlob(columnIndex);
245 if (data == null) {
246 return null;
The Android Open Source Project0c908882009-03-03 19:32:16 -0800247 }
John Reckf94abcf2011-10-10 15:33:48 -0700248 Options opts = sOptions.get();
249 opts.inBitmap = inBitmap;
250 opts.inSampleSize = 1;
251 opts.inScaled = false;
252 try {
253 return BitmapFactory.decodeByteArray(data, 0, data.length, opts);
254 } catch (IllegalArgumentException ex) {
255 // Failed to re-use bitmap, create a new one
256 return BitmapFactory.decodeByteArray(data, 0, data.length);
257 }
Jeff Hamilton84029622010-08-05 14:29:28 -0500258 }
259
John Reckc8490812010-11-22 14:15:36 -0800260 private MenuItem.OnMenuItemClickListener mContextItemClickListener =
261 new MenuItem.OnMenuItemClickListener() {
262 @Override
263 public boolean onMenuItemClick(MenuItem item) {
264 return onContextItemSelected(item);
265 }
266 };
267
Jeff Hamilton84029622010-08-05 14:29:28 -0500268 @Override
269 public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
John Reck71efc2b2011-05-09 16:54:28 -0700270 BookmarkContextMenuInfo info = (BookmarkContextMenuInfo) menuInfo;
271 BrowserBookmarksAdapter adapter = getChildAdapter(info.groupPosition);
272 Cursor cursor = adapter.getItem(info.childPosition);
John Reck1cebb442010-12-10 12:02:16 -0800273 if (!canEdit(cursor)) {
274 return;
275 }
Leon Scroggins5e8a40f2010-09-27 17:13:35 -0400276 boolean isFolder
277 = cursor.getInt(BookmarksLoader.COLUMN_INDEX_IS_FOLDER) != 0;
Michael Kolb801ecb72010-08-25 12:57:38 -0700278
Jeff Hamilton64144e42010-08-18 16:31:59 -0500279 final Activity activity = getActivity();
280 MenuInflater inflater = activity.getMenuInflater();
Jeff Hamilton84029622010-08-05 14:29:28 -0500281 inflater.inflate(R.menu.bookmarkscontext, menu);
John Reckc8490812010-11-22 14:15:36 -0800282 if (isFolder) {
283 menu.setGroupVisible(R.id.FOLDER_CONTEXT_MENU, true);
284 } else {
285 menu.setGroupVisible(R.id.BOOKMARK_CONTEXT_MENU, true);
286 if (mDisableNewWindow) {
287 menu.findItem(R.id.new_window_context_menu_id).setVisible(false);
288 }
Jeff Hamilton84029622010-08-05 14:29:28 -0500289 }
John Reckc8490812010-11-22 14:15:36 -0800290 BookmarkItem header = new BookmarkItem(activity);
John Reck83c01512011-09-09 11:14:16 -0700291 header.setEnableScrolling(true);
John Reckc8490812010-11-22 14:15:36 -0800292 populateBookmarkItem(cursor, header, isFolder);
John Reckc8490812010-11-22 14:15:36 -0800293 menu.setHeaderView(header);
Jeff Hamilton84029622010-08-05 14:29:28 -0500294
John Reckc8490812010-11-22 14:15:36 -0800295 int count = menu.size();
296 for (int i = 0; i < count; i++) {
297 menu.getItem(i).setOnMenuItemClickListener(mContextItemClickListener);
Jeff Hamilton84029622010-08-05 14:29:28 -0500298 }
Jeff Hamilton84029622010-08-05 14:29:28 -0500299 }
300
John Reck1cebb442010-12-10 12:02:16 -0800301 boolean canEdit(Cursor c) {
John Reckd18ac4b2012-04-12 17:27:34 -0700302 int type = c.getInt(BookmarksLoader.COLUMN_INDEX_TYPE);
303 return type == BrowserContract.Bookmarks.BOOKMARK_TYPE_BOOKMARK
304 || type == BrowserContract.Bookmarks.BOOKMARK_TYPE_FOLDER;
John Reck1cebb442010-12-10 12:02:16 -0800305 }
306
John Reckc8490812010-11-22 14:15:36 -0800307 private void populateBookmarkItem(Cursor cursor, BookmarkItem item, boolean isFolder) {
Jeff Hamilton84029622010-08-05 14:29:28 -0500308 item.setName(cursor.getString(BookmarksLoader.COLUMN_INDEX_TITLE));
John Reckc8490812010-11-22 14:15:36 -0800309 if (isFolder) {
310 item.setUrl(null);
311 Bitmap bitmap =
Michael Kolb5a72f182011-01-13 20:35:06 -0800312 BitmapFactory.decodeResource(getResources(), R.drawable.ic_folder_holo_dark);
John Reckc8490812010-11-22 14:15:36 -0800313 item.setFavicon(bitmap);
John Reck60e33362010-12-02 17:52:38 -0800314 new LookupBookmarkCount(getActivity(), item)
315 .execute(cursor.getLong(BookmarksLoader.COLUMN_INDEX_ID));
John Reckc8490812010-11-22 14:15:36 -0800316 } else {
317 String url = cursor.getString(BookmarksLoader.COLUMN_INDEX_URL);
318 item.setUrl(url);
319 Bitmap bitmap = getBitmap(cursor, BookmarksLoader.COLUMN_INDEX_FAVICON);
John Reckc8490812010-11-22 14:15:36 -0800320 item.setFavicon(bitmap);
Jeff Hamilton84029622010-08-05 14:29:28 -0500321 }
Jeff Hamilton84029622010-08-05 14:29:28 -0500322 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800323
324 /**
325 * Create a new BrowserBookmarksPage.
Nicolas Catania095292f2010-03-15 09:00:14 -0700326 */
The Android Open Source Project0c908882009-03-03 19:32:16 -0800327 @Override
Jeff Hamilton64144e42010-08-18 16:31:59 -0500328 public void onCreate(Bundle icicle) {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800329 super.onCreate(icicle);
John Reck4f61ee42011-08-22 16:31:14 -0700330 SharedPreferences prefs = BrowserSettings.getInstance().getPreferences();
331 try {
332 mState = new JSONObject(prefs.getString(PREF_GROUP_STATE, "{}"));
333 } catch (JSONException e) {
334 // Parse failed, clear preference and start with empty state
335 prefs.edit().remove(PREF_GROUP_STATE).apply();
336 mState = new JSONObject();
337 }
Jeff Hamilton64144e42010-08-18 16:31:59 -0500338 Bundle args = getArguments();
Leon Scrogginsbc922852010-10-22 12:15:27 -0400339 mDisableNewWindow = args == null ? false : args.getBoolean(EXTRA_DISABLE_WINDOW, false);
John Reck8f295d12011-03-18 15:25:18 -0700340 setHasOptionsMenu(true);
John Reck2d963a22011-08-10 15:53:07 -0700341 if (mCallbacks == null && getActivity() instanceof CombinedBookmarksCallbacks) {
342 mCallbacks = new CombinedBookmarksCallbackWrapper(
343 (CombinedBookmarksCallbacks) getActivity());
344 }
Jeff Hamilton64144e42010-08-18 16:31:59 -0500345 }
Leon Scrogginsd87f85e2009-08-18 14:13:31 -0400346
John Reck4f61ee42011-08-22 16:31:14 -0700347 @Override
348 public void onPause() {
349 super.onPause();
350 try {
351 mState = mGrid.saveGroupState();
352 // Save state
353 SharedPreferences prefs = BrowserSettings.getInstance().getPreferences();
354 prefs.edit()
355 .putString(PREF_GROUP_STATE, mState.toString())
356 .apply();
357 } catch (JSONException e) {
358 // Not critical, ignore
359 }
360 }
361
John Reck2d963a22011-08-10 15:53:07 -0700362 private static class CombinedBookmarksCallbackWrapper
363 implements BookmarksPageCallbacks {
364
365 private CombinedBookmarksCallbacks mCombinedCallback;
366
367 private CombinedBookmarksCallbackWrapper(CombinedBookmarksCallbacks cb) {
368 mCombinedCallback = cb;
369 }
370
371 @Override
372 public boolean onOpenInNewWindow(String... urls) {
373 mCombinedCallback.openInNewTab(urls);
374 return true;
375 }
376
377 @Override
378 public boolean onBookmarkSelected(Cursor c, boolean isFolder) {
379 if (isFolder) {
380 return false;
381 }
382 mCombinedCallback.openUrl(BrowserBookmarksPage.getUrl(c));
383 return true;
384 }
385 };
386
Jeff Hamilton64144e42010-08-18 16:31:59 -0500387 @Override
Jeff Hamilton64144e42010-08-18 16:31:59 -0500388 public View onCreateView(LayoutInflater inflater, ViewGroup container,
389 Bundle savedInstanceState) {
John Reck0ce8a942011-01-14 19:57:09 -0800390 mRoot = inflater.inflate(R.layout.bookmarks, container, false);
391 mEmptyView = mRoot.findViewById(android.R.id.empty);
Leon Scrogginsd87f85e2009-08-18 14:13:31 -0400392
John Reck9db95292011-06-20 13:00:12 -0700393 mGrid = (BookmarkExpandableView) mRoot.findViewById(R.id.grid);
John Reck71efc2b2011-05-09 16:54:28 -0700394 mGrid.setOnChildClickListener(this);
395 mGrid.setColumnWidthFromLayout(R.layout.bookmark_thumbnail);
396 mGrid.setBreadcrumbController(this);
John Reck608baa72010-11-24 10:32:28 -0800397 setEnableContextMenu(mEnableContextMenu);
398
Jeff Hamiltondb90aa82010-09-16 03:38:04 -0500399 // Start the loaders
400 LoaderManager lm = getLoaderManager();
John Reck71efc2b2011-05-09 16:54:28 -0700401 lm.restartLoader(LOADER_ACCOUNTS, null, this);
Jeff Hamilton84029622010-08-05 14:29:28 -0500402
John Reck0ce8a942011-01-14 19:57:09 -0800403 return mRoot;
Jeff Hamilton64144e42010-08-18 16:31:59 -0500404 }
Michael Kolb801ecb72010-08-25 12:57:38 -0700405
John Reck608baa72010-11-24 10:32:28 -0800406 @Override
407 public void onDestroyView() {
408 super.onDestroyView();
John Reck71efc2b2011-05-09 16:54:28 -0700409 mGrid.setBreadcrumbController(null);
John Reckc3c46d72011-09-06 16:17:34 -0700410 mGrid.clearAccounts();
John Reck71efc2b2011-05-09 16:54:28 -0700411 LoaderManager lm = getLoaderManager();
412 lm.destroyLoader(LOADER_ACCOUNTS);
413 for (int id : mBookmarkAdapters.keySet()) {
414 lm.destroyLoader(id);
John Reck608baa72010-11-24 10:32:28 -0800415 }
John Reck71efc2b2011-05-09 16:54:28 -0700416 mBookmarkAdapters.clear();
John Reck71efc2b2011-05-09 16:54:28 -0700417 }
418
419 private BrowserBookmarksAdapter getChildAdapter(int groupPosition) {
John Reck9db95292011-06-20 13:00:12 -0700420 return mGrid.getChildAdapter(groupPosition);
John Reck71efc2b2011-05-09 16:54:28 -0700421 }
422
423 private BreadCrumbView getBreadCrumbs(int groupPosition) {
John Reck9db95292011-06-20 13:00:12 -0700424 return mGrid.getBreadCrumbs(groupPosition);
Nicolas Catania095292f2010-03-15 09:00:14 -0700425 }
426
Jeff Hamilton84029622010-08-05 14:29:28 -0500427 @Override
John Reck71efc2b2011-05-09 16:54:28 -0700428 public boolean onChildClick(ExpandableListView parent, View v,
429 int groupPosition, int childPosition, long id) {
430 BrowserBookmarksAdapter adapter = getChildAdapter(groupPosition);
431 Cursor cursor = adapter.getItem(childPosition);
John Reck608baa72010-11-24 10:32:28 -0800432 boolean isFolder = cursor.getInt(BookmarksLoader.COLUMN_INDEX_IS_FOLDER) != 0;
433 if (mCallbacks != null &&
434 mCallbacks.onBookmarkSelected(cursor, isFolder)) {
John Reck71efc2b2011-05-09 16:54:28 -0700435 return true;
Ben Murdoch328ea872009-09-16 13:33:29 +0100436 }
437
John Reck608baa72010-11-24 10:32:28 -0800438 if (isFolder) {
Michael Kolb370a4f32010-10-06 10:45:32 -0700439 String title = cursor.getString(BookmarksLoader.COLUMN_INDEX_TITLE);
Jeff Hamilton84029622010-08-05 14:29:28 -0500440 Uri uri = ContentUris.withAppendedId(
441 BrowserContract.Bookmarks.CONTENT_URI_DEFAULT_FOLDER, id);
John Reck71efc2b2011-05-09 16:54:28 -0700442 BreadCrumbView crumbs = getBreadCrumbs(groupPosition);
443 if (crumbs != null) {
Michael Kolb370a4f32010-10-06 10:45:32 -0700444 // update crumbs
John Reck71efc2b2011-05-09 16:54:28 -0700445 crumbs.pushView(title, uri);
John Reckcece1fe2011-09-13 08:47:17 -0700446 crumbs.setVisibility(View.VISIBLE);
Michael Kolb370a4f32010-10-06 10:45:32 -0700447 }
John Reck71efc2b2011-05-09 16:54:28 -0700448 loadFolder(groupPosition, uri);
Leon Scrogginsa5d669e2009-08-05 14:07:58 -0400449 }
John Reck71efc2b2011-05-09 16:54:28 -0700450 return true;
The Android Open Source Project0c908882009-03-03 19:32:16 -0800451 }
452
John Reck608baa72010-11-24 10:32:28 -0800453 /* package */ static Intent createShortcutIntent(Context context, Cursor cursor) {
Jeff Hamilton84029622010-08-05 14:29:28 -0500454 String url = cursor.getString(BookmarksLoader.COLUMN_INDEX_URL);
Michael Kolbccaa8962010-10-20 17:03:10 -0700455 String title = cursor.getString(BookmarksLoader.COLUMN_INDEX_TITLE);
Jeff Hamilton84029622010-08-05 14:29:28 -0500456 Bitmap touchIcon = getBitmap(cursor, BookmarksLoader.COLUMN_INDEX_TOUCH_ICON);
457 Bitmap favicon = getBitmap(cursor, BookmarksLoader.COLUMN_INDEX_FAVICON);
John Reck608baa72010-11-24 10:32:28 -0800458 return BookmarkUtils.createAddToHomeIntent(context, url, title, touchIcon, favicon);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800459 }
460
John Reck71efc2b2011-05-09 16:54:28 -0700461 private void loadUrl(BrowserBookmarksAdapter adapter, int position) {
462 if (mCallbacks != null && adapter != null) {
463 mCallbacks.onBookmarkSelected(adapter.getItem(position), false);
John Reck608baa72010-11-24 10:32:28 -0800464 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800465 }
466
John Reck71efc2b2011-05-09 16:54:28 -0700467 private void openInNewWindow(BrowserBookmarksAdapter adapter, int position) {
John Reck608baa72010-11-24 10:32:28 -0800468 if (mCallbacks != null) {
John Reck71efc2b2011-05-09 16:54:28 -0700469 Cursor c = adapter.getItem(position);
John Reck1b3c66e2011-01-11 13:20:32 -0800470 boolean isFolder = c.getInt(BookmarksLoader.COLUMN_INDEX_IS_FOLDER) == 1;
471 if (isFolder) {
472 long id = c.getLong(BookmarksLoader.COLUMN_INDEX_ID);
473 new OpenAllInTabsTask(id).execute();
474 } else {
John Reckd3e4d5b2011-07-13 15:48:43 -0700475 mCallbacks.onOpenInNewWindow(BrowserBookmarksPage.getUrl(c));
John Reck1b3c66e2011-01-11 13:20:32 -0800476 }
John Reck608baa72010-11-24 10:32:28 -0800477 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800478 }
Nicolas Catania095292f2010-03-15 09:00:14 -0700479
John Reck1b3c66e2011-01-11 13:20:32 -0800480 class OpenAllInTabsTask extends AsyncTask<Void, Void, Cursor> {
481 long mFolderId;
482 public OpenAllInTabsTask(long id) {
483 mFolderId = id;
484 }
485
486 @Override
487 protected Cursor doInBackground(Void... params) {
488 Context c = getActivity();
489 if (c == null) return null;
490 return c.getContentResolver().query(BookmarkUtils.getBookmarksUri(c),
491 BookmarksLoader.PROJECTION, BrowserContract.Bookmarks.PARENT + "=?",
492 new String[] { Long.toString(mFolderId) }, null);
493 }
494
495 @Override
496 protected void onPostExecute(Cursor result) {
John Reckd3e4d5b2011-07-13 15:48:43 -0700497 if (mCallbacks != null && result.getCount() > 0) {
498 String[] urls = new String[result.getCount()];
499 int i = 0;
John Reck1b3c66e2011-01-11 13:20:32 -0800500 while (result.moveToNext()) {
John Reckd3e4d5b2011-07-13 15:48:43 -0700501 urls[i++] = BrowserBookmarksPage.getUrl(result);
John Reck1b3c66e2011-01-11 13:20:32 -0800502 }
John Reckd3e4d5b2011-07-13 15:48:43 -0700503 mCallbacks.onOpenInNewWindow(urls);
John Reck1b3c66e2011-01-11 13:20:32 -0800504 }
505 }
506
507 }
508
John Reck71efc2b2011-05-09 16:54:28 -0700509 private void editBookmark(BrowserBookmarksAdapter adapter, int position) {
Jeff Hamilton64144e42010-08-18 16:31:59 -0500510 Intent intent = new Intent(getActivity(), AddBookmarkPage.class);
John Reck71efc2b2011-05-09 16:54:28 -0700511 Cursor cursor = adapter.getItem(position);
Jeff Hamilton84029622010-08-05 14:29:28 -0500512 Bundle item = new Bundle();
Michael Kolbbef7a752010-08-25 17:11:35 -0700513 item.putString(BrowserContract.Bookmarks.TITLE,
Jeff Hamilton84029622010-08-05 14:29:28 -0500514 cursor.getString(BookmarksLoader.COLUMN_INDEX_TITLE));
Michael Kolbbef7a752010-08-25 17:11:35 -0700515 item.putString(BrowserContract.Bookmarks.URL,
Jeff Hamilton84029622010-08-05 14:29:28 -0500516 cursor.getString(BookmarksLoader.COLUMN_INDEX_URL));
517 byte[] data = cursor.getBlob(BookmarksLoader.COLUMN_INDEX_FAVICON);
518 if (data != null) {
Michael Kolbbef7a752010-08-25 17:11:35 -0700519 item.putParcelable(BrowserContract.Bookmarks.FAVICON,
Jeff Hamilton84029622010-08-05 14:29:28 -0500520 BitmapFactory.decodeByteArray(data, 0, data.length));
521 }
John Reckc8490812010-11-22 14:15:36 -0800522 item.putLong(BrowserContract.Bookmarks._ID,
523 cursor.getLong(BookmarksLoader.COLUMN_INDEX_ID));
Leon Scroggins III052ce662010-09-13 14:44:16 -0400524 item.putLong(BrowserContract.Bookmarks.PARENT,
525 cursor.getLong(BookmarksLoader.COLUMN_INDEX_PARENT));
John Reckc8490812010-11-22 14:15:36 -0800526 intent.putExtra(AddBookmarkPage.EXTRA_EDIT_BOOKMARK, item);
527 intent.putExtra(AddBookmarkPage.EXTRA_IS_FOLDER,
528 cursor.getInt(BookmarksLoader.COLUMN_INDEX_IS_FOLDER) == 1);
529 startActivity(intent);
Jeff Hamilton84029622010-08-05 14:29:28 -0500530 }
531
John Reck71efc2b2011-05-09 16:54:28 -0700532 private void displayRemoveBookmarkDialog(BrowserBookmarksAdapter adapter,
533 int position) {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800534 // Put up a dialog asking if the user really wants to
535 // delete the bookmark
John Reck71efc2b2011-05-09 16:54:28 -0700536 Cursor cursor = adapter.getItem(position);
Leon Scroggins75630672011-01-13 17:56:15 -0500537 long id = cursor.getLong(BookmarksLoader.COLUMN_INDEX_ID);
538 String title = cursor.getString(BookmarksLoader.COLUMN_INDEX_TITLE);
Jeff Hamilton64144e42010-08-18 16:31:59 -0500539 Context context = getActivity();
Leon Scroggins75630672011-01-13 17:56:15 -0500540 BookmarkUtils.displayRemoveBookmarkDialog(id, title, context, null);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800541 }
542
John Reck71efc2b2011-05-09 16:54:28 -0700543 private String getUrl(BrowserBookmarksAdapter adapter, int position) {
544 return getUrl(adapter.getItem(position));
John Reck608baa72010-11-24 10:32:28 -0800545 }
546
547 /* package */ static String getUrl(Cursor c) {
548 return c.getString(BookmarksLoader.COLUMN_INDEX_URL);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800549 }
Michael Kolb801ecb72010-08-25 12:57:38 -0700550
The Android Open Source Project0c908882009-03-03 19:32:16 -0800551 private void copy(CharSequence text) {
Jeff Hamilton64144e42010-08-18 16:31:59 -0500552 ClipboardManager cm = (ClipboardManager) getActivity().getSystemService(
553 Context.CLIPBOARD_SERVICE);
Dianne Hackbornbf1d10a2011-01-17 12:29:51 -0800554 cm.setPrimaryClip(ClipData.newRawUri(null, Uri.parse(text.toString())));
The Android Open Source Project0c908882009-03-03 19:32:16 -0800555 }
Michael Kolb370a4f32010-10-06 10:45:32 -0700556
John Reckb3417f02011-01-14 11:01:05 -0800557 @Override
John Reckb3417f02011-01-14 11:01:05 -0800558 public void onConfigurationChanged(Configuration newConfig) {
559 super.onConfigurationChanged(newConfig);
560 Resources res = getActivity().getResources();
John Reck71efc2b2011-05-09 16:54:28 -0700561 mGrid.setColumnWidthFromLayout(R.layout.bookmark_thumbnail);
John Reck0ce8a942011-01-14 19:57:09 -0800562 int paddingTop = (int) res.getDimension(R.dimen.combo_paddingTop);
John Reckf3828cd2011-06-14 17:21:55 -0700563 mRoot.setPadding(0, paddingTop, 0, 0);
John Reck9fdd9812011-05-17 14:16:38 -0700564 getActivity().invalidateOptionsMenu();
John Reckb3417f02011-01-14 11:01:05 -0800565 }
566
John Reck608baa72010-11-24 10:32:28 -0800567 /**
568 * BreadCrumb controller callback
569 */
570 @Override
John Reck71efc2b2011-05-09 16:54:28 -0700571 public void onTop(BreadCrumbView view, int level, Object data) {
572 int groupPosition = (Integer) view.getTag(R.id.group_position);
John Reck89f73c12010-12-01 10:10:14 -0800573 Uri uri = (Uri) data;
574 if (uri == null) {
575 // top level
576 uri = BrowserContract.Bookmarks.CONTENT_URI_DEFAULT_FOLDER;
577 }
John Reck71efc2b2011-05-09 16:54:28 -0700578 loadFolder(groupPosition, uri);
John Reckcece1fe2011-09-13 08:47:17 -0700579 if (level <= 1) {
580 view.setVisibility(View.GONE);
581 } else {
582 view.setVisibility(View.VISIBLE);
583 }
John Reck89f73c12010-12-01 10:10:14 -0800584 }
585
586 /**
587 * @param uri
588 */
John Reck71efc2b2011-05-09 16:54:28 -0700589 private void loadFolder(int groupPosition, Uri uri) {
John Reck89f73c12010-12-01 10:10:14 -0800590 LoaderManager manager = getLoaderManager();
John Reck71efc2b2011-05-09 16:54:28 -0700591 // This assumes groups are ordered the same as loaders
592 BookmarksLoader loader = (BookmarksLoader) ((Loader<?>)
593 manager.getLoader(LOADER_BOOKMARKS + groupPosition));
John Reck89f73c12010-12-01 10:10:14 -0800594 loader.setUri(uri);
595 loader.forceLoad();
John Reck608baa72010-11-24 10:32:28 -0800596 }
597
John Reck608baa72010-11-24 10:32:28 -0800598 public void setCallbackListener(BookmarksPageCallbacks callbackListener) {
599 mCallbacks = callbackListener;
600 }
601
602 public void setEnableContextMenu(boolean enable) {
603 mEnableContextMenu = enable;
604 if (mGrid != null) {
605 if (mEnableContextMenu) {
606 registerForContextMenu(mGrid);
607 } else {
608 unregisterForContextMenu(mGrid);
609 mGrid.setLongClickable(false);
610 }
611 }
John Reck608baa72010-11-24 10:32:28 -0800612 }
John Reckc8490812010-11-22 14:15:36 -0800613
614 private static class LookupBookmarkCount extends AsyncTask<Long, Void, Integer> {
615 Context mContext;
616 BookmarkItem mHeader;
617
618 public LookupBookmarkCount(Context context, BookmarkItem header) {
Ben Murdoch914c5592011-08-01 13:58:47 +0100619 mContext = context.getApplicationContext();
John Reckc8490812010-11-22 14:15:36 -0800620 mHeader = header;
621 }
622
623 @Override
624 protected Integer doInBackground(Long... params) {
625 if (params.length != 1) {
626 throw new IllegalArgumentException("Missing folder id!");
627 }
628 Uri uri = BookmarkUtils.getBookmarksUri(mContext);
629 Cursor c = mContext.getContentResolver().query(uri,
630 null, BrowserContract.Bookmarks.PARENT + "=?",
631 new String[] {params[0].toString()}, null);
632 return c.getCount();
633 }
634
635 @Override
636 protected void onPostExecute(Integer result) {
637 if (result > 0) {
638 mHeader.setUrl(mContext.getString(R.string.contextheader_folder_bookmarkcount,
639 result));
640 } else if (result == 0) {
641 mHeader.setUrl(mContext.getString(R.string.contextheader_folder_empty));
642 }
643 }
644 }
John Reck89f73c12010-12-01 10:10:14 -0800645
John Reck71efc2b2011-05-09 16:54:28 -0700646 static class AccountsLoader extends CursorLoader {
John Reck89f73c12010-12-01 10:10:14 -0800647
John Reck71efc2b2011-05-09 16:54:28 -0700648 static String[] ACCOUNTS_PROJECTION = new String[] {
649 Accounts.ACCOUNT_NAME,
650 Accounts.ACCOUNT_TYPE
651 };
John Reck89f73c12010-12-01 10:10:14 -0800652
John Reck71efc2b2011-05-09 16:54:28 -0700653 public AccountsLoader(Context context) {
John Reck1e9815d2011-08-08 17:45:05 -0700654 super(context, Accounts.CONTENT_URI
655 .buildUpon()
656 .appendQueryParameter(BrowserProvider2.PARAM_ALLOW_EMPTY_ACCOUNTS, "false")
657 .build(),
658 ACCOUNTS_PROJECTION, null, null, null);
John Reck89f73c12010-12-01 10:10:14 -0800659 }
John Reck522a4382011-01-26 13:23:47 -0800660
John Reck522a4382011-01-26 13:23:47 -0800661 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800662}