Home | History | Annotate | Download | only in browser
      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
     63     boolean onBookmarkSelected(Cursor c, boolean isFolder);
     64     // Return true if handled
     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
    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
    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
    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
    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 
    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 
    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     };
    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
    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 
    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 
    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
    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
    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 
    367         private CombinedBookmarksCallbackWrapper(CombinedBookmarksCallbacks cb) {
    368             mCombinedCallback = cb;
    369         }
    370 
    371         @Override
    372         public boolean onOpenInNewWindow(String... urls) {
    373             mCombinedCallback.openInNewTab(urls);
    374             return true;
    375         }
    376 
    377         @Override
    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
    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
    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 
    419     private BrowserBookmarksAdapter getChildAdapter(int groupPosition) {
    420         return mGrid.getChildAdapter(groupPosition);
    421     }
    422 
    423     private BreadCrumbView getBreadCrumbs(int groupPosition) {
    424         return mGrid.getBreadCrumbs(groupPosition);
    425     }
    426 
    427     @Override
    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 
    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 
    461     private void loadUrl(BrowserBookmarksAdapter adapter, int position) {
    462         if (mCallbacks != null && adapter != null) {
    463             mCallbacks.onBookmarkSelected(adapter.getItem(position), false);
    464         }
    465     }
    466 
    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;
    482         public OpenAllInTabsTask(long id) {
    483             mFolderId = id;
    484         }
    485 
    486         @Override
    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
    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 
    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 
    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 
    543     private String getUrl(BrowserBookmarksAdapter adapter, int position) {
    544         return getUrl(adapter.getItem(position));
    545     }
    546 
    547     /* package */ static String getUrl(Cursor c) {
    548         return c.getString(BookmarksLoader.COLUMN_INDEX_URL);
    549     }
    550 
    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
    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
    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      */
    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 
    598     public void setCallbackListener(BookmarksPageCallbacks callbackListener) {
    599         mCallbacks = callbackListener;
    600     }
    601 
    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 
    618         public LookupBookmarkCount(Context context, BookmarkItem header) {
    619             mContext = context.getApplicationContext();
    620             mHeader = header;
    621         }
    622 
    623         @Override
    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
    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 
    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