blob: 3fea1ed7031c135ccea8528b0eefb39858439681 [file] [log] [blame]
The Android Open Source Project0c908882009-03-03 19:32:16 -08001/*
Dany Rybnikov535d4af2014-06-23 09:16:09 +03002 * Copyright (c) 2014, Linux Foundation. All rights reserved.
The Android Open Source Project0c908882009-03-03 19:32:16 -08003 * Copyright (C) 2006 The Android Open Source Project
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
Bijan Amirzada41242f22014-03-21 12:12:18 -070018package com.android.browser;
The Android Open Source Project0c908882009-03-03 19:32:16 -080019
20import android.app.Activity;
Jeff Hamilton64144e42010-08-18 16:31:59 -050021import android.app.Fragment;
Jeff Hamilton84029622010-08-05 14:29:28 -050022import android.app.LoaderManager;
Dianne Hackborneb07b172010-08-26 22:18:14 -070023import android.content.ClipData;
Michael Kolbbef7a752010-08-25 17:11:35 -070024import android.content.ClipboardManager;
Jeff Hamilton84029622010-08-05 14:29:28 -050025import android.content.ContentUris;
Dianne Hackborn80f32622010-08-05 14:17:53 -070026import android.content.Context;
John Reck71efc2b2011-05-09 16:54:28 -070027import android.content.CursorLoader;
The Android Open Source Project0c908882009-03-03 19:32:16 -080028import android.content.Intent;
Jeff Hamilton84029622010-08-05 14:29:28 -050029import android.content.Loader;
John Reck4f61ee42011-08-22 16:31:14 -070030import android.content.SharedPreferences;
John Reckb3417f02011-01-14 11:01:05 -080031import android.content.res.Configuration;
32import android.content.res.Resources;
Jeff Hamilton84029622010-08-05 14:29:28 -050033import android.database.Cursor;
The Android Open Source Project0c908882009-03-03 19:32:16 -080034import android.graphics.Bitmap;
Jeff Hamilton84029622010-08-05 14:29:28 -050035import android.graphics.BitmapFactory;
John Reckf94abcf2011-10-10 15:33:48 -070036import android.graphics.BitmapFactory.Options;
Jeff Hamilton84029622010-08-05 14:29:28 -050037import android.net.Uri;
John Reckc8490812010-11-22 14:15:36 -080038import android.os.AsyncTask;
The Android Open Source Project0c908882009-03-03 19:32:16 -080039import android.os.Bundle;
The Android Open Source Project0c908882009-03-03 19:32:16 -080040import android.view.ContextMenu;
Jeff Hamiltondb90aa82010-09-16 03:38:04 -050041import android.view.ContextMenu.ContextMenuInfo;
Jeff Hamilton64144e42010-08-18 16:31:59 -050042import android.view.LayoutInflater;
luxiaoldbe4a622013-07-19 17:14:06 +080043import android.view.Menu;
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;
Pankaj Garg49b79252014-11-07 17:33:41 -080048import android.widget.Button;
John Reck71efc2b2011-05-09 16:54:28 -070049import android.widget.ExpandableListView;
50import android.widget.ExpandableListView.OnChildClickListener;
Jeff Hamilton84029622010-08-05 14:29:28 -050051import android.widget.Toast;
The Android Open Source Project0c908882009-03-03 19:32:16 -080052
Bijan Amirzada41242f22014-03-21 12:12:18 -070053import com.android.browser.R;
54import com.android.browser.platformsupport.BrowserContract;
55import com.android.browser.platformsupport.BrowserContract.Accounts;
56import com.android.browser.provider.BrowserProvider2;
57import com.android.browser.view.BookmarkExpandableView;
58import com.android.browser.view.BookmarkExpandableView.BookmarkContextMenuInfo;
Bijan Amirzada9b1e9882014-02-26 17:15:46 -080059
60import java.util.HashMap;
John Recke7c97de2011-05-27 14:42:43 -070061
John Reck4f61ee42011-08-22 16:31:14 -070062import org.json.JSONException;
63import org.json.JSONObject;
64
Dany Rybnikovb80159e2014-06-23 09:16:09 +030065import org.codeaurora.net.NetworkServices;
Dany Rybnikov535d4af2014-06-23 09:16:09 +030066
John Reck608baa72010-11-24 10:32:28 -080067interface BookmarksPageCallbacks {
68 // Return true if handled
69 boolean onBookmarkSelected(Cursor c, boolean isFolder);
70 // Return true if handled
John Reckd3e4d5b2011-07-13 15:48:43 -070071 boolean onOpenInNewWindow(String... urls);
John Reck608baa72010-11-24 10:32:28 -080072}
73
The Android Open Source Project0c908882009-03-03 19:32:16 -080074/**
75 * View showing the user's bookmarks in the browser.
76 */
Jeff Hamilton64144e42010-08-18 16:31:59 -050077public class BrowserBookmarksPage extends Fragment implements View.OnCreateContextMenuListener,
John Reck9db95292011-06-20 13:00:12 -070078 LoaderManager.LoaderCallbacks<Cursor>, BreadCrumbView.Controller,
John Reckd3e4d5b2011-07-13 15:48:43 -070079 OnChildClickListener {
The Android Open Source Project0c908882009-03-03 19:32:16 -080080
John Recke7c97de2011-05-27 14:42:43 -070081 public static class ExtraDragState {
82 public int childPosition;
83 public int groupPosition;
84 }
85
Jeff Hamilton84029622010-08-05 14:29:28 -050086 static final String LOGTAG = "browser";
Nicolas Catania095292f2010-03-15 09:00:14 -070087
John Reck71efc2b2011-05-09 16:54:28 -070088 static final int LOADER_ACCOUNTS = 1;
89 static final int LOADER_BOOKMARKS = 100;
Jeff Hamilton84029622010-08-05 14:29:28 -050090
Michael Kolb801ecb72010-08-25 12:57:38 -070091 static final String EXTRA_DISABLE_WINDOW = "disable_new_window";
John Reck4f61ee42011-08-22 16:31:14 -070092 static final String PREF_GROUP_STATE = "bbp_group_state";
Michael Kolb801ecb72010-08-25 12:57:38 -070093
John Reck71efc2b2011-05-09 16:54:28 -070094 static final String ACCOUNT_TYPE = "account_type";
95 static final String ACCOUNT_NAME = "account_name";
96
luxiaoldbe4a622013-07-19 17:14:06 +080097 static final long DEFAULT_FOLDER_ID = -1;
98
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 Reck4f61ee42011-08-22 16:31:14 -0700107 JSONObject mState;
luxiaoldbe4a622013-07-19 17:14:06 +0800108 long mCurrentFolderId = BrowserProvider2.FIXED_ID_ROOT;
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 Reckaf991f92012-04-24 15:31:16 -0700137 getActivity());
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);
luxiaoldbe4a622013-07-19 17:14:06 +0800159 if (adapter.getCount() != 0) {
160 mCurrentFolderId = adapter.getItem(0).getLong(BookmarksLoader.COLUMN_INDEX_PARENT);
161 }
Jeff Hamiltondb90aa82010-09-16 03:38:04 -0500162 }
Jeff Hamilton84029622010-08-05 14:29:28 -0500163 }
164
John Reck522a4382011-01-26 13:23:47 -0800165 @Override
Dianne Hackborn39772c82010-12-16 00:43:54 -0800166 public void onLoaderReset(Loader<Cursor> loader) {
John Reck3ad8d5c2012-04-23 14:50:09 -0700167 if (loader.getId() >= LOADER_BOOKMARKS) {
168 BrowserBookmarksAdapter adapter = mBookmarkAdapters.get(loader.getId());
169 adapter.changeCursor(null);
170 }
Dianne Hackborn39772c82010-12-16 00:43:54 -0800171 }
172
The Android Open Source Project0c908882009-03-03 19:32:16 -0800173 @Override
174 public boolean onContextItemSelected(MenuItem item) {
John Reck54ed3aa2011-08-19 15:16:05 -0700175 if (!(item.getMenuInfo() instanceof BookmarkContextMenuInfo)) {
176 return false;
177 }
178 BookmarkContextMenuInfo i = (BookmarkContextMenuInfo) item.getMenuInfo();
The Android Open Source Project0c908882009-03-03 19:32:16 -0800179 // If we have no menu info, we can't tell which item was selected.
180 if (i == null) {
Leon Scrogginsc8cae082010-11-03 18:51:46 -0400181 return false;
The Android Open Source Project0c908882009-03-03 19:32:16 -0800182 }
Nicolas Catania095292f2010-03-15 09:00:14 -0700183
John Recke7c97de2011-05-27 14:42:43 -0700184 if (handleContextItem(item.getItemId(), i.groupPosition, i.childPosition)) {
185 return true;
186 }
187 return super.onContextItemSelected(item);
188 }
189
190 public boolean handleContextItem(int itemId, int groupPosition,
191 int childPosition) {
192 final Activity activity = getActivity();
193 BrowserBookmarksAdapter adapter = getChildAdapter(groupPosition);
194
195 switch (itemId) {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800196 case R.id.open_context_menu_id:
John Recke7c97de2011-05-27 14:42:43 -0700197 loadUrl(adapter, childPosition);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800198 break;
199 case R.id.edit_context_menu_id:
John Recke7c97de2011-05-27 14:42:43 -0700200 editBookmark(adapter, childPosition);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800201 break;
202 case R.id.shortcut_context_menu_id:
John Recke7c97de2011-05-27 14:42:43 -0700203 Cursor c = adapter.getItem(childPosition);
John Reck608baa72010-11-24 10:32:28 -0800204 activity.sendBroadcast(createShortcutIntent(getActivity(), c));
The Android Open Source Project0c908882009-03-03 19:32:16 -0800205 break;
206 case R.id.delete_context_menu_id:
John Recke7c97de2011-05-27 14:42:43 -0700207 displayRemoveBookmarkDialog(adapter, childPosition);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800208 break;
209 case R.id.new_window_context_menu_id:
John Recke7c97de2011-05-27 14:42:43 -0700210 openInNewWindow(adapter, childPosition);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800211 break;
Jeff Hamilton84029622010-08-05 14:29:28 -0500212 case R.id.share_link_context_menu_id: {
John Recke7c97de2011-05-27 14:42:43 -0700213 Cursor cursor = adapter.getItem(childPosition);
Michael Kolb8233fac2010-10-26 16:08:53 -0700214 Controller.sharePage(activity,
Jeff Hamilton84029622010-08-05 14:29:28 -0500215 cursor.getString(BookmarksLoader.COLUMN_INDEX_TITLE),
216 cursor.getString(BookmarksLoader.COLUMN_INDEX_URL),
217 getBitmap(cursor, BookmarksLoader.COLUMN_INDEX_FAVICON),
218 getBitmap(cursor, BookmarksLoader.COLUMN_INDEX_THUMBNAIL));
The Android Open Source Project0c908882009-03-03 19:32:16 -0800219 break;
Jeff Hamilton84029622010-08-05 14:29:28 -0500220 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800221 case R.id.copy_url_context_menu_id:
John Recke7c97de2011-05-27 14:42:43 -0700222 copy(getUrl(adapter, childPosition));
Leon Scrogginsfeb941d2009-05-28 17:27:38 -0400223 break;
Jeff Hamilton84029622010-08-05 14:29:28 -0500224 case R.id.homepage_context_menu_id: {
John Recke7c97de2011-05-27 14:42:43 -0700225 BrowserSettings.getInstance().setHomePage(getUrl(adapter, childPosition));
Jeff Hamilton64144e42010-08-18 16:31:59 -0500226 Toast.makeText(activity, R.string.homepage_set, Toast.LENGTH_LONG).show();
Leon Scrogginsfeb941d2009-05-28 17:27:38 -0400227 break;
Jeff Hamilton84029622010-08-05 14:29:28 -0500228 }
Leon Scrogginsa5d669e2009-08-05 14:07:58 -0400229 // Only for the Most visited page
Jeff Hamilton84029622010-08-05 14:29:28 -0500230 case R.id.save_to_bookmarks_menu_id: {
John Recke7c97de2011-05-27 14:42:43 -0700231 Cursor cursor = adapter.getItem(childPosition);
Jeff Hamilton84029622010-08-05 14:29:28 -0500232 String name = cursor.getString(BookmarksLoader.COLUMN_INDEX_TITLE);
233 String url = cursor.getString(BookmarksLoader.COLUMN_INDEX_URL);
Leon Scrogginsa5d669e2009-08-05 14:07:58 -0400234 // If the site is bookmarked, the item becomes remove from
235 // bookmarks.
Jeff Hamilton64144e42010-08-18 16:31:59 -0500236 Bookmarks.removeFromBookmarks(activity, activity.getContentResolver(), url, name);
Leon Scrogginsa5d669e2009-08-05 14:07:58 -0400237 break;
Jeff Hamilton84029622010-08-05 14:29:28 -0500238 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800239 default:
John Recke7c97de2011-05-27 14:42:43 -0700240 return false;
The Android Open Source Project0c908882009-03-03 19:32:16 -0800241 }
242 return true;
243 }
244
John Reck608baa72010-11-24 10:32:28 -0800245 static Bitmap getBitmap(Cursor cursor, int columnIndex) {
John Reckf94abcf2011-10-10 15:33:48 -0700246 return getBitmap(cursor, columnIndex, null);
247 }
248
249 static ThreadLocal<Options> sOptions = new ThreadLocal<Options>() {
250 @Override
251 protected Options initialValue() {
252 return new Options();
253 };
254 };
255 static Bitmap getBitmap(Cursor cursor, int columnIndex, Bitmap inBitmap) {
Jeff Hamilton84029622010-08-05 14:29:28 -0500256 byte[] data = cursor.getBlob(columnIndex);
257 if (data == null) {
258 return null;
The Android Open Source Project0c908882009-03-03 19:32:16 -0800259 }
John Reckf94abcf2011-10-10 15:33:48 -0700260 Options opts = sOptions.get();
261 opts.inBitmap = inBitmap;
262 opts.inSampleSize = 1;
263 opts.inScaled = false;
264 try {
265 return BitmapFactory.decodeByteArray(data, 0, data.length, opts);
266 } catch (IllegalArgumentException ex) {
267 // Failed to re-use bitmap, create a new one
268 return BitmapFactory.decodeByteArray(data, 0, data.length);
269 }
Jeff Hamilton84029622010-08-05 14:29:28 -0500270 }
271
John Reckc8490812010-11-22 14:15:36 -0800272 private MenuItem.OnMenuItemClickListener mContextItemClickListener =
273 new MenuItem.OnMenuItemClickListener() {
274 @Override
275 public boolean onMenuItemClick(MenuItem item) {
276 return onContextItemSelected(item);
277 }
278 };
279
Jeff Hamilton84029622010-08-05 14:29:28 -0500280 @Override
281 public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
John Reck71efc2b2011-05-09 16:54:28 -0700282 BookmarkContextMenuInfo info = (BookmarkContextMenuInfo) menuInfo;
283 BrowserBookmarksAdapter adapter = getChildAdapter(info.groupPosition);
284 Cursor cursor = adapter.getItem(info.childPosition);
John Reck1cebb442010-12-10 12:02:16 -0800285 if (!canEdit(cursor)) {
286 return;
287 }
Leon Scroggins5e8a40f2010-09-27 17:13:35 -0400288 boolean isFolder
289 = cursor.getInt(BookmarksLoader.COLUMN_INDEX_IS_FOLDER) != 0;
Michael Kolb801ecb72010-08-25 12:57:38 -0700290
Jeff Hamilton64144e42010-08-18 16:31:59 -0500291 final Activity activity = getActivity();
292 MenuInflater inflater = activity.getMenuInflater();
Jeff Hamilton84029622010-08-05 14:29:28 -0500293 inflater.inflate(R.menu.bookmarkscontext, menu);
John Reckc8490812010-11-22 14:15:36 -0800294 if (isFolder) {
295 menu.setGroupVisible(R.id.FOLDER_CONTEXT_MENU, true);
296 } else {
297 menu.setGroupVisible(R.id.BOOKMARK_CONTEXT_MENU, true);
298 if (mDisableNewWindow) {
299 menu.findItem(R.id.new_window_context_menu_id).setVisible(false);
300 }
Jeff Hamilton84029622010-08-05 14:29:28 -0500301 }
John Reckc8490812010-11-22 14:15:36 -0800302 BookmarkItem header = new BookmarkItem(activity);
John Reck83c01512011-09-09 11:14:16 -0700303 header.setEnableScrolling(true);
John Reckc8490812010-11-22 14:15:36 -0800304 populateBookmarkItem(cursor, header, isFolder);
John Reckc8490812010-11-22 14:15:36 -0800305 menu.setHeaderView(header);
Jeff Hamilton84029622010-08-05 14:29:28 -0500306
John Reckc8490812010-11-22 14:15:36 -0800307 int count = menu.size();
308 for (int i = 0; i < count; i++) {
309 menu.getItem(i).setOnMenuItemClickListener(mContextItemClickListener);
Jeff Hamilton84029622010-08-05 14:29:28 -0500310 }
Jeff Hamilton84029622010-08-05 14:29:28 -0500311 }
312
John Reck1cebb442010-12-10 12:02:16 -0800313 boolean canEdit(Cursor c) {
John Reckd18ac4b2012-04-12 17:27:34 -0700314 int type = c.getInt(BookmarksLoader.COLUMN_INDEX_TYPE);
315 return type == BrowserContract.Bookmarks.BOOKMARK_TYPE_BOOKMARK
316 || type == BrowserContract.Bookmarks.BOOKMARK_TYPE_FOLDER;
John Reck1cebb442010-12-10 12:02:16 -0800317 }
318
John Reckc8490812010-11-22 14:15:36 -0800319 private void populateBookmarkItem(Cursor cursor, BookmarkItem item, boolean isFolder) {
Jeff Hamilton84029622010-08-05 14:29:28 -0500320 item.setName(cursor.getString(BookmarksLoader.COLUMN_INDEX_TITLE));
John Reckc8490812010-11-22 14:15:36 -0800321 if (isFolder) {
322 item.setUrl(null);
323 Bitmap bitmap =
Enrico Rosd6efa972014-12-02 19:49:59 -0800324 BitmapFactory.decodeResource(getResources(), R.drawable.ic_deco_folder_normal);
John Reckc8490812010-11-22 14:15:36 -0800325 item.setFavicon(bitmap);
John Reck60e33362010-12-02 17:52:38 -0800326 new LookupBookmarkCount(getActivity(), item)
327 .execute(cursor.getLong(BookmarksLoader.COLUMN_INDEX_ID));
John Reckc8490812010-11-22 14:15:36 -0800328 } else {
329 String url = cursor.getString(BookmarksLoader.COLUMN_INDEX_URL);
330 item.setUrl(url);
331 Bitmap bitmap = getBitmap(cursor, BookmarksLoader.COLUMN_INDEX_FAVICON);
John Reckc8490812010-11-22 14:15:36 -0800332 item.setFavicon(bitmap);
Jeff Hamilton84029622010-08-05 14:29:28 -0500333 }
Jeff Hamilton84029622010-08-05 14:29:28 -0500334 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800335
336 /**
337 * Create a new BrowserBookmarksPage.
Nicolas Catania095292f2010-03-15 09:00:14 -0700338 */
The Android Open Source Project0c908882009-03-03 19:32:16 -0800339 @Override
Jeff Hamilton64144e42010-08-18 16:31:59 -0500340 public void onCreate(Bundle icicle) {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800341 super.onCreate(icicle);
John Reck4f61ee42011-08-22 16:31:14 -0700342 SharedPreferences prefs = BrowserSettings.getInstance().getPreferences();
343 try {
344 mState = new JSONObject(prefs.getString(PREF_GROUP_STATE, "{}"));
345 } catch (JSONException e) {
346 // Parse failed, clear preference and start with empty state
347 prefs.edit().remove(PREF_GROUP_STATE).apply();
348 mState = new JSONObject();
349 }
Jeff Hamilton64144e42010-08-18 16:31:59 -0500350 Bundle args = getArguments();
Leon Scrogginsbc922852010-10-22 12:15:27 -0400351 mDisableNewWindow = args == null ? false : args.getBoolean(EXTRA_DISABLE_WINDOW, false);
John Reck2d963a22011-08-10 15:53:07 -0700352 if (mCallbacks == null && getActivity() instanceof CombinedBookmarksCallbacks) {
353 mCallbacks = new CombinedBookmarksCallbackWrapper(
354 (CombinedBookmarksCallbacks) getActivity());
355 }
Jeff Hamilton64144e42010-08-18 16:31:59 -0500356 }
Leon Scrogginsd87f85e2009-08-18 14:13:31 -0400357
John Reck4f61ee42011-08-22 16:31:14 -0700358 @Override
359 public void onPause() {
360 super.onPause();
361 try {
362 mState = mGrid.saveGroupState();
363 // Save state
364 SharedPreferences prefs = BrowserSettings.getInstance().getPreferences();
365 prefs.edit()
366 .putString(PREF_GROUP_STATE, mState.toString())
367 .apply();
368 } catch (JSONException e) {
369 // Not critical, ignore
370 }
371 }
372
John Reck2d963a22011-08-10 15:53:07 -0700373 private static class CombinedBookmarksCallbackWrapper
374 implements BookmarksPageCallbacks {
375
376 private CombinedBookmarksCallbacks mCombinedCallback;
377
378 private CombinedBookmarksCallbackWrapper(CombinedBookmarksCallbacks cb) {
379 mCombinedCallback = cb;
380 }
381
382 @Override
383 public boolean onOpenInNewWindow(String... urls) {
384 mCombinedCallback.openInNewTab(urls);
385 return true;
386 }
387
388 @Override
389 public boolean onBookmarkSelected(Cursor c, boolean isFolder) {
390 if (isFolder) {
391 return false;
392 }
393 mCombinedCallback.openUrl(BrowserBookmarksPage.getUrl(c));
394 return true;
395 }
396 };
397
Jeff Hamilton64144e42010-08-18 16:31:59 -0500398 @Override
Jeff Hamilton64144e42010-08-18 16:31:59 -0500399 public View onCreateView(LayoutInflater inflater, ViewGroup container,
400 Bundle savedInstanceState) {
Pankaj Garg49b79252014-11-07 17:33:41 -0800401 final Activity activity = getActivity();
John Reck0ce8a942011-01-14 19:57:09 -0800402 mRoot = inflater.inflate(R.layout.bookmarks, container, false);
403 mEmptyView = mRoot.findViewById(android.R.id.empty);
Leon Scrogginsd87f85e2009-08-18 14:13:31 -0400404
John Reck9db95292011-06-20 13:00:12 -0700405 mGrid = (BookmarkExpandableView) mRoot.findViewById(R.id.grid);
John Reck71efc2b2011-05-09 16:54:28 -0700406 mGrid.setOnChildClickListener(this);
407 mGrid.setColumnWidthFromLayout(R.layout.bookmark_thumbnail);
408 mGrid.setBreadcrumbController(this);
John Reck608baa72010-11-24 10:32:28 -0800409 setEnableContextMenu(mEnableContextMenu);
410
Pankaj Garg49b79252014-11-07 17:33:41 -0800411 Button btn = (Button) mRoot.findViewById(R.id.add_bookmark_button);
412 btn.setOnClickListener(new View.OnClickListener() {
413 @Override
414 public void onClick(View view) {
415 Intent intent = new Intent(activity, AddBookmarkPage.class);
416 intent.putExtra(BrowserContract.Bookmarks.URL, "http://");
417 intent.putExtra(BrowserContract.Bookmarks.TITLE, "");
418 intent.putExtra(BrowserContract.Bookmarks.PARENT, mCurrentFolderId);
419 activity.startActivity(intent);
420 }
421 });
422
423 btn = (Button) mRoot.findViewById(R.id.new_bmfolder_button);
424 btn.setOnClickListener(new View.OnClickListener() {
425 @Override
426 public void onClick(View view) {
427 Intent intent = new Intent(activity, AddBookmarkFolder.class);
428 intent.putExtra(BrowserContract.Bookmarks.PARENT, mCurrentFolderId);
429 activity.startActivity(intent);
430 }
431 });
432
Jeff Hamiltondb90aa82010-09-16 03:38:04 -0500433 // Start the loaders
434 LoaderManager lm = getLoaderManager();
John Reck71efc2b2011-05-09 16:54:28 -0700435 lm.restartLoader(LOADER_ACCOUNTS, null, this);
Jeff Hamilton84029622010-08-05 14:29:28 -0500436
Dany Rybnikovb80159e2014-06-23 09:16:09 +0300437 //Notify about anticipated network activity
438 NetworkServices.HintUpcomingUserActivity();
439
John Reck0ce8a942011-01-14 19:57:09 -0800440 return mRoot;
Jeff Hamilton64144e42010-08-18 16:31:59 -0500441 }
Michael Kolb801ecb72010-08-25 12:57:38 -0700442
John Reck608baa72010-11-24 10:32:28 -0800443 @Override
444 public void onDestroyView() {
445 super.onDestroyView();
John Reck71efc2b2011-05-09 16:54:28 -0700446 mGrid.setBreadcrumbController(null);
John Reckc3c46d72011-09-06 16:17:34 -0700447 mGrid.clearAccounts();
John Reck71efc2b2011-05-09 16:54:28 -0700448 LoaderManager lm = getLoaderManager();
449 lm.destroyLoader(LOADER_ACCOUNTS);
450 for (int id : mBookmarkAdapters.keySet()) {
Tarun Nainaniebdf18e2014-07-09 19:35:30 -0700451 mBookmarkAdapters.get(id).quitThread();
John Reck71efc2b2011-05-09 16:54:28 -0700452 lm.destroyLoader(id);
John Reck608baa72010-11-24 10:32:28 -0800453 }
John Reck71efc2b2011-05-09 16:54:28 -0700454 mBookmarkAdapters.clear();
John Reck71efc2b2011-05-09 16:54:28 -0700455 }
456
457 private BrowserBookmarksAdapter getChildAdapter(int groupPosition) {
John Reck9db95292011-06-20 13:00:12 -0700458 return mGrid.getChildAdapter(groupPosition);
John Reck71efc2b2011-05-09 16:54:28 -0700459 }
460
461 private BreadCrumbView getBreadCrumbs(int groupPosition) {
John Reck9db95292011-06-20 13:00:12 -0700462 return mGrid.getBreadCrumbs(groupPosition);
Nicolas Catania095292f2010-03-15 09:00:14 -0700463 }
464
Jeff Hamilton84029622010-08-05 14:29:28 -0500465 @Override
John Reck71efc2b2011-05-09 16:54:28 -0700466 public boolean onChildClick(ExpandableListView parent, View v,
467 int groupPosition, int childPosition, long id) {
468 BrowserBookmarksAdapter adapter = getChildAdapter(groupPosition);
469 Cursor cursor = adapter.getItem(childPosition);
John Reck608baa72010-11-24 10:32:28 -0800470 boolean isFolder = cursor.getInt(BookmarksLoader.COLUMN_INDEX_IS_FOLDER) != 0;
471 if (mCallbacks != null &&
472 mCallbacks.onBookmarkSelected(cursor, isFolder)) {
John Reck71efc2b2011-05-09 16:54:28 -0700473 return true;
Ben Murdoch328ea872009-09-16 13:33:29 +0100474 }
475
John Reck608baa72010-11-24 10:32:28 -0800476 if (isFolder) {
Michael Kolb370a4f32010-10-06 10:45:32 -0700477 String title = cursor.getString(BookmarksLoader.COLUMN_INDEX_TITLE);
Jeff Hamilton84029622010-08-05 14:29:28 -0500478 Uri uri = ContentUris.withAppendedId(
479 BrowserContract.Bookmarks.CONTENT_URI_DEFAULT_FOLDER, id);
John Reck71efc2b2011-05-09 16:54:28 -0700480 BreadCrumbView crumbs = getBreadCrumbs(groupPosition);
481 if (crumbs != null) {
Michael Kolb370a4f32010-10-06 10:45:32 -0700482 // update crumbs
John Reck71efc2b2011-05-09 16:54:28 -0700483 crumbs.pushView(title, uri);
John Reckcece1fe2011-09-13 08:47:17 -0700484 crumbs.setVisibility(View.VISIBLE);
luxiaoldbe4a622013-07-19 17:14:06 +0800485 Object data = crumbs.getTopData();
486 mCurrentFolderId = (data != null ? ContentUris.parseId((Uri) data)
487 : DEFAULT_FOLDER_ID);
Michael Kolb370a4f32010-10-06 10:45:32 -0700488 }
John Reck71efc2b2011-05-09 16:54:28 -0700489 loadFolder(groupPosition, uri);
Leon Scrogginsa5d669e2009-08-05 14:07:58 -0400490 }
John Reck71efc2b2011-05-09 16:54:28 -0700491 return true;
The Android Open Source Project0c908882009-03-03 19:32:16 -0800492 }
493
John Reck608baa72010-11-24 10:32:28 -0800494 /* package */ static Intent createShortcutIntent(Context context, Cursor cursor) {
Jeff Hamilton84029622010-08-05 14:29:28 -0500495 String url = cursor.getString(BookmarksLoader.COLUMN_INDEX_URL);
Michael Kolbccaa8962010-10-20 17:03:10 -0700496 String title = cursor.getString(BookmarksLoader.COLUMN_INDEX_TITLE);
Jeff Hamilton84029622010-08-05 14:29:28 -0500497 Bitmap touchIcon = getBitmap(cursor, BookmarksLoader.COLUMN_INDEX_TOUCH_ICON);
498 Bitmap favicon = getBitmap(cursor, BookmarksLoader.COLUMN_INDEX_FAVICON);
John Reck608baa72010-11-24 10:32:28 -0800499 return BookmarkUtils.createAddToHomeIntent(context, url, title, touchIcon, favicon);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800500 }
501
John Reck71efc2b2011-05-09 16:54:28 -0700502 private void loadUrl(BrowserBookmarksAdapter adapter, int position) {
503 if (mCallbacks != null && adapter != null) {
504 mCallbacks.onBookmarkSelected(adapter.getItem(position), false);
John Reck608baa72010-11-24 10:32:28 -0800505 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800506 }
507
John Reck71efc2b2011-05-09 16:54:28 -0700508 private void openInNewWindow(BrowserBookmarksAdapter adapter, int position) {
John Reck608baa72010-11-24 10:32:28 -0800509 if (mCallbacks != null) {
John Reck71efc2b2011-05-09 16:54:28 -0700510 Cursor c = adapter.getItem(position);
John Reck1b3c66e2011-01-11 13:20:32 -0800511 boolean isFolder = c.getInt(BookmarksLoader.COLUMN_INDEX_IS_FOLDER) == 1;
512 if (isFolder) {
513 long id = c.getLong(BookmarksLoader.COLUMN_INDEX_ID);
514 new OpenAllInTabsTask(id).execute();
515 } else {
John Reckd3e4d5b2011-07-13 15:48:43 -0700516 mCallbacks.onOpenInNewWindow(BrowserBookmarksPage.getUrl(c));
John Reck1b3c66e2011-01-11 13:20:32 -0800517 }
John Reck608baa72010-11-24 10:32:28 -0800518 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800519 }
Nicolas Catania095292f2010-03-15 09:00:14 -0700520
John Reck1b3c66e2011-01-11 13:20:32 -0800521 class OpenAllInTabsTask extends AsyncTask<Void, Void, Cursor> {
522 long mFolderId;
523 public OpenAllInTabsTask(long id) {
524 mFolderId = id;
525 }
526
527 @Override
528 protected Cursor doInBackground(Void... params) {
529 Context c = getActivity();
530 if (c == null) return null;
531 return c.getContentResolver().query(BookmarkUtils.getBookmarksUri(c),
532 BookmarksLoader.PROJECTION, BrowserContract.Bookmarks.PARENT + "=?",
533 new String[] { Long.toString(mFolderId) }, null);
534 }
535
536 @Override
537 protected void onPostExecute(Cursor result) {
John Reckd3e4d5b2011-07-13 15:48:43 -0700538 if (mCallbacks != null && result.getCount() > 0) {
539 String[] urls = new String[result.getCount()];
540 int i = 0;
John Reck1b3c66e2011-01-11 13:20:32 -0800541 while (result.moveToNext()) {
John Reckd3e4d5b2011-07-13 15:48:43 -0700542 urls[i++] = BrowserBookmarksPage.getUrl(result);
John Reck1b3c66e2011-01-11 13:20:32 -0800543 }
John Reckd3e4d5b2011-07-13 15:48:43 -0700544 mCallbacks.onOpenInNewWindow(urls);
John Reck1b3c66e2011-01-11 13:20:32 -0800545 }
546 }
547
548 }
549
John Reck71efc2b2011-05-09 16:54:28 -0700550 private void editBookmark(BrowserBookmarksAdapter adapter, int position) {
Jeff Hamilton64144e42010-08-18 16:31:59 -0500551 Intent intent = new Intent(getActivity(), AddBookmarkPage.class);
John Reck71efc2b2011-05-09 16:54:28 -0700552 Cursor cursor = adapter.getItem(position);
Jeff Hamilton84029622010-08-05 14:29:28 -0500553 Bundle item = new Bundle();
Michael Kolbbef7a752010-08-25 17:11:35 -0700554 item.putString(BrowserContract.Bookmarks.TITLE,
Jeff Hamilton84029622010-08-05 14:29:28 -0500555 cursor.getString(BookmarksLoader.COLUMN_INDEX_TITLE));
Michael Kolbbef7a752010-08-25 17:11:35 -0700556 item.putString(BrowserContract.Bookmarks.URL,
Jeff Hamilton84029622010-08-05 14:29:28 -0500557 cursor.getString(BookmarksLoader.COLUMN_INDEX_URL));
558 byte[] data = cursor.getBlob(BookmarksLoader.COLUMN_INDEX_FAVICON);
559 if (data != null) {
Michael Kolbbef7a752010-08-25 17:11:35 -0700560 item.putParcelable(BrowserContract.Bookmarks.FAVICON,
Jeff Hamilton84029622010-08-05 14:29:28 -0500561 BitmapFactory.decodeByteArray(data, 0, data.length));
562 }
John Reckc8490812010-11-22 14:15:36 -0800563 item.putLong(BrowserContract.Bookmarks._ID,
564 cursor.getLong(BookmarksLoader.COLUMN_INDEX_ID));
Leon Scroggins III052ce662010-09-13 14:44:16 -0400565 item.putLong(BrowserContract.Bookmarks.PARENT,
566 cursor.getLong(BookmarksLoader.COLUMN_INDEX_PARENT));
John Reckc8490812010-11-22 14:15:36 -0800567 intent.putExtra(AddBookmarkPage.EXTRA_EDIT_BOOKMARK, item);
568 intent.putExtra(AddBookmarkPage.EXTRA_IS_FOLDER,
569 cursor.getInt(BookmarksLoader.COLUMN_INDEX_IS_FOLDER) == 1);
570 startActivity(intent);
Jeff Hamilton84029622010-08-05 14:29:28 -0500571 }
572
John Reck71efc2b2011-05-09 16:54:28 -0700573 private void displayRemoveBookmarkDialog(BrowserBookmarksAdapter adapter,
574 int position) {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800575 // Put up a dialog asking if the user really wants to
576 // delete the bookmark
John Reck71efc2b2011-05-09 16:54:28 -0700577 Cursor cursor = adapter.getItem(position);
Leon Scroggins75630672011-01-13 17:56:15 -0500578 long id = cursor.getLong(BookmarksLoader.COLUMN_INDEX_ID);
579 String title = cursor.getString(BookmarksLoader.COLUMN_INDEX_TITLE);
Jeff Hamilton64144e42010-08-18 16:31:59 -0500580 Context context = getActivity();
Leon Scroggins75630672011-01-13 17:56:15 -0500581 BookmarkUtils.displayRemoveBookmarkDialog(id, title, context, null);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800582 }
583
John Reck71efc2b2011-05-09 16:54:28 -0700584 private String getUrl(BrowserBookmarksAdapter adapter, int position) {
585 return getUrl(adapter.getItem(position));
John Reck608baa72010-11-24 10:32:28 -0800586 }
587
588 /* package */ static String getUrl(Cursor c) {
589 return c.getString(BookmarksLoader.COLUMN_INDEX_URL);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800590 }
Michael Kolb801ecb72010-08-25 12:57:38 -0700591
The Android Open Source Project0c908882009-03-03 19:32:16 -0800592 private void copy(CharSequence text) {
Jeff Hamilton64144e42010-08-18 16:31:59 -0500593 ClipboardManager cm = (ClipboardManager) getActivity().getSystemService(
594 Context.CLIPBOARD_SERVICE);
Dianne Hackbornbf1d10a2011-01-17 12:29:51 -0800595 cm.setPrimaryClip(ClipData.newRawUri(null, Uri.parse(text.toString())));
The Android Open Source Project0c908882009-03-03 19:32:16 -0800596 }
Michael Kolb370a4f32010-10-06 10:45:32 -0700597
John Reckb3417f02011-01-14 11:01:05 -0800598 @Override
John Reckb3417f02011-01-14 11:01:05 -0800599 public void onConfigurationChanged(Configuration newConfig) {
600 super.onConfigurationChanged(newConfig);
601 Resources res = getActivity().getResources();
John Reck71efc2b2011-05-09 16:54:28 -0700602 mGrid.setColumnWidthFromLayout(R.layout.bookmark_thumbnail);
John Reck0ce8a942011-01-14 19:57:09 -0800603 int paddingTop = (int) res.getDimension(R.dimen.combo_paddingTop);
John Reckf3828cd2011-06-14 17:21:55 -0700604 mRoot.setPadding(0, paddingTop, 0, 0);
John Reckb3417f02011-01-14 11:01:05 -0800605 }
606
John Reck608baa72010-11-24 10:32:28 -0800607 /**
608 * BreadCrumb controller callback
609 */
610 @Override
John Reck71efc2b2011-05-09 16:54:28 -0700611 public void onTop(BreadCrumbView view, int level, Object data) {
612 int groupPosition = (Integer) view.getTag(R.id.group_position);
John Reck89f73c12010-12-01 10:10:14 -0800613 Uri uri = (Uri) data;
614 if (uri == null) {
615 // top level
616 uri = BrowserContract.Bookmarks.CONTENT_URI_DEFAULT_FOLDER;
617 }
John Reck71efc2b2011-05-09 16:54:28 -0700618 loadFolder(groupPosition, uri);
John Reckcece1fe2011-09-13 08:47:17 -0700619 if (level <= 1) {
620 view.setVisibility(View.GONE);
621 } else {
622 view.setVisibility(View.VISIBLE);
623 }
John Reck89f73c12010-12-01 10:10:14 -0800624 }
625
626 /**
627 * @param uri
628 */
John Reck71efc2b2011-05-09 16:54:28 -0700629 private void loadFolder(int groupPosition, Uri uri) {
John Reck89f73c12010-12-01 10:10:14 -0800630 LoaderManager manager = getLoaderManager();
John Reck71efc2b2011-05-09 16:54:28 -0700631 // This assumes groups are ordered the same as loaders
632 BookmarksLoader loader = (BookmarksLoader) ((Loader<?>)
633 manager.getLoader(LOADER_BOOKMARKS + groupPosition));
John Reck89f73c12010-12-01 10:10:14 -0800634 loader.setUri(uri);
635 loader.forceLoad();
John Reck608baa72010-11-24 10:32:28 -0800636 }
637
John Reck608baa72010-11-24 10:32:28 -0800638 public void setCallbackListener(BookmarksPageCallbacks callbackListener) {
639 mCallbacks = callbackListener;
640 }
641
642 public void setEnableContextMenu(boolean enable) {
643 mEnableContextMenu = enable;
644 if (mGrid != null) {
645 if (mEnableContextMenu) {
646 registerForContextMenu(mGrid);
647 } else {
648 unregisterForContextMenu(mGrid);
649 mGrid.setLongClickable(false);
650 }
651 }
John Reck608baa72010-11-24 10:32:28 -0800652 }
John Reckc8490812010-11-22 14:15:36 -0800653
654 private static class LookupBookmarkCount extends AsyncTask<Long, Void, Integer> {
655 Context mContext;
656 BookmarkItem mHeader;
657
658 public LookupBookmarkCount(Context context, BookmarkItem header) {
Ben Murdoch914c5592011-08-01 13:58:47 +0100659 mContext = context.getApplicationContext();
John Reckc8490812010-11-22 14:15:36 -0800660 mHeader = header;
661 }
662
663 @Override
664 protected Integer doInBackground(Long... params) {
665 if (params.length != 1) {
666 throw new IllegalArgumentException("Missing folder id!");
667 }
668 Uri uri = BookmarkUtils.getBookmarksUri(mContext);
Axesh R. Ajmera632e5a02014-11-13 09:52:46 -0800669 Cursor c = null;
670 try {
671 c = mContext.getContentResolver().query(uri,
672 null, BrowserContract.Bookmarks.PARENT + "=?",
673 new String[] {params[0].toString()}, null);
674 return c.getCount();
675 } finally {
676 if (c != null) {
677 c.close();
678 }
679 }
John Reckc8490812010-11-22 14:15:36 -0800680 }
681
682 @Override
683 protected void onPostExecute(Integer result) {
684 if (result > 0) {
685 mHeader.setUrl(mContext.getString(R.string.contextheader_folder_bookmarkcount,
686 result));
687 } else if (result == 0) {
688 mHeader.setUrl(mContext.getString(R.string.contextheader_folder_empty));
689 }
690 }
691 }
John Reck89f73c12010-12-01 10:10:14 -0800692
John Reck71efc2b2011-05-09 16:54:28 -0700693 static class AccountsLoader extends CursorLoader {
John Reck89f73c12010-12-01 10:10:14 -0800694
John Reck71efc2b2011-05-09 16:54:28 -0700695 static String[] ACCOUNTS_PROJECTION = new String[] {
696 Accounts.ACCOUNT_NAME,
697 Accounts.ACCOUNT_TYPE
698 };
John Reck89f73c12010-12-01 10:10:14 -0800699
John Reck71efc2b2011-05-09 16:54:28 -0700700 public AccountsLoader(Context context) {
John Reck1e9815d2011-08-08 17:45:05 -0700701 super(context, Accounts.CONTENT_URI
702 .buildUpon()
703 .appendQueryParameter(BrowserProvider2.PARAM_ALLOW_EMPTY_ACCOUNTS, "false")
704 .build(),
705 ACCOUNTS_PROJECTION, null, null, null);
John Reck89f73c12010-12-01 10:10:14 -0800706 }
John Reck522a4382011-01-26 13:23:47 -0800707
John Reck522a4382011-01-26 13:23:47 -0800708 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800709}