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