BrowserBookmarksPage.java revision bf1d10af39eb23d68db69a8eda9e12e62f1a9682
1/* 2 * Copyright (C) 2006 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.browser; 18 19import com.android.browser.BreadCrumbView.Crumb; 20 21import android.app.Activity; 22import android.app.AlertDialog; 23import android.app.Fragment; 24import android.app.LoaderManager; 25import android.content.ClipData; 26import android.content.ClipboardManager; 27import android.content.ContentResolver; 28import android.content.ContentUris; 29import android.content.Context; 30import android.content.CursorLoader; 31import android.content.DialogInterface; 32import android.content.Intent; 33import android.content.Loader; 34import android.content.SharedPreferences; 35import android.content.SharedPreferences.Editor; 36import android.content.res.Configuration; 37import android.content.res.Resources; 38import android.database.Cursor; 39import android.graphics.Bitmap; 40import android.graphics.BitmapFactory; 41import android.net.Uri; 42import android.os.AsyncTask; 43import android.os.Bundle; 44import android.preference.PreferenceManager; 45import android.provider.BrowserContract; 46import android.provider.BrowserContract.Accounts; 47import android.provider.BrowserContract.ChromeSyncColumns; 48import android.text.TextUtils; 49import android.view.ContextMenu; 50import android.view.ContextMenu.ContextMenuInfo; 51import android.view.LayoutInflater; 52import android.view.Menu; 53import android.view.MenuInflater; 54import android.view.MenuItem; 55import android.view.View; 56import android.view.ViewGroup; 57import android.webkit.WebIconDatabase.IconListener; 58import android.widget.Adapter; 59import android.widget.AdapterView; 60import android.widget.AdapterView.OnItemClickListener; 61import android.widget.AdapterView.OnItemSelectedListener; 62import android.widget.GridView; 63import android.widget.ListView; 64import android.widget.PopupMenu.OnMenuItemClickListener; 65import android.widget.Toast; 66 67interface BookmarksPageCallbacks { 68 // Return true if handled 69 boolean onBookmarkSelected(Cursor c, boolean isFolder); 70 // Return true if handled 71 boolean onOpenInNewWindow(Cursor c); 72 void onFolderChanged(int level, Uri uri); 73} 74 75/** 76 * View showing the user's bookmarks in the browser. 77 */ 78public class BrowserBookmarksPage extends Fragment implements View.OnCreateContextMenuListener, 79 LoaderManager.LoaderCallbacks<Cursor>, OnItemClickListener, IconListener, 80 OnItemSelectedListener, BreadCrumbView.Controller, OnMenuItemClickListener { 81 82 static final String LOGTAG = "browser"; 83 84 static final int LOADER_BOOKMARKS = 1; 85 static final int LOADER_ACCOUNTS_THEN_BOOKMARKS = 2; 86 87 static final String EXTRA_DISABLE_WINDOW = "disable_new_window"; 88 89 static final String ACCOUNT_NAME_UNSYNCED = "Unsynced"; 90 91 public static final String PREF_ACCOUNT_TYPE = "acct_type"; 92 public static final String PREF_ACCOUNT_NAME = "acct_name"; 93 94 static final String DEFAULT_ACCOUNT = "local"; 95 static final int VIEW_THUMBNAILS = 1; 96 static final int VIEW_LIST = 2; 97 static final String PREF_SELECTED_VIEW = "bookmarks_view"; 98 99 BookmarksPageCallbacks mCallbacks; 100 GridView mGrid; 101 ListView mList; 102 BrowserBookmarksAdapter mAdapter; 103 boolean mDisableNewWindow; 104 boolean mCanceled = false; 105 boolean mEnableContextMenu = true; 106 boolean mShowRootFolder = false; 107 View mEmptyView; 108 int mCurrentView; 109 View mHeader; 110 ViewGroup mHeaderContainer; 111 BreadCrumbView mCrumbs; 112 int mCrumbVisibility = View.VISIBLE; 113 int mCrumbMaxVisible = -1; 114 boolean mCrumbBackButton = false; 115 116 static BrowserBookmarksPage newInstance(BookmarksPageCallbacks cb, 117 Bundle args, ViewGroup headerContainer) { 118 BrowserBookmarksPage bbp = new BrowserBookmarksPage(); 119 bbp.mCallbacks = cb; 120 bbp.mHeaderContainer = headerContainer; 121 bbp.setArguments(args); 122 return bbp; 123 } 124 125 @Override 126 public Loader<Cursor> onCreateLoader(int id, Bundle args) { 127 switch (id) { 128 case LOADER_BOOKMARKS: { 129 String accountType = null; 130 String accountName = null; 131 if (args != null) { 132 accountType = args.getString(BookmarksLoader.ARG_ACCOUNT_TYPE); 133 accountName = args.getString(BookmarksLoader.ARG_ACCOUNT_NAME); 134 } 135 BookmarksLoader bl = new BookmarksLoader(getActivity(), accountType, accountName); 136 if (mCrumbs != null) { 137 Uri uri = (Uri) mCrumbs.getTopData(); 138 if (uri != null) { 139 bl.setUri(uri); 140 } 141 } 142 return bl; 143 } 144 case LOADER_ACCOUNTS_THEN_BOOKMARKS: { 145 return new CursorLoader(getActivity(), Accounts.CONTENT_URI, 146 new String[] { Accounts.ACCOUNT_TYPE, Accounts.ACCOUNT_NAME }, null, null, 147 null); 148 } 149 } 150 throw new UnsupportedOperationException("Unknown loader id " + id); 151 } 152 153 @Override 154 public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) { 155 switch (loader.getId()) { 156 case LOADER_BOOKMARKS: { 157 // Set the visibility of the empty vs. content views 158 if (cursor == null || cursor.getCount() == 0) { 159 mEmptyView.setVisibility(View.VISIBLE); 160 mGrid.setVisibility(View.GONE); 161 mList.setVisibility(View.GONE); 162 } else { 163 mEmptyView.setVisibility(View.GONE); 164 setupBookmarkView(); 165 } 166 167 // Give the new data to the adapter 168 mAdapter.changeCursor(cursor); 169 break; 170 } 171 172 case LOADER_ACCOUNTS_THEN_BOOKMARKS: { 173 SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences( 174 getActivity()); 175 String storedAccountType = prefs.getString(PREF_ACCOUNT_TYPE, null); 176 String storedAccountName = prefs.getString(PREF_ACCOUNT_NAME, null); 177 String accountType = 178 TextUtils.isEmpty(storedAccountType) ? DEFAULT_ACCOUNT : storedAccountType; 179 String accountName = 180 TextUtils.isEmpty(storedAccountName) ? DEFAULT_ACCOUNT : storedAccountName; 181 182 Bundle args = null; 183 if (cursor == null || !cursor.moveToFirst()) { 184 // No accounts, set the prefs to the default 185 accountType = DEFAULT_ACCOUNT; 186 accountName = DEFAULT_ACCOUNT; 187 } else { 188 int accountPosition = -1; 189 190 if (!DEFAULT_ACCOUNT.equals(accountType) && 191 !DEFAULT_ACCOUNT.equals(accountName)) { 192 // Check to see if the account in prefs still exists 193 cursor.moveToFirst(); 194 do { 195 if (accountType.equals(cursor.getString(0)) 196 && accountName.equals(cursor.getString(1))) { 197 accountPosition = cursor.getPosition(); 198 break; 199 } 200 } while (cursor.moveToNext()); 201 } 202 203 if (accountPosition == -1) { 204 if (!(DEFAULT_ACCOUNT.equals(accountType) 205 && DEFAULT_ACCOUNT.equals(accountName))) { 206 // No account is set in prefs and there is at least one, 207 // so pick the first one as the default 208 cursor.moveToFirst(); 209 accountType = cursor.getString(0); 210 accountName = cursor.getString(1); 211 } 212 } 213 214 args = new Bundle(); 215 args.putString(BookmarksLoader.ARG_ACCOUNT_TYPE, accountType); 216 args.putString(BookmarksLoader.ARG_ACCOUNT_NAME, accountName); 217 } 218 219 // The stored account name wasn't found, update the stored account with a valid one 220 if (!accountType.equals(storedAccountType) 221 || !accountName.equals(storedAccountName)) { 222 prefs.edit() 223 .putString(PREF_ACCOUNT_TYPE, accountType) 224 .putString(PREF_ACCOUNT_NAME, accountName) 225 .apply(); 226 } 227 getLoaderManager().initLoader(LOADER_BOOKMARKS, args, this); 228 229 break; 230 } 231 } 232 } 233 234 public void onLoaderReset(Loader<Cursor> loader) { 235 onLoadFinished(loader, null); 236 switch (loader.getId()) { 237 case LOADER_BOOKMARKS: { 238 onLoadFinished(loader, null); 239 break; 240 } 241 } 242 } 243 244 long getFolderId() { 245 LoaderManager manager = getLoaderManager(); 246 BookmarksLoader loader = 247 (BookmarksLoader) ((Loader<?>)manager.getLoader(LOADER_BOOKMARKS)); 248 249 Uri uri = loader.getUri(); 250 if (uri != null) { 251 try { 252 return ContentUris.parseId(uri); 253 } catch (NumberFormatException nfx) { 254 return -1; 255 } 256 } 257 return -1; 258 } 259 260 @Override 261 public boolean onContextItemSelected(MenuItem item) { 262 final Activity activity = getActivity(); 263 // It is possible that the view has been canceled when we get to 264 // this point as back has a higher priority 265 if (mCanceled) { 266 return false; 267 } 268 AdapterView.AdapterContextMenuInfo i = 269 (AdapterView.AdapterContextMenuInfo)item.getMenuInfo(); 270 // If we have no menu info, we can't tell which item was selected. 271 if (i == null) { 272 return false; 273 } 274 275 switch (item.getItemId()) { 276 case R.id.open_context_menu_id: 277 loadUrl(i.position); 278 break; 279 case R.id.edit_context_menu_id: 280 editBookmark(i.position); 281 break; 282 case R.id.shortcut_context_menu_id: 283 Cursor c = mAdapter.getItem(i.position); 284 activity.sendBroadcast(createShortcutIntent(getActivity(), c)); 285 break; 286 case R.id.delete_context_menu_id: 287 displayRemoveBookmarkDialog(i.position); 288 break; 289 case R.id.new_window_context_menu_id: 290 openInNewWindow(i.position); 291 break; 292 case R.id.share_link_context_menu_id: { 293 Cursor cursor = mAdapter.getItem(i.position); 294 Controller.sharePage(activity, 295 cursor.getString(BookmarksLoader.COLUMN_INDEX_TITLE), 296 cursor.getString(BookmarksLoader.COLUMN_INDEX_URL), 297 getBitmap(cursor, BookmarksLoader.COLUMN_INDEX_FAVICON), 298 getBitmap(cursor, BookmarksLoader.COLUMN_INDEX_THUMBNAIL)); 299 break; 300 } 301 case R.id.copy_url_context_menu_id: 302 copy(getUrl(i.position)); 303 break; 304 case R.id.homepage_context_menu_id: { 305 BrowserSettings.getInstance().setHomePage(activity, getUrl(i.position)); 306 Toast.makeText(activity, R.string.homepage_set, Toast.LENGTH_LONG).show(); 307 break; 308 } 309 // Only for the Most visited page 310 case R.id.save_to_bookmarks_menu_id: { 311 Cursor cursor = mAdapter.getItem(i.position); 312 String name = cursor.getString(BookmarksLoader.COLUMN_INDEX_TITLE); 313 String url = cursor.getString(BookmarksLoader.COLUMN_INDEX_URL); 314 // If the site is bookmarked, the item becomes remove from 315 // bookmarks. 316 Bookmarks.removeFromBookmarks(activity, activity.getContentResolver(), url, name); 317 break; 318 } 319 default: 320 return super.onContextItemSelected(item); 321 } 322 return true; 323 } 324 325 static Bitmap getBitmap(Cursor cursor, int columnIndex) { 326 byte[] data = cursor.getBlob(columnIndex); 327 if (data == null) { 328 return null; 329 } 330 return BitmapFactory.decodeByteArray(data, 0, data.length); 331 } 332 333 private MenuItem.OnMenuItemClickListener mContextItemClickListener = 334 new MenuItem.OnMenuItemClickListener() { 335 @Override 336 public boolean onMenuItemClick(MenuItem item) { 337 return onContextItemSelected(item); 338 } 339 }; 340 341 @Override 342 public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { 343 inflater.inflate(R.menu.bookmark, menu); 344 } 345 346 @Override 347 public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) { 348 AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo; 349 Cursor cursor = mAdapter.getItem(info.position); 350 if (!canEdit(cursor)) { 351 return; 352 } 353 boolean isFolder 354 = cursor.getInt(BookmarksLoader.COLUMN_INDEX_IS_FOLDER) != 0; 355 356 final Activity activity = getActivity(); 357 MenuInflater inflater = activity.getMenuInflater(); 358 inflater.inflate(R.menu.bookmarkscontext, menu); 359 if (isFolder) { 360 menu.setGroupVisible(R.id.FOLDER_CONTEXT_MENU, true); 361 } else { 362 menu.setGroupVisible(R.id.BOOKMARK_CONTEXT_MENU, true); 363 if (mDisableNewWindow) { 364 menu.findItem(R.id.new_window_context_menu_id).setVisible(false); 365 } 366 } 367 BookmarkItem header = new BookmarkItem(activity); 368 populateBookmarkItem(cursor, header, isFolder); 369 menu.setHeaderView(header); 370 371 int count = menu.size(); 372 for (int i = 0; i < count; i++) { 373 menu.getItem(i).setOnMenuItemClickListener(mContextItemClickListener); 374 } 375 } 376 377 boolean canEdit(Cursor c) { 378 String unique = c.getString(BookmarksLoader.COLUMN_INDEX_SERVER_UNIQUE); 379 return !ChromeSyncColumns.FOLDER_NAME_OTHER_BOOKMARKS.equals(unique); 380 } 381 382 private void populateBookmarkItem(Cursor cursor, BookmarkItem item, boolean isFolder) { 383 item.setName(cursor.getString(BookmarksLoader.COLUMN_INDEX_TITLE)); 384 if (isFolder) { 385 item.setUrl(null); 386 Bitmap bitmap = 387 BitmapFactory.decodeResource(getResources(), R.drawable.ic_folder_bookmark_widget_holo_dark); 388 item.setFavicon(bitmap); 389 new LookupBookmarkCount(getActivity(), item) 390 .execute(cursor.getLong(BookmarksLoader.COLUMN_INDEX_ID)); 391 } else { 392 String url = cursor.getString(BookmarksLoader.COLUMN_INDEX_URL); 393 item.setUrl(url); 394 Bitmap bitmap = getBitmap(cursor, BookmarksLoader.COLUMN_INDEX_FAVICON); 395 if (bitmap == null) { 396 bitmap = CombinedBookmarkHistoryView.getIconListenerSet().getFavicon(url); 397 } 398 item.setFavicon(bitmap); 399 } 400 } 401 402 /** 403 * Create a new BrowserBookmarksPage. 404 */ 405 @Override 406 public void onCreate(Bundle icicle) { 407 super.onCreate(icicle); 408 409 setHasOptionsMenu(true); 410 411 Bundle args = getArguments(); 412 mDisableNewWindow = args == null ? false : args.getBoolean(EXTRA_DISABLE_WINDOW, false); 413 } 414 415 @Override 416 public View onCreateView(LayoutInflater inflater, ViewGroup container, 417 Bundle savedInstanceState) { 418 Context context = getActivity(); 419 420 View root = inflater.inflate(R.layout.bookmarks, container, false); 421 mEmptyView = root.findViewById(android.R.id.empty); 422 423 mGrid = (GridView) root.findViewById(R.id.grid); 424 mGrid.setOnItemClickListener(this); 425 mGrid.setColumnWidth(Controller.getDesiredThumbnailWidth(getActivity())); 426 mList = (ListView) root.findViewById(R.id.list); 427 mList.setOnItemClickListener(this); 428 setEnableContextMenu(mEnableContextMenu); 429 430 // Prep the header 431 ViewGroup hc = mHeaderContainer; 432 if (hc == null) { 433 hc = (ViewGroup) root.findViewById(R.id.header_container); 434 hc.setVisibility(View.VISIBLE); 435 } 436 mHeader = inflater.inflate(R.layout.bookmarks_header, hc, false); 437 hc.addView(mHeader); 438 mCrumbs = (BreadCrumbView) mHeader.findViewById(R.id.crumbs); 439 mCrumbs.setController(this); 440 mCrumbs.setUseBackButton(mCrumbBackButton); 441 mCrumbs.setMaxVisible(mCrumbMaxVisible); 442 mCrumbs.setVisibility(mCrumbVisibility); 443 String name = getString(R.string.bookmarks); 444 mCrumbs.pushView(name, false, BrowserContract.Bookmarks.CONTENT_URI_DEFAULT_FOLDER); 445 if (mCallbacks != null) { 446 mCallbacks.onFolderChanged(1, BrowserContract.Bookmarks.CONTENT_URI_DEFAULT_FOLDER); 447 } 448 449 // Start the loaders 450 LoaderManager lm = getLoaderManager(); 451 SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); 452 mCurrentView = 453 prefs.getInt(PREF_SELECTED_VIEW, BrowserBookmarksPage.VIEW_THUMBNAILS); 454 mAdapter = new BrowserBookmarksAdapter(getActivity(), mCurrentView); 455 String accountType = prefs.getString(PREF_ACCOUNT_TYPE, DEFAULT_ACCOUNT); 456 String accountName = prefs.getString(PREF_ACCOUNT_NAME, DEFAULT_ACCOUNT); 457 if (!TextUtils.isEmpty(accountType) && !TextUtils.isEmpty(accountName)) { 458 // There is an account set, load up that one 459 Bundle args = null; 460 if (!DEFAULT_ACCOUNT.equals(accountType) && !DEFAULT_ACCOUNT.equals(accountName)) { 461 args = new Bundle(); 462 args.putString(BookmarksLoader.ARG_ACCOUNT_TYPE, accountType); 463 args.putString(BookmarksLoader.ARG_ACCOUNT_NAME, accountName); 464 } 465 lm.restartLoader(LOADER_BOOKMARKS, args, this); 466 } else { 467 // No account set, load the account list first 468 lm.restartLoader(LOADER_ACCOUNTS_THEN_BOOKMARKS, null, this); 469 } 470 471 // Add our own listener in case there are favicons that have yet to be loaded. 472 CombinedBookmarkHistoryView.getIconListenerSet().addListener(this); 473 474 return root; 475 } 476 477 @Override 478 public void onDestroyView() { 479 super.onDestroyView(); 480 if (mHeaderContainer != null) { 481 mHeaderContainer.removeView(mHeader); 482 } 483 mCrumbs.setController(null); 484 mCrumbs = null; 485 getLoaderManager().destroyLoader(LOADER_BOOKMARKS); 486 } 487 488 @Override 489 public void onReceivedIcon(String url, Bitmap icon) { 490 // A new favicon has been loaded, so let anything attached to the adapter know about it 491 // so new icons will be loaded. 492 mAdapter.notifyDataSetChanged(); 493 } 494 495 @Override 496 public void onItemClick(AdapterView<?> parent, View v, int position, long id) { 497 // It is possible that the view has been canceled when we get to 498 // this point as back has a higher priority 499 if (mCanceled) { 500 android.util.Log.e(LOGTAG, "item clicked when dismissing"); 501 return; 502 } 503 504 Cursor cursor = mAdapter.getItem(position); 505 boolean isFolder = cursor.getInt(BookmarksLoader.COLUMN_INDEX_IS_FOLDER) != 0; 506 if (mCallbacks != null && 507 mCallbacks.onBookmarkSelected(cursor, isFolder)) { 508 return; 509 } 510 511 if (isFolder) { 512 String title = cursor.getString(BookmarksLoader.COLUMN_INDEX_TITLE); 513 Uri uri = ContentUris.withAppendedId( 514 BrowserContract.Bookmarks.CONTENT_URI_DEFAULT_FOLDER, id); 515 if (mCrumbs != null) { 516 // update crumbs 517 mCrumbs.pushView(title, uri); 518 } 519 loadFolder(uri); 520 } 521 } 522 523 @Override 524 public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { 525 Adapter adapter = parent.getAdapter(); 526 String accountType = "com.google"; 527 String accountName = adapter.getItem(position).toString(); 528 529 Bundle args = null; 530 if (ACCOUNT_NAME_UNSYNCED.equals(accountName)) { 531 accountType = DEFAULT_ACCOUNT; 532 accountName = DEFAULT_ACCOUNT; 533 } else { 534 args = new Bundle(); 535 args.putString(BookmarksLoader.ARG_ACCOUNT_TYPE, accountType); 536 args.putString(BookmarksLoader.ARG_ACCOUNT_NAME, accountName); 537 } 538 539 // Remember the selection for later 540 PreferenceManager.getDefaultSharedPreferences(getActivity()).edit() 541 .putString(PREF_ACCOUNT_TYPE, accountType) 542 .putString(PREF_ACCOUNT_NAME, accountName) 543 .apply(); 544 545 getLoaderManager().restartLoader(LOADER_BOOKMARKS, args, this); 546 } 547 548 @Override 549 public void onNothingSelected(AdapterView<?> parent) { 550 // Do nothing 551 } 552 553 /* package */ static Intent createShortcutIntent(Context context, Cursor cursor) { 554 String url = cursor.getString(BookmarksLoader.COLUMN_INDEX_URL); 555 String title = cursor.getString(BookmarksLoader.COLUMN_INDEX_TITLE); 556 Bitmap touchIcon = getBitmap(cursor, BookmarksLoader.COLUMN_INDEX_TOUCH_ICON); 557 Bitmap favicon = getBitmap(cursor, BookmarksLoader.COLUMN_INDEX_FAVICON); 558 return BookmarkUtils.createAddToHomeIntent(context, url, title, touchIcon, favicon); 559 } 560 561 private void loadUrl(int position) { 562 if (mCallbacks != null) { 563 mCallbacks.onBookmarkSelected(mAdapter.getItem(position), false); 564 } 565 } 566 567 private void openInNewWindow(int position) { 568 if (mCallbacks != null) { 569 Cursor c = mAdapter.getItem(position); 570 boolean isFolder = c.getInt(BookmarksLoader.COLUMN_INDEX_IS_FOLDER) == 1; 571 if (isFolder) { 572 long id = c.getLong(BookmarksLoader.COLUMN_INDEX_ID); 573 new OpenAllInTabsTask(id).execute(); 574 } else { 575 mCallbacks.onOpenInNewWindow(c); 576 } 577 } 578 } 579 580 class OpenAllInTabsTask extends AsyncTask<Void, Void, Cursor> { 581 long mFolderId; 582 public OpenAllInTabsTask(long id) { 583 mFolderId = id; 584 } 585 586 @Override 587 protected Cursor doInBackground(Void... params) { 588 Context c = getActivity(); 589 if (c == null) return null; 590 return c.getContentResolver().query(BookmarkUtils.getBookmarksUri(c), 591 BookmarksLoader.PROJECTION, BrowserContract.Bookmarks.PARENT + "=?", 592 new String[] { Long.toString(mFolderId) }, null); 593 } 594 595 @Override 596 protected void onPostExecute(Cursor result) { 597 if (mCallbacks != null) { 598 while (result.moveToNext()) { 599 mCallbacks.onOpenInNewWindow(result); 600 } 601 } 602 } 603 604 } 605 606 private void editBookmark(int position) { 607 Intent intent = new Intent(getActivity(), AddBookmarkPage.class); 608 Cursor cursor = mAdapter.getItem(position); 609 Bundle item = new Bundle(); 610 item.putString(BrowserContract.Bookmarks.TITLE, 611 cursor.getString(BookmarksLoader.COLUMN_INDEX_TITLE)); 612 item.putString(BrowserContract.Bookmarks.URL, 613 cursor.getString(BookmarksLoader.COLUMN_INDEX_URL)); 614 byte[] data = cursor.getBlob(BookmarksLoader.COLUMN_INDEX_FAVICON); 615 if (data != null) { 616 item.putParcelable(BrowserContract.Bookmarks.FAVICON, 617 BitmapFactory.decodeByteArray(data, 0, data.length)); 618 } 619 item.putLong(BrowserContract.Bookmarks._ID, 620 cursor.getLong(BookmarksLoader.COLUMN_INDEX_ID)); 621 item.putLong(BrowserContract.Bookmarks.PARENT, 622 cursor.getLong(BookmarksLoader.COLUMN_INDEX_PARENT)); 623 intent.putExtra(AddBookmarkPage.EXTRA_EDIT_BOOKMARK, item); 624 intent.putExtra(AddBookmarkPage.EXTRA_IS_FOLDER, 625 cursor.getInt(BookmarksLoader.COLUMN_INDEX_IS_FOLDER) == 1); 626 startActivity(intent); 627 } 628 629 private void displayRemoveBookmarkDialog(final int position) { 630 // Put up a dialog asking if the user really wants to 631 // delete the bookmark 632 Cursor cursor = mAdapter.getItem(position); 633 long id = cursor.getLong(BookmarksLoader.COLUMN_INDEX_ID); 634 String title = cursor.getString(BookmarksLoader.COLUMN_INDEX_TITLE); 635 Context context = getActivity(); 636 BookmarkUtils.displayRemoveBookmarkDialog(id, title, context, null); 637 } 638 639 private String getUrl(int position) { 640 return getUrl(mAdapter.getItem(position)); 641 } 642 643 /* package */ static String getUrl(Cursor c) { 644 return c.getString(BookmarksLoader.COLUMN_INDEX_URL); 645 } 646 647 private void copy(CharSequence text) { 648 ClipboardManager cm = (ClipboardManager) getActivity().getSystemService( 649 Context.CLIPBOARD_SERVICE); 650 cm.setPrimaryClip(ClipData.newRawUri(null, Uri.parse(text.toString()))); 651 } 652 653 @Override 654 public boolean onOptionsItemSelected(MenuItem item) { 655 switch (item.getItemId()) { 656 case R.id.thumbnail_view: 657 selectView(VIEW_THUMBNAILS); 658 return true; 659 case R.id.list_view: 660 selectView(VIEW_LIST); 661 return true; 662 } 663 return super.onOptionsItemSelected(item); 664 } 665 666 @Override 667 public void onConfigurationChanged(Configuration newConfig) { 668 super.onConfigurationChanged(newConfig); 669 Resources res = getActivity().getResources(); 670 int horizontalSpacing = (int) res.getDimension(R.dimen.combo_horizontalSpacing); 671 mGrid.setHorizontalSpacing(horizontalSpacing); 672 } 673 674 @Override 675 public void onPrepareOptionsMenu(Menu menu) { 676 super.onPrepareOptionsMenu(menu); 677 menu.findItem(R.id.list_view).setVisible(mCurrentView != VIEW_LIST); 678 menu.findItem(R.id.thumbnail_view).setVisible(mCurrentView != VIEW_THUMBNAILS); 679 } 680 681 void selectView(int view) { 682 if (view == mCurrentView) { 683 return; 684 } 685 mCurrentView = view; 686 SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getActivity()); 687 Editor edit = prefs.edit(); 688 edit.putInt(PREF_SELECTED_VIEW, mCurrentView); 689 edit.apply(); 690 if (mEmptyView.getVisibility() == View.VISIBLE) { 691 return; 692 } 693 setupBookmarkView(); 694 } 695 696 private void setupBookmarkView() { 697 mAdapter.selectView(mCurrentView); 698 switch (mCurrentView) { 699 case VIEW_THUMBNAILS: 700 mList.setAdapter(null); 701 mGrid.setAdapter(mAdapter); 702 mGrid.setVisibility(View.VISIBLE); 703 mList.setVisibility(View.GONE); 704 break; 705 case VIEW_LIST: 706 mGrid.setAdapter(null); 707 mList.setAdapter(mAdapter); 708 mGrid.setVisibility(View.GONE); 709 mList.setVisibility(View.VISIBLE); 710 break; 711 } 712 } 713 714 /** 715 * BreadCrumb controller callback 716 */ 717 @Override 718 public void onTop(int level, Object data) { 719 Uri uri = (Uri) data; 720 if (uri == null) { 721 // top level 722 uri = BrowserContract.Bookmarks.CONTENT_URI_DEFAULT_FOLDER; 723 } 724 loadFolder(uri); 725 } 726 727 /** 728 * @param uri 729 */ 730 private void loadFolder(Uri uri) { 731 LoaderManager manager = getLoaderManager(); 732 BookmarksLoader loader = 733 (BookmarksLoader) ((Loader<?>) manager.getLoader(LOADER_BOOKMARKS)); 734 loader.setUri(uri); 735 loader.forceLoad(); 736 if (mCallbacks != null) { 737 mCallbacks.onFolderChanged(mCrumbs.getTopLevel(), uri); 738 } 739 } 740 741 @Override 742 public boolean onMenuItemClick(MenuItem item) { 743 switch (item.getItemId()) { 744 case R.id.list_view: 745 selectView(BrowserBookmarksPage.VIEW_LIST); 746 return true; 747 case R.id.thumbnail_view: 748 selectView(BrowserBookmarksPage.VIEW_THUMBNAILS); 749 return true; 750 } 751 return false; 752 } 753 754 public boolean onBackPressed() { 755 if (canGoBack()) { 756 mCrumbs.popView(); 757 return true; 758 } 759 return false; 760 } 761 762 private boolean canGoBack() { 763 Crumb c = mCrumbs.getTopCrumb(); 764 return c != null && c.canGoBack; 765 } 766 767 public void setCallbackListener(BookmarksPageCallbacks callbackListener) { 768 mCallbacks = callbackListener; 769 } 770 771 public void setEnableContextMenu(boolean enable) { 772 mEnableContextMenu = enable; 773 if (mGrid != null) { 774 if (mEnableContextMenu) { 775 registerForContextMenu(mGrid); 776 } else { 777 unregisterForContextMenu(mGrid); 778 mGrid.setLongClickable(false); 779 } 780 } 781 if (mList != null) { 782 if (mEnableContextMenu) { 783 registerForContextMenu(mList); 784 } else { 785 unregisterForContextMenu(mList); 786 mList.setLongClickable(false); 787 } 788 } 789 } 790 791 private static class LookupBookmarkCount extends AsyncTask<Long, Void, Integer> { 792 Context mContext; 793 BookmarkItem mHeader; 794 795 public LookupBookmarkCount(Context context, BookmarkItem header) { 796 mContext = context; 797 mHeader = header; 798 } 799 800 @Override 801 protected Integer doInBackground(Long... params) { 802 if (params.length != 1) { 803 throw new IllegalArgumentException("Missing folder id!"); 804 } 805 Uri uri = BookmarkUtils.getBookmarksUri(mContext); 806 Cursor c = mContext.getContentResolver().query(uri, 807 null, BrowserContract.Bookmarks.PARENT + "=?", 808 new String[] {params[0].toString()}, null); 809 return c.getCount(); 810 } 811 812 @Override 813 protected void onPostExecute(Integer result) { 814 if (result > 0) { 815 mHeader.setUrl(mContext.getString(R.string.contextheader_folder_bookmarkcount, 816 result)); 817 } else if (result == 0) { 818 mHeader.setUrl(mContext.getString(R.string.contextheader_folder_empty)); 819 } 820 } 821 } 822 823 public void setBreadCrumbVisibility(int visibility) { 824 mCrumbVisibility = visibility; 825 if (mCrumbs != null) { 826 mCrumbs.setVisibility(mCrumbVisibility); 827 } 828 } 829 830 public void setBreadCrumbUseBackButton(boolean use) { 831 mCrumbBackButton = use; 832 if (mCrumbs != null) { 833 mCrumbs.setUseBackButton(mCrumbBackButton); 834 } 835 } 836 837 public void setBreadCrumbMaxVisible(int max) { 838 mCrumbMaxVisible = max; 839 if (mCrumbs != null) { 840 mCrumbs.setMaxVisible(mCrumbMaxVisible); 841 } 842 } 843} 844