• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
onBookmarkSelected(Cursor c, boolean isFolder)68     boolean onBookmarkSelected(Cursor c, boolean isFolder);
69     // Return true if handled
onOpenInNewWindow(String... urls)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
onCreateLoader(int id, Bundle args)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
onLoadFinished(Loader<Cursor> loader, Cursor cursor)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
onLoaderReset(Loader<Cursor> loader)163     public void onLoaderReset(Loader<Cursor> loader) {
164     }
165 
166     @Override
onContextItemSelected(MenuItem item)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 
handleContextItem(int itemId, int groupPosition, int childPosition)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 
getBitmap(Cursor cursor, int columnIndex)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
onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo)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 
canEdit(Cursor c)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 
populateBookmarkItem(Cursor cursor, BookmarkItem item, boolean isFolder)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
onCreate(Bundle icicle)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
onPause()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 
CombinedBookmarksCallbackWrapper(CombinedBookmarksCallbacks cb)352         private CombinedBookmarksCallbackWrapper(CombinedBookmarksCallbacks cb) {
353             mCombinedCallback = cb;
354         }
355 
356         @Override
onOpenInNewWindow(String... urls)357         public boolean onOpenInNewWindow(String... urls) {
358             mCombinedCallback.openInNewTab(urls);
359             return true;
360         }
361 
362         @Override
onBookmarkSelected(Cursor c, boolean isFolder)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
onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)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
onDestroyView()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 
getChildAdapter(int groupPosition)406     private BrowserBookmarksAdapter getChildAdapter(int groupPosition) {
407         return mGrid.getChildAdapter(groupPosition);
408     }
409 
getBreadCrumbs(int groupPosition)410     private BreadCrumbView getBreadCrumbs(int groupPosition) {
411         return mGrid.getBreadCrumbs(groupPosition);
412     }
413 
414     @Override
onChildClick(ExpandableListView parent, View v, int groupPosition, int childPosition, long id)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 
createShortcutIntent(Context context, Cursor cursor)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 
loadUrl(BrowserBookmarksAdapter adapter, int position)448     private void loadUrl(BrowserBookmarksAdapter adapter, int position) {
449         if (mCallbacks != null && adapter != null) {
450             mCallbacks.onBookmarkSelected(adapter.getItem(position), false);
451         }
452     }
453 
openInNewWindow(BrowserBookmarksAdapter adapter, int position)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;
OpenAllInTabsTask(long id)469         public OpenAllInTabsTask(long id) {
470             mFolderId = id;
471         }
472 
473         @Override
doInBackground(Void... params)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
onPostExecute(Cursor result)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 
editBookmark(BrowserBookmarksAdapter adapter, int position)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 
displayRemoveBookmarkDialog(BrowserBookmarksAdapter adapter, int position)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 
getUrl(BrowserBookmarksAdapter adapter, int position)530     private String getUrl(BrowserBookmarksAdapter adapter, int position) {
531         return getUrl(adapter.getItem(position));
532     }
533 
getUrl(Cursor c)534     /* package */ static String getUrl(Cursor c) {
535         return c.getString(BookmarksLoader.COLUMN_INDEX_URL);
536     }
537 
copy(CharSequence text)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
onConfigurationChanged(Configuration newConfig)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
onTop(BreadCrumbView view, int level, Object data)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      */
loadFolder(int groupPosition, Uri uri)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 
setCallbackListener(BookmarksPageCallbacks callbackListener)585     public void setCallbackListener(BookmarksPageCallbacks callbackListener) {
586         mCallbacks = callbackListener;
587     }
588 
setEnableContextMenu(boolean enable)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 
LookupBookmarkCount(Context context, BookmarkItem header)644         public LookupBookmarkCount(Context context, BookmarkItem header) {
645             mContext = context.getApplicationContext();
646             mHeader = header;
647         }
648 
649         @Override
doInBackground(Long... params)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
onPostExecute(Integer result)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 
AccountsLoader(Context context)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