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