blob: 448f881abe25239bbd03a8d03d5b2604e296b72b [file] [log] [blame]
The Android Open Source Project0c908882009-03-03 19:32:16 -08001/*
2 * Copyright (C) 2006 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.browser;
18
John Reck66302e52011-05-18 17:12:33 -070019import com.android.browser.BookmarkDragHandler.BookmarkDragController;
John Reck71efc2b2011-05-09 16:54:28 -070020import com.android.browser.view.BookmarkExpandableGridView;
21import com.android.browser.view.BookmarkExpandableGridView.BookmarkContextMenuInfo;
John Reckd4893b02010-12-07 17:38:34 -080022
The Android Open Source Project0c908882009-03-03 19:32:16 -080023import android.app.Activity;
Jeff Hamilton64144e42010-08-18 16:31:59 -050024import android.app.Fragment;
Jeff Hamilton84029622010-08-05 14:29:28 -050025import android.app.LoaderManager;
Dianne Hackborneb07b172010-08-26 22:18:14 -070026import android.content.ClipData;
Michael Kolbbef7a752010-08-25 17:11:35 -070027import android.content.ClipboardManager;
Jeff Hamilton84029622010-08-05 14:29:28 -050028import android.content.ContentUris;
Dianne Hackborn80f32622010-08-05 14:17:53 -070029import android.content.Context;
John Reck71efc2b2011-05-09 16:54:28 -070030import android.content.CursorLoader;
The Android Open Source Project0c908882009-03-03 19:32:16 -080031import android.content.Intent;
Jeff Hamilton84029622010-08-05 14:29:28 -050032import android.content.Loader;
Jeff Hamiltondb90aa82010-09-16 03:38:04 -050033import android.content.SharedPreferences;
John Reck8af90642010-11-23 10:27:29 -080034import android.content.SharedPreferences.Editor;
John Reckb3417f02011-01-14 11:01:05 -080035import android.content.res.Configuration;
36import android.content.res.Resources;
Jeff Hamilton84029622010-08-05 14:29:28 -050037import android.database.Cursor;
The Android Open Source Project0c908882009-03-03 19:32:16 -080038import android.graphics.Bitmap;
Jeff Hamilton84029622010-08-05 14:29:28 -050039import android.graphics.BitmapFactory;
40import android.net.Uri;
John Reckc8490812010-11-22 14:15:36 -080041import android.os.AsyncTask;
The Android Open Source Project0c908882009-03-03 19:32:16 -080042import android.os.Bundle;
Jeff Hamiltondb90aa82010-09-16 03:38:04 -050043import android.preference.PreferenceManager;
Jeff Hamilton69bd7072010-08-17 12:38:22 -050044import android.provider.BrowserContract;
John Reck71efc2b2011-05-09 16:54:28 -070045import android.provider.BrowserContract.Accounts;
John Reck1cebb442010-12-10 12:02:16 -080046import android.provider.BrowserContract.ChromeSyncColumns;
The Android Open Source Project0c908882009-03-03 19:32:16 -080047import android.view.ContextMenu;
Jeff Hamiltondb90aa82010-09-16 03:38:04 -050048import android.view.ContextMenu.ContextMenuInfo;
Jeff Hamilton64144e42010-08-18 16:31:59 -050049import android.view.LayoutInflater;
John Reckb3417f02011-01-14 11:01:05 -080050import android.view.Menu;
The Android Open Source Project0c908882009-03-03 19:32:16 -080051import android.view.MenuInflater;
52import android.view.MenuItem;
53import android.view.View;
Jeff Hamiltondb90aa82010-09-16 03:38:04 -050054import android.view.ViewGroup;
Patrick Scottc1cf63a2010-03-09 16:02:08 -050055import android.webkit.WebIconDatabase.IconListener;
John Reck71efc2b2011-05-09 16:54:28 -070056import android.widget.ExpandableListView;
57import android.widget.ExpandableListView.OnChildClickListener;
John Reck8af90642010-11-23 10:27:29 -080058import android.widget.ListView;
John Reck608baa72010-11-24 10:32:28 -080059import android.widget.PopupMenu.OnMenuItemClickListener;
Jeff Hamilton84029622010-08-05 14:29:28 -050060import android.widget.Toast;
The Android Open Source Project0c908882009-03-03 19:32:16 -080061
John Reck71efc2b2011-05-09 16:54:28 -070062import java.util.HashMap;
63
John Reck608baa72010-11-24 10:32:28 -080064interface BookmarksPageCallbacks {
65 // Return true if handled
66 boolean onBookmarkSelected(Cursor c, boolean isFolder);
67 // Return true if handled
68 boolean onOpenInNewWindow(Cursor c);
69}
70
The Android Open Source Project0c908882009-03-03 19:32:16 -080071/**
72 * View showing the user's bookmarks in the browser.
73 */
Jeff Hamilton64144e42010-08-18 16:31:59 -050074public class BrowserBookmarksPage extends Fragment implements View.OnCreateContextMenuListener,
John Reck71efc2b2011-05-09 16:54:28 -070075 LoaderManager.LoaderCallbacks<Cursor>, IconListener,
76 BreadCrumbView.Controller, OnMenuItemClickListener, OnChildClickListener {
The Android Open Source Project0c908882009-03-03 19:32:16 -080077
Jeff Hamilton84029622010-08-05 14:29:28 -050078 static final String LOGTAG = "browser";
Nicolas Catania095292f2010-03-15 09:00:14 -070079
John Reck71efc2b2011-05-09 16:54:28 -070080 static final int LOADER_ACCOUNTS = 1;
81 static final int LOADER_BOOKMARKS = 100;
Jeff Hamilton84029622010-08-05 14:29:28 -050082
Michael Kolb801ecb72010-08-25 12:57:38 -070083 static final String EXTRA_DISABLE_WINDOW = "disable_new_window";
84
Jeff Hamilton7f6cf3e2010-09-17 17:22:21 -050085 public static final String PREF_ACCOUNT_TYPE = "acct_type";
86 public static final String PREF_ACCOUNT_NAME = "acct_name";
Jeff Hamiltondb90aa82010-09-16 03:38:04 -050087
John Reck71efc2b2011-05-09 16:54:28 -070088 static final String ACCOUNT_TYPE = "account_type";
89 static final String ACCOUNT_NAME = "account_name";
90
John Reck8af90642010-11-23 10:27:29 -080091 static final int VIEW_THUMBNAILS = 1;
92 static final int VIEW_LIST = 2;
93 static final String PREF_SELECTED_VIEW = "bookmarks_view";
Jeff Hamiltondb90aa82010-09-16 03:38:04 -050094
John Reck608baa72010-11-24 10:32:28 -080095 BookmarksPageCallbacks mCallbacks;
John Reck0ce8a942011-01-14 19:57:09 -080096 View mRoot;
John Reck71efc2b2011-05-09 16:54:28 -070097 BookmarkExpandableGridView mGrid;
John Reck8af90642010-11-23 10:27:29 -080098 ListView mList;
Jeff Hamilton84029622010-08-05 14:29:28 -050099 boolean mDisableNewWindow;
John Reck608baa72010-11-24 10:32:28 -0800100 boolean mEnableContextMenu = true;
Jeff Hamilton84029622010-08-05 14:29:28 -0500101 View mEmptyView;
John Reck8af90642010-11-23 10:27:29 -0800102 int mCurrentView;
John Reck608baa72010-11-24 10:32:28 -0800103 View mHeader;
John Reck71efc2b2011-05-09 16:54:28 -0700104 HashMap<Integer, BrowserBookmarksAdapter> mBookmarkAdapters = new HashMap<Integer, BrowserBookmarksAdapter>();
John Reck66302e52011-05-18 17:12:33 -0700105 BookmarkDragHandler mDragHandler;
Michael Kolb370a4f32010-10-06 10:45:32 -0700106
John Reck608baa72010-11-24 10:32:28 -0800107 static BrowserBookmarksPage newInstance(BookmarksPageCallbacks cb,
108 Bundle args, ViewGroup headerContainer) {
Michael Kolbe421c242010-10-04 19:29:01 -0700109 BrowserBookmarksPage bbp = new BrowserBookmarksPage();
110 bbp.mCallbacks = cb;
111 bbp.setArguments(args);
112 return bbp;
113 }
114
Jeff Hamilton84029622010-08-05 14:29:28 -0500115 @Override
116 public Loader<Cursor> onCreateLoader(int id, Bundle args) {
John Reck71efc2b2011-05-09 16:54:28 -0700117 if (id == LOADER_ACCOUNTS) {
118 return new AccountsLoader(getActivity());
119 } else if (id >= LOADER_BOOKMARKS) {
120 String accountType = args.getString(ACCOUNT_TYPE);
121 String accountName = args.getString(ACCOUNT_NAME);
122 BookmarksLoader bl = new BookmarksLoader(getActivity(),
123 accountType, accountName);
124 return bl;
125 } else {
126 throw new UnsupportedOperationException("Unknown loader id " + id);
Jeff Hamilton84029622010-08-05 14:29:28 -0500127 }
Jeff Hamilton84029622010-08-05 14:29:28 -0500128 }
129
130 @Override
Jeff Hamiltondb90aa82010-09-16 03:38:04 -0500131 public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
John Reck71efc2b2011-05-09 16:54:28 -0700132 if (loader.getId() == LOADER_ACCOUNTS) {
133 LoaderManager lm = getLoaderManager();
134 int id = LOADER_BOOKMARKS;
135 while (cursor.moveToNext()) {
136 String accountName = cursor.getString(0);
137 String accountType = cursor.getString(1);
138 Bundle args = new Bundle();
139 args.putString(ACCOUNT_NAME, accountName);
140 args.putString(ACCOUNT_TYPE, accountType);
141 BrowserBookmarksAdapter adapter = new BrowserBookmarksAdapter(
142 getActivity(), mCurrentView);
143 mBookmarkAdapters.put(id, adapter);
144 mGrid.addAccount(accountName, adapter);
145 lm.restartLoader(id, args, this);
146 id++;
Jeff Hamiltondb90aa82010-09-16 03:38:04 -0500147 }
John Reck71efc2b2011-05-09 16:54:28 -0700148 // TODO: Figure out what a reload of these means
149 // Currently, a reload is triggered whenever bookmarks change
150 // This is less than ideal
151 // It also causes UI flickering as a new adapter is created
152 // instead of re-using an existing one when the account_name is the
153 // same.
154 // For now, this is a one-shot load
155 getLoaderManager().destroyLoader(LOADER_ACCOUNTS);
156 } else if (loader.getId() >= LOADER_BOOKMARKS) {
157 BrowserBookmarksAdapter adapter = mBookmarkAdapters.get(loader.getId());
158 adapter.changeCursor(cursor);
Jeff Hamiltondb90aa82010-09-16 03:38:04 -0500159 }
Jeff Hamilton84029622010-08-05 14:29:28 -0500160 }
161
John Reck522a4382011-01-26 13:23:47 -0800162 @Override
Dianne Hackborn39772c82010-12-16 00:43:54 -0800163 public void onLoaderReset(Loader<Cursor> loader) {
John Reck71efc2b2011-05-09 16:54:28 -0700164 // TODO: Figure out what to do here (if anything?)
Dianne Hackborn39772c82010-12-16 00:43:54 -0800165 }
166
Michael Kolb370a4f32010-10-06 10:45:32 -0700167 long getFolderId() {
168 LoaderManager manager = getLoaderManager();
169 BookmarksLoader loader =
John Reck608baa72010-11-24 10:32:28 -0800170 (BookmarksLoader) ((Loader<?>)manager.getLoader(LOADER_BOOKMARKS));
Michael Kolb370a4f32010-10-06 10:45:32 -0700171
172 Uri uri = loader.getUri();
173 if (uri != null) {
174 try {
175 return ContentUris.parseId(uri);
176 } catch (NumberFormatException nfx) {
177 return -1;
178 }
Jeff Hamilton84029622010-08-05 14:29:28 -0500179 }
Michael Kolb370a4f32010-10-06 10:45:32 -0700180 return -1;
181 }
182
The Android Open Source Project0c908882009-03-03 19:32:16 -0800183 @Override
184 public boolean onContextItemSelected(MenuItem item) {
Jeff Hamilton64144e42010-08-18 16:31:59 -0500185 final Activity activity = getActivity();
John Reck71efc2b2011-05-09 16:54:28 -0700186 BookmarkContextMenuInfo i = (BookmarkContextMenuInfo)item.getMenuInfo();
The Android Open Source Project0c908882009-03-03 19:32:16 -0800187 // If we have no menu info, we can't tell which item was selected.
188 if (i == null) {
Leon Scrogginsc8cae082010-11-03 18:51:46 -0400189 return false;
The Android Open Source Project0c908882009-03-03 19:32:16 -0800190 }
John Reck71efc2b2011-05-09 16:54:28 -0700191 BrowserBookmarksAdapter adapter = getChildAdapter(i.groupPosition);
Nicolas Catania095292f2010-03-15 09:00:14 -0700192
The Android Open Source Project0c908882009-03-03 19:32:16 -0800193 switch (item.getItemId()) {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800194 case R.id.open_context_menu_id:
John Reck71efc2b2011-05-09 16:54:28 -0700195 loadUrl(adapter, i.childPosition);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800196 break;
197 case R.id.edit_context_menu_id:
John Reck71efc2b2011-05-09 16:54:28 -0700198 editBookmark(adapter, i.childPosition);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800199 break;
200 case R.id.shortcut_context_menu_id:
John Reck71efc2b2011-05-09 16:54:28 -0700201 Cursor c = adapter.getItem(i.childPosition);
John Reck608baa72010-11-24 10:32:28 -0800202 activity.sendBroadcast(createShortcutIntent(getActivity(), c));
The Android Open Source Project0c908882009-03-03 19:32:16 -0800203 break;
204 case R.id.delete_context_menu_id:
John Reck71efc2b2011-05-09 16:54:28 -0700205 displayRemoveBookmarkDialog(adapter, i.childPosition);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800206 break;
207 case R.id.new_window_context_menu_id:
John Reck71efc2b2011-05-09 16:54:28 -0700208 openInNewWindow(adapter, i.childPosition);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800209 break;
Jeff Hamilton84029622010-08-05 14:29:28 -0500210 case R.id.share_link_context_menu_id: {
John Reck71efc2b2011-05-09 16:54:28 -0700211 Cursor cursor = adapter.getItem(i.childPosition);
Michael Kolb8233fac2010-10-26 16:08:53 -0700212 Controller.sharePage(activity,
Jeff Hamilton84029622010-08-05 14:29:28 -0500213 cursor.getString(BookmarksLoader.COLUMN_INDEX_TITLE),
214 cursor.getString(BookmarksLoader.COLUMN_INDEX_URL),
215 getBitmap(cursor, BookmarksLoader.COLUMN_INDEX_FAVICON),
216 getBitmap(cursor, BookmarksLoader.COLUMN_INDEX_THUMBNAIL));
The Android Open Source Project0c908882009-03-03 19:32:16 -0800217 break;
Jeff Hamilton84029622010-08-05 14:29:28 -0500218 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800219 case R.id.copy_url_context_menu_id:
John Reck71efc2b2011-05-09 16:54:28 -0700220 copy(getUrl(adapter, i.childPosition));
Leon Scrogginsfeb941d2009-05-28 17:27:38 -0400221 break;
Jeff Hamilton84029622010-08-05 14:29:28 -0500222 case R.id.homepage_context_menu_id: {
John Reck71efc2b2011-05-09 16:54:28 -0700223 BrowserSettings.getInstance().setHomePage(getUrl(adapter, i.childPosition));
Jeff Hamilton64144e42010-08-18 16:31:59 -0500224 Toast.makeText(activity, R.string.homepage_set, Toast.LENGTH_LONG).show();
Leon Scrogginsfeb941d2009-05-28 17:27:38 -0400225 break;
Jeff Hamilton84029622010-08-05 14:29:28 -0500226 }
Leon Scrogginsa5d669e2009-08-05 14:07:58 -0400227 // Only for the Most visited page
Jeff Hamilton84029622010-08-05 14:29:28 -0500228 case R.id.save_to_bookmarks_menu_id: {
John Reck71efc2b2011-05-09 16:54:28 -0700229 Cursor cursor = adapter.getItem(i.childPosition);
Jeff Hamilton84029622010-08-05 14:29:28 -0500230 String name = cursor.getString(BookmarksLoader.COLUMN_INDEX_TITLE);
231 String url = cursor.getString(BookmarksLoader.COLUMN_INDEX_URL);
Leon Scrogginsa5d669e2009-08-05 14:07:58 -0400232 // If the site is bookmarked, the item becomes remove from
233 // bookmarks.
Jeff Hamilton64144e42010-08-18 16:31:59 -0500234 Bookmarks.removeFromBookmarks(activity, activity.getContentResolver(), url, name);
Leon Scrogginsa5d669e2009-08-05 14:07:58 -0400235 break;
Jeff Hamilton84029622010-08-05 14:29:28 -0500236 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800237 default:
238 return super.onContextItemSelected(item);
239 }
240 return true;
241 }
242
John Reck608baa72010-11-24 10:32:28 -0800243 static Bitmap getBitmap(Cursor cursor, int columnIndex) {
Jeff Hamilton84029622010-08-05 14:29:28 -0500244 byte[] data = cursor.getBlob(columnIndex);
245 if (data == null) {
246 return null;
The Android Open Source Project0c908882009-03-03 19:32:16 -0800247 }
Jeff Hamilton84029622010-08-05 14:29:28 -0500248 return BitmapFactory.decodeByteArray(data, 0, data.length);
249 }
250
John Reckc8490812010-11-22 14:15:36 -0800251 private MenuItem.OnMenuItemClickListener mContextItemClickListener =
252 new MenuItem.OnMenuItemClickListener() {
253 @Override
254 public boolean onMenuItemClick(MenuItem item) {
255 return onContextItemSelected(item);
256 }
257 };
258
Jeff Hamilton84029622010-08-05 14:29:28 -0500259 @Override
John Reckb3417f02011-01-14 11:01:05 -0800260 public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
261 inflater.inflate(R.menu.bookmark, menu);
John Recka5176f32011-05-17 12:35:37 -0700262 if (!BrowserActivity.isTablet(getActivity())) {
John Reck9d27ff52011-02-11 17:47:54 -0800263 MenuItem item = menu.findItem(R.id.add_bookmark);
264 item.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
265 }
John Reckb3417f02011-01-14 11:01:05 -0800266 }
267
268 @Override
Jeff Hamilton84029622010-08-05 14:29:28 -0500269 public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
John Reck71efc2b2011-05-09 16:54:28 -0700270 BookmarkContextMenuInfo info = (BookmarkContextMenuInfo) menuInfo;
271 BrowserBookmarksAdapter adapter = getChildAdapter(info.groupPosition);
272 Cursor cursor = adapter.getItem(info.childPosition);
John Reck1cebb442010-12-10 12:02:16 -0800273 if (!canEdit(cursor)) {
274 return;
275 }
Leon Scroggins5e8a40f2010-09-27 17:13:35 -0400276 boolean isFolder
277 = cursor.getInt(BookmarksLoader.COLUMN_INDEX_IS_FOLDER) != 0;
Michael Kolb801ecb72010-08-25 12:57:38 -0700278
Jeff Hamilton64144e42010-08-18 16:31:59 -0500279 final Activity activity = getActivity();
280 MenuInflater inflater = activity.getMenuInflater();
Jeff Hamilton84029622010-08-05 14:29:28 -0500281 inflater.inflate(R.menu.bookmarkscontext, menu);
John Reckc8490812010-11-22 14:15:36 -0800282 if (isFolder) {
283 menu.setGroupVisible(R.id.FOLDER_CONTEXT_MENU, true);
284 } else {
285 menu.setGroupVisible(R.id.BOOKMARK_CONTEXT_MENU, true);
286 if (mDisableNewWindow) {
287 menu.findItem(R.id.new_window_context_menu_id).setVisible(false);
288 }
Jeff Hamilton84029622010-08-05 14:29:28 -0500289 }
John Reckc8490812010-11-22 14:15:36 -0800290 BookmarkItem header = new BookmarkItem(activity);
291 populateBookmarkItem(cursor, header, isFolder);
John Reckc8490812010-11-22 14:15:36 -0800292 menu.setHeaderView(header);
Jeff Hamilton84029622010-08-05 14:29:28 -0500293
John Reckc8490812010-11-22 14:15:36 -0800294 int count = menu.size();
295 for (int i = 0; i < count; i++) {
296 menu.getItem(i).setOnMenuItemClickListener(mContextItemClickListener);
Jeff Hamilton84029622010-08-05 14:29:28 -0500297 }
Jeff Hamilton84029622010-08-05 14:29:28 -0500298 }
299
John Reck1cebb442010-12-10 12:02:16 -0800300 boolean canEdit(Cursor c) {
301 String unique = c.getString(BookmarksLoader.COLUMN_INDEX_SERVER_UNIQUE);
302 return !ChromeSyncColumns.FOLDER_NAME_OTHER_BOOKMARKS.equals(unique);
303 }
304
John Reckc8490812010-11-22 14:15:36 -0800305 private void populateBookmarkItem(Cursor cursor, BookmarkItem item, boolean isFolder) {
Jeff Hamilton84029622010-08-05 14:29:28 -0500306 item.setName(cursor.getString(BookmarksLoader.COLUMN_INDEX_TITLE));
John Reckc8490812010-11-22 14:15:36 -0800307 if (isFolder) {
308 item.setUrl(null);
309 Bitmap bitmap =
Michael Kolb5a72f182011-01-13 20:35:06 -0800310 BitmapFactory.decodeResource(getResources(), R.drawable.ic_folder_holo_dark);
John Reckc8490812010-11-22 14:15:36 -0800311 item.setFavicon(bitmap);
John Reck60e33362010-12-02 17:52:38 -0800312 new LookupBookmarkCount(getActivity(), item)
313 .execute(cursor.getLong(BookmarksLoader.COLUMN_INDEX_ID));
John Reckc8490812010-11-22 14:15:36 -0800314 } else {
315 String url = cursor.getString(BookmarksLoader.COLUMN_INDEX_URL);
316 item.setUrl(url);
317 Bitmap bitmap = getBitmap(cursor, BookmarksLoader.COLUMN_INDEX_FAVICON);
318 if (bitmap == null) {
319 bitmap = CombinedBookmarkHistoryView.getIconListenerSet().getFavicon(url);
320 }
321 item.setFavicon(bitmap);
Jeff Hamilton84029622010-08-05 14:29:28 -0500322 }
Jeff Hamilton84029622010-08-05 14:29:28 -0500323 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800324
325 /**
326 * Create a new BrowserBookmarksPage.
Nicolas Catania095292f2010-03-15 09:00:14 -0700327 */
The Android Open Source Project0c908882009-03-03 19:32:16 -0800328 @Override
Jeff Hamilton64144e42010-08-18 16:31:59 -0500329 public void onCreate(Bundle icicle) {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800330 super.onCreate(icicle);
John Reck8f295d12011-03-18 15:25:18 -0700331 SharedPreferences prefs = PreferenceManager
John Reck71efc2b2011-05-09 16:54:28 -0700332 .getDefaultSharedPreferences(getActivity());
John Reck8f295d12011-03-18 15:25:18 -0700333 mCurrentView = prefs.getInt(PREF_SELECTED_VIEW, getDefaultView());
John Reck71efc2b2011-05-09 16:54:28 -0700334 // TODO: Support list view
335 mCurrentView = VIEW_THUMBNAILS;
John Reckb3417f02011-01-14 11:01:05 -0800336
Jeff Hamilton64144e42010-08-18 16:31:59 -0500337 Bundle args = getArguments();
Leon Scrogginsbc922852010-10-22 12:15:27 -0400338 mDisableNewWindow = args == null ? false : args.getBoolean(EXTRA_DISABLE_WINDOW, false);
John Reck8f295d12011-03-18 15:25:18 -0700339
340 setHasOptionsMenu(true);
Jeff Hamilton64144e42010-08-18 16:31:59 -0500341 }
Leon Scrogginsd87f85e2009-08-18 14:13:31 -0400342
Jeff Hamilton64144e42010-08-18 16:31:59 -0500343 @Override
Jeff Hamilton64144e42010-08-18 16:31:59 -0500344 public View onCreateView(LayoutInflater inflater, ViewGroup container,
345 Bundle savedInstanceState) {
John Reck0ce8a942011-01-14 19:57:09 -0800346 mRoot = inflater.inflate(R.layout.bookmarks, container, false);
347 mEmptyView = mRoot.findViewById(android.R.id.empty);
Leon Scrogginsd87f85e2009-08-18 14:13:31 -0400348
John Reck71efc2b2011-05-09 16:54:28 -0700349 mGrid = (BookmarkExpandableGridView) mRoot.findViewById(R.id.grid);
350 mGrid.setOnChildClickListener(this);
351 mGrid.setColumnWidthFromLayout(R.layout.bookmark_thumbnail);
352 mGrid.setBreadcrumbController(this);
John Reck0ce8a942011-01-14 19:57:09 -0800353 mList = (ListView) mRoot.findViewById(R.id.list);
John Reck71efc2b2011-05-09 16:54:28 -0700354 // TODO: mList.setOnItemClickListener(this);
John Reck608baa72010-11-24 10:32:28 -0800355 setEnableContextMenu(mEnableContextMenu);
John Reck66302e52011-05-18 17:12:33 -0700356 mDragHandler = new BookmarkDragHandler(getActivity(), mDragController,
357 mGrid.getDragAdapter());
John Reck608baa72010-11-24 10:32:28 -0800358
Jeff Hamiltondb90aa82010-09-16 03:38:04 -0500359 // Start the loaders
360 LoaderManager lm = getLoaderManager();
John Reck71efc2b2011-05-09 16:54:28 -0700361 lm.restartLoader(LOADER_ACCOUNTS, null, this);
Jeff Hamilton84029622010-08-05 14:29:28 -0500362
363 // Add our own listener in case there are favicons that have yet to be loaded.
Michael Kolbe421c242010-10-04 19:29:01 -0700364 CombinedBookmarkHistoryView.getIconListenerSet().addListener(this);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800365
John Reck0ce8a942011-01-14 19:57:09 -0800366 return mRoot;
Jeff Hamilton64144e42010-08-18 16:31:59 -0500367 }
Michael Kolb801ecb72010-08-25 12:57:38 -0700368
John Reck9d27ff52011-02-11 17:47:54 -0800369 private int getDefaultView() {
John Recka5176f32011-05-17 12:35:37 -0700370 if (BrowserActivity.isTablet(getActivity())) {
John Reck9d27ff52011-02-11 17:47:54 -0800371 return VIEW_THUMBNAILS;
372 }
373 return VIEW_LIST;
374 }
375
John Reck608baa72010-11-24 10:32:28 -0800376 @Override
377 public void onDestroyView() {
378 super.onDestroyView();
John Reck71efc2b2011-05-09 16:54:28 -0700379 mGrid.setBreadcrumbController(null);
380 LoaderManager lm = getLoaderManager();
381 lm.destroyLoader(LOADER_ACCOUNTS);
382 for (int id : mBookmarkAdapters.keySet()) {
383 lm.destroyLoader(id);
John Reck608baa72010-11-24 10:32:28 -0800384 }
John Reck71efc2b2011-05-09 16:54:28 -0700385 mBookmarkAdapters.clear();
386
John Recke3063ae2011-01-27 14:57:40 -0800387 CombinedBookmarkHistoryView.getIconListenerSet().removeListener(this);
John Reck608baa72010-11-24 10:32:28 -0800388 }
389
Nicolas Catania095292f2010-03-15 09:00:14 -0700390 @Override
Jeff Hamilton84029622010-08-05 14:29:28 -0500391 public void onReceivedIcon(String url, Bitmap icon) {
392 // A new favicon has been loaded, so let anything attached to the adapter know about it
393 // so new icons will be loaded.
John Reck71efc2b2011-05-09 16:54:28 -0700394 // TODO: Notify all of data set changed
395 // TODO: Wait, is this even needed? Won't this trigger a DB change anyway?
396 }
397
398 private BrowserBookmarksAdapter getChildAdapter(int groupPosition) {
399 if (mCurrentView == VIEW_THUMBNAILS) {
400 return mGrid.getChildAdapter(groupPosition);
401 } else {
402 // TODO: Support expandable list
403 return null;
404 }
405 }
406
407 private BreadCrumbView getBreadCrumbs(int groupPosition) {
408 if (mCurrentView == VIEW_THUMBNAILS) {
409 return mGrid.getBreadCrumbs(groupPosition);
410 } else {
411 // TODO: Support expandable list
412 return null;
413 }
Nicolas Catania095292f2010-03-15 09:00:14 -0700414 }
415
Jeff Hamilton84029622010-08-05 14:29:28 -0500416 @Override
John Reck71efc2b2011-05-09 16:54:28 -0700417 public boolean onChildClick(ExpandableListView parent, View v,
418 int groupPosition, int childPosition, long id) {
419 BrowserBookmarksAdapter adapter = getChildAdapter(groupPosition);
420 Cursor cursor = adapter.getItem(childPosition);
John Reck608baa72010-11-24 10:32:28 -0800421 boolean isFolder = cursor.getInt(BookmarksLoader.COLUMN_INDEX_IS_FOLDER) != 0;
422 if (mCallbacks != null &&
423 mCallbacks.onBookmarkSelected(cursor, isFolder)) {
John Reck71efc2b2011-05-09 16:54:28 -0700424 return true;
Ben Murdoch328ea872009-09-16 13:33:29 +0100425 }
426
John Reck71efc2b2011-05-09 16:54:28 -0700427 // TODO: Folder stuff
John Reck608baa72010-11-24 10:32:28 -0800428 if (isFolder) {
Michael Kolb370a4f32010-10-06 10:45:32 -0700429 String title = cursor.getString(BookmarksLoader.COLUMN_INDEX_TITLE);
Jeff Hamilton84029622010-08-05 14:29:28 -0500430 Uri uri = ContentUris.withAppendedId(
431 BrowserContract.Bookmarks.CONTENT_URI_DEFAULT_FOLDER, id);
John Reck71efc2b2011-05-09 16:54:28 -0700432 BreadCrumbView crumbs = getBreadCrumbs(groupPosition);
433 if (crumbs != null) {
Michael Kolb370a4f32010-10-06 10:45:32 -0700434 // update crumbs
John Reck71efc2b2011-05-09 16:54:28 -0700435 crumbs.pushView(title, uri);
Michael Kolb370a4f32010-10-06 10:45:32 -0700436 }
John Reck71efc2b2011-05-09 16:54:28 -0700437 loadFolder(groupPosition, uri);
Leon Scrogginsa5d669e2009-08-05 14:07:58 -0400438 }
John Reck71efc2b2011-05-09 16:54:28 -0700439 return true;
The Android Open Source Project0c908882009-03-03 19:32:16 -0800440 }
441
John Reck608baa72010-11-24 10:32:28 -0800442 /* package */ static Intent createShortcutIntent(Context context, Cursor cursor) {
Jeff Hamilton84029622010-08-05 14:29:28 -0500443 String url = cursor.getString(BookmarksLoader.COLUMN_INDEX_URL);
Michael Kolbccaa8962010-10-20 17:03:10 -0700444 String title = cursor.getString(BookmarksLoader.COLUMN_INDEX_TITLE);
Jeff Hamilton84029622010-08-05 14:29:28 -0500445 Bitmap touchIcon = getBitmap(cursor, BookmarksLoader.COLUMN_INDEX_TOUCH_ICON);
446 Bitmap favicon = getBitmap(cursor, BookmarksLoader.COLUMN_INDEX_FAVICON);
John Reck608baa72010-11-24 10:32:28 -0800447 return BookmarkUtils.createAddToHomeIntent(context, url, title, touchIcon, favicon);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800448 }
449
John Reck71efc2b2011-05-09 16:54:28 -0700450 private void loadUrl(BrowserBookmarksAdapter adapter, int position) {
451 if (mCallbacks != null && adapter != null) {
452 mCallbacks.onBookmarkSelected(adapter.getItem(position), false);
John Reck608baa72010-11-24 10:32:28 -0800453 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800454 }
455
John Reck71efc2b2011-05-09 16:54:28 -0700456 private void openInNewWindow(BrowserBookmarksAdapter adapter, int position) {
John Reck608baa72010-11-24 10:32:28 -0800457 if (mCallbacks != null) {
John Reck71efc2b2011-05-09 16:54:28 -0700458 Cursor c = adapter.getItem(position);
John Reck1b3c66e2011-01-11 13:20:32 -0800459 boolean isFolder = c.getInt(BookmarksLoader.COLUMN_INDEX_IS_FOLDER) == 1;
460 if (isFolder) {
461 long id = c.getLong(BookmarksLoader.COLUMN_INDEX_ID);
462 new OpenAllInTabsTask(id).execute();
463 } else {
464 mCallbacks.onOpenInNewWindow(c);
465 }
John Reck608baa72010-11-24 10:32:28 -0800466 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800467 }
Nicolas Catania095292f2010-03-15 09:00:14 -0700468
John Reck1b3c66e2011-01-11 13:20:32 -0800469 class OpenAllInTabsTask extends AsyncTask<Void, Void, Cursor> {
470 long mFolderId;
471 public OpenAllInTabsTask(long id) {
472 mFolderId = id;
473 }
474
475 @Override
476 protected Cursor doInBackground(Void... params) {
477 Context c = getActivity();
478 if (c == null) return null;
479 return c.getContentResolver().query(BookmarkUtils.getBookmarksUri(c),
480 BookmarksLoader.PROJECTION, BrowserContract.Bookmarks.PARENT + "=?",
481 new String[] { Long.toString(mFolderId) }, null);
482 }
483
484 @Override
485 protected void onPostExecute(Cursor result) {
486 if (mCallbacks != null) {
487 while (result.moveToNext()) {
488 mCallbacks.onOpenInNewWindow(result);
489 }
490 }
491 }
492
493 }
494
John Reck71efc2b2011-05-09 16:54:28 -0700495 private void editBookmark(BrowserBookmarksAdapter adapter, int position) {
Jeff Hamilton64144e42010-08-18 16:31:59 -0500496 Intent intent = new Intent(getActivity(), AddBookmarkPage.class);
John Reck71efc2b2011-05-09 16:54:28 -0700497 Cursor cursor = adapter.getItem(position);
Jeff Hamilton84029622010-08-05 14:29:28 -0500498 Bundle item = new Bundle();
Michael Kolbbef7a752010-08-25 17:11:35 -0700499 item.putString(BrowserContract.Bookmarks.TITLE,
Jeff Hamilton84029622010-08-05 14:29:28 -0500500 cursor.getString(BookmarksLoader.COLUMN_INDEX_TITLE));
Michael Kolbbef7a752010-08-25 17:11:35 -0700501 item.putString(BrowserContract.Bookmarks.URL,
Jeff Hamilton84029622010-08-05 14:29:28 -0500502 cursor.getString(BookmarksLoader.COLUMN_INDEX_URL));
503 byte[] data = cursor.getBlob(BookmarksLoader.COLUMN_INDEX_FAVICON);
504 if (data != null) {
Michael Kolbbef7a752010-08-25 17:11:35 -0700505 item.putParcelable(BrowserContract.Bookmarks.FAVICON,
Jeff Hamilton84029622010-08-05 14:29:28 -0500506 BitmapFactory.decodeByteArray(data, 0, data.length));
507 }
John Reckc8490812010-11-22 14:15:36 -0800508 item.putLong(BrowserContract.Bookmarks._ID,
509 cursor.getLong(BookmarksLoader.COLUMN_INDEX_ID));
Leon Scroggins III052ce662010-09-13 14:44:16 -0400510 item.putLong(BrowserContract.Bookmarks.PARENT,
511 cursor.getLong(BookmarksLoader.COLUMN_INDEX_PARENT));
John Reckc8490812010-11-22 14:15:36 -0800512 intent.putExtra(AddBookmarkPage.EXTRA_EDIT_BOOKMARK, item);
513 intent.putExtra(AddBookmarkPage.EXTRA_IS_FOLDER,
514 cursor.getInt(BookmarksLoader.COLUMN_INDEX_IS_FOLDER) == 1);
515 startActivity(intent);
Jeff Hamilton84029622010-08-05 14:29:28 -0500516 }
517
John Reck71efc2b2011-05-09 16:54:28 -0700518 private void displayRemoveBookmarkDialog(BrowserBookmarksAdapter adapter,
519 int position) {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800520 // Put up a dialog asking if the user really wants to
521 // delete the bookmark
John Reck71efc2b2011-05-09 16:54:28 -0700522 Cursor cursor = adapter.getItem(position);
Leon Scroggins75630672011-01-13 17:56:15 -0500523 long id = cursor.getLong(BookmarksLoader.COLUMN_INDEX_ID);
524 String title = cursor.getString(BookmarksLoader.COLUMN_INDEX_TITLE);
Jeff Hamilton64144e42010-08-18 16:31:59 -0500525 Context context = getActivity();
Leon Scroggins75630672011-01-13 17:56:15 -0500526 BookmarkUtils.displayRemoveBookmarkDialog(id, title, context, null);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800527 }
528
John Reck71efc2b2011-05-09 16:54:28 -0700529 private String getUrl(BrowserBookmarksAdapter adapter, int position) {
530 return getUrl(adapter.getItem(position));
John Reck608baa72010-11-24 10:32:28 -0800531 }
532
533 /* package */ static String getUrl(Cursor c) {
534 return c.getString(BookmarksLoader.COLUMN_INDEX_URL);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800535 }
Michael Kolb801ecb72010-08-25 12:57:38 -0700536
The Android Open Source Project0c908882009-03-03 19:32:16 -0800537 private void copy(CharSequence text) {
Jeff Hamilton64144e42010-08-18 16:31:59 -0500538 ClipboardManager cm = (ClipboardManager) getActivity().getSystemService(
539 Context.CLIPBOARD_SERVICE);
Dianne Hackbornbf1d10a2011-01-17 12:29:51 -0800540 cm.setPrimaryClip(ClipData.newRawUri(null, Uri.parse(text.toString())));
The Android Open Source Project0c908882009-03-03 19:32:16 -0800541 }
Michael Kolb370a4f32010-10-06 10:45:32 -0700542
John Reckb3417f02011-01-14 11:01:05 -0800543 @Override
544 public boolean onOptionsItemSelected(MenuItem item) {
545 switch (item.getItemId()) {
546 case R.id.thumbnail_view:
547 selectView(VIEW_THUMBNAILS);
548 return true;
549 case R.id.list_view:
550 selectView(VIEW_LIST);
551 return true;
552 }
553 return super.onOptionsItemSelected(item);
554 }
555
556 @Override
557 public void onConfigurationChanged(Configuration newConfig) {
558 super.onConfigurationChanged(newConfig);
559 Resources res = getActivity().getResources();
560 int horizontalSpacing = (int) res.getDimension(R.dimen.combo_horizontalSpacing);
561 mGrid.setHorizontalSpacing(horizontalSpacing);
John Reck71efc2b2011-05-09 16:54:28 -0700562 mGrid.setColumnWidthFromLayout(R.layout.bookmark_thumbnail);
John Reck0ce8a942011-01-14 19:57:09 -0800563 int paddingLeftRight = (int) res.getDimension(R.dimen.combo_paddingLeftRight);
564 int paddingTop = (int) res.getDimension(R.dimen.combo_paddingTop);
565 mRoot.setPadding(paddingLeftRight, paddingTop,
566 paddingLeftRight, 0);
John Reck9fdd9812011-05-17 14:16:38 -0700567 getActivity().invalidateOptionsMenu();
John Reckb3417f02011-01-14 11:01:05 -0800568 }
569
570 @Override
571 public void onPrepareOptionsMenu(Menu menu) {
572 super.onPrepareOptionsMenu(menu);
573 menu.findItem(R.id.list_view).setVisible(mCurrentView != VIEW_LIST);
574 menu.findItem(R.id.thumbnail_view).setVisible(mCurrentView != VIEW_THUMBNAILS);
575 }
576
John Reck8af90642010-11-23 10:27:29 -0800577 void selectView(int view) {
John Reck71efc2b2011-05-09 16:54:28 -0700578 // TODO: Support list view
579 view = mCurrentView;
John Reck8af90642010-11-23 10:27:29 -0800580 if (view == mCurrentView) {
581 return;
582 }
John Reck8af90642010-11-23 10:27:29 -0800583 SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getActivity());
584 Editor edit = prefs.edit();
585 edit.putInt(PREF_SELECTED_VIEW, mCurrentView);
586 edit.apply();
587 if (mEmptyView.getVisibility() == View.VISIBLE) {
588 return;
589 }
590 setupBookmarkView();
591 }
592
593 private void setupBookmarkView() {
John Reck71efc2b2011-05-09 16:54:28 -0700594 // TODO: Support list view
595// mAdapter.selectView(mCurrentView);
596// switch (mCurrentView) {
597// case VIEW_THUMBNAILS:
598// mList.setAdapter(null);
599// SharedPreferences prefs = PreferenceManager
600// .getDefaultSharedPreferences(getActivity());
601// String accountName = prefs.getString(PREF_ACCOUNT_NAME, null);
602// mGrid.addAccount(accountName, mAdapter);
603// mGrid.setVisibility(View.VISIBLE);
604// mList.setVisibility(View.GONE);
605// break;
606// case VIEW_LIST:
607// mGrid.clearAccounts();
608// if (mList.getAdapter() != mAdapter) {
609// mList.setAdapter(mAdapter);
610// }
611// mGrid.setVisibility(View.GONE);
612// mList.setVisibility(View.VISIBLE);
613// break;
614// }
John Reck8af90642010-11-23 10:27:29 -0800615 }
John Reck608baa72010-11-24 10:32:28 -0800616
John Reck608baa72010-11-24 10:32:28 -0800617 /**
618 * BreadCrumb controller callback
619 */
620 @Override
John Reck71efc2b2011-05-09 16:54:28 -0700621 public void onTop(BreadCrumbView view, int level, Object data) {
622 int groupPosition = (Integer) view.getTag(R.id.group_position);
John Reck89f73c12010-12-01 10:10:14 -0800623 Uri uri = (Uri) data;
624 if (uri == null) {
625 // top level
626 uri = BrowserContract.Bookmarks.CONTENT_URI_DEFAULT_FOLDER;
627 }
John Reck71efc2b2011-05-09 16:54:28 -0700628 loadFolder(groupPosition, uri);
John Reck89f73c12010-12-01 10:10:14 -0800629 }
630
631 /**
632 * @param uri
633 */
John Reck71efc2b2011-05-09 16:54:28 -0700634 private void loadFolder(int groupPosition, Uri uri) {
John Reck89f73c12010-12-01 10:10:14 -0800635 LoaderManager manager = getLoaderManager();
John Reck71efc2b2011-05-09 16:54:28 -0700636 // This assumes groups are ordered the same as loaders
637 BookmarksLoader loader = (BookmarksLoader) ((Loader<?>)
638 manager.getLoader(LOADER_BOOKMARKS + groupPosition));
John Reck89f73c12010-12-01 10:10:14 -0800639 loader.setUri(uri);
640 loader.forceLoad();
John Reck608baa72010-11-24 10:32:28 -0800641 }
642
643 @Override
John Reck608baa72010-11-24 10:32:28 -0800644 public boolean onMenuItemClick(MenuItem item) {
645 switch (item.getItemId()) {
646 case R.id.list_view:
John Reck608baa72010-11-24 10:32:28 -0800647 selectView(BrowserBookmarksPage.VIEW_LIST);
648 return true;
649 case R.id.thumbnail_view:
John Reck608baa72010-11-24 10:32:28 -0800650 selectView(BrowserBookmarksPage.VIEW_THUMBNAILS);
651 return true;
652 }
653 return false;
654 }
655
656 public boolean onBackPressed() {
John Reck608baa72010-11-24 10:32:28 -0800657 return false;
658 }
659
660 public void setCallbackListener(BookmarksPageCallbacks callbackListener) {
661 mCallbacks = callbackListener;
662 }
663
664 public void setEnableContextMenu(boolean enable) {
665 mEnableContextMenu = enable;
666 if (mGrid != null) {
667 if (mEnableContextMenu) {
668 registerForContextMenu(mGrid);
669 } else {
670 unregisterForContextMenu(mGrid);
671 mGrid.setLongClickable(false);
672 }
673 }
674 if (mList != null) {
675 if (mEnableContextMenu) {
676 registerForContextMenu(mList);
677 } else {
678 unregisterForContextMenu(mList);
679 mList.setLongClickable(false);
680 }
681 }
682 }
John Reckc8490812010-11-22 14:15:36 -0800683
John Reck66302e52011-05-18 17:12:33 -0700684 private BookmarkDragController mDragController = new BookmarkDragController() {
685
686 @Override
687 public boolean startDrag(Cursor item) {
688 return canEdit(item);
689 }
690 };
691
John Reckc8490812010-11-22 14:15:36 -0800692 private static class LookupBookmarkCount extends AsyncTask<Long, Void, Integer> {
693 Context mContext;
694 BookmarkItem mHeader;
695
696 public LookupBookmarkCount(Context context, BookmarkItem header) {
697 mContext = context;
698 mHeader = header;
699 }
700
701 @Override
702 protected Integer doInBackground(Long... params) {
703 if (params.length != 1) {
704 throw new IllegalArgumentException("Missing folder id!");
705 }
706 Uri uri = BookmarkUtils.getBookmarksUri(mContext);
707 Cursor c = mContext.getContentResolver().query(uri,
708 null, BrowserContract.Bookmarks.PARENT + "=?",
709 new String[] {params[0].toString()}, null);
710 return c.getCount();
711 }
712
713 @Override
714 protected void onPostExecute(Integer result) {
715 if (result > 0) {
716 mHeader.setUrl(mContext.getString(R.string.contextheader_folder_bookmarkcount,
717 result));
718 } else if (result == 0) {
719 mHeader.setUrl(mContext.getString(R.string.contextheader_folder_empty));
720 }
721 }
722 }
John Reck89f73c12010-12-01 10:10:14 -0800723
John Reck71efc2b2011-05-09 16:54:28 -0700724 static class AccountsLoader extends CursorLoader {
John Reck89f73c12010-12-01 10:10:14 -0800725
John Reck71efc2b2011-05-09 16:54:28 -0700726 static String[] ACCOUNTS_PROJECTION = new String[] {
727 Accounts.ACCOUNT_NAME,
728 Accounts.ACCOUNT_TYPE
729 };
John Reck89f73c12010-12-01 10:10:14 -0800730
John Reck71efc2b2011-05-09 16:54:28 -0700731 public AccountsLoader(Context context) {
732 super(context, Accounts.CONTENT_URI, ACCOUNTS_PROJECTION, null, null,
733 Accounts.ACCOUNT_NAME + " ASC");
John Reck89f73c12010-12-01 10:10:14 -0800734 }
John Reck522a4382011-01-26 13:23:47 -0800735
John Reck522a4382011-01-26 13:23:47 -0800736 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800737}