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 17 package com.android.browser; 18 19 import android.app.Activity; 20 import android.app.Fragment; 21 import android.app.LoaderManager; 22 import android.content.ClipData; 23 import android.content.ClipboardManager; 24 import android.content.ContentUris; 25 import android.content.Context; 26 import android.content.CursorLoader; 27 import android.content.Intent; 28 import android.content.Loader; 29 import android.content.SharedPreferences; 30 import android.content.res.Configuration; 31 import android.content.res.Resources; 32 import android.database.Cursor; 33 import android.graphics.Bitmap; 34 import android.graphics.BitmapFactory; 35 import android.graphics.BitmapFactory.Options; 36 import android.net.Uri; 37 import android.os.AsyncTask; 38 import android.os.Bundle; 39 import android.provider.BrowserContract; 40 import android.provider.BrowserContract.Accounts; 41 import android.view.ContextMenu; 42 import android.view.ContextMenu.ContextMenuInfo; 43 import android.view.LayoutInflater; 44 import android.view.MenuInflater; 45 import android.view.MenuItem; 46 import android.view.View; 47 import android.view.ViewGroup; 48 import android.widget.ExpandableListView; 49 import android.widget.ExpandableListView.OnChildClickListener; 50 import android.widget.Toast; 51 52 import com.android.browser.provider.BrowserProvider2; 53 import com.android.browser.view.BookmarkExpandableView; 54 import com.android.browser.view.BookmarkExpandableView.BookmarkContextMenuInfo; 55 56 import org.json.JSONException; 57 import org.json.JSONObject; 58 59 import java.util.HashMap; 60 61 interface BookmarksPageCallbacks { 62 // Return true if handled onBookmarkSelected(Cursor c, boolean isFolder)63 boolean onBookmarkSelected(Cursor c, boolean isFolder); 64 // Return true if handled onOpenInNewWindow(String... urls)65 boolean onOpenInNewWindow(String... urls); 66 } 67 68 /** 69 * View showing the user's bookmarks in the browser. 70 */ 71 public class BrowserBookmarksPage extends Fragment implements View.OnCreateContextMenuListener, 72 LoaderManager.LoaderCallbacks<Cursor>, BreadCrumbView.Controller, 73 OnChildClickListener { 74 75 public static class ExtraDragState { 76 public int childPosition; 77 public int groupPosition; 78 } 79 80 static final String LOGTAG = "browser"; 81 82 static final int LOADER_ACCOUNTS = 1; 83 static final int LOADER_BOOKMARKS = 100; 84 85 static final String EXTRA_DISABLE_WINDOW = "disable_new_window"; 86 static final String PREF_GROUP_STATE = "bbp_group_state"; 87 88 static final String ACCOUNT_TYPE = "account_type"; 89 static final String ACCOUNT_NAME = "account_name"; 90 91 BookmarksPageCallbacks mCallbacks; 92 View mRoot; 93 BookmarkExpandableView mGrid; 94 boolean mDisableNewWindow; 95 boolean mEnableContextMenu = true; 96 View mEmptyView; 97 View mHeader; 98 HashMap<Integer, BrowserBookmarksAdapter> mBookmarkAdapters = new HashMap<Integer, BrowserBookmarksAdapter>(); 99 JSONObject mState; 100 101 @Override onCreateLoader(int id, Bundle args)102 public Loader<Cursor> onCreateLoader(int id, Bundle args) { 103 if (id == LOADER_ACCOUNTS) { 104 return new AccountsLoader(getActivity()); 105 } else if (id >= LOADER_BOOKMARKS) { 106 String accountType = args.getString(ACCOUNT_TYPE); 107 String accountName = args.getString(ACCOUNT_NAME); 108 BookmarksLoader bl = new BookmarksLoader(getActivity(), 109 accountType, accountName); 110 return bl; 111 } else { 112 throw new UnsupportedOperationException("Unknown loader id " + id); 113 } 114 } 115 116 @Override onLoadFinished(Loader<Cursor> loader, Cursor cursor)117 public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) { 118 if (loader.getId() == LOADER_ACCOUNTS) { 119 LoaderManager lm = getLoaderManager(); 120 int id = LOADER_BOOKMARKS; 121 while (cursor.moveToNext()) { 122 String accountName = cursor.getString(0); 123 String accountType = cursor.getString(1); 124 Bundle args = new Bundle(); 125 args.putString(ACCOUNT_NAME, accountName); 126 args.putString(ACCOUNT_TYPE, accountType); 127 BrowserBookmarksAdapter adapter = new BrowserBookmarksAdapter( 128 getActivity()); 129 mBookmarkAdapters.put(id, adapter); 130 boolean expand = true; 131 try { 132 expand = mState.getBoolean(accountName != null ? accountName 133 : BookmarkExpandableView.LOCAL_ACCOUNT_NAME); 134 } catch (JSONException e) {} // no state for accountName 135 mGrid.addAccount(accountName, adapter, expand); 136 lm.restartLoader(id, args, this); 137 id++; 138 } 139 // TODO: Figure out what a reload of these means 140 // Currently, a reload is triggered whenever bookmarks change 141 // This is less than ideal 142 // It also causes UI flickering as a new adapter is created 143 // instead of re-using an existing one when the account_name is the 144 // same. 145 // For now, this is a one-shot load 146 getLoaderManager().destroyLoader(LOADER_ACCOUNTS); 147 } else if (loader.getId() >= LOADER_BOOKMARKS) { 148 BrowserBookmarksAdapter adapter = mBookmarkAdapters.get(loader.getId()); 149 adapter.changeCursor(cursor); 150 } 151 } 152 153 @Override onLoaderReset(Loader<Cursor> loader)154 public void onLoaderReset(Loader<Cursor> loader) { 155 if (loader.getId() >= LOADER_BOOKMARKS) { 156 BrowserBookmarksAdapter adapter = mBookmarkAdapters.get(loader.getId()); 157 adapter.changeCursor(null); 158 } 159 } 160 161 @Override onContextItemSelected(MenuItem item)162 public boolean onContextItemSelected(MenuItem item) { 163 if (!(item.getMenuInfo() instanceof BookmarkContextMenuInfo)) { 164 return false; 165 } 166 BookmarkContextMenuInfo i = (BookmarkContextMenuInfo) item.getMenuInfo(); 167 // If we have no menu info, we can't tell which item was selected. 168 if (i == null) { 169 return false; 170 } 171 172 if (handleContextItem(item.getItemId(), i.groupPosition, i.childPosition)) { 173 return true; 174 } 175 return super.onContextItemSelected(item); 176 } 177 handleContextItem(int itemId, int groupPosition, int childPosition)178 public boolean handleContextItem(int itemId, int groupPosition, 179 int childPosition) { 180 final Activity activity = getActivity(); 181 BrowserBookmarksAdapter adapter = getChildAdapter(groupPosition); 182 183 switch (itemId) { 184 case R.id.open_context_menu_id: 185 loadUrl(adapter, childPosition); 186 break; 187 case R.id.edit_context_menu_id: 188 editBookmark(adapter, childPosition); 189 break; 190 case R.id.shortcut_context_menu_id: 191 Cursor c = adapter.getItem(childPosition); 192 activity.sendBroadcast(createShortcutIntent(getActivity(), c)); 193 break; 194 case R.id.delete_context_menu_id: 195 displayRemoveBookmarkDialog(adapter, childPosition); 196 break; 197 case R.id.new_window_context_menu_id: 198 openInNewWindow(adapter, childPosition); 199 break; 200 case R.id.share_link_context_menu_id: { 201 Cursor cursor = adapter.getItem(childPosition); 202 Controller.sharePage(activity, 203 cursor.getString(BookmarksLoader.COLUMN_INDEX_TITLE), 204 cursor.getString(BookmarksLoader.COLUMN_INDEX_URL), 205 getBitmap(cursor, BookmarksLoader.COLUMN_INDEX_FAVICON), 206 getBitmap(cursor, BookmarksLoader.COLUMN_INDEX_THUMBNAIL)); 207 break; 208 } 209 case R.id.copy_url_context_menu_id: 210 copy(getUrl(adapter, childPosition)); 211 break; 212 case R.id.homepage_context_menu_id: { 213 BrowserSettings.getInstance().setHomePage(getUrl(adapter, childPosition)); 214 Toast.makeText(activity, R.string.homepage_set, Toast.LENGTH_LONG).show(); 215 break; 216 } 217 // Only for the Most visited page 218 case R.id.save_to_bookmarks_menu_id: { 219 Cursor cursor = adapter.getItem(childPosition); 220 String name = cursor.getString(BookmarksLoader.COLUMN_INDEX_TITLE); 221 String url = cursor.getString(BookmarksLoader.COLUMN_INDEX_URL); 222 // If the site is bookmarked, the item becomes remove from 223 // bookmarks. 224 Bookmarks.removeFromBookmarks(activity, activity.getContentResolver(), url, name); 225 break; 226 } 227 default: 228 return false; 229 } 230 return true; 231 } 232 getBitmap(Cursor cursor, int columnIndex)233 static Bitmap getBitmap(Cursor cursor, int columnIndex) { 234 return getBitmap(cursor, columnIndex, null); 235 } 236 237 static ThreadLocal<Options> sOptions = new ThreadLocal<Options>() { 238 @Override 239 protected Options initialValue() { 240 return new Options(); 241 }; 242 }; getBitmap(Cursor cursor, int columnIndex, Bitmap inBitmap)243 static Bitmap getBitmap(Cursor cursor, int columnIndex, Bitmap inBitmap) { 244 byte[] data = cursor.getBlob(columnIndex); 245 if (data == null) { 246 return null; 247 } 248 Options opts = sOptions.get(); 249 opts.inBitmap = inBitmap; 250 opts.inSampleSize = 1; 251 opts.inScaled = false; 252 try { 253 return BitmapFactory.decodeByteArray(data, 0, data.length, opts); 254 } catch (IllegalArgumentException ex) { 255 // Failed to re-use bitmap, create a new one 256 return BitmapFactory.decodeByteArray(data, 0, data.length); 257 } 258 } 259 260 private MenuItem.OnMenuItemClickListener mContextItemClickListener = 261 new MenuItem.OnMenuItemClickListener() { 262 @Override 263 public boolean onMenuItemClick(MenuItem item) { 264 return onContextItemSelected(item); 265 } 266 }; 267 268 @Override onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo)269 public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) { 270 BookmarkContextMenuInfo info = (BookmarkContextMenuInfo) menuInfo; 271 BrowserBookmarksAdapter adapter = getChildAdapter(info.groupPosition); 272 Cursor cursor = adapter.getItem(info.childPosition); 273 if (!canEdit(cursor)) { 274 return; 275 } 276 boolean isFolder 277 = cursor.getInt(BookmarksLoader.COLUMN_INDEX_IS_FOLDER) != 0; 278 279 final Activity activity = getActivity(); 280 MenuInflater inflater = activity.getMenuInflater(); 281 inflater.inflate(R.menu.bookmarkscontext, menu); 282 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 } 289 } 290 BookmarkItem header = new BookmarkItem(activity); 291 header.setEnableScrolling(true); 292 populateBookmarkItem(cursor, header, isFolder); 293 menu.setHeaderView(header); 294 295 int count = menu.size(); 296 for (int i = 0; i < count; i++) { 297 menu.getItem(i).setOnMenuItemClickListener(mContextItemClickListener); 298 } 299 } 300 canEdit(Cursor c)301 boolean canEdit(Cursor c) { 302 int type = c.getInt(BookmarksLoader.COLUMN_INDEX_TYPE); 303 return type == BrowserContract.Bookmarks.BOOKMARK_TYPE_BOOKMARK 304 || type == BrowserContract.Bookmarks.BOOKMARK_TYPE_FOLDER; 305 } 306 populateBookmarkItem(Cursor cursor, BookmarkItem item, boolean isFolder)307 private void populateBookmarkItem(Cursor cursor, BookmarkItem item, boolean isFolder) { 308 item.setName(cursor.getString(BookmarksLoader.COLUMN_INDEX_TITLE)); 309 if (isFolder) { 310 item.setUrl(null); 311 Bitmap bitmap = 312 BitmapFactory.decodeResource(getResources(), R.drawable.ic_folder_holo_dark); 313 item.setFavicon(bitmap); 314 new LookupBookmarkCount(getActivity(), item) 315 .execute(cursor.getLong(BookmarksLoader.COLUMN_INDEX_ID)); 316 } else { 317 String url = cursor.getString(BookmarksLoader.COLUMN_INDEX_URL); 318 item.setUrl(url); 319 Bitmap bitmap = getBitmap(cursor, BookmarksLoader.COLUMN_INDEX_FAVICON); 320 item.setFavicon(bitmap); 321 } 322 } 323 324 /** 325 * Create a new BrowserBookmarksPage. 326 */ 327 @Override onCreate(Bundle icicle)328 public void onCreate(Bundle icicle) { 329 super.onCreate(icicle); 330 SharedPreferences prefs = BrowserSettings.getInstance().getPreferences(); 331 try { 332 mState = new JSONObject(prefs.getString(PREF_GROUP_STATE, "{}")); 333 } catch (JSONException e) { 334 // Parse failed, clear preference and start with empty state 335 prefs.edit().remove(PREF_GROUP_STATE).apply(); 336 mState = new JSONObject(); 337 } 338 Bundle args = getArguments(); 339 mDisableNewWindow = args == null ? false : args.getBoolean(EXTRA_DISABLE_WINDOW, false); 340 setHasOptionsMenu(true); 341 if (mCallbacks == null && getActivity() instanceof CombinedBookmarksCallbacks) { 342 mCallbacks = new CombinedBookmarksCallbackWrapper( 343 (CombinedBookmarksCallbacks) getActivity()); 344 } 345 } 346 347 @Override onPause()348 public void onPause() { 349 super.onPause(); 350 try { 351 mState = mGrid.saveGroupState(); 352 // Save state 353 SharedPreferences prefs = BrowserSettings.getInstance().getPreferences(); 354 prefs.edit() 355 .putString(PREF_GROUP_STATE, mState.toString()) 356 .apply(); 357 } catch (JSONException e) { 358 // Not critical, ignore 359 } 360 } 361 362 private static class CombinedBookmarksCallbackWrapper 363 implements BookmarksPageCallbacks { 364 365 private CombinedBookmarksCallbacks mCombinedCallback; 366 CombinedBookmarksCallbackWrapper(CombinedBookmarksCallbacks cb)367 private CombinedBookmarksCallbackWrapper(CombinedBookmarksCallbacks cb) { 368 mCombinedCallback = cb; 369 } 370 371 @Override onOpenInNewWindow(String... urls)372 public boolean onOpenInNewWindow(String... urls) { 373 mCombinedCallback.openInNewTab(urls); 374 return true; 375 } 376 377 @Override onBookmarkSelected(Cursor c, boolean isFolder)378 public boolean onBookmarkSelected(Cursor c, boolean isFolder) { 379 if (isFolder) { 380 return false; 381 } 382 mCombinedCallback.openUrl(BrowserBookmarksPage.getUrl(c)); 383 return true; 384 } 385 }; 386 387 @Override onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)388 public View onCreateView(LayoutInflater inflater, ViewGroup container, 389 Bundle savedInstanceState) { 390 mRoot = inflater.inflate(R.layout.bookmarks, container, false); 391 mEmptyView = mRoot.findViewById(android.R.id.empty); 392 393 mGrid = (BookmarkExpandableView) mRoot.findViewById(R.id.grid); 394 mGrid.setOnChildClickListener(this); 395 mGrid.setColumnWidthFromLayout(R.layout.bookmark_thumbnail); 396 mGrid.setBreadcrumbController(this); 397 setEnableContextMenu(mEnableContextMenu); 398 399 // Start the loaders 400 LoaderManager lm = getLoaderManager(); 401 lm.restartLoader(LOADER_ACCOUNTS, null, this); 402 403 return mRoot; 404 } 405 406 @Override onDestroyView()407 public void onDestroyView() { 408 super.onDestroyView(); 409 mGrid.setBreadcrumbController(null); 410 mGrid.clearAccounts(); 411 LoaderManager lm = getLoaderManager(); 412 lm.destroyLoader(LOADER_ACCOUNTS); 413 for (int id : mBookmarkAdapters.keySet()) { 414 lm.destroyLoader(id); 415 } 416 mBookmarkAdapters.clear(); 417 } 418 getChildAdapter(int groupPosition)419 private BrowserBookmarksAdapter getChildAdapter(int groupPosition) { 420 return mGrid.getChildAdapter(groupPosition); 421 } 422 getBreadCrumbs(int groupPosition)423 private BreadCrumbView getBreadCrumbs(int groupPosition) { 424 return mGrid.getBreadCrumbs(groupPosition); 425 } 426 427 @Override onChildClick(ExpandableListView parent, View v, int groupPosition, int childPosition, long id)428 public boolean onChildClick(ExpandableListView parent, View v, 429 int groupPosition, int childPosition, long id) { 430 BrowserBookmarksAdapter adapter = getChildAdapter(groupPosition); 431 Cursor cursor = adapter.getItem(childPosition); 432 boolean isFolder = cursor.getInt(BookmarksLoader.COLUMN_INDEX_IS_FOLDER) != 0; 433 if (mCallbacks != null && 434 mCallbacks.onBookmarkSelected(cursor, isFolder)) { 435 return true; 436 } 437 438 if (isFolder) { 439 String title = cursor.getString(BookmarksLoader.COLUMN_INDEX_TITLE); 440 Uri uri = ContentUris.withAppendedId( 441 BrowserContract.Bookmarks.CONTENT_URI_DEFAULT_FOLDER, id); 442 BreadCrumbView crumbs = getBreadCrumbs(groupPosition); 443 if (crumbs != null) { 444 // update crumbs 445 crumbs.pushView(title, uri); 446 crumbs.setVisibility(View.VISIBLE); 447 } 448 loadFolder(groupPosition, uri); 449 } 450 return true; 451 } 452 createShortcutIntent(Context context, Cursor cursor)453 /* package */ static Intent createShortcutIntent(Context context, Cursor cursor) { 454 String url = cursor.getString(BookmarksLoader.COLUMN_INDEX_URL); 455 String title = cursor.getString(BookmarksLoader.COLUMN_INDEX_TITLE); 456 Bitmap touchIcon = getBitmap(cursor, BookmarksLoader.COLUMN_INDEX_TOUCH_ICON); 457 Bitmap favicon = getBitmap(cursor, BookmarksLoader.COLUMN_INDEX_FAVICON); 458 return BookmarkUtils.createAddToHomeIntent(context, url, title, touchIcon, favicon); 459 } 460 loadUrl(BrowserBookmarksAdapter adapter, int position)461 private void loadUrl(BrowserBookmarksAdapter adapter, int position) { 462 if (mCallbacks != null && adapter != null) { 463 mCallbacks.onBookmarkSelected(adapter.getItem(position), false); 464 } 465 } 466 openInNewWindow(BrowserBookmarksAdapter adapter, int position)467 private void openInNewWindow(BrowserBookmarksAdapter adapter, int position) { 468 if (mCallbacks != null) { 469 Cursor c = adapter.getItem(position); 470 boolean isFolder = c.getInt(BookmarksLoader.COLUMN_INDEX_IS_FOLDER) == 1; 471 if (isFolder) { 472 long id = c.getLong(BookmarksLoader.COLUMN_INDEX_ID); 473 new OpenAllInTabsTask(id).execute(); 474 } else { 475 mCallbacks.onOpenInNewWindow(BrowserBookmarksPage.getUrl(c)); 476 } 477 } 478 } 479 480 class OpenAllInTabsTask extends AsyncTask<Void, Void, Cursor> { 481 long mFolderId; OpenAllInTabsTask(long id)482 public OpenAllInTabsTask(long id) { 483 mFolderId = id; 484 } 485 486 @Override doInBackground(Void... params)487 protected Cursor doInBackground(Void... params) { 488 Context c = getActivity(); 489 if (c == null) return null; 490 return c.getContentResolver().query(BookmarkUtils.getBookmarksUri(c), 491 BookmarksLoader.PROJECTION, BrowserContract.Bookmarks.PARENT + "=?", 492 new String[] { Long.toString(mFolderId) }, null); 493 } 494 495 @Override onPostExecute(Cursor result)496 protected void onPostExecute(Cursor result) { 497 if (mCallbacks != null && result.getCount() > 0) { 498 String[] urls = new String[result.getCount()]; 499 int i = 0; 500 while (result.moveToNext()) { 501 urls[i++] = BrowserBookmarksPage.getUrl(result); 502 } 503 mCallbacks.onOpenInNewWindow(urls); 504 } 505 } 506 507 } 508 editBookmark(BrowserBookmarksAdapter adapter, int position)509 private void editBookmark(BrowserBookmarksAdapter adapter, int position) { 510 Intent intent = new Intent(getActivity(), AddBookmarkPage.class); 511 Cursor cursor = adapter.getItem(position); 512 Bundle item = new Bundle(); 513 item.putString(BrowserContract.Bookmarks.TITLE, 514 cursor.getString(BookmarksLoader.COLUMN_INDEX_TITLE)); 515 item.putString(BrowserContract.Bookmarks.URL, 516 cursor.getString(BookmarksLoader.COLUMN_INDEX_URL)); 517 byte[] data = cursor.getBlob(BookmarksLoader.COLUMN_INDEX_FAVICON); 518 if (data != null) { 519 item.putParcelable(BrowserContract.Bookmarks.FAVICON, 520 BitmapFactory.decodeByteArray(data, 0, data.length)); 521 } 522 item.putLong(BrowserContract.Bookmarks._ID, 523 cursor.getLong(BookmarksLoader.COLUMN_INDEX_ID)); 524 item.putLong(BrowserContract.Bookmarks.PARENT, 525 cursor.getLong(BookmarksLoader.COLUMN_INDEX_PARENT)); 526 intent.putExtra(AddBookmarkPage.EXTRA_EDIT_BOOKMARK, item); 527 intent.putExtra(AddBookmarkPage.EXTRA_IS_FOLDER, 528 cursor.getInt(BookmarksLoader.COLUMN_INDEX_IS_FOLDER) == 1); 529 startActivity(intent); 530 } 531 displayRemoveBookmarkDialog(BrowserBookmarksAdapter adapter, int position)532 private void displayRemoveBookmarkDialog(BrowserBookmarksAdapter adapter, 533 int position) { 534 // Put up a dialog asking if the user really wants to 535 // delete the bookmark 536 Cursor cursor = adapter.getItem(position); 537 long id = cursor.getLong(BookmarksLoader.COLUMN_INDEX_ID); 538 String title = cursor.getString(BookmarksLoader.COLUMN_INDEX_TITLE); 539 Context context = getActivity(); 540 BookmarkUtils.displayRemoveBookmarkDialog(id, title, context, null); 541 } 542 getUrl(BrowserBookmarksAdapter adapter, int position)543 private String getUrl(BrowserBookmarksAdapter adapter, int position) { 544 return getUrl(adapter.getItem(position)); 545 } 546 getUrl(Cursor c)547 /* package */ static String getUrl(Cursor c) { 548 return c.getString(BookmarksLoader.COLUMN_INDEX_URL); 549 } 550 copy(CharSequence text)551 private void copy(CharSequence text) { 552 ClipboardManager cm = (ClipboardManager) getActivity().getSystemService( 553 Context.CLIPBOARD_SERVICE); 554 cm.setPrimaryClip(ClipData.newRawUri(null, Uri.parse(text.toString()))); 555 } 556 557 @Override onConfigurationChanged(Configuration newConfig)558 public void onConfigurationChanged(Configuration newConfig) { 559 super.onConfigurationChanged(newConfig); 560 Resources res = getActivity().getResources(); 561 mGrid.setColumnWidthFromLayout(R.layout.bookmark_thumbnail); 562 int paddingTop = (int) res.getDimension(R.dimen.combo_paddingTop); 563 mRoot.setPadding(0, paddingTop, 0, 0); 564 getActivity().invalidateOptionsMenu(); 565 } 566 567 /** 568 * BreadCrumb controller callback 569 */ 570 @Override onTop(BreadCrumbView view, int level, Object data)571 public void onTop(BreadCrumbView view, int level, Object data) { 572 int groupPosition = (Integer) view.getTag(R.id.group_position); 573 Uri uri = (Uri) data; 574 if (uri == null) { 575 // top level 576 uri = BrowserContract.Bookmarks.CONTENT_URI_DEFAULT_FOLDER; 577 } 578 loadFolder(groupPosition, uri); 579 if (level <= 1) { 580 view.setVisibility(View.GONE); 581 } else { 582 view.setVisibility(View.VISIBLE); 583 } 584 } 585 586 /** 587 * @param uri 588 */ loadFolder(int groupPosition, Uri uri)589 private void loadFolder(int groupPosition, Uri uri) { 590 LoaderManager manager = getLoaderManager(); 591 // This assumes groups are ordered the same as loaders 592 BookmarksLoader loader = (BookmarksLoader) ((Loader<?>) 593 manager.getLoader(LOADER_BOOKMARKS + groupPosition)); 594 loader.setUri(uri); 595 loader.forceLoad(); 596 } 597 setCallbackListener(BookmarksPageCallbacks callbackListener)598 public void setCallbackListener(BookmarksPageCallbacks callbackListener) { 599 mCallbacks = callbackListener; 600 } 601 setEnableContextMenu(boolean enable)602 public void setEnableContextMenu(boolean enable) { 603 mEnableContextMenu = enable; 604 if (mGrid != null) { 605 if (mEnableContextMenu) { 606 registerForContextMenu(mGrid); 607 } else { 608 unregisterForContextMenu(mGrid); 609 mGrid.setLongClickable(false); 610 } 611 } 612 } 613 614 private static class LookupBookmarkCount extends AsyncTask<Long, Void, Integer> { 615 Context mContext; 616 BookmarkItem mHeader; 617 LookupBookmarkCount(Context context, BookmarkItem header)618 public LookupBookmarkCount(Context context, BookmarkItem header) { 619 mContext = context.getApplicationContext(); 620 mHeader = header; 621 } 622 623 @Override doInBackground(Long... params)624 protected Integer doInBackground(Long... params) { 625 if (params.length != 1) { 626 throw new IllegalArgumentException("Missing folder id!"); 627 } 628 Uri uri = BookmarkUtils.getBookmarksUri(mContext); 629 Cursor c = null; 630 try { 631 c = mContext.getContentResolver().query(uri, 632 null, BrowserContract.Bookmarks.PARENT + "=?", 633 new String[] {params[0].toString()}, null); 634 635 return c.getCount(); 636 } finally { 637 if ( c != null) { 638 c.close(); 639 } 640 } 641 } 642 643 @Override onPostExecute(Integer result)644 protected void onPostExecute(Integer result) { 645 if (result > 0) { 646 mHeader.setUrl(mContext.getString(R.string.contextheader_folder_bookmarkcount, 647 result)); 648 } else if (result == 0) { 649 mHeader.setUrl(mContext.getString(R.string.contextheader_folder_empty)); 650 } 651 } 652 } 653 654 static class AccountsLoader extends CursorLoader { 655 656 static String[] ACCOUNTS_PROJECTION = new String[] { 657 Accounts.ACCOUNT_NAME, 658 Accounts.ACCOUNT_TYPE 659 }; 660 AccountsLoader(Context context)661 public AccountsLoader(Context context) { 662 super(context, Accounts.CONTENT_URI 663 .buildUpon() 664 .appendQueryParameter(BrowserProvider2.PARAM_ALLOW_EMPTY_ACCOUNTS, "false") 665 .build(), 666 ACCOUNTS_PROJECTION, null, null, null); 667 } 668 669 } 670 } 671