blob: 16eae77bcc293df477c0a4f1a212daff54fed680 [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;
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
Bijan Amirzada41242f22014-03-21 12:12:18 -070052import com.android.browser.R;
53import com.android.browser.platformsupport.BrowserContract;
54import com.android.browser.platformsupport.BrowserContract.Accounts;
55import com.android.browser.provider.BrowserProvider2;
56import com.android.browser.view.BookmarkExpandableView;
57import com.android.browser.view.BookmarkExpandableView.BookmarkContextMenuInfo;
Bijan Amirzada9b1e9882014-02-26 17:15:46 -080058
59import java.util.HashMap;
John Recke7c97de2011-05-27 14:42:43 -070060
John Reck4f61ee42011-08-22 16:31:14 -070061import org.json.JSONException;
62import org.json.JSONObject;
63
Dany Rybnikovb80159e2014-06-23 09:16:09 +030064import org.codeaurora.net.NetworkServices;
Dany Rybnikov535d4af2014-06-23 09:16:09 +030065
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
luxiaoldbe4a622013-07-19 17:14:06 +080096 static final long DEFAULT_FOLDER_ID = -1;
97
John Reck608baa72010-11-24 10:32:28 -080098 BookmarksPageCallbacks mCallbacks;
John Reck0ce8a942011-01-14 19:57:09 -080099 View mRoot;
John Reck9db95292011-06-20 13:00:12 -0700100 BookmarkExpandableView mGrid;
Jeff Hamilton84029622010-08-05 14:29:28 -0500101 boolean mDisableNewWindow;
John Reck608baa72010-11-24 10:32:28 -0800102 boolean mEnableContextMenu = true;
Jeff Hamilton84029622010-08-05 14:29:28 -0500103 View mEmptyView;
John Reck608baa72010-11-24 10:32:28 -0800104 View mHeader;
John Reck71efc2b2011-05-09 16:54:28 -0700105 HashMap<Integer, BrowserBookmarksAdapter> mBookmarkAdapters = new HashMap<Integer, BrowserBookmarksAdapter>();
John Reck4f61ee42011-08-22 16:31:14 -0700106 JSONObject mState;
luxiaoldbe4a622013-07-19 17:14:06 +0800107 long mCurrentFolderId = BrowserProvider2.FIXED_ID_ROOT;
Michael Kolb370a4f32010-10-06 10:45:32 -0700108
Jeff Hamilton84029622010-08-05 14:29:28 -0500109 @Override
110 public Loader<Cursor> onCreateLoader(int id, Bundle args) {
John Reck71efc2b2011-05-09 16:54:28 -0700111 if (id == LOADER_ACCOUNTS) {
112 return new AccountsLoader(getActivity());
113 } else if (id >= LOADER_BOOKMARKS) {
114 String accountType = args.getString(ACCOUNT_TYPE);
115 String accountName = args.getString(ACCOUNT_NAME);
116 BookmarksLoader bl = new BookmarksLoader(getActivity(),
117 accountType, accountName);
118 return bl;
119 } else {
120 throw new UnsupportedOperationException("Unknown loader id " + id);
Jeff Hamilton84029622010-08-05 14:29:28 -0500121 }
Jeff Hamilton84029622010-08-05 14:29:28 -0500122 }
123
124 @Override
Jeff Hamiltondb90aa82010-09-16 03:38:04 -0500125 public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
John Reck71efc2b2011-05-09 16:54:28 -0700126 if (loader.getId() == LOADER_ACCOUNTS) {
127 LoaderManager lm = getLoaderManager();
128 int id = LOADER_BOOKMARKS;
129 while (cursor.moveToNext()) {
130 String accountName = cursor.getString(0);
131 String accountType = cursor.getString(1);
132 Bundle args = new Bundle();
133 args.putString(ACCOUNT_NAME, accountName);
134 args.putString(ACCOUNT_TYPE, accountType);
135 BrowserBookmarksAdapter adapter = new BrowserBookmarksAdapter(
John Reckaf991f92012-04-24 15:31:16 -0700136 getActivity());
John Reck71efc2b2011-05-09 16:54:28 -0700137 mBookmarkAdapters.put(id, adapter);
John Reck4f61ee42011-08-22 16:31:14 -0700138 boolean expand = true;
139 try {
140 expand = mState.getBoolean(accountName != null ? accountName
141 : BookmarkExpandableView.LOCAL_ACCOUNT_NAME);
142 } catch (JSONException e) {} // no state for accountName
143 mGrid.addAccount(accountName, adapter, expand);
John Reck71efc2b2011-05-09 16:54:28 -0700144 lm.restartLoader(id, args, this);
145 id++;
Jeff Hamiltondb90aa82010-09-16 03:38:04 -0500146 }
John Reck71efc2b2011-05-09 16:54:28 -0700147 // TODO: Figure out what a reload of these means
148 // Currently, a reload is triggered whenever bookmarks change
149 // This is less than ideal
150 // It also causes UI flickering as a new adapter is created
151 // instead of re-using an existing one when the account_name is the
152 // same.
153 // For now, this is a one-shot load
154 getLoaderManager().destroyLoader(LOADER_ACCOUNTS);
155 } else if (loader.getId() >= LOADER_BOOKMARKS) {
156 BrowserBookmarksAdapter adapter = mBookmarkAdapters.get(loader.getId());
157 adapter.changeCursor(cursor);
luxiaoldbe4a622013-07-19 17:14:06 +0800158 if (adapter.getCount() != 0) {
159 mCurrentFolderId = adapter.getItem(0).getLong(BookmarksLoader.COLUMN_INDEX_PARENT);
160 }
Jeff Hamiltondb90aa82010-09-16 03:38:04 -0500161 }
Jeff Hamilton84029622010-08-05 14:29:28 -0500162 }
163
John Reck522a4382011-01-26 13:23:47 -0800164 @Override
Dianne Hackborn39772c82010-12-16 00:43:54 -0800165 public void onLoaderReset(Loader<Cursor> loader) {
John Reck3ad8d5c2012-04-23 14:50:09 -0700166 if (loader.getId() >= LOADER_BOOKMARKS) {
167 BrowserBookmarksAdapter adapter = mBookmarkAdapters.get(loader.getId());
168 adapter.changeCursor(null);
169 }
Dianne Hackborn39772c82010-12-16 00:43:54 -0800170 }
171
luxiaoldbe4a622013-07-19 17:14:06 +0800172 //add for carrier feature which adds new bookmark/folder function.
173 @Override
174 public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
175 super.onCreateOptionsMenu(menu, inflater);
176 inflater.inflate(R.menu.bookmark, menu);
177 }
178
179 @Override
180 public boolean onOptionsItemSelected(MenuItem item) {
181 final Activity activity = getActivity();
182 if (item.getItemId() == R.id.add_bookmark_menu_id) {
183 Intent intent = new Intent(activity, AddBookmarkPage.class);
184 intent.putExtra(BrowserContract.Bookmarks.URL, "http://");
185 intent.putExtra(BrowserContract.Bookmarks.TITLE, "");
186 intent.putExtra(BrowserContract.Bookmarks.PARENT, mCurrentFolderId);
187 activity.startActivity(intent);
188 }
189 if (item.getItemId() == R.id.new_bmfolder_menu_id) {
190 Intent intent = new Intent(activity, AddBookmarkFolder.class);
191 intent.putExtra(BrowserContract.Bookmarks.PARENT, mCurrentFolderId);
192 activity.startActivity(intent);
193 }
194 return super.onOptionsItemSelected(item);
195 }
196
The Android Open Source Project0c908882009-03-03 19:32:16 -0800197 @Override
198 public boolean onContextItemSelected(MenuItem item) {
John Reck54ed3aa2011-08-19 15:16:05 -0700199 if (!(item.getMenuInfo() instanceof BookmarkContextMenuInfo)) {
200 return false;
201 }
202 BookmarkContextMenuInfo i = (BookmarkContextMenuInfo) item.getMenuInfo();
The Android Open Source Project0c908882009-03-03 19:32:16 -0800203 // If we have no menu info, we can't tell which item was selected.
204 if (i == null) {
Leon Scrogginsc8cae082010-11-03 18:51:46 -0400205 return false;
The Android Open Source Project0c908882009-03-03 19:32:16 -0800206 }
Nicolas Catania095292f2010-03-15 09:00:14 -0700207
John Recke7c97de2011-05-27 14:42:43 -0700208 if (handleContextItem(item.getItemId(), i.groupPosition, i.childPosition)) {
209 return true;
210 }
211 return super.onContextItemSelected(item);
212 }
213
214 public boolean handleContextItem(int itemId, int groupPosition,
215 int childPosition) {
216 final Activity activity = getActivity();
217 BrowserBookmarksAdapter adapter = getChildAdapter(groupPosition);
218
219 switch (itemId) {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800220 case R.id.open_context_menu_id:
John Recke7c97de2011-05-27 14:42:43 -0700221 loadUrl(adapter, childPosition);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800222 break;
223 case R.id.edit_context_menu_id:
John Recke7c97de2011-05-27 14:42:43 -0700224 editBookmark(adapter, childPosition);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800225 break;
226 case R.id.shortcut_context_menu_id:
John Recke7c97de2011-05-27 14:42:43 -0700227 Cursor c = adapter.getItem(childPosition);
John Reck608baa72010-11-24 10:32:28 -0800228 activity.sendBroadcast(createShortcutIntent(getActivity(), c));
The Android Open Source Project0c908882009-03-03 19:32:16 -0800229 break;
230 case R.id.delete_context_menu_id:
John Recke7c97de2011-05-27 14:42:43 -0700231 displayRemoveBookmarkDialog(adapter, childPosition);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800232 break;
233 case R.id.new_window_context_menu_id:
John Recke7c97de2011-05-27 14:42:43 -0700234 openInNewWindow(adapter, childPosition);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800235 break;
Jeff Hamilton84029622010-08-05 14:29:28 -0500236 case R.id.share_link_context_menu_id: {
John Recke7c97de2011-05-27 14:42:43 -0700237 Cursor cursor = adapter.getItem(childPosition);
Michael Kolb8233fac2010-10-26 16:08:53 -0700238 Controller.sharePage(activity,
Jeff Hamilton84029622010-08-05 14:29:28 -0500239 cursor.getString(BookmarksLoader.COLUMN_INDEX_TITLE),
240 cursor.getString(BookmarksLoader.COLUMN_INDEX_URL),
241 getBitmap(cursor, BookmarksLoader.COLUMN_INDEX_FAVICON),
242 getBitmap(cursor, BookmarksLoader.COLUMN_INDEX_THUMBNAIL));
The Android Open Source Project0c908882009-03-03 19:32:16 -0800243 break;
Jeff Hamilton84029622010-08-05 14:29:28 -0500244 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800245 case R.id.copy_url_context_menu_id:
John Recke7c97de2011-05-27 14:42:43 -0700246 copy(getUrl(adapter, childPosition));
Leon Scrogginsfeb941d2009-05-28 17:27:38 -0400247 break;
Jeff Hamilton84029622010-08-05 14:29:28 -0500248 case R.id.homepage_context_menu_id: {
John Recke7c97de2011-05-27 14:42:43 -0700249 BrowserSettings.getInstance().setHomePage(getUrl(adapter, childPosition));
Jeff Hamilton64144e42010-08-18 16:31:59 -0500250 Toast.makeText(activity, R.string.homepage_set, Toast.LENGTH_LONG).show();
Leon Scrogginsfeb941d2009-05-28 17:27:38 -0400251 break;
Jeff Hamilton84029622010-08-05 14:29:28 -0500252 }
Leon Scrogginsa5d669e2009-08-05 14:07:58 -0400253 // Only for the Most visited page
Jeff Hamilton84029622010-08-05 14:29:28 -0500254 case R.id.save_to_bookmarks_menu_id: {
John Recke7c97de2011-05-27 14:42:43 -0700255 Cursor cursor = adapter.getItem(childPosition);
Jeff Hamilton84029622010-08-05 14:29:28 -0500256 String name = cursor.getString(BookmarksLoader.COLUMN_INDEX_TITLE);
257 String url = cursor.getString(BookmarksLoader.COLUMN_INDEX_URL);
Leon Scrogginsa5d669e2009-08-05 14:07:58 -0400258 // If the site is bookmarked, the item becomes remove from
259 // bookmarks.
Jeff Hamilton64144e42010-08-18 16:31:59 -0500260 Bookmarks.removeFromBookmarks(activity, activity.getContentResolver(), url, name);
Leon Scrogginsa5d669e2009-08-05 14:07:58 -0400261 break;
Jeff Hamilton84029622010-08-05 14:29:28 -0500262 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800263 default:
John Recke7c97de2011-05-27 14:42:43 -0700264 return false;
The Android Open Source Project0c908882009-03-03 19:32:16 -0800265 }
266 return true;
267 }
268
John Reck608baa72010-11-24 10:32:28 -0800269 static Bitmap getBitmap(Cursor cursor, int columnIndex) {
John Reckf94abcf2011-10-10 15:33:48 -0700270 return getBitmap(cursor, columnIndex, null);
271 }
272
273 static ThreadLocal<Options> sOptions = new ThreadLocal<Options>() {
274 @Override
275 protected Options initialValue() {
276 return new Options();
277 };
278 };
279 static Bitmap getBitmap(Cursor cursor, int columnIndex, Bitmap inBitmap) {
Jeff Hamilton84029622010-08-05 14:29:28 -0500280 byte[] data = cursor.getBlob(columnIndex);
281 if (data == null) {
282 return null;
The Android Open Source Project0c908882009-03-03 19:32:16 -0800283 }
John Reckf94abcf2011-10-10 15:33:48 -0700284 Options opts = sOptions.get();
285 opts.inBitmap = inBitmap;
286 opts.inSampleSize = 1;
287 opts.inScaled = false;
288 try {
289 return BitmapFactory.decodeByteArray(data, 0, data.length, opts);
290 } catch (IllegalArgumentException ex) {
291 // Failed to re-use bitmap, create a new one
292 return BitmapFactory.decodeByteArray(data, 0, data.length);
293 }
Jeff Hamilton84029622010-08-05 14:29:28 -0500294 }
295
John Reckc8490812010-11-22 14:15:36 -0800296 private MenuItem.OnMenuItemClickListener mContextItemClickListener =
297 new MenuItem.OnMenuItemClickListener() {
298 @Override
299 public boolean onMenuItemClick(MenuItem item) {
300 return onContextItemSelected(item);
301 }
302 };
303
Jeff Hamilton84029622010-08-05 14:29:28 -0500304 @Override
305 public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
John Reck71efc2b2011-05-09 16:54:28 -0700306 BookmarkContextMenuInfo info = (BookmarkContextMenuInfo) menuInfo;
307 BrowserBookmarksAdapter adapter = getChildAdapter(info.groupPosition);
308 Cursor cursor = adapter.getItem(info.childPosition);
John Reck1cebb442010-12-10 12:02:16 -0800309 if (!canEdit(cursor)) {
310 return;
311 }
Leon Scroggins5e8a40f2010-09-27 17:13:35 -0400312 boolean isFolder
313 = cursor.getInt(BookmarksLoader.COLUMN_INDEX_IS_FOLDER) != 0;
Michael Kolb801ecb72010-08-25 12:57:38 -0700314
Jeff Hamilton64144e42010-08-18 16:31:59 -0500315 final Activity activity = getActivity();
316 MenuInflater inflater = activity.getMenuInflater();
Jeff Hamilton84029622010-08-05 14:29:28 -0500317 inflater.inflate(R.menu.bookmarkscontext, menu);
John Reckc8490812010-11-22 14:15:36 -0800318 if (isFolder) {
319 menu.setGroupVisible(R.id.FOLDER_CONTEXT_MENU, true);
320 } else {
321 menu.setGroupVisible(R.id.BOOKMARK_CONTEXT_MENU, true);
322 if (mDisableNewWindow) {
323 menu.findItem(R.id.new_window_context_menu_id).setVisible(false);
324 }
Jeff Hamilton84029622010-08-05 14:29:28 -0500325 }
John Reckc8490812010-11-22 14:15:36 -0800326 BookmarkItem header = new BookmarkItem(activity);
John Reck83c01512011-09-09 11:14:16 -0700327 header.setEnableScrolling(true);
John Reckc8490812010-11-22 14:15:36 -0800328 populateBookmarkItem(cursor, header, isFolder);
John Reckc8490812010-11-22 14:15:36 -0800329 menu.setHeaderView(header);
Jeff Hamilton84029622010-08-05 14:29:28 -0500330
John Reckc8490812010-11-22 14:15:36 -0800331 int count = menu.size();
332 for (int i = 0; i < count; i++) {
333 menu.getItem(i).setOnMenuItemClickListener(mContextItemClickListener);
Jeff Hamilton84029622010-08-05 14:29:28 -0500334 }
Jeff Hamilton84029622010-08-05 14:29:28 -0500335 }
336
John Reck1cebb442010-12-10 12:02:16 -0800337 boolean canEdit(Cursor c) {
John Reckd18ac4b2012-04-12 17:27:34 -0700338 int type = c.getInt(BookmarksLoader.COLUMN_INDEX_TYPE);
339 return type == BrowserContract.Bookmarks.BOOKMARK_TYPE_BOOKMARK
340 || type == BrowserContract.Bookmarks.BOOKMARK_TYPE_FOLDER;
John Reck1cebb442010-12-10 12:02:16 -0800341 }
342
John Reckc8490812010-11-22 14:15:36 -0800343 private void populateBookmarkItem(Cursor cursor, BookmarkItem item, boolean isFolder) {
Jeff Hamilton84029622010-08-05 14:29:28 -0500344 item.setName(cursor.getString(BookmarksLoader.COLUMN_INDEX_TITLE));
John Reckc8490812010-11-22 14:15:36 -0800345 if (isFolder) {
346 item.setUrl(null);
347 Bitmap bitmap =
Michael Kolb5a72f182011-01-13 20:35:06 -0800348 BitmapFactory.decodeResource(getResources(), R.drawable.ic_folder_holo_dark);
John Reckc8490812010-11-22 14:15:36 -0800349 item.setFavicon(bitmap);
John Reck60e33362010-12-02 17:52:38 -0800350 new LookupBookmarkCount(getActivity(), item)
351 .execute(cursor.getLong(BookmarksLoader.COLUMN_INDEX_ID));
John Reckc8490812010-11-22 14:15:36 -0800352 } else {
353 String url = cursor.getString(BookmarksLoader.COLUMN_INDEX_URL);
354 item.setUrl(url);
355 Bitmap bitmap = getBitmap(cursor, BookmarksLoader.COLUMN_INDEX_FAVICON);
John Reckc8490812010-11-22 14:15:36 -0800356 item.setFavicon(bitmap);
Jeff Hamilton84029622010-08-05 14:29:28 -0500357 }
Jeff Hamilton84029622010-08-05 14:29:28 -0500358 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800359
360 /**
361 * Create a new BrowserBookmarksPage.
Nicolas Catania095292f2010-03-15 09:00:14 -0700362 */
The Android Open Source Project0c908882009-03-03 19:32:16 -0800363 @Override
Jeff Hamilton64144e42010-08-18 16:31:59 -0500364 public void onCreate(Bundle icicle) {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800365 super.onCreate(icicle);
John Reck4f61ee42011-08-22 16:31:14 -0700366 SharedPreferences prefs = BrowserSettings.getInstance().getPreferences();
367 try {
368 mState = new JSONObject(prefs.getString(PREF_GROUP_STATE, "{}"));
369 } catch (JSONException e) {
370 // Parse failed, clear preference and start with empty state
371 prefs.edit().remove(PREF_GROUP_STATE).apply();
372 mState = new JSONObject();
373 }
Jeff Hamilton64144e42010-08-18 16:31:59 -0500374 Bundle args = getArguments();
Leon Scrogginsbc922852010-10-22 12:15:27 -0400375 mDisableNewWindow = args == null ? false : args.getBoolean(EXTRA_DISABLE_WINDOW, false);
John Reck8f295d12011-03-18 15:25:18 -0700376 setHasOptionsMenu(true);
John Reck2d963a22011-08-10 15:53:07 -0700377 if (mCallbacks == null && getActivity() instanceof CombinedBookmarksCallbacks) {
378 mCallbacks = new CombinedBookmarksCallbackWrapper(
379 (CombinedBookmarksCallbacks) getActivity());
380 }
Jeff Hamilton64144e42010-08-18 16:31:59 -0500381 }
Leon Scrogginsd87f85e2009-08-18 14:13:31 -0400382
John Reck4f61ee42011-08-22 16:31:14 -0700383 @Override
384 public void onPause() {
385 super.onPause();
386 try {
387 mState = mGrid.saveGroupState();
388 // Save state
389 SharedPreferences prefs = BrowserSettings.getInstance().getPreferences();
390 prefs.edit()
391 .putString(PREF_GROUP_STATE, mState.toString())
392 .apply();
393 } catch (JSONException e) {
394 // Not critical, ignore
395 }
396 }
397
John Reck2d963a22011-08-10 15:53:07 -0700398 private static class CombinedBookmarksCallbackWrapper
399 implements BookmarksPageCallbacks {
400
401 private CombinedBookmarksCallbacks mCombinedCallback;
402
403 private CombinedBookmarksCallbackWrapper(CombinedBookmarksCallbacks cb) {
404 mCombinedCallback = cb;
405 }
406
407 @Override
408 public boolean onOpenInNewWindow(String... urls) {
409 mCombinedCallback.openInNewTab(urls);
410 return true;
411 }
412
413 @Override
414 public boolean onBookmarkSelected(Cursor c, boolean isFolder) {
415 if (isFolder) {
416 return false;
417 }
418 mCombinedCallback.openUrl(BrowserBookmarksPage.getUrl(c));
419 return true;
420 }
421 };
422
Jeff Hamilton64144e42010-08-18 16:31:59 -0500423 @Override
Jeff Hamilton64144e42010-08-18 16:31:59 -0500424 public View onCreateView(LayoutInflater inflater, ViewGroup container,
425 Bundle savedInstanceState) {
John Reck0ce8a942011-01-14 19:57:09 -0800426 mRoot = inflater.inflate(R.layout.bookmarks, container, false);
427 mEmptyView = mRoot.findViewById(android.R.id.empty);
Leon Scrogginsd87f85e2009-08-18 14:13:31 -0400428
John Reck9db95292011-06-20 13:00:12 -0700429 mGrid = (BookmarkExpandableView) mRoot.findViewById(R.id.grid);
John Reck71efc2b2011-05-09 16:54:28 -0700430 mGrid.setOnChildClickListener(this);
431 mGrid.setColumnWidthFromLayout(R.layout.bookmark_thumbnail);
432 mGrid.setBreadcrumbController(this);
John Reck608baa72010-11-24 10:32:28 -0800433 setEnableContextMenu(mEnableContextMenu);
434
Jeff Hamiltondb90aa82010-09-16 03:38:04 -0500435 // Start the loaders
436 LoaderManager lm = getLoaderManager();
John Reck71efc2b2011-05-09 16:54:28 -0700437 lm.restartLoader(LOADER_ACCOUNTS, null, this);
Jeff Hamilton84029622010-08-05 14:29:28 -0500438
Dany Rybnikovb80159e2014-06-23 09:16:09 +0300439 //Notify about anticipated network activity
440 NetworkServices.HintUpcomingUserActivity();
441
John Reck0ce8a942011-01-14 19:57:09 -0800442 return mRoot;
Jeff Hamilton64144e42010-08-18 16:31:59 -0500443 }
Michael Kolb801ecb72010-08-25 12:57:38 -0700444
John Reck608baa72010-11-24 10:32:28 -0800445 @Override
446 public void onDestroyView() {
447 super.onDestroyView();
John Reck71efc2b2011-05-09 16:54:28 -0700448 mGrid.setBreadcrumbController(null);
John Reckc3c46d72011-09-06 16:17:34 -0700449 mGrid.clearAccounts();
John Reck71efc2b2011-05-09 16:54:28 -0700450 LoaderManager lm = getLoaderManager();
451 lm.destroyLoader(LOADER_ACCOUNTS);
452 for (int id : mBookmarkAdapters.keySet()) {
Tarun Nainaniebdf18e2014-07-09 19:35:30 -0700453 mBookmarkAdapters.get(id).quitThread();
John Reck71efc2b2011-05-09 16:54:28 -0700454 lm.destroyLoader(id);
John Reck608baa72010-11-24 10:32:28 -0800455 }
John Reck71efc2b2011-05-09 16:54:28 -0700456 mBookmarkAdapters.clear();
John Reck71efc2b2011-05-09 16:54:28 -0700457 }
458
459 private BrowserBookmarksAdapter getChildAdapter(int groupPosition) {
John Reck9db95292011-06-20 13:00:12 -0700460 return mGrid.getChildAdapter(groupPosition);
John Reck71efc2b2011-05-09 16:54:28 -0700461 }
462
463 private BreadCrumbView getBreadCrumbs(int groupPosition) {
John Reck9db95292011-06-20 13:00:12 -0700464 return mGrid.getBreadCrumbs(groupPosition);
Nicolas Catania095292f2010-03-15 09:00:14 -0700465 }
466
Jeff Hamilton84029622010-08-05 14:29:28 -0500467 @Override
John Reck71efc2b2011-05-09 16:54:28 -0700468 public boolean onChildClick(ExpandableListView parent, View v,
469 int groupPosition, int childPosition, long id) {
470 BrowserBookmarksAdapter adapter = getChildAdapter(groupPosition);
471 Cursor cursor = adapter.getItem(childPosition);
John Reck608baa72010-11-24 10:32:28 -0800472 boolean isFolder = cursor.getInt(BookmarksLoader.COLUMN_INDEX_IS_FOLDER) != 0;
473 if (mCallbacks != null &&
474 mCallbacks.onBookmarkSelected(cursor, isFolder)) {
John Reck71efc2b2011-05-09 16:54:28 -0700475 return true;
Ben Murdoch328ea872009-09-16 13:33:29 +0100476 }
477
John Reck608baa72010-11-24 10:32:28 -0800478 if (isFolder) {
Michael Kolb370a4f32010-10-06 10:45:32 -0700479 String title = cursor.getString(BookmarksLoader.COLUMN_INDEX_TITLE);
Jeff Hamilton84029622010-08-05 14:29:28 -0500480 Uri uri = ContentUris.withAppendedId(
481 BrowserContract.Bookmarks.CONTENT_URI_DEFAULT_FOLDER, id);
John Reck71efc2b2011-05-09 16:54:28 -0700482 BreadCrumbView crumbs = getBreadCrumbs(groupPosition);
483 if (crumbs != null) {
Michael Kolb370a4f32010-10-06 10:45:32 -0700484 // update crumbs
John Reck71efc2b2011-05-09 16:54:28 -0700485 crumbs.pushView(title, uri);
John Reckcece1fe2011-09-13 08:47:17 -0700486 crumbs.setVisibility(View.VISIBLE);
luxiaoldbe4a622013-07-19 17:14:06 +0800487 Object data = crumbs.getTopData();
488 mCurrentFolderId = (data != null ? ContentUris.parseId((Uri) data)
489 : DEFAULT_FOLDER_ID);
Michael Kolb370a4f32010-10-06 10:45:32 -0700490 }
John Reck71efc2b2011-05-09 16:54:28 -0700491 loadFolder(groupPosition, uri);
Leon Scrogginsa5d669e2009-08-05 14:07:58 -0400492 }
John Reck71efc2b2011-05-09 16:54:28 -0700493 return true;
The Android Open Source Project0c908882009-03-03 19:32:16 -0800494 }
495
John Reck608baa72010-11-24 10:32:28 -0800496 /* package */ static Intent createShortcutIntent(Context context, Cursor cursor) {
Jeff Hamilton84029622010-08-05 14:29:28 -0500497 String url = cursor.getString(BookmarksLoader.COLUMN_INDEX_URL);
Michael Kolbccaa8962010-10-20 17:03:10 -0700498 String title = cursor.getString(BookmarksLoader.COLUMN_INDEX_TITLE);
Jeff Hamilton84029622010-08-05 14:29:28 -0500499 Bitmap touchIcon = getBitmap(cursor, BookmarksLoader.COLUMN_INDEX_TOUCH_ICON);
500 Bitmap favicon = getBitmap(cursor, BookmarksLoader.COLUMN_INDEX_FAVICON);
John Reck608baa72010-11-24 10:32:28 -0800501 return BookmarkUtils.createAddToHomeIntent(context, url, title, touchIcon, favicon);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800502 }
503
John Reck71efc2b2011-05-09 16:54:28 -0700504 private void loadUrl(BrowserBookmarksAdapter adapter, int position) {
505 if (mCallbacks != null && adapter != null) {
506 mCallbacks.onBookmarkSelected(adapter.getItem(position), false);
John Reck608baa72010-11-24 10:32:28 -0800507 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800508 }
509
John Reck71efc2b2011-05-09 16:54:28 -0700510 private void openInNewWindow(BrowserBookmarksAdapter adapter, int position) {
John Reck608baa72010-11-24 10:32:28 -0800511 if (mCallbacks != null) {
John Reck71efc2b2011-05-09 16:54:28 -0700512 Cursor c = adapter.getItem(position);
John Reck1b3c66e2011-01-11 13:20:32 -0800513 boolean isFolder = c.getInt(BookmarksLoader.COLUMN_INDEX_IS_FOLDER) == 1;
514 if (isFolder) {
515 long id = c.getLong(BookmarksLoader.COLUMN_INDEX_ID);
516 new OpenAllInTabsTask(id).execute();
517 } else {
John Reckd3e4d5b2011-07-13 15:48:43 -0700518 mCallbacks.onOpenInNewWindow(BrowserBookmarksPage.getUrl(c));
John Reck1b3c66e2011-01-11 13:20:32 -0800519 }
John Reck608baa72010-11-24 10:32:28 -0800520 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800521 }
Nicolas Catania095292f2010-03-15 09:00:14 -0700522
John Reck1b3c66e2011-01-11 13:20:32 -0800523 class OpenAllInTabsTask extends AsyncTask<Void, Void, Cursor> {
524 long mFolderId;
525 public OpenAllInTabsTask(long id) {
526 mFolderId = id;
527 }
528
529 @Override
530 protected Cursor doInBackground(Void... params) {
531 Context c = getActivity();
532 if (c == null) return null;
533 return c.getContentResolver().query(BookmarkUtils.getBookmarksUri(c),
534 BookmarksLoader.PROJECTION, BrowserContract.Bookmarks.PARENT + "=?",
535 new String[] { Long.toString(mFolderId) }, null);
536 }
537
538 @Override
539 protected void onPostExecute(Cursor result) {
John Reckd3e4d5b2011-07-13 15:48:43 -0700540 if (mCallbacks != null && result.getCount() > 0) {
541 String[] urls = new String[result.getCount()];
542 int i = 0;
John Reck1b3c66e2011-01-11 13:20:32 -0800543 while (result.moveToNext()) {
John Reckd3e4d5b2011-07-13 15:48:43 -0700544 urls[i++] = BrowserBookmarksPage.getUrl(result);
John Reck1b3c66e2011-01-11 13:20:32 -0800545 }
John Reckd3e4d5b2011-07-13 15:48:43 -0700546 mCallbacks.onOpenInNewWindow(urls);
John Reck1b3c66e2011-01-11 13:20:32 -0800547 }
548 }
549
550 }
551
John Reck71efc2b2011-05-09 16:54:28 -0700552 private void editBookmark(BrowserBookmarksAdapter adapter, int position) {
Jeff Hamilton64144e42010-08-18 16:31:59 -0500553 Intent intent = new Intent(getActivity(), AddBookmarkPage.class);
John Reck71efc2b2011-05-09 16:54:28 -0700554 Cursor cursor = adapter.getItem(position);
Jeff Hamilton84029622010-08-05 14:29:28 -0500555 Bundle item = new Bundle();
Michael Kolbbef7a752010-08-25 17:11:35 -0700556 item.putString(BrowserContract.Bookmarks.TITLE,
Jeff Hamilton84029622010-08-05 14:29:28 -0500557 cursor.getString(BookmarksLoader.COLUMN_INDEX_TITLE));
Michael Kolbbef7a752010-08-25 17:11:35 -0700558 item.putString(BrowserContract.Bookmarks.URL,
Jeff Hamilton84029622010-08-05 14:29:28 -0500559 cursor.getString(BookmarksLoader.COLUMN_INDEX_URL));
560 byte[] data = cursor.getBlob(BookmarksLoader.COLUMN_INDEX_FAVICON);
561 if (data != null) {
Michael Kolbbef7a752010-08-25 17:11:35 -0700562 item.putParcelable(BrowserContract.Bookmarks.FAVICON,
Jeff Hamilton84029622010-08-05 14:29:28 -0500563 BitmapFactory.decodeByteArray(data, 0, data.length));
564 }
John Reckc8490812010-11-22 14:15:36 -0800565 item.putLong(BrowserContract.Bookmarks._ID,
566 cursor.getLong(BookmarksLoader.COLUMN_INDEX_ID));
Leon Scroggins III052ce662010-09-13 14:44:16 -0400567 item.putLong(BrowserContract.Bookmarks.PARENT,
568 cursor.getLong(BookmarksLoader.COLUMN_INDEX_PARENT));
John Reckc8490812010-11-22 14:15:36 -0800569 intent.putExtra(AddBookmarkPage.EXTRA_EDIT_BOOKMARK, item);
570 intent.putExtra(AddBookmarkPage.EXTRA_IS_FOLDER,
571 cursor.getInt(BookmarksLoader.COLUMN_INDEX_IS_FOLDER) == 1);
572 startActivity(intent);
Jeff Hamilton84029622010-08-05 14:29:28 -0500573 }
574
John Reck71efc2b2011-05-09 16:54:28 -0700575 private void displayRemoveBookmarkDialog(BrowserBookmarksAdapter adapter,
576 int position) {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800577 // Put up a dialog asking if the user really wants to
578 // delete the bookmark
John Reck71efc2b2011-05-09 16:54:28 -0700579 Cursor cursor = adapter.getItem(position);
Leon Scroggins75630672011-01-13 17:56:15 -0500580 long id = cursor.getLong(BookmarksLoader.COLUMN_INDEX_ID);
581 String title = cursor.getString(BookmarksLoader.COLUMN_INDEX_TITLE);
Jeff Hamilton64144e42010-08-18 16:31:59 -0500582 Context context = getActivity();
Leon Scroggins75630672011-01-13 17:56:15 -0500583 BookmarkUtils.displayRemoveBookmarkDialog(id, title, context, null);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800584 }
585
John Reck71efc2b2011-05-09 16:54:28 -0700586 private String getUrl(BrowserBookmarksAdapter adapter, int position) {
587 return getUrl(adapter.getItem(position));
John Reck608baa72010-11-24 10:32:28 -0800588 }
589
590 /* package */ static String getUrl(Cursor c) {
591 return c.getString(BookmarksLoader.COLUMN_INDEX_URL);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800592 }
Michael Kolb801ecb72010-08-25 12:57:38 -0700593
The Android Open Source Project0c908882009-03-03 19:32:16 -0800594 private void copy(CharSequence text) {
Jeff Hamilton64144e42010-08-18 16:31:59 -0500595 ClipboardManager cm = (ClipboardManager) getActivity().getSystemService(
596 Context.CLIPBOARD_SERVICE);
Dianne Hackbornbf1d10a2011-01-17 12:29:51 -0800597 cm.setPrimaryClip(ClipData.newRawUri(null, Uri.parse(text.toString())));
The Android Open Source Project0c908882009-03-03 19:32:16 -0800598 }
Michael Kolb370a4f32010-10-06 10:45:32 -0700599
John Reckb3417f02011-01-14 11:01:05 -0800600 @Override
John Reckb3417f02011-01-14 11:01:05 -0800601 public void onConfigurationChanged(Configuration newConfig) {
602 super.onConfigurationChanged(newConfig);
603 Resources res = getActivity().getResources();
John Reck71efc2b2011-05-09 16:54:28 -0700604 mGrid.setColumnWidthFromLayout(R.layout.bookmark_thumbnail);
John Reck0ce8a942011-01-14 19:57:09 -0800605 int paddingTop = (int) res.getDimension(R.dimen.combo_paddingTop);
John Reckf3828cd2011-06-14 17:21:55 -0700606 mRoot.setPadding(0, paddingTop, 0, 0);
John Reck9fdd9812011-05-17 14:16:38 -0700607 getActivity().invalidateOptionsMenu();
John Reckb3417f02011-01-14 11:01:05 -0800608 }
609
John Reck608baa72010-11-24 10:32:28 -0800610 /**
611 * BreadCrumb controller callback
612 */
613 @Override
John Reck71efc2b2011-05-09 16:54:28 -0700614 public void onTop(BreadCrumbView view, int level, Object data) {
615 int groupPosition = (Integer) view.getTag(R.id.group_position);
John Reck89f73c12010-12-01 10:10:14 -0800616 Uri uri = (Uri) data;
617 if (uri == null) {
618 // top level
619 uri = BrowserContract.Bookmarks.CONTENT_URI_DEFAULT_FOLDER;
620 }
John Reck71efc2b2011-05-09 16:54:28 -0700621 loadFolder(groupPosition, uri);
John Reckcece1fe2011-09-13 08:47:17 -0700622 if (level <= 1) {
623 view.setVisibility(View.GONE);
624 } else {
625 view.setVisibility(View.VISIBLE);
626 }
John Reck89f73c12010-12-01 10:10:14 -0800627 }
628
629 /**
630 * @param uri
631 */
John Reck71efc2b2011-05-09 16:54:28 -0700632 private void loadFolder(int groupPosition, Uri uri) {
John Reck89f73c12010-12-01 10:10:14 -0800633 LoaderManager manager = getLoaderManager();
John Reck71efc2b2011-05-09 16:54:28 -0700634 // This assumes groups are ordered the same as loaders
635 BookmarksLoader loader = (BookmarksLoader) ((Loader<?>)
636 manager.getLoader(LOADER_BOOKMARKS + groupPosition));
John Reck89f73c12010-12-01 10:10:14 -0800637 loader.setUri(uri);
638 loader.forceLoad();
John Reck608baa72010-11-24 10:32:28 -0800639 }
640
John Reck608baa72010-11-24 10:32:28 -0800641 public void setCallbackListener(BookmarksPageCallbacks callbackListener) {
642 mCallbacks = callbackListener;
643 }
644
645 public void setEnableContextMenu(boolean enable) {
646 mEnableContextMenu = enable;
647 if (mGrid != null) {
648 if (mEnableContextMenu) {
649 registerForContextMenu(mGrid);
650 } else {
651 unregisterForContextMenu(mGrid);
652 mGrid.setLongClickable(false);
653 }
654 }
John Reck608baa72010-11-24 10:32:28 -0800655 }
John Reckc8490812010-11-22 14:15:36 -0800656
657 private static class LookupBookmarkCount extends AsyncTask<Long, Void, Integer> {
658 Context mContext;
659 BookmarkItem mHeader;
660
661 public LookupBookmarkCount(Context context, BookmarkItem header) {
Ben Murdoch914c5592011-08-01 13:58:47 +0100662 mContext = context.getApplicationContext();
John Reckc8490812010-11-22 14:15:36 -0800663 mHeader = header;
664 }
665
666 @Override
667 protected Integer doInBackground(Long... params) {
668 if (params.length != 1) {
669 throw new IllegalArgumentException("Missing folder id!");
670 }
671 Uri uri = BookmarkUtils.getBookmarksUri(mContext);
Axesh R. Ajmera632e5a02014-11-13 09:52:46 -0800672 Cursor c = null;
673 try {
674 c = mContext.getContentResolver().query(uri,
675 null, BrowserContract.Bookmarks.PARENT + "=?",
676 new String[] {params[0].toString()}, null);
677 return c.getCount();
678 } finally {
679 if (c != null) {
680 c.close();
681 }
682 }
John Reckc8490812010-11-22 14:15:36 -0800683 }
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}