blob: a038a2d5962713b5dc109fe71f7e28359344df99 [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;
John Recke7c97de2011-05-27 14:42:43 -070041import android.view.ActionMode;
The Android Open Source Project0c908882009-03-03 19:32:16 -080042import android.view.ContextMenu;
Jeff Hamiltondb90aa82010-09-16 03:38:04 -050043import android.view.ContextMenu.ContextMenuInfo;
Jeff Hamilton64144e42010-08-18 16:31:59 -050044import android.view.LayoutInflater;
The Android Open Source Project0c908882009-03-03 19:32:16 -080045import android.view.MenuInflater;
46import android.view.MenuItem;
47import android.view.View;
Jeff Hamiltondb90aa82010-09-16 03:38:04 -050048import android.view.ViewGroup;
John Reck71efc2b2011-05-09 16:54:28 -070049import android.widget.ExpandableListView;
50import android.widget.ExpandableListView.OnChildClickListener;
John Recke7c97de2011-05-27 14:42:43 -070051import android.widget.ImageView;
52import android.widget.LinearLayout;
Jeff Hamilton84029622010-08-05 14:29:28 -050053import android.widget.Toast;
The Android Open Source Project0c908882009-03-03 19:32:16 -080054
John Recke7c97de2011-05-27 14:42:43 -070055import com.android.browser.BookmarkDragHandler.BookmarkDragController;
56import com.android.browser.BookmarkDragHandler.BookmarkDragState;
John Reck1e9815d2011-08-08 17:45:05 -070057import com.android.browser.provider.BrowserProvider2;
John Reck9db95292011-06-20 13:00:12 -070058import com.android.browser.view.BookmarkExpandableView;
59import com.android.browser.view.BookmarkExpandableView.BookmarkContextMenuInfo;
John Recke7c97de2011-05-27 14:42:43 -070060
John Reck4f61ee42011-08-22 16:31:14 -070061import org.json.JSONException;
62import org.json.JSONObject;
63
John Reck71efc2b2011-05-09 16:54:28 -070064import java.util.HashMap;
65
John Reck608baa72010-11-24 10:32:28 -080066interface BookmarksPageCallbacks {
67 // Return true if handled
68 boolean onBookmarkSelected(Cursor c, boolean isFolder);
69 // Return true if handled
John Reckd3e4d5b2011-07-13 15:48:43 -070070 boolean onOpenInNewWindow(String... urls);
John Reck608baa72010-11-24 10:32:28 -080071}
72
The Android Open Source Project0c908882009-03-03 19:32:16 -080073/**
74 * View showing the user's bookmarks in the browser.
75 */
Jeff Hamilton64144e42010-08-18 16:31:59 -050076public class BrowserBookmarksPage extends Fragment implements View.OnCreateContextMenuListener,
John Reck9db95292011-06-20 13:00:12 -070077 LoaderManager.LoaderCallbacks<Cursor>, BreadCrumbView.Controller,
John Reckd3e4d5b2011-07-13 15:48:43 -070078 OnChildClickListener {
The Android Open Source Project0c908882009-03-03 19:32:16 -080079
John Recke7c97de2011-05-27 14:42:43 -070080 public static class ExtraDragState {
81 public int childPosition;
82 public int groupPosition;
83 }
84
Jeff Hamilton84029622010-08-05 14:29:28 -050085 static final String LOGTAG = "browser";
Nicolas Catania095292f2010-03-15 09:00:14 -070086
John Reck71efc2b2011-05-09 16:54:28 -070087 static final int LOADER_ACCOUNTS = 1;
88 static final int LOADER_BOOKMARKS = 100;
Jeff Hamilton84029622010-08-05 14:29:28 -050089
Michael Kolb801ecb72010-08-25 12:57:38 -070090 static final String EXTRA_DISABLE_WINDOW = "disable_new_window";
John Reck4f61ee42011-08-22 16:31:14 -070091 static final String PREF_GROUP_STATE = "bbp_group_state";
Michael Kolb801ecb72010-08-25 12:57:38 -070092
John Reck71efc2b2011-05-09 16:54:28 -070093 static final String ACCOUNT_TYPE = "account_type";
94 static final String ACCOUNT_NAME = "account_name";
95
John Reck9db95292011-06-20 13:00:12 -070096 public static final int VIEW_THUMBNAILS = 1;
97 public static final int VIEW_LIST = 2;
Jeff Hamiltondb90aa82010-09-16 03:38:04 -050098
John Reck608baa72010-11-24 10:32:28 -080099 BookmarksPageCallbacks mCallbacks;
John Reck0ce8a942011-01-14 19:57:09 -0800100 View mRoot;
John Reck9db95292011-06-20 13:00:12 -0700101 BookmarkExpandableView mGrid;
Jeff Hamilton84029622010-08-05 14:29:28 -0500102 boolean mDisableNewWindow;
John Reck608baa72010-11-24 10:32:28 -0800103 boolean mEnableContextMenu = true;
Jeff Hamilton84029622010-08-05 14:29:28 -0500104 View mEmptyView;
John Reck608baa72010-11-24 10:32:28 -0800105 View mHeader;
John Reck71efc2b2011-05-09 16:54:28 -0700106 HashMap<Integer, BrowserBookmarksAdapter> mBookmarkAdapters = new HashMap<Integer, BrowserBookmarksAdapter>();
John Reck66302e52011-05-18 17:12:33 -0700107 BookmarkDragHandler mDragHandler;
John Reck4f61ee42011-08-22 16:31:14 -0700108 JSONObject mState;
Michael Kolb370a4f32010-10-06 10:45:32 -0700109
Jeff Hamilton84029622010-08-05 14:29:28 -0500110 @Override
111 public Loader<Cursor> onCreateLoader(int id, Bundle args) {
John Reck71efc2b2011-05-09 16:54:28 -0700112 if (id == LOADER_ACCOUNTS) {
113 return new AccountsLoader(getActivity());
114 } else if (id >= LOADER_BOOKMARKS) {
115 String accountType = args.getString(ACCOUNT_TYPE);
116 String accountName = args.getString(ACCOUNT_NAME);
117 BookmarksLoader bl = new BookmarksLoader(getActivity(),
118 accountType, accountName);
119 return bl;
120 } else {
121 throw new UnsupportedOperationException("Unknown loader id " + id);
Jeff Hamilton84029622010-08-05 14:29:28 -0500122 }
Jeff Hamilton84029622010-08-05 14:29:28 -0500123 }
124
125 @Override
Jeff Hamiltondb90aa82010-09-16 03:38:04 -0500126 public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
John Reck71efc2b2011-05-09 16:54:28 -0700127 if (loader.getId() == LOADER_ACCOUNTS) {
128 LoaderManager lm = getLoaderManager();
129 int id = LOADER_BOOKMARKS;
130 while (cursor.moveToNext()) {
131 String accountName = cursor.getString(0);
132 String accountType = cursor.getString(1);
133 Bundle args = new Bundle();
134 args.putString(ACCOUNT_NAME, accountName);
135 args.putString(ACCOUNT_TYPE, accountType);
136 BrowserBookmarksAdapter adapter = new BrowserBookmarksAdapter(
John Reckd3e4d5b2011-07-13 15:48:43 -0700137 getActivity(), VIEW_THUMBNAILS);
John Reck71efc2b2011-05-09 16:54:28 -0700138 mBookmarkAdapters.put(id, adapter);
John Reck4f61ee42011-08-22 16:31:14 -0700139 boolean expand = true;
140 try {
141 expand = mState.getBoolean(accountName != null ? accountName
142 : BookmarkExpandableView.LOCAL_ACCOUNT_NAME);
143 } catch (JSONException e) {} // no state for accountName
144 mGrid.addAccount(accountName, adapter, expand);
John Reck71efc2b2011-05-09 16:54:28 -0700145 lm.restartLoader(id, args, this);
146 id++;
Jeff Hamiltondb90aa82010-09-16 03:38:04 -0500147 }
John Reck71efc2b2011-05-09 16:54:28 -0700148 // TODO: Figure out what a reload of these means
149 // Currently, a reload is triggered whenever bookmarks change
150 // This is less than ideal
151 // It also causes UI flickering as a new adapter is created
152 // instead of re-using an existing one when the account_name is the
153 // same.
154 // For now, this is a one-shot load
155 getLoaderManager().destroyLoader(LOADER_ACCOUNTS);
156 } else if (loader.getId() >= LOADER_BOOKMARKS) {
157 BrowserBookmarksAdapter adapter = mBookmarkAdapters.get(loader.getId());
158 adapter.changeCursor(cursor);
Jeff Hamiltondb90aa82010-09-16 03:38:04 -0500159 }
Jeff Hamilton84029622010-08-05 14:29:28 -0500160 }
161
John Reck522a4382011-01-26 13:23:47 -0800162 @Override
Dianne Hackborn39772c82010-12-16 00:43:54 -0800163 public void onLoaderReset(Loader<Cursor> loader) {
John Reck3ad8d5c2012-04-23 14:50:09 -0700164 if (loader.getId() >= LOADER_BOOKMARKS) {
165 BrowserBookmarksAdapter adapter = mBookmarkAdapters.get(loader.getId());
166 adapter.changeCursor(null);
167 }
Dianne Hackborn39772c82010-12-16 00:43:54 -0800168 }
169
The Android Open Source Project0c908882009-03-03 19:32:16 -0800170 @Override
171 public boolean onContextItemSelected(MenuItem item) {
John Reck54ed3aa2011-08-19 15:16:05 -0700172 if (!(item.getMenuInfo() instanceof BookmarkContextMenuInfo)) {
173 return false;
174 }
175 BookmarkContextMenuInfo i = (BookmarkContextMenuInfo) item.getMenuInfo();
The Android Open Source Project0c908882009-03-03 19:32:16 -0800176 // If we have no menu info, we can't tell which item was selected.
177 if (i == null) {
Leon Scrogginsc8cae082010-11-03 18:51:46 -0400178 return false;
The Android Open Source Project0c908882009-03-03 19:32:16 -0800179 }
Nicolas Catania095292f2010-03-15 09:00:14 -0700180
John Recke7c97de2011-05-27 14:42:43 -0700181 if (handleContextItem(item.getItemId(), i.groupPosition, i.childPosition)) {
182 return true;
183 }
184 return super.onContextItemSelected(item);
185 }
186
187 public boolean handleContextItem(int itemId, int groupPosition,
188 int childPosition) {
189 final Activity activity = getActivity();
190 BrowserBookmarksAdapter adapter = getChildAdapter(groupPosition);
191
192 switch (itemId) {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800193 case R.id.open_context_menu_id:
John Recke7c97de2011-05-27 14:42:43 -0700194 loadUrl(adapter, childPosition);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800195 break;
196 case R.id.edit_context_menu_id:
John Recke7c97de2011-05-27 14:42:43 -0700197 editBookmark(adapter, childPosition);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800198 break;
199 case R.id.shortcut_context_menu_id:
John Recke7c97de2011-05-27 14:42:43 -0700200 Cursor c = adapter.getItem(childPosition);
John Reck608baa72010-11-24 10:32:28 -0800201 activity.sendBroadcast(createShortcutIntent(getActivity(), c));
The Android Open Source Project0c908882009-03-03 19:32:16 -0800202 break;
203 case R.id.delete_context_menu_id:
John Recke7c97de2011-05-27 14:42:43 -0700204 displayRemoveBookmarkDialog(adapter, childPosition);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800205 break;
206 case R.id.new_window_context_menu_id:
John Recke7c97de2011-05-27 14:42:43 -0700207 openInNewWindow(adapter, childPosition);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800208 break;
Jeff Hamilton84029622010-08-05 14:29:28 -0500209 case R.id.share_link_context_menu_id: {
John Recke7c97de2011-05-27 14:42:43 -0700210 Cursor cursor = adapter.getItem(childPosition);
Michael Kolb8233fac2010-10-26 16:08:53 -0700211 Controller.sharePage(activity,
Jeff Hamilton84029622010-08-05 14:29:28 -0500212 cursor.getString(BookmarksLoader.COLUMN_INDEX_TITLE),
213 cursor.getString(BookmarksLoader.COLUMN_INDEX_URL),
214 getBitmap(cursor, BookmarksLoader.COLUMN_INDEX_FAVICON),
215 getBitmap(cursor, BookmarksLoader.COLUMN_INDEX_THUMBNAIL));
The Android Open Source Project0c908882009-03-03 19:32:16 -0800216 break;
Jeff Hamilton84029622010-08-05 14:29:28 -0500217 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800218 case R.id.copy_url_context_menu_id:
John Recke7c97de2011-05-27 14:42:43 -0700219 copy(getUrl(adapter, childPosition));
Leon Scrogginsfeb941d2009-05-28 17:27:38 -0400220 break;
Jeff Hamilton84029622010-08-05 14:29:28 -0500221 case R.id.homepage_context_menu_id: {
John Recke7c97de2011-05-27 14:42:43 -0700222 BrowserSettings.getInstance().setHomePage(getUrl(adapter, childPosition));
Jeff Hamilton64144e42010-08-18 16:31:59 -0500223 Toast.makeText(activity, R.string.homepage_set, Toast.LENGTH_LONG).show();
Leon Scrogginsfeb941d2009-05-28 17:27:38 -0400224 break;
Jeff Hamilton84029622010-08-05 14:29:28 -0500225 }
Leon Scrogginsa5d669e2009-08-05 14:07:58 -0400226 // Only for the Most visited page
Jeff Hamilton84029622010-08-05 14:29:28 -0500227 case R.id.save_to_bookmarks_menu_id: {
John Recke7c97de2011-05-27 14:42:43 -0700228 Cursor cursor = adapter.getItem(childPosition);
Jeff Hamilton84029622010-08-05 14:29:28 -0500229 String name = cursor.getString(BookmarksLoader.COLUMN_INDEX_TITLE);
230 String url = cursor.getString(BookmarksLoader.COLUMN_INDEX_URL);
Leon Scrogginsa5d669e2009-08-05 14:07:58 -0400231 // If the site is bookmarked, the item becomes remove from
232 // bookmarks.
Jeff Hamilton64144e42010-08-18 16:31:59 -0500233 Bookmarks.removeFromBookmarks(activity, activity.getContentResolver(), url, name);
Leon Scrogginsa5d669e2009-08-05 14:07:58 -0400234 break;
Jeff Hamilton84029622010-08-05 14:29:28 -0500235 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800236 default:
John Recke7c97de2011-05-27 14:42:43 -0700237 return false;
The Android Open Source Project0c908882009-03-03 19:32:16 -0800238 }
239 return true;
240 }
241
John Reck608baa72010-11-24 10:32:28 -0800242 static Bitmap getBitmap(Cursor cursor, int columnIndex) {
John Reckf94abcf2011-10-10 15:33:48 -0700243 return getBitmap(cursor, columnIndex, null);
244 }
245
246 static ThreadLocal<Options> sOptions = new ThreadLocal<Options>() {
247 @Override
248 protected Options initialValue() {
249 return new Options();
250 };
251 };
252 static Bitmap getBitmap(Cursor cursor, int columnIndex, Bitmap inBitmap) {
Jeff Hamilton84029622010-08-05 14:29:28 -0500253 byte[] data = cursor.getBlob(columnIndex);
254 if (data == null) {
255 return null;
The Android Open Source Project0c908882009-03-03 19:32:16 -0800256 }
John Reckf94abcf2011-10-10 15:33:48 -0700257 Options opts = sOptions.get();
258 opts.inBitmap = inBitmap;
259 opts.inSampleSize = 1;
260 opts.inScaled = false;
261 try {
262 return BitmapFactory.decodeByteArray(data, 0, data.length, opts);
263 } catch (IllegalArgumentException ex) {
264 // Failed to re-use bitmap, create a new one
265 return BitmapFactory.decodeByteArray(data, 0, data.length);
266 }
Jeff Hamilton84029622010-08-05 14:29:28 -0500267 }
268
John Reckc8490812010-11-22 14:15:36 -0800269 private MenuItem.OnMenuItemClickListener mContextItemClickListener =
270 new MenuItem.OnMenuItemClickListener() {
271 @Override
272 public boolean onMenuItemClick(MenuItem item) {
273 return onContextItemSelected(item);
274 }
275 };
276
Jeff Hamilton84029622010-08-05 14:29:28 -0500277 @Override
278 public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
John Reck71efc2b2011-05-09 16:54:28 -0700279 BookmarkContextMenuInfo info = (BookmarkContextMenuInfo) menuInfo;
280 BrowserBookmarksAdapter adapter = getChildAdapter(info.groupPosition);
281 Cursor cursor = adapter.getItem(info.childPosition);
John Reck1cebb442010-12-10 12:02:16 -0800282 if (!canEdit(cursor)) {
283 return;
284 }
Leon Scroggins5e8a40f2010-09-27 17:13:35 -0400285 boolean isFolder
286 = cursor.getInt(BookmarksLoader.COLUMN_INDEX_IS_FOLDER) != 0;
Michael Kolb801ecb72010-08-25 12:57:38 -0700287
Jeff Hamilton64144e42010-08-18 16:31:59 -0500288 final Activity activity = getActivity();
289 MenuInflater inflater = activity.getMenuInflater();
Jeff Hamilton84029622010-08-05 14:29:28 -0500290 inflater.inflate(R.menu.bookmarkscontext, menu);
John Reckc8490812010-11-22 14:15:36 -0800291 if (isFolder) {
292 menu.setGroupVisible(R.id.FOLDER_CONTEXT_MENU, true);
293 } else {
294 menu.setGroupVisible(R.id.BOOKMARK_CONTEXT_MENU, true);
295 if (mDisableNewWindow) {
296 menu.findItem(R.id.new_window_context_menu_id).setVisible(false);
297 }
Jeff Hamilton84029622010-08-05 14:29:28 -0500298 }
John Reckc8490812010-11-22 14:15:36 -0800299 BookmarkItem header = new BookmarkItem(activity);
John Reck83c01512011-09-09 11:14:16 -0700300 header.setEnableScrolling(true);
John Reckc8490812010-11-22 14:15:36 -0800301 populateBookmarkItem(cursor, header, isFolder);
John Reckc8490812010-11-22 14:15:36 -0800302 menu.setHeaderView(header);
Jeff Hamilton84029622010-08-05 14:29:28 -0500303
John Reckc8490812010-11-22 14:15:36 -0800304 int count = menu.size();
305 for (int i = 0; i < count; i++) {
306 menu.getItem(i).setOnMenuItemClickListener(mContextItemClickListener);
Jeff Hamilton84029622010-08-05 14:29:28 -0500307 }
Jeff Hamilton84029622010-08-05 14:29:28 -0500308 }
309
John Reck1cebb442010-12-10 12:02:16 -0800310 boolean canEdit(Cursor c) {
John Reckd18ac4b2012-04-12 17:27:34 -0700311 int type = c.getInt(BookmarksLoader.COLUMN_INDEX_TYPE);
312 return type == BrowserContract.Bookmarks.BOOKMARK_TYPE_BOOKMARK
313 || type == BrowserContract.Bookmarks.BOOKMARK_TYPE_FOLDER;
John Reck1cebb442010-12-10 12:02:16 -0800314 }
315
John Reckc8490812010-11-22 14:15:36 -0800316 private void populateBookmarkItem(Cursor cursor, BookmarkItem item, boolean isFolder) {
Jeff Hamilton84029622010-08-05 14:29:28 -0500317 item.setName(cursor.getString(BookmarksLoader.COLUMN_INDEX_TITLE));
John Reckc8490812010-11-22 14:15:36 -0800318 if (isFolder) {
319 item.setUrl(null);
320 Bitmap bitmap =
Michael Kolb5a72f182011-01-13 20:35:06 -0800321 BitmapFactory.decodeResource(getResources(), R.drawable.ic_folder_holo_dark);
John Reckc8490812010-11-22 14:15:36 -0800322 item.setFavicon(bitmap);
John Reck60e33362010-12-02 17:52:38 -0800323 new LookupBookmarkCount(getActivity(), item)
324 .execute(cursor.getLong(BookmarksLoader.COLUMN_INDEX_ID));
John Reckc8490812010-11-22 14:15:36 -0800325 } else {
326 String url = cursor.getString(BookmarksLoader.COLUMN_INDEX_URL);
327 item.setUrl(url);
328 Bitmap bitmap = getBitmap(cursor, BookmarksLoader.COLUMN_INDEX_FAVICON);
John Reckc8490812010-11-22 14:15:36 -0800329 item.setFavicon(bitmap);
Jeff Hamilton84029622010-08-05 14:29:28 -0500330 }
Jeff Hamilton84029622010-08-05 14:29:28 -0500331 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800332
333 /**
334 * Create a new BrowserBookmarksPage.
Nicolas Catania095292f2010-03-15 09:00:14 -0700335 */
The Android Open Source Project0c908882009-03-03 19:32:16 -0800336 @Override
Jeff Hamilton64144e42010-08-18 16:31:59 -0500337 public void onCreate(Bundle icicle) {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800338 super.onCreate(icicle);
John Reck4f61ee42011-08-22 16:31:14 -0700339 SharedPreferences prefs = BrowserSettings.getInstance().getPreferences();
340 try {
341 mState = new JSONObject(prefs.getString(PREF_GROUP_STATE, "{}"));
342 } catch (JSONException e) {
343 // Parse failed, clear preference and start with empty state
344 prefs.edit().remove(PREF_GROUP_STATE).apply();
345 mState = new JSONObject();
346 }
Jeff Hamilton64144e42010-08-18 16:31:59 -0500347 Bundle args = getArguments();
Leon Scrogginsbc922852010-10-22 12:15:27 -0400348 mDisableNewWindow = args == null ? false : args.getBoolean(EXTRA_DISABLE_WINDOW, false);
John Reck8f295d12011-03-18 15:25:18 -0700349 setHasOptionsMenu(true);
John Reck2d963a22011-08-10 15:53:07 -0700350 if (mCallbacks == null && getActivity() instanceof CombinedBookmarksCallbacks) {
351 mCallbacks = new CombinedBookmarksCallbackWrapper(
352 (CombinedBookmarksCallbacks) getActivity());
353 }
Jeff Hamilton64144e42010-08-18 16:31:59 -0500354 }
Leon Scrogginsd87f85e2009-08-18 14:13:31 -0400355
John Reck4f61ee42011-08-22 16:31:14 -0700356 @Override
357 public void onPause() {
358 super.onPause();
359 try {
360 mState = mGrid.saveGroupState();
361 // Save state
362 SharedPreferences prefs = BrowserSettings.getInstance().getPreferences();
363 prefs.edit()
364 .putString(PREF_GROUP_STATE, mState.toString())
365 .apply();
366 } catch (JSONException e) {
367 // Not critical, ignore
368 }
369 }
370
John Reck2d963a22011-08-10 15:53:07 -0700371 private static class CombinedBookmarksCallbackWrapper
372 implements BookmarksPageCallbacks {
373
374 private CombinedBookmarksCallbacks mCombinedCallback;
375
376 private CombinedBookmarksCallbackWrapper(CombinedBookmarksCallbacks cb) {
377 mCombinedCallback = cb;
378 }
379
380 @Override
381 public boolean onOpenInNewWindow(String... urls) {
382 mCombinedCallback.openInNewTab(urls);
383 return true;
384 }
385
386 @Override
387 public boolean onBookmarkSelected(Cursor c, boolean isFolder) {
388 if (isFolder) {
389 return false;
390 }
391 mCombinedCallback.openUrl(BrowserBookmarksPage.getUrl(c));
392 return true;
393 }
394 };
395
Jeff Hamilton64144e42010-08-18 16:31:59 -0500396 @Override
Jeff Hamilton64144e42010-08-18 16:31:59 -0500397 public View onCreateView(LayoutInflater inflater, ViewGroup container,
398 Bundle savedInstanceState) {
John Reck0ce8a942011-01-14 19:57:09 -0800399 mRoot = inflater.inflate(R.layout.bookmarks, container, false);
400 mEmptyView = mRoot.findViewById(android.R.id.empty);
Leon Scrogginsd87f85e2009-08-18 14:13:31 -0400401
John Reck9db95292011-06-20 13:00:12 -0700402 mGrid = (BookmarkExpandableView) mRoot.findViewById(R.id.grid);
John Reck71efc2b2011-05-09 16:54:28 -0700403 mGrid.setOnChildClickListener(this);
404 mGrid.setColumnWidthFromLayout(R.layout.bookmark_thumbnail);
405 mGrid.setBreadcrumbController(this);
John Reck608baa72010-11-24 10:32:28 -0800406 setEnableContextMenu(mEnableContextMenu);
John Reck66302e52011-05-18 17:12:33 -0700407 mDragHandler = new BookmarkDragHandler(getActivity(), mDragController,
408 mGrid.getDragAdapter());
John Reck608baa72010-11-24 10:32:28 -0800409
Jeff Hamiltondb90aa82010-09-16 03:38:04 -0500410 // Start the loaders
411 LoaderManager lm = getLoaderManager();
John Reck71efc2b2011-05-09 16:54:28 -0700412 lm.restartLoader(LOADER_ACCOUNTS, null, this);
Jeff Hamilton84029622010-08-05 14:29:28 -0500413
John Reck0ce8a942011-01-14 19:57:09 -0800414 return mRoot;
Jeff Hamilton64144e42010-08-18 16:31:59 -0500415 }
Michael Kolb801ecb72010-08-25 12:57:38 -0700416
John Reck608baa72010-11-24 10:32:28 -0800417 @Override
418 public void onDestroyView() {
419 super.onDestroyView();
John Reck71efc2b2011-05-09 16:54:28 -0700420 mGrid.setBreadcrumbController(null);
John Reckc3c46d72011-09-06 16:17:34 -0700421 mGrid.clearAccounts();
John Reck71efc2b2011-05-09 16:54:28 -0700422 LoaderManager lm = getLoaderManager();
423 lm.destroyLoader(LOADER_ACCOUNTS);
424 for (int id : mBookmarkAdapters.keySet()) {
425 lm.destroyLoader(id);
John Reck608baa72010-11-24 10:32:28 -0800426 }
John Reck71efc2b2011-05-09 16:54:28 -0700427 mBookmarkAdapters.clear();
John Reck71efc2b2011-05-09 16:54:28 -0700428 }
429
430 private BrowserBookmarksAdapter getChildAdapter(int groupPosition) {
John Reck9db95292011-06-20 13:00:12 -0700431 return mGrid.getChildAdapter(groupPosition);
John Reck71efc2b2011-05-09 16:54:28 -0700432 }
433
434 private BreadCrumbView getBreadCrumbs(int groupPosition) {
John Reck9db95292011-06-20 13:00:12 -0700435 return mGrid.getBreadCrumbs(groupPosition);
Nicolas Catania095292f2010-03-15 09:00:14 -0700436 }
437
Jeff Hamilton84029622010-08-05 14:29:28 -0500438 @Override
John Reck71efc2b2011-05-09 16:54:28 -0700439 public boolean onChildClick(ExpandableListView parent, View v,
440 int groupPosition, int childPosition, long id) {
441 BrowserBookmarksAdapter adapter = getChildAdapter(groupPosition);
442 Cursor cursor = adapter.getItem(childPosition);
John Reck608baa72010-11-24 10:32:28 -0800443 boolean isFolder = cursor.getInt(BookmarksLoader.COLUMN_INDEX_IS_FOLDER) != 0;
444 if (mCallbacks != null &&
445 mCallbacks.onBookmarkSelected(cursor, isFolder)) {
John Reck71efc2b2011-05-09 16:54:28 -0700446 return true;
Ben Murdoch328ea872009-09-16 13:33:29 +0100447 }
448
John Reck608baa72010-11-24 10:32:28 -0800449 if (isFolder) {
Michael Kolb370a4f32010-10-06 10:45:32 -0700450 String title = cursor.getString(BookmarksLoader.COLUMN_INDEX_TITLE);
Jeff Hamilton84029622010-08-05 14:29:28 -0500451 Uri uri = ContentUris.withAppendedId(
452 BrowserContract.Bookmarks.CONTENT_URI_DEFAULT_FOLDER, id);
John Reck71efc2b2011-05-09 16:54:28 -0700453 BreadCrumbView crumbs = getBreadCrumbs(groupPosition);
454 if (crumbs != null) {
Michael Kolb370a4f32010-10-06 10:45:32 -0700455 // update crumbs
John Reck71efc2b2011-05-09 16:54:28 -0700456 crumbs.pushView(title, uri);
John Reckcece1fe2011-09-13 08:47:17 -0700457 crumbs.setVisibility(View.VISIBLE);
Michael Kolb370a4f32010-10-06 10:45:32 -0700458 }
John Reck71efc2b2011-05-09 16:54:28 -0700459 loadFolder(groupPosition, uri);
Leon Scrogginsa5d669e2009-08-05 14:07:58 -0400460 }
John Reck71efc2b2011-05-09 16:54:28 -0700461 return true;
The Android Open Source Project0c908882009-03-03 19:32:16 -0800462 }
463
John Reck608baa72010-11-24 10:32:28 -0800464 /* package */ static Intent createShortcutIntent(Context context, Cursor cursor) {
Jeff Hamilton84029622010-08-05 14:29:28 -0500465 String url = cursor.getString(BookmarksLoader.COLUMN_INDEX_URL);
Michael Kolbccaa8962010-10-20 17:03:10 -0700466 String title = cursor.getString(BookmarksLoader.COLUMN_INDEX_TITLE);
Jeff Hamilton84029622010-08-05 14:29:28 -0500467 Bitmap touchIcon = getBitmap(cursor, BookmarksLoader.COLUMN_INDEX_TOUCH_ICON);
468 Bitmap favicon = getBitmap(cursor, BookmarksLoader.COLUMN_INDEX_FAVICON);
John Reck608baa72010-11-24 10:32:28 -0800469 return BookmarkUtils.createAddToHomeIntent(context, url, title, touchIcon, favicon);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800470 }
471
John Reck71efc2b2011-05-09 16:54:28 -0700472 private void loadUrl(BrowserBookmarksAdapter adapter, int position) {
473 if (mCallbacks != null && adapter != null) {
474 mCallbacks.onBookmarkSelected(adapter.getItem(position), false);
John Reck608baa72010-11-24 10:32:28 -0800475 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800476 }
477
John Reck71efc2b2011-05-09 16:54:28 -0700478 private void openInNewWindow(BrowserBookmarksAdapter adapter, int position) {
John Reck608baa72010-11-24 10:32:28 -0800479 if (mCallbacks != null) {
John Reck71efc2b2011-05-09 16:54:28 -0700480 Cursor c = adapter.getItem(position);
John Reck1b3c66e2011-01-11 13:20:32 -0800481 boolean isFolder = c.getInt(BookmarksLoader.COLUMN_INDEX_IS_FOLDER) == 1;
482 if (isFolder) {
483 long id = c.getLong(BookmarksLoader.COLUMN_INDEX_ID);
484 new OpenAllInTabsTask(id).execute();
485 } else {
John Reckd3e4d5b2011-07-13 15:48:43 -0700486 mCallbacks.onOpenInNewWindow(BrowserBookmarksPage.getUrl(c));
John Reck1b3c66e2011-01-11 13:20:32 -0800487 }
John Reck608baa72010-11-24 10:32:28 -0800488 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800489 }
Nicolas Catania095292f2010-03-15 09:00:14 -0700490
John Reck1b3c66e2011-01-11 13:20:32 -0800491 class OpenAllInTabsTask extends AsyncTask<Void, Void, Cursor> {
492 long mFolderId;
493 public OpenAllInTabsTask(long id) {
494 mFolderId = id;
495 }
496
497 @Override
498 protected Cursor doInBackground(Void... params) {
499 Context c = getActivity();
500 if (c == null) return null;
501 return c.getContentResolver().query(BookmarkUtils.getBookmarksUri(c),
502 BookmarksLoader.PROJECTION, BrowserContract.Bookmarks.PARENT + "=?",
503 new String[] { Long.toString(mFolderId) }, null);
504 }
505
506 @Override
507 protected void onPostExecute(Cursor result) {
John Reckd3e4d5b2011-07-13 15:48:43 -0700508 if (mCallbacks != null && result.getCount() > 0) {
509 String[] urls = new String[result.getCount()];
510 int i = 0;
John Reck1b3c66e2011-01-11 13:20:32 -0800511 while (result.moveToNext()) {
John Reckd3e4d5b2011-07-13 15:48:43 -0700512 urls[i++] = BrowserBookmarksPage.getUrl(result);
John Reck1b3c66e2011-01-11 13:20:32 -0800513 }
John Reckd3e4d5b2011-07-13 15:48:43 -0700514 mCallbacks.onOpenInNewWindow(urls);
John Reck1b3c66e2011-01-11 13:20:32 -0800515 }
516 }
517
518 }
519
John Reck71efc2b2011-05-09 16:54:28 -0700520 private void editBookmark(BrowserBookmarksAdapter adapter, int position) {
Jeff Hamilton64144e42010-08-18 16:31:59 -0500521 Intent intent = new Intent(getActivity(), AddBookmarkPage.class);
John Reck71efc2b2011-05-09 16:54:28 -0700522 Cursor cursor = adapter.getItem(position);
Jeff Hamilton84029622010-08-05 14:29:28 -0500523 Bundle item = new Bundle();
Michael Kolbbef7a752010-08-25 17:11:35 -0700524 item.putString(BrowserContract.Bookmarks.TITLE,
Jeff Hamilton84029622010-08-05 14:29:28 -0500525 cursor.getString(BookmarksLoader.COLUMN_INDEX_TITLE));
Michael Kolbbef7a752010-08-25 17:11:35 -0700526 item.putString(BrowserContract.Bookmarks.URL,
Jeff Hamilton84029622010-08-05 14:29:28 -0500527 cursor.getString(BookmarksLoader.COLUMN_INDEX_URL));
528 byte[] data = cursor.getBlob(BookmarksLoader.COLUMN_INDEX_FAVICON);
529 if (data != null) {
Michael Kolbbef7a752010-08-25 17:11:35 -0700530 item.putParcelable(BrowserContract.Bookmarks.FAVICON,
Jeff Hamilton84029622010-08-05 14:29:28 -0500531 BitmapFactory.decodeByteArray(data, 0, data.length));
532 }
John Reckc8490812010-11-22 14:15:36 -0800533 item.putLong(BrowserContract.Bookmarks._ID,
534 cursor.getLong(BookmarksLoader.COLUMN_INDEX_ID));
Leon Scroggins III052ce662010-09-13 14:44:16 -0400535 item.putLong(BrowserContract.Bookmarks.PARENT,
536 cursor.getLong(BookmarksLoader.COLUMN_INDEX_PARENT));
John Reckc8490812010-11-22 14:15:36 -0800537 intent.putExtra(AddBookmarkPage.EXTRA_EDIT_BOOKMARK, item);
538 intent.putExtra(AddBookmarkPage.EXTRA_IS_FOLDER,
539 cursor.getInt(BookmarksLoader.COLUMN_INDEX_IS_FOLDER) == 1);
540 startActivity(intent);
Jeff Hamilton84029622010-08-05 14:29:28 -0500541 }
542
John Reck71efc2b2011-05-09 16:54:28 -0700543 private void displayRemoveBookmarkDialog(BrowserBookmarksAdapter adapter,
544 int position) {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800545 // Put up a dialog asking if the user really wants to
546 // delete the bookmark
John Reck71efc2b2011-05-09 16:54:28 -0700547 Cursor cursor = adapter.getItem(position);
Leon Scroggins75630672011-01-13 17:56:15 -0500548 long id = cursor.getLong(BookmarksLoader.COLUMN_INDEX_ID);
549 String title = cursor.getString(BookmarksLoader.COLUMN_INDEX_TITLE);
Jeff Hamilton64144e42010-08-18 16:31:59 -0500550 Context context = getActivity();
Leon Scroggins75630672011-01-13 17:56:15 -0500551 BookmarkUtils.displayRemoveBookmarkDialog(id, title, context, null);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800552 }
553
John Reck71efc2b2011-05-09 16:54:28 -0700554 private String getUrl(BrowserBookmarksAdapter adapter, int position) {
555 return getUrl(adapter.getItem(position));
John Reck608baa72010-11-24 10:32:28 -0800556 }
557
558 /* package */ static String getUrl(Cursor c) {
559 return c.getString(BookmarksLoader.COLUMN_INDEX_URL);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800560 }
Michael Kolb801ecb72010-08-25 12:57:38 -0700561
The Android Open Source Project0c908882009-03-03 19:32:16 -0800562 private void copy(CharSequence text) {
Jeff Hamilton64144e42010-08-18 16:31:59 -0500563 ClipboardManager cm = (ClipboardManager) getActivity().getSystemService(
564 Context.CLIPBOARD_SERVICE);
Dianne Hackbornbf1d10a2011-01-17 12:29:51 -0800565 cm.setPrimaryClip(ClipData.newRawUri(null, Uri.parse(text.toString())));
The Android Open Source Project0c908882009-03-03 19:32:16 -0800566 }
Michael Kolb370a4f32010-10-06 10:45:32 -0700567
John Reckb3417f02011-01-14 11:01:05 -0800568 @Override
John Reckb3417f02011-01-14 11:01:05 -0800569 public void onConfigurationChanged(Configuration newConfig) {
570 super.onConfigurationChanged(newConfig);
571 Resources res = getActivity().getResources();
John Reck71efc2b2011-05-09 16:54:28 -0700572 mGrid.setColumnWidthFromLayout(R.layout.bookmark_thumbnail);
John Reck0ce8a942011-01-14 19:57:09 -0800573 int paddingTop = (int) res.getDimension(R.dimen.combo_paddingTop);
John Reckf3828cd2011-06-14 17:21:55 -0700574 mRoot.setPadding(0, paddingTop, 0, 0);
John Reck9fdd9812011-05-17 14:16:38 -0700575 getActivity().invalidateOptionsMenu();
John Reckb3417f02011-01-14 11:01:05 -0800576 }
577
John Reck608baa72010-11-24 10:32:28 -0800578 /**
579 * BreadCrumb controller callback
580 */
581 @Override
John Reck71efc2b2011-05-09 16:54:28 -0700582 public void onTop(BreadCrumbView view, int level, Object data) {
583 int groupPosition = (Integer) view.getTag(R.id.group_position);
John Reck89f73c12010-12-01 10:10:14 -0800584 Uri uri = (Uri) data;
585 if (uri == null) {
586 // top level
587 uri = BrowserContract.Bookmarks.CONTENT_URI_DEFAULT_FOLDER;
588 }
John Reck71efc2b2011-05-09 16:54:28 -0700589 loadFolder(groupPosition, uri);
John Reckcece1fe2011-09-13 08:47:17 -0700590 if (level <= 1) {
591 view.setVisibility(View.GONE);
592 } else {
593 view.setVisibility(View.VISIBLE);
594 }
John Reck89f73c12010-12-01 10:10:14 -0800595 }
596
597 /**
598 * @param uri
599 */
John Reck71efc2b2011-05-09 16:54:28 -0700600 private void loadFolder(int groupPosition, Uri uri) {
John Reck89f73c12010-12-01 10:10:14 -0800601 LoaderManager manager = getLoaderManager();
John Reck71efc2b2011-05-09 16:54:28 -0700602 // This assumes groups are ordered the same as loaders
603 BookmarksLoader loader = (BookmarksLoader) ((Loader<?>)
604 manager.getLoader(LOADER_BOOKMARKS + groupPosition));
John Reck89f73c12010-12-01 10:10:14 -0800605 loader.setUri(uri);
606 loader.forceLoad();
John Reck608baa72010-11-24 10:32:28 -0800607 }
608
John Reck608baa72010-11-24 10:32:28 -0800609 public void setCallbackListener(BookmarksPageCallbacks callbackListener) {
610 mCallbacks = callbackListener;
611 }
612
613 public void setEnableContextMenu(boolean enable) {
614 mEnableContextMenu = enable;
615 if (mGrid != null) {
616 if (mEnableContextMenu) {
617 registerForContextMenu(mGrid);
618 } else {
619 unregisterForContextMenu(mGrid);
620 mGrid.setLongClickable(false);
621 }
622 }
John Reck608baa72010-11-24 10:32:28 -0800623 }
John Reckc8490812010-11-22 14:15:36 -0800624
John Reck66302e52011-05-18 17:12:33 -0700625 private BookmarkDragController mDragController = new BookmarkDragController() {
626
627 @Override
628 public boolean startDrag(Cursor item) {
629 return canEdit(item);
630 }
John Recke7c97de2011-05-27 14:42:43 -0700631
632 @Override
633 public ViewGroup getActionModeView(ActionMode mode,
634 BookmarkDragState state) {
635 LayoutInflater inflater = LayoutInflater.from(getActivity());
636 LinearLayout view = (LinearLayout) inflater.inflate(
637 R.layout.bookmarks_drag_actionmode, null);
638 view.setShowDividers(LinearLayout.SHOW_DIVIDER_MIDDLE);
639 ExtraDragState extraState = (ExtraDragState) state.extraState;
640 BrowserBookmarksAdapter adapter = getChildAdapter(extraState.groupPosition);
641 Cursor c = adapter.getItem(extraState.childPosition);
642 boolean isFolder = c.getInt(BookmarksLoader.COLUMN_INDEX_IS_FOLDER) != 0;
643 if (isFolder) {
644 view.findViewById(R.id.open_context_menu_id).setVisibility(View.GONE);
645 ImageView iv = (ImageView) view.findViewById(
646 R.id.new_window_context_menu_id);
647 iv.setImageResource(R.drawable.ic_windows_holo_dark);
648 }
649 return view;
650 }
651
652 @Override
653 public void actionItemClicked(View v, BookmarkDragState state) {
654 if (v.getId() == R.id.info) {
John Reck9db95292011-06-20 13:00:12 -0700655 mGrid.showContextMenuForState(state);
John Recke7c97de2011-05-27 14:42:43 -0700656 } else {
657 ExtraDragState extraState = (ExtraDragState) state.extraState;
658 handleContextItem(v.getId(), extraState.groupPosition,
659 extraState.childPosition);
660 }
661 }
John Reck66302e52011-05-18 17:12:33 -0700662 };
663
John Reckc8490812010-11-22 14:15:36 -0800664 private static class LookupBookmarkCount extends AsyncTask<Long, Void, Integer> {
665 Context mContext;
666 BookmarkItem mHeader;
667
668 public LookupBookmarkCount(Context context, BookmarkItem header) {
Ben Murdoch914c5592011-08-01 13:58:47 +0100669 mContext = context.getApplicationContext();
John Reckc8490812010-11-22 14:15:36 -0800670 mHeader = header;
671 }
672
673 @Override
674 protected Integer doInBackground(Long... params) {
675 if (params.length != 1) {
676 throw new IllegalArgumentException("Missing folder id!");
677 }
678 Uri uri = BookmarkUtils.getBookmarksUri(mContext);
679 Cursor c = mContext.getContentResolver().query(uri,
680 null, BrowserContract.Bookmarks.PARENT + "=?",
681 new String[] {params[0].toString()}, null);
682 return c.getCount();
683 }
684
685 @Override
686 protected void onPostExecute(Integer result) {
687 if (result > 0) {
688 mHeader.setUrl(mContext.getString(R.string.contextheader_folder_bookmarkcount,
689 result));
690 } else if (result == 0) {
691 mHeader.setUrl(mContext.getString(R.string.contextheader_folder_empty));
692 }
693 }
694 }
John Reck89f73c12010-12-01 10:10:14 -0800695
John Reck71efc2b2011-05-09 16:54:28 -0700696 static class AccountsLoader extends CursorLoader {
John Reck89f73c12010-12-01 10:10:14 -0800697
John Reck71efc2b2011-05-09 16:54:28 -0700698 static String[] ACCOUNTS_PROJECTION = new String[] {
699 Accounts.ACCOUNT_NAME,
700 Accounts.ACCOUNT_TYPE
701 };
John Reck89f73c12010-12-01 10:10:14 -0800702
John Reck71efc2b2011-05-09 16:54:28 -0700703 public AccountsLoader(Context context) {
John Reck1e9815d2011-08-08 17:45:05 -0700704 super(context, Accounts.CONTENT_URI
705 .buildUpon()
706 .appendQueryParameter(BrowserProvider2.PARAM_ALLOW_EMPTY_ACCOUNTS, "false")
707 .build(),
708 ACCOUNTS_PROJECTION, null, null, null);
John Reck89f73c12010-12-01 10:10:14 -0800709 }
John Reck522a4382011-01-26 13:23:47 -0800710
John Reck522a4382011-01-26 13:23:47 -0800711 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800712}