blob: a255d28df86bc4726bf14f7034c093c729ea283b [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
Bijan Amirzada41242f22014-03-21 12:12:18 -070017package com.android.browser;
The Android Open Source Project0c908882009-03-03 19:32:16 -080018
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;
The Android Open Source Project0c908882009-03-03 19:32:16 -080039import android.view.ContextMenu;
Jeff Hamiltondb90aa82010-09-16 03:38:04 -050040import android.view.ContextMenu.ContextMenuInfo;
Jeff Hamilton64144e42010-08-18 16:31:59 -050041import android.view.LayoutInflater;
luxiaoldbe4a622013-07-19 17:14:06 +080042import android.view.Menu;
The Android Open Source Project0c908882009-03-03 19:32:16 -080043import android.view.MenuInflater;
44import android.view.MenuItem;
45import android.view.View;
Jeff Hamiltondb90aa82010-09-16 03:38:04 -050046import android.view.ViewGroup;
John Reck71efc2b2011-05-09 16:54:28 -070047import android.widget.ExpandableListView;
48import android.widget.ExpandableListView.OnChildClickListener;
Jeff Hamilton84029622010-08-05 14:29:28 -050049import android.widget.Toast;
The Android Open Source Project0c908882009-03-03 19:32:16 -080050
Bijan Amirzada41242f22014-03-21 12:12:18 -070051import com.android.browser.R;
52import com.android.browser.platformsupport.BrowserContract;
53import com.android.browser.platformsupport.BrowserContract.Accounts;
54import com.android.browser.provider.BrowserProvider2;
55import com.android.browser.view.BookmarkExpandableView;
56import com.android.browser.view.BookmarkExpandableView.BookmarkContextMenuInfo;
Bijan Amirzada9b1e9882014-02-26 17:15:46 -080057
58import java.util.HashMap;
John Recke7c97de2011-05-27 14:42:43 -070059
John Reck4f61ee42011-08-22 16:31:14 -070060import org.json.JSONException;
61import org.json.JSONObject;
62
John Reck608baa72010-11-24 10:32:28 -080063interface BookmarksPageCallbacks {
64 // Return true if handled
65 boolean onBookmarkSelected(Cursor c, boolean isFolder);
66 // Return true if handled
John Reckd3e4d5b2011-07-13 15:48:43 -070067 boolean onOpenInNewWindow(String... urls);
John Reck608baa72010-11-24 10:32:28 -080068}
69
The Android Open Source Project0c908882009-03-03 19:32:16 -080070/**
71 * View showing the user's bookmarks in the browser.
72 */
Jeff Hamilton64144e42010-08-18 16:31:59 -050073public class BrowserBookmarksPage extends Fragment implements View.OnCreateContextMenuListener,
John Reck9db95292011-06-20 13:00:12 -070074 LoaderManager.LoaderCallbacks<Cursor>, BreadCrumbView.Controller,
John Reckd3e4d5b2011-07-13 15:48:43 -070075 OnChildClickListener {
The Android Open Source Project0c908882009-03-03 19:32:16 -080076
John Recke7c97de2011-05-27 14:42:43 -070077 public static class ExtraDragState {
78 public int childPosition;
79 public int groupPosition;
80 }
81
Jeff Hamilton84029622010-08-05 14:29:28 -050082 static final String LOGTAG = "browser";
Nicolas Catania095292f2010-03-15 09:00:14 -070083
John Reck71efc2b2011-05-09 16:54:28 -070084 static final int LOADER_ACCOUNTS = 1;
85 static final int LOADER_BOOKMARKS = 100;
Jeff Hamilton84029622010-08-05 14:29:28 -050086
Michael Kolb801ecb72010-08-25 12:57:38 -070087 static final String EXTRA_DISABLE_WINDOW = "disable_new_window";
John Reck4f61ee42011-08-22 16:31:14 -070088 static final String PREF_GROUP_STATE = "bbp_group_state";
Michael Kolb801ecb72010-08-25 12:57:38 -070089
John Reck71efc2b2011-05-09 16:54:28 -070090 static final String ACCOUNT_TYPE = "account_type";
91 static final String ACCOUNT_NAME = "account_name";
92
luxiaoldbe4a622013-07-19 17:14:06 +080093 static final long DEFAULT_FOLDER_ID = -1;
94
John Reck608baa72010-11-24 10:32:28 -080095 BookmarksPageCallbacks mCallbacks;
John Reck0ce8a942011-01-14 19:57:09 -080096 View mRoot;
John Reck9db95292011-06-20 13:00:12 -070097 BookmarkExpandableView mGrid;
Jeff Hamilton84029622010-08-05 14:29:28 -050098 boolean mDisableNewWindow;
John Reck608baa72010-11-24 10:32:28 -080099 boolean mEnableContextMenu = true;
Jeff Hamilton84029622010-08-05 14:29:28 -0500100 View mEmptyView;
John Reck608baa72010-11-24 10:32:28 -0800101 View mHeader;
John Reck71efc2b2011-05-09 16:54:28 -0700102 HashMap<Integer, BrowserBookmarksAdapter> mBookmarkAdapters = new HashMap<Integer, BrowserBookmarksAdapter>();
John Reck4f61ee42011-08-22 16:31:14 -0700103 JSONObject mState;
luxiaoldbe4a622013-07-19 17:14:06 +0800104 long mCurrentFolderId = BrowserProvider2.FIXED_ID_ROOT;
Michael Kolb370a4f32010-10-06 10:45:32 -0700105
Jeff Hamilton84029622010-08-05 14:29:28 -0500106 @Override
107 public Loader<Cursor> onCreateLoader(int id, Bundle args) {
John Reck71efc2b2011-05-09 16:54:28 -0700108 if (id == LOADER_ACCOUNTS) {
109 return new AccountsLoader(getActivity());
110 } else if (id >= LOADER_BOOKMARKS) {
111 String accountType = args.getString(ACCOUNT_TYPE);
112 String accountName = args.getString(ACCOUNT_NAME);
113 BookmarksLoader bl = new BookmarksLoader(getActivity(),
114 accountType, accountName);
115 return bl;
116 } else {
117 throw new UnsupportedOperationException("Unknown loader id " + id);
Jeff Hamilton84029622010-08-05 14:29:28 -0500118 }
Jeff Hamilton84029622010-08-05 14:29:28 -0500119 }
120
121 @Override
Jeff Hamiltondb90aa82010-09-16 03:38:04 -0500122 public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
John Reck71efc2b2011-05-09 16:54:28 -0700123 if (loader.getId() == LOADER_ACCOUNTS) {
124 LoaderManager lm = getLoaderManager();
125 int id = LOADER_BOOKMARKS;
126 while (cursor.moveToNext()) {
127 String accountName = cursor.getString(0);
128 String accountType = cursor.getString(1);
129 Bundle args = new Bundle();
130 args.putString(ACCOUNT_NAME, accountName);
131 args.putString(ACCOUNT_TYPE, accountType);
132 BrowserBookmarksAdapter adapter = new BrowserBookmarksAdapter(
John Reckaf991f92012-04-24 15:31:16 -0700133 getActivity());
John Reck71efc2b2011-05-09 16:54:28 -0700134 mBookmarkAdapters.put(id, adapter);
John Reck4f61ee42011-08-22 16:31:14 -0700135 boolean expand = true;
136 try {
137 expand = mState.getBoolean(accountName != null ? accountName
138 : BookmarkExpandableView.LOCAL_ACCOUNT_NAME);
139 } catch (JSONException e) {} // no state for accountName
140 mGrid.addAccount(accountName, adapter, expand);
John Reck71efc2b2011-05-09 16:54:28 -0700141 lm.restartLoader(id, args, this);
142 id++;
Jeff Hamiltondb90aa82010-09-16 03:38:04 -0500143 }
John Reck71efc2b2011-05-09 16:54:28 -0700144 // TODO: Figure out what a reload of these means
145 // Currently, a reload is triggered whenever bookmarks change
146 // This is less than ideal
147 // It also causes UI flickering as a new adapter is created
148 // instead of re-using an existing one when the account_name is the
149 // same.
150 // For now, this is a one-shot load
151 getLoaderManager().destroyLoader(LOADER_ACCOUNTS);
152 } else if (loader.getId() >= LOADER_BOOKMARKS) {
153 BrowserBookmarksAdapter adapter = mBookmarkAdapters.get(loader.getId());
154 adapter.changeCursor(cursor);
luxiaoldbe4a622013-07-19 17:14:06 +0800155 if (adapter.getCount() != 0) {
156 mCurrentFolderId = adapter.getItem(0).getLong(BookmarksLoader.COLUMN_INDEX_PARENT);
157 }
Jeff Hamiltondb90aa82010-09-16 03:38:04 -0500158 }
Jeff Hamilton84029622010-08-05 14:29:28 -0500159 }
160
John Reck522a4382011-01-26 13:23:47 -0800161 @Override
Dianne Hackborn39772c82010-12-16 00:43:54 -0800162 public void onLoaderReset(Loader<Cursor> loader) {
John Reck3ad8d5c2012-04-23 14:50:09 -0700163 if (loader.getId() >= LOADER_BOOKMARKS) {
164 BrowserBookmarksAdapter adapter = mBookmarkAdapters.get(loader.getId());
165 adapter.changeCursor(null);
166 }
Dianne Hackborn39772c82010-12-16 00:43:54 -0800167 }
168
luxiaoldbe4a622013-07-19 17:14:06 +0800169 //add for carrier feature which adds new bookmark/folder function.
170 @Override
171 public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
172 super.onCreateOptionsMenu(menu, inflater);
173 inflater.inflate(R.menu.bookmark, menu);
174 }
175
176 @Override
177 public boolean onOptionsItemSelected(MenuItem item) {
178 final Activity activity = getActivity();
179 if (item.getItemId() == R.id.add_bookmark_menu_id) {
180 Intent intent = new Intent(activity, AddBookmarkPage.class);
181 intent.putExtra(BrowserContract.Bookmarks.URL, "http://");
182 intent.putExtra(BrowserContract.Bookmarks.TITLE, "");
183 intent.putExtra(BrowserContract.Bookmarks.PARENT, mCurrentFolderId);
184 activity.startActivity(intent);
185 }
186 if (item.getItemId() == R.id.new_bmfolder_menu_id) {
187 Intent intent = new Intent(activity, AddBookmarkFolder.class);
188 intent.putExtra(BrowserContract.Bookmarks.PARENT, mCurrentFolderId);
189 activity.startActivity(intent);
190 }
191 return super.onOptionsItemSelected(item);
192 }
193
The Android Open Source Project0c908882009-03-03 19:32:16 -0800194 @Override
195 public boolean onContextItemSelected(MenuItem item) {
John Reck54ed3aa2011-08-19 15:16:05 -0700196 if (!(item.getMenuInfo() instanceof BookmarkContextMenuInfo)) {
197 return false;
198 }
199 BookmarkContextMenuInfo i = (BookmarkContextMenuInfo) item.getMenuInfo();
The Android Open Source Project0c908882009-03-03 19:32:16 -0800200 // If we have no menu info, we can't tell which item was selected.
201 if (i == null) {
Leon Scrogginsc8cae082010-11-03 18:51:46 -0400202 return false;
The Android Open Source Project0c908882009-03-03 19:32:16 -0800203 }
Nicolas Catania095292f2010-03-15 09:00:14 -0700204
John Recke7c97de2011-05-27 14:42:43 -0700205 if (handleContextItem(item.getItemId(), i.groupPosition, i.childPosition)) {
206 return true;
207 }
208 return super.onContextItemSelected(item);
209 }
210
211 public boolean handleContextItem(int itemId, int groupPosition,
212 int childPosition) {
213 final Activity activity = getActivity();
214 BrowserBookmarksAdapter adapter = getChildAdapter(groupPosition);
215
216 switch (itemId) {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800217 case R.id.open_context_menu_id:
John Recke7c97de2011-05-27 14:42:43 -0700218 loadUrl(adapter, childPosition);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800219 break;
220 case R.id.edit_context_menu_id:
John Recke7c97de2011-05-27 14:42:43 -0700221 editBookmark(adapter, childPosition);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800222 break;
223 case R.id.shortcut_context_menu_id:
John Recke7c97de2011-05-27 14:42:43 -0700224 Cursor c = adapter.getItem(childPosition);
John Reck608baa72010-11-24 10:32:28 -0800225 activity.sendBroadcast(createShortcutIntent(getActivity(), c));
The Android Open Source Project0c908882009-03-03 19:32:16 -0800226 break;
227 case R.id.delete_context_menu_id:
John Recke7c97de2011-05-27 14:42:43 -0700228 displayRemoveBookmarkDialog(adapter, childPosition);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800229 break;
230 case R.id.new_window_context_menu_id:
John Recke7c97de2011-05-27 14:42:43 -0700231 openInNewWindow(adapter, childPosition);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800232 break;
Jeff Hamilton84029622010-08-05 14:29:28 -0500233 case R.id.share_link_context_menu_id: {
John Recke7c97de2011-05-27 14:42:43 -0700234 Cursor cursor = adapter.getItem(childPosition);
Michael Kolb8233fac2010-10-26 16:08:53 -0700235 Controller.sharePage(activity,
Jeff Hamilton84029622010-08-05 14:29:28 -0500236 cursor.getString(BookmarksLoader.COLUMN_INDEX_TITLE),
237 cursor.getString(BookmarksLoader.COLUMN_INDEX_URL),
238 getBitmap(cursor, BookmarksLoader.COLUMN_INDEX_FAVICON),
239 getBitmap(cursor, BookmarksLoader.COLUMN_INDEX_THUMBNAIL));
The Android Open Source Project0c908882009-03-03 19:32:16 -0800240 break;
Jeff Hamilton84029622010-08-05 14:29:28 -0500241 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800242 case R.id.copy_url_context_menu_id:
John Recke7c97de2011-05-27 14:42:43 -0700243 copy(getUrl(adapter, childPosition));
Leon Scrogginsfeb941d2009-05-28 17:27:38 -0400244 break;
Jeff Hamilton84029622010-08-05 14:29:28 -0500245 case R.id.homepage_context_menu_id: {
John Recke7c97de2011-05-27 14:42:43 -0700246 BrowserSettings.getInstance().setHomePage(getUrl(adapter, childPosition));
Jeff Hamilton64144e42010-08-18 16:31:59 -0500247 Toast.makeText(activity, R.string.homepage_set, Toast.LENGTH_LONG).show();
Leon Scrogginsfeb941d2009-05-28 17:27:38 -0400248 break;
Jeff Hamilton84029622010-08-05 14:29:28 -0500249 }
Leon Scrogginsa5d669e2009-08-05 14:07:58 -0400250 // Only for the Most visited page
Jeff Hamilton84029622010-08-05 14:29:28 -0500251 case R.id.save_to_bookmarks_menu_id: {
John Recke7c97de2011-05-27 14:42:43 -0700252 Cursor cursor = adapter.getItem(childPosition);
Jeff Hamilton84029622010-08-05 14:29:28 -0500253 String name = cursor.getString(BookmarksLoader.COLUMN_INDEX_TITLE);
254 String url = cursor.getString(BookmarksLoader.COLUMN_INDEX_URL);
Leon Scrogginsa5d669e2009-08-05 14:07:58 -0400255 // If the site is bookmarked, the item becomes remove from
256 // bookmarks.
Jeff Hamilton64144e42010-08-18 16:31:59 -0500257 Bookmarks.removeFromBookmarks(activity, activity.getContentResolver(), url, name);
Leon Scrogginsa5d669e2009-08-05 14:07:58 -0400258 break;
Jeff Hamilton84029622010-08-05 14:29:28 -0500259 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800260 default:
John Recke7c97de2011-05-27 14:42:43 -0700261 return false;
The Android Open Source Project0c908882009-03-03 19:32:16 -0800262 }
263 return true;
264 }
265
John Reck608baa72010-11-24 10:32:28 -0800266 static Bitmap getBitmap(Cursor cursor, int columnIndex) {
John Reckf94abcf2011-10-10 15:33:48 -0700267 return getBitmap(cursor, columnIndex, null);
268 }
269
270 static ThreadLocal<Options> sOptions = new ThreadLocal<Options>() {
271 @Override
272 protected Options initialValue() {
273 return new Options();
274 };
275 };
276 static Bitmap getBitmap(Cursor cursor, int columnIndex, Bitmap inBitmap) {
Jeff Hamilton84029622010-08-05 14:29:28 -0500277 byte[] data = cursor.getBlob(columnIndex);
278 if (data == null) {
279 return null;
The Android Open Source Project0c908882009-03-03 19:32:16 -0800280 }
John Reckf94abcf2011-10-10 15:33:48 -0700281 Options opts = sOptions.get();
282 opts.inBitmap = inBitmap;
283 opts.inSampleSize = 1;
284 opts.inScaled = false;
285 try {
286 return BitmapFactory.decodeByteArray(data, 0, data.length, opts);
287 } catch (IllegalArgumentException ex) {
288 // Failed to re-use bitmap, create a new one
289 return BitmapFactory.decodeByteArray(data, 0, data.length);
290 }
Jeff Hamilton84029622010-08-05 14:29:28 -0500291 }
292
John Reckc8490812010-11-22 14:15:36 -0800293 private MenuItem.OnMenuItemClickListener mContextItemClickListener =
294 new MenuItem.OnMenuItemClickListener() {
295 @Override
296 public boolean onMenuItemClick(MenuItem item) {
297 return onContextItemSelected(item);
298 }
299 };
300
Jeff Hamilton84029622010-08-05 14:29:28 -0500301 @Override
302 public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
John Reck71efc2b2011-05-09 16:54:28 -0700303 BookmarkContextMenuInfo info = (BookmarkContextMenuInfo) menuInfo;
304 BrowserBookmarksAdapter adapter = getChildAdapter(info.groupPosition);
305 Cursor cursor = adapter.getItem(info.childPosition);
John Reck1cebb442010-12-10 12:02:16 -0800306 if (!canEdit(cursor)) {
307 return;
308 }
Leon Scroggins5e8a40f2010-09-27 17:13:35 -0400309 boolean isFolder
310 = cursor.getInt(BookmarksLoader.COLUMN_INDEX_IS_FOLDER) != 0;
Michael Kolb801ecb72010-08-25 12:57:38 -0700311
Jeff Hamilton64144e42010-08-18 16:31:59 -0500312 final Activity activity = getActivity();
313 MenuInflater inflater = activity.getMenuInflater();
Jeff Hamilton84029622010-08-05 14:29:28 -0500314 inflater.inflate(R.menu.bookmarkscontext, menu);
John Reckc8490812010-11-22 14:15:36 -0800315 if (isFolder) {
316 menu.setGroupVisible(R.id.FOLDER_CONTEXT_MENU, true);
317 } else {
318 menu.setGroupVisible(R.id.BOOKMARK_CONTEXT_MENU, true);
319 if (mDisableNewWindow) {
320 menu.findItem(R.id.new_window_context_menu_id).setVisible(false);
321 }
Jeff Hamilton84029622010-08-05 14:29:28 -0500322 }
John Reckc8490812010-11-22 14:15:36 -0800323 BookmarkItem header = new BookmarkItem(activity);
John Reck83c01512011-09-09 11:14:16 -0700324 header.setEnableScrolling(true);
John Reckc8490812010-11-22 14:15:36 -0800325 populateBookmarkItem(cursor, header, isFolder);
John Reckc8490812010-11-22 14:15:36 -0800326 menu.setHeaderView(header);
Jeff Hamilton84029622010-08-05 14:29:28 -0500327
John Reckc8490812010-11-22 14:15:36 -0800328 int count = menu.size();
329 for (int i = 0; i < count; i++) {
330 menu.getItem(i).setOnMenuItemClickListener(mContextItemClickListener);
Jeff Hamilton84029622010-08-05 14:29:28 -0500331 }
Jeff Hamilton84029622010-08-05 14:29:28 -0500332 }
333
John Reck1cebb442010-12-10 12:02:16 -0800334 boolean canEdit(Cursor c) {
John Reckd18ac4b2012-04-12 17:27:34 -0700335 int type = c.getInt(BookmarksLoader.COLUMN_INDEX_TYPE);
336 return type == BrowserContract.Bookmarks.BOOKMARK_TYPE_BOOKMARK
337 || type == BrowserContract.Bookmarks.BOOKMARK_TYPE_FOLDER;
John Reck1cebb442010-12-10 12:02:16 -0800338 }
339
John Reckc8490812010-11-22 14:15:36 -0800340 private void populateBookmarkItem(Cursor cursor, BookmarkItem item, boolean isFolder) {
Jeff Hamilton84029622010-08-05 14:29:28 -0500341 item.setName(cursor.getString(BookmarksLoader.COLUMN_INDEX_TITLE));
John Reckc8490812010-11-22 14:15:36 -0800342 if (isFolder) {
343 item.setUrl(null);
344 Bitmap bitmap =
Michael Kolb5a72f182011-01-13 20:35:06 -0800345 BitmapFactory.decodeResource(getResources(), R.drawable.ic_folder_holo_dark);
John Reckc8490812010-11-22 14:15:36 -0800346 item.setFavicon(bitmap);
John Reck60e33362010-12-02 17:52:38 -0800347 new LookupBookmarkCount(getActivity(), item)
348 .execute(cursor.getLong(BookmarksLoader.COLUMN_INDEX_ID));
John Reckc8490812010-11-22 14:15:36 -0800349 } else {
350 String url = cursor.getString(BookmarksLoader.COLUMN_INDEX_URL);
351 item.setUrl(url);
352 Bitmap bitmap = getBitmap(cursor, BookmarksLoader.COLUMN_INDEX_FAVICON);
John Reckc8490812010-11-22 14:15:36 -0800353 item.setFavicon(bitmap);
Jeff Hamilton84029622010-08-05 14:29:28 -0500354 }
Jeff Hamilton84029622010-08-05 14:29:28 -0500355 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800356
357 /**
358 * Create a new BrowserBookmarksPage.
Nicolas Catania095292f2010-03-15 09:00:14 -0700359 */
The Android Open Source Project0c908882009-03-03 19:32:16 -0800360 @Override
Jeff Hamilton64144e42010-08-18 16:31:59 -0500361 public void onCreate(Bundle icicle) {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800362 super.onCreate(icicle);
John Reck4f61ee42011-08-22 16:31:14 -0700363 SharedPreferences prefs = BrowserSettings.getInstance().getPreferences();
364 try {
365 mState = new JSONObject(prefs.getString(PREF_GROUP_STATE, "{}"));
366 } catch (JSONException e) {
367 // Parse failed, clear preference and start with empty state
368 prefs.edit().remove(PREF_GROUP_STATE).apply();
369 mState = new JSONObject();
370 }
Jeff Hamilton64144e42010-08-18 16:31:59 -0500371 Bundle args = getArguments();
Leon Scrogginsbc922852010-10-22 12:15:27 -0400372 mDisableNewWindow = args == null ? false : args.getBoolean(EXTRA_DISABLE_WINDOW, false);
John Reck8f295d12011-03-18 15:25:18 -0700373 setHasOptionsMenu(true);
John Reck2d963a22011-08-10 15:53:07 -0700374 if (mCallbacks == null && getActivity() instanceof CombinedBookmarksCallbacks) {
375 mCallbacks = new CombinedBookmarksCallbackWrapper(
376 (CombinedBookmarksCallbacks) getActivity());
377 }
Jeff Hamilton64144e42010-08-18 16:31:59 -0500378 }
Leon Scrogginsd87f85e2009-08-18 14:13:31 -0400379
John Reck4f61ee42011-08-22 16:31:14 -0700380 @Override
381 public void onPause() {
382 super.onPause();
383 try {
384 mState = mGrid.saveGroupState();
385 // Save state
386 SharedPreferences prefs = BrowserSettings.getInstance().getPreferences();
387 prefs.edit()
388 .putString(PREF_GROUP_STATE, mState.toString())
389 .apply();
390 } catch (JSONException e) {
391 // Not critical, ignore
392 }
393 }
394
John Reck2d963a22011-08-10 15:53:07 -0700395 private static class CombinedBookmarksCallbackWrapper
396 implements BookmarksPageCallbacks {
397
398 private CombinedBookmarksCallbacks mCombinedCallback;
399
400 private CombinedBookmarksCallbackWrapper(CombinedBookmarksCallbacks cb) {
401 mCombinedCallback = cb;
402 }
403
404 @Override
405 public boolean onOpenInNewWindow(String... urls) {
406 mCombinedCallback.openInNewTab(urls);
407 return true;
408 }
409
410 @Override
411 public boolean onBookmarkSelected(Cursor c, boolean isFolder) {
412 if (isFolder) {
413 return false;
414 }
415 mCombinedCallback.openUrl(BrowserBookmarksPage.getUrl(c));
416 return true;
417 }
418 };
419
Jeff Hamilton64144e42010-08-18 16:31:59 -0500420 @Override
Jeff Hamilton64144e42010-08-18 16:31:59 -0500421 public View onCreateView(LayoutInflater inflater, ViewGroup container,
422 Bundle savedInstanceState) {
John Reck0ce8a942011-01-14 19:57:09 -0800423 mRoot = inflater.inflate(R.layout.bookmarks, container, false);
424 mEmptyView = mRoot.findViewById(android.R.id.empty);
Leon Scrogginsd87f85e2009-08-18 14:13:31 -0400425
John Reck9db95292011-06-20 13:00:12 -0700426 mGrid = (BookmarkExpandableView) mRoot.findViewById(R.id.grid);
John Reck71efc2b2011-05-09 16:54:28 -0700427 mGrid.setOnChildClickListener(this);
428 mGrid.setColumnWidthFromLayout(R.layout.bookmark_thumbnail);
429 mGrid.setBreadcrumbController(this);
John Reck608baa72010-11-24 10:32:28 -0800430 setEnableContextMenu(mEnableContextMenu);
431
Jeff Hamiltondb90aa82010-09-16 03:38:04 -0500432 // Start the loaders
433 LoaderManager lm = getLoaderManager();
John Reck71efc2b2011-05-09 16:54:28 -0700434 lm.restartLoader(LOADER_ACCOUNTS, null, this);
Jeff Hamilton84029622010-08-05 14:29:28 -0500435
John Reck0ce8a942011-01-14 19:57:09 -0800436 return mRoot;
Jeff Hamilton64144e42010-08-18 16:31:59 -0500437 }
Michael Kolb801ecb72010-08-25 12:57:38 -0700438
John Reck608baa72010-11-24 10:32:28 -0800439 @Override
440 public void onDestroyView() {
441 super.onDestroyView();
John Reck71efc2b2011-05-09 16:54:28 -0700442 mGrid.setBreadcrumbController(null);
John Reckc3c46d72011-09-06 16:17:34 -0700443 mGrid.clearAccounts();
John Reck71efc2b2011-05-09 16:54:28 -0700444 LoaderManager lm = getLoaderManager();
445 lm.destroyLoader(LOADER_ACCOUNTS);
446 for (int id : mBookmarkAdapters.keySet()) {
447 lm.destroyLoader(id);
John Reck608baa72010-11-24 10:32:28 -0800448 }
John Reck71efc2b2011-05-09 16:54:28 -0700449 mBookmarkAdapters.clear();
John Reck71efc2b2011-05-09 16:54:28 -0700450 }
451
452 private BrowserBookmarksAdapter getChildAdapter(int groupPosition) {
John Reck9db95292011-06-20 13:00:12 -0700453 return mGrid.getChildAdapter(groupPosition);
John Reck71efc2b2011-05-09 16:54:28 -0700454 }
455
456 private BreadCrumbView getBreadCrumbs(int groupPosition) {
John Reck9db95292011-06-20 13:00:12 -0700457 return mGrid.getBreadCrumbs(groupPosition);
Nicolas Catania095292f2010-03-15 09:00:14 -0700458 }
459
Jeff Hamilton84029622010-08-05 14:29:28 -0500460 @Override
John Reck71efc2b2011-05-09 16:54:28 -0700461 public boolean onChildClick(ExpandableListView parent, View v,
462 int groupPosition, int childPosition, long id) {
463 BrowserBookmarksAdapter adapter = getChildAdapter(groupPosition);
464 Cursor cursor = adapter.getItem(childPosition);
John Reck608baa72010-11-24 10:32:28 -0800465 boolean isFolder = cursor.getInt(BookmarksLoader.COLUMN_INDEX_IS_FOLDER) != 0;
466 if (mCallbacks != null &&
467 mCallbacks.onBookmarkSelected(cursor, isFolder)) {
John Reck71efc2b2011-05-09 16:54:28 -0700468 return true;
Ben Murdoch328ea872009-09-16 13:33:29 +0100469 }
470
John Reck608baa72010-11-24 10:32:28 -0800471 if (isFolder) {
Michael Kolb370a4f32010-10-06 10:45:32 -0700472 String title = cursor.getString(BookmarksLoader.COLUMN_INDEX_TITLE);
Jeff Hamilton84029622010-08-05 14:29:28 -0500473 Uri uri = ContentUris.withAppendedId(
474 BrowserContract.Bookmarks.CONTENT_URI_DEFAULT_FOLDER, id);
John Reck71efc2b2011-05-09 16:54:28 -0700475 BreadCrumbView crumbs = getBreadCrumbs(groupPosition);
476 if (crumbs != null) {
Michael Kolb370a4f32010-10-06 10:45:32 -0700477 // update crumbs
John Reck71efc2b2011-05-09 16:54:28 -0700478 crumbs.pushView(title, uri);
John Reckcece1fe2011-09-13 08:47:17 -0700479 crumbs.setVisibility(View.VISIBLE);
luxiaoldbe4a622013-07-19 17:14:06 +0800480 Object data = crumbs.getTopData();
481 mCurrentFolderId = (data != null ? ContentUris.parseId((Uri) data)
482 : DEFAULT_FOLDER_ID);
Michael Kolb370a4f32010-10-06 10:45:32 -0700483 }
John Reck71efc2b2011-05-09 16:54:28 -0700484 loadFolder(groupPosition, uri);
Leon Scrogginsa5d669e2009-08-05 14:07:58 -0400485 }
John Reck71efc2b2011-05-09 16:54:28 -0700486 return true;
The Android Open Source Project0c908882009-03-03 19:32:16 -0800487 }
488
John Reck608baa72010-11-24 10:32:28 -0800489 /* package */ static Intent createShortcutIntent(Context context, Cursor cursor) {
Jeff Hamilton84029622010-08-05 14:29:28 -0500490 String url = cursor.getString(BookmarksLoader.COLUMN_INDEX_URL);
Michael Kolbccaa8962010-10-20 17:03:10 -0700491 String title = cursor.getString(BookmarksLoader.COLUMN_INDEX_TITLE);
Jeff Hamilton84029622010-08-05 14:29:28 -0500492 Bitmap touchIcon = getBitmap(cursor, BookmarksLoader.COLUMN_INDEX_TOUCH_ICON);
493 Bitmap favicon = getBitmap(cursor, BookmarksLoader.COLUMN_INDEX_FAVICON);
John Reck608baa72010-11-24 10:32:28 -0800494 return BookmarkUtils.createAddToHomeIntent(context, url, title, touchIcon, favicon);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800495 }
496
John Reck71efc2b2011-05-09 16:54:28 -0700497 private void loadUrl(BrowserBookmarksAdapter adapter, int position) {
498 if (mCallbacks != null && adapter != null) {
499 mCallbacks.onBookmarkSelected(adapter.getItem(position), false);
John Reck608baa72010-11-24 10:32:28 -0800500 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800501 }
502
John Reck71efc2b2011-05-09 16:54:28 -0700503 private void openInNewWindow(BrowserBookmarksAdapter adapter, int position) {
John Reck608baa72010-11-24 10:32:28 -0800504 if (mCallbacks != null) {
John Reck71efc2b2011-05-09 16:54:28 -0700505 Cursor c = adapter.getItem(position);
John Reck1b3c66e2011-01-11 13:20:32 -0800506 boolean isFolder = c.getInt(BookmarksLoader.COLUMN_INDEX_IS_FOLDER) == 1;
507 if (isFolder) {
508 long id = c.getLong(BookmarksLoader.COLUMN_INDEX_ID);
509 new OpenAllInTabsTask(id).execute();
510 } else {
John Reckd3e4d5b2011-07-13 15:48:43 -0700511 mCallbacks.onOpenInNewWindow(BrowserBookmarksPage.getUrl(c));
John Reck1b3c66e2011-01-11 13:20:32 -0800512 }
John Reck608baa72010-11-24 10:32:28 -0800513 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800514 }
Nicolas Catania095292f2010-03-15 09:00:14 -0700515
John Reck1b3c66e2011-01-11 13:20:32 -0800516 class OpenAllInTabsTask extends AsyncTask<Void, Void, Cursor> {
517 long mFolderId;
518 public OpenAllInTabsTask(long id) {
519 mFolderId = id;
520 }
521
522 @Override
523 protected Cursor doInBackground(Void... params) {
524 Context c = getActivity();
525 if (c == null) return null;
526 return c.getContentResolver().query(BookmarkUtils.getBookmarksUri(c),
527 BookmarksLoader.PROJECTION, BrowserContract.Bookmarks.PARENT + "=?",
528 new String[] { Long.toString(mFolderId) }, null);
529 }
530
531 @Override
532 protected void onPostExecute(Cursor result) {
John Reckd3e4d5b2011-07-13 15:48:43 -0700533 if (mCallbacks != null && result.getCount() > 0) {
534 String[] urls = new String[result.getCount()];
535 int i = 0;
John Reck1b3c66e2011-01-11 13:20:32 -0800536 while (result.moveToNext()) {
John Reckd3e4d5b2011-07-13 15:48:43 -0700537 urls[i++] = BrowserBookmarksPage.getUrl(result);
John Reck1b3c66e2011-01-11 13:20:32 -0800538 }
John Reckd3e4d5b2011-07-13 15:48:43 -0700539 mCallbacks.onOpenInNewWindow(urls);
John Reck1b3c66e2011-01-11 13:20:32 -0800540 }
541 }
542
543 }
544
John Reck71efc2b2011-05-09 16:54:28 -0700545 private void editBookmark(BrowserBookmarksAdapter adapter, int position) {
Jeff Hamilton64144e42010-08-18 16:31:59 -0500546 Intent intent = new Intent(getActivity(), AddBookmarkPage.class);
John Reck71efc2b2011-05-09 16:54:28 -0700547 Cursor cursor = adapter.getItem(position);
Jeff Hamilton84029622010-08-05 14:29:28 -0500548 Bundle item = new Bundle();
Michael Kolbbef7a752010-08-25 17:11:35 -0700549 item.putString(BrowserContract.Bookmarks.TITLE,
Jeff Hamilton84029622010-08-05 14:29:28 -0500550 cursor.getString(BookmarksLoader.COLUMN_INDEX_TITLE));
Michael Kolbbef7a752010-08-25 17:11:35 -0700551 item.putString(BrowserContract.Bookmarks.URL,
Jeff Hamilton84029622010-08-05 14:29:28 -0500552 cursor.getString(BookmarksLoader.COLUMN_INDEX_URL));
553 byte[] data = cursor.getBlob(BookmarksLoader.COLUMN_INDEX_FAVICON);
554 if (data != null) {
Michael Kolbbef7a752010-08-25 17:11:35 -0700555 item.putParcelable(BrowserContract.Bookmarks.FAVICON,
Jeff Hamilton84029622010-08-05 14:29:28 -0500556 BitmapFactory.decodeByteArray(data, 0, data.length));
557 }
John Reckc8490812010-11-22 14:15:36 -0800558 item.putLong(BrowserContract.Bookmarks._ID,
559 cursor.getLong(BookmarksLoader.COLUMN_INDEX_ID));
Leon Scroggins III052ce662010-09-13 14:44:16 -0400560 item.putLong(BrowserContract.Bookmarks.PARENT,
561 cursor.getLong(BookmarksLoader.COLUMN_INDEX_PARENT));
John Reckc8490812010-11-22 14:15:36 -0800562 intent.putExtra(AddBookmarkPage.EXTRA_EDIT_BOOKMARK, item);
563 intent.putExtra(AddBookmarkPage.EXTRA_IS_FOLDER,
564 cursor.getInt(BookmarksLoader.COLUMN_INDEX_IS_FOLDER) == 1);
565 startActivity(intent);
Jeff Hamilton84029622010-08-05 14:29:28 -0500566 }
567
John Reck71efc2b2011-05-09 16:54:28 -0700568 private void displayRemoveBookmarkDialog(BrowserBookmarksAdapter adapter,
569 int position) {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800570 // Put up a dialog asking if the user really wants to
571 // delete the bookmark
John Reck71efc2b2011-05-09 16:54:28 -0700572 Cursor cursor = adapter.getItem(position);
Leon Scroggins75630672011-01-13 17:56:15 -0500573 long id = cursor.getLong(BookmarksLoader.COLUMN_INDEX_ID);
574 String title = cursor.getString(BookmarksLoader.COLUMN_INDEX_TITLE);
Jeff Hamilton64144e42010-08-18 16:31:59 -0500575 Context context = getActivity();
Leon Scroggins75630672011-01-13 17:56:15 -0500576 BookmarkUtils.displayRemoveBookmarkDialog(id, title, context, null);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800577 }
578
John Reck71efc2b2011-05-09 16:54:28 -0700579 private String getUrl(BrowserBookmarksAdapter adapter, int position) {
580 return getUrl(adapter.getItem(position));
John Reck608baa72010-11-24 10:32:28 -0800581 }
582
583 /* package */ static String getUrl(Cursor c) {
584 return c.getString(BookmarksLoader.COLUMN_INDEX_URL);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800585 }
Michael Kolb801ecb72010-08-25 12:57:38 -0700586
The Android Open Source Project0c908882009-03-03 19:32:16 -0800587 private void copy(CharSequence text) {
Jeff Hamilton64144e42010-08-18 16:31:59 -0500588 ClipboardManager cm = (ClipboardManager) getActivity().getSystemService(
589 Context.CLIPBOARD_SERVICE);
Dianne Hackbornbf1d10a2011-01-17 12:29:51 -0800590 cm.setPrimaryClip(ClipData.newRawUri(null, Uri.parse(text.toString())));
The Android Open Source Project0c908882009-03-03 19:32:16 -0800591 }
Michael Kolb370a4f32010-10-06 10:45:32 -0700592
John Reckb3417f02011-01-14 11:01:05 -0800593 @Override
John Reckb3417f02011-01-14 11:01:05 -0800594 public void onConfigurationChanged(Configuration newConfig) {
595 super.onConfigurationChanged(newConfig);
596 Resources res = getActivity().getResources();
John Reck71efc2b2011-05-09 16:54:28 -0700597 mGrid.setColumnWidthFromLayout(R.layout.bookmark_thumbnail);
John Reck0ce8a942011-01-14 19:57:09 -0800598 int paddingTop = (int) res.getDimension(R.dimen.combo_paddingTop);
John Reckf3828cd2011-06-14 17:21:55 -0700599 mRoot.setPadding(0, paddingTop, 0, 0);
John Reck9fdd9812011-05-17 14:16:38 -0700600 getActivity().invalidateOptionsMenu();
John Reckb3417f02011-01-14 11:01:05 -0800601 }
602
John Reck608baa72010-11-24 10:32:28 -0800603 /**
604 * BreadCrumb controller callback
605 */
606 @Override
John Reck71efc2b2011-05-09 16:54:28 -0700607 public void onTop(BreadCrumbView view, int level, Object data) {
608 int groupPosition = (Integer) view.getTag(R.id.group_position);
John Reck89f73c12010-12-01 10:10:14 -0800609 Uri uri = (Uri) data;
610 if (uri == null) {
611 // top level
612 uri = BrowserContract.Bookmarks.CONTENT_URI_DEFAULT_FOLDER;
613 }
John Reck71efc2b2011-05-09 16:54:28 -0700614 loadFolder(groupPosition, uri);
John Reckcece1fe2011-09-13 08:47:17 -0700615 if (level <= 1) {
616 view.setVisibility(View.GONE);
617 } else {
618 view.setVisibility(View.VISIBLE);
619 }
John Reck89f73c12010-12-01 10:10:14 -0800620 }
621
622 /**
623 * @param uri
624 */
John Reck71efc2b2011-05-09 16:54:28 -0700625 private void loadFolder(int groupPosition, Uri uri) {
John Reck89f73c12010-12-01 10:10:14 -0800626 LoaderManager manager = getLoaderManager();
John Reck71efc2b2011-05-09 16:54:28 -0700627 // This assumes groups are ordered the same as loaders
628 BookmarksLoader loader = (BookmarksLoader) ((Loader<?>)
629 manager.getLoader(LOADER_BOOKMARKS + groupPosition));
John Reck89f73c12010-12-01 10:10:14 -0800630 loader.setUri(uri);
631 loader.forceLoad();
John Reck608baa72010-11-24 10:32:28 -0800632 }
633
John Reck608baa72010-11-24 10:32:28 -0800634 public void setCallbackListener(BookmarksPageCallbacks callbackListener) {
635 mCallbacks = callbackListener;
636 }
637
638 public void setEnableContextMenu(boolean enable) {
639 mEnableContextMenu = enable;
640 if (mGrid != null) {
641 if (mEnableContextMenu) {
642 registerForContextMenu(mGrid);
643 } else {
644 unregisterForContextMenu(mGrid);
645 mGrid.setLongClickable(false);
646 }
647 }
John Reck608baa72010-11-24 10:32:28 -0800648 }
John Reckc8490812010-11-22 14:15:36 -0800649
650 private static class LookupBookmarkCount extends AsyncTask<Long, Void, Integer> {
651 Context mContext;
652 BookmarkItem mHeader;
653
654 public LookupBookmarkCount(Context context, BookmarkItem header) {
Ben Murdoch914c5592011-08-01 13:58:47 +0100655 mContext = context.getApplicationContext();
John Reckc8490812010-11-22 14:15:36 -0800656 mHeader = header;
657 }
658
659 @Override
660 protected Integer doInBackground(Long... params) {
661 if (params.length != 1) {
662 throw new IllegalArgumentException("Missing folder id!");
663 }
664 Uri uri = BookmarkUtils.getBookmarksUri(mContext);
665 Cursor c = mContext.getContentResolver().query(uri,
666 null, BrowserContract.Bookmarks.PARENT + "=?",
667 new String[] {params[0].toString()}, null);
668 return c.getCount();
669 }
670
671 @Override
672 protected void onPostExecute(Integer result) {
673 if (result > 0) {
674 mHeader.setUrl(mContext.getString(R.string.contextheader_folder_bookmarkcount,
675 result));
676 } else if (result == 0) {
677 mHeader.setUrl(mContext.getString(R.string.contextheader_folder_empty));
678 }
679 }
680 }
John Reck89f73c12010-12-01 10:10:14 -0800681
John Reck71efc2b2011-05-09 16:54:28 -0700682 static class AccountsLoader extends CursorLoader {
John Reck89f73c12010-12-01 10:10:14 -0800683
John Reck71efc2b2011-05-09 16:54:28 -0700684 static String[] ACCOUNTS_PROJECTION = new String[] {
685 Accounts.ACCOUNT_NAME,
686 Accounts.ACCOUNT_TYPE
687 };
John Reck89f73c12010-12-01 10:10:14 -0800688
John Reck71efc2b2011-05-09 16:54:28 -0700689 public AccountsLoader(Context context) {
John Reck1e9815d2011-08-08 17:45:05 -0700690 super(context, Accounts.CONTENT_URI
691 .buildUpon()
692 .appendQueryParameter(BrowserProvider2.PARAM_ALLOW_EMPTY_ACCOUNTS, "false")
693 .build(),
694 ACCOUNTS_PROJECTION, null, null, null);
John Reck89f73c12010-12-01 10:10:14 -0800695 }
John Reck522a4382011-01-26 13:23:47 -0800696
John Reck522a4382011-01-26 13:23:47 -0800697 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800698}