BrowserBookmarksPage.java revision e7c97de989b75a591d598da63e0bef51831462d4
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 android.app.Activity;
20import android.app.Fragment;
21import android.app.LoaderManager;
22import android.content.ClipData;
23import android.content.ClipboardManager;
24import android.content.ContentUris;
25import android.content.Context;
26import android.content.CursorLoader;
27import android.content.Intent;
28import android.content.Loader;
29import android.content.SharedPreferences;
30import android.content.SharedPreferences.Editor;
31import android.content.res.Configuration;
32import android.content.res.Resources;
33import android.database.Cursor;
34import android.graphics.Bitmap;
35import android.graphics.BitmapFactory;
36import android.net.Uri;
37import android.os.AsyncTask;
38import android.os.Bundle;
39import android.preference.PreferenceManager;
40import android.provider.BrowserContract;
41import android.provider.BrowserContract.Accounts;
42import android.provider.BrowserContract.ChromeSyncColumns;
43import android.view.ActionMode;
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.ExpandableListView;
54import android.widget.ExpandableListView.OnChildClickListener;
55import android.widget.ImageView;
56import android.widget.LinearLayout;
57import android.widget.ListView;
58import android.widget.PopupMenu.OnMenuItemClickListener;
59import android.widget.Toast;
60
61import com.android.browser.BookmarkDragHandler.BookmarkDragController;
62import com.android.browser.BookmarkDragHandler.BookmarkDragState;
63import com.android.browser.view.BookmarkExpandableGridView;
64import com.android.browser.view.BookmarkExpandableGridView.BookmarkContextMenuInfo;
65
66import java.util.HashMap;
67
68interface BookmarksPageCallbacks {
69    // Return true if handled
70    boolean onBookmarkSelected(Cursor c, boolean isFolder);
71    // Return true if handled
72    boolean onOpenInNewWindow(Cursor c);
73}
74
75/**
76 *  View showing the user's bookmarks in the browser.
77 */
78public class BrowserBookmarksPage extends Fragment implements View.OnCreateContextMenuListener,
79        LoaderManager.LoaderCallbacks<Cursor>, IconListener,
80        BreadCrumbView.Controller, OnMenuItemClickListener, OnChildClickListener {
81
82    public static class ExtraDragState {
83        public int childPosition;
84        public int groupPosition;
85    }
86
87    static final String LOGTAG = "browser";
88
89    static final int LOADER_ACCOUNTS = 1;
90    static final int LOADER_BOOKMARKS = 100;
91
92    static final String EXTRA_DISABLE_WINDOW = "disable_new_window";
93
94    static final String ACCOUNT_TYPE = "account_type";
95    static final String ACCOUNT_NAME = "account_name";
96
97    static final int VIEW_THUMBNAILS = 1;
98    static final int VIEW_LIST = 2;
99    static final String PREF_SELECTED_VIEW = "bookmarks_view";
100
101    BookmarksPageCallbacks mCallbacks;
102    View mRoot;
103    BookmarkExpandableGridView mGrid;
104    ListView mList;
105    boolean mDisableNewWindow;
106    boolean mEnableContextMenu = true;
107    View mEmptyView;
108    int mCurrentView;
109    View mHeader;
110    HashMap<Integer, BrowserBookmarksAdapter> mBookmarkAdapters = new HashMap<Integer, BrowserBookmarksAdapter>();
111    BookmarkDragHandler mDragHandler;
112
113    static BrowserBookmarksPage newInstance(BookmarksPageCallbacks cb,
114            Bundle args, ViewGroup headerContainer) {
115        BrowserBookmarksPage bbp = new BrowserBookmarksPage();
116        bbp.mCallbacks = cb;
117        bbp.setArguments(args);
118        return bbp;
119    }
120
121    @Override
122    public Loader<Cursor> onCreateLoader(int id, Bundle args) {
123        if (id == LOADER_ACCOUNTS) {
124            return new AccountsLoader(getActivity());
125        } else if (id >= LOADER_BOOKMARKS) {
126            String accountType = args.getString(ACCOUNT_TYPE);
127            String accountName = args.getString(ACCOUNT_NAME);
128            BookmarksLoader bl = new BookmarksLoader(getActivity(),
129                    accountType, accountName);
130            return bl;
131        } else {
132            throw new UnsupportedOperationException("Unknown loader id " + id);
133        }
134    }
135
136    @Override
137    public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
138        if (loader.getId() == LOADER_ACCOUNTS) {
139            LoaderManager lm = getLoaderManager();
140            int id = LOADER_BOOKMARKS;
141            while (cursor.moveToNext()) {
142                String accountName = cursor.getString(0);
143                String accountType = cursor.getString(1);
144                Bundle args = new Bundle();
145                args.putString(ACCOUNT_NAME, accountName);
146                args.putString(ACCOUNT_TYPE, accountType);
147                BrowserBookmarksAdapter adapter = new BrowserBookmarksAdapter(
148                        getActivity(), mCurrentView);
149                mBookmarkAdapters.put(id, adapter);
150                mGrid.addAccount(accountName, adapter);
151                lm.restartLoader(id, args, this);
152                id++;
153            }
154            // TODO: Figure out what a reload of these means
155            // Currently, a reload is triggered whenever bookmarks change
156            // This is less than ideal
157            // It also causes UI flickering as a new adapter is created
158            // instead of re-using an existing one when the account_name is the
159            // same.
160            // For now, this is a one-shot load
161            getLoaderManager().destroyLoader(LOADER_ACCOUNTS);
162        } else if (loader.getId() >= LOADER_BOOKMARKS) {
163            BrowserBookmarksAdapter adapter = mBookmarkAdapters.get(loader.getId());
164            adapter.changeCursor(cursor);
165        }
166    }
167
168    @Override
169    public void onLoaderReset(Loader<Cursor> loader) {
170        // TODO: Figure out what to do here (if anything?)
171    }
172
173    long getFolderId() {
174        LoaderManager manager = getLoaderManager();
175        BookmarksLoader loader =
176                (BookmarksLoader) ((Loader<?>)manager.getLoader(LOADER_BOOKMARKS));
177
178        Uri uri = loader.getUri();
179        if (uri != null) {
180            try {
181                return ContentUris.parseId(uri);
182            } catch (NumberFormatException nfx) {
183                return -1;
184            }
185        }
186        return -1;
187    }
188
189    @Override
190    public boolean onContextItemSelected(MenuItem item) {
191        BookmarkContextMenuInfo i = (BookmarkContextMenuInfo)item.getMenuInfo();
192        // If we have no menu info, we can't tell which item was selected.
193        if (i == null) {
194            return false;
195        }
196
197        if (handleContextItem(item.getItemId(), i.groupPosition, i.childPosition)) {
198            return true;
199        }
200        return super.onContextItemSelected(item);
201    }
202
203    public boolean handleContextItem(int itemId, int groupPosition,
204            int childPosition) {
205        final Activity activity = getActivity();
206        BrowserBookmarksAdapter adapter = getChildAdapter(groupPosition);
207
208        switch (itemId) {
209        case R.id.open_context_menu_id:
210            loadUrl(adapter, childPosition);
211            break;
212        case R.id.edit_context_menu_id:
213            editBookmark(adapter, childPosition);
214            break;
215        case R.id.shortcut_context_menu_id:
216            Cursor c = adapter.getItem(childPosition);
217            activity.sendBroadcast(createShortcutIntent(getActivity(), c));
218            break;
219        case R.id.delete_context_menu_id:
220            displayRemoveBookmarkDialog(adapter, childPosition);
221            break;
222        case R.id.new_window_context_menu_id:
223            openInNewWindow(adapter, childPosition);
224            break;
225        case R.id.share_link_context_menu_id: {
226            Cursor cursor = adapter.getItem(childPosition);
227            Controller.sharePage(activity,
228                    cursor.getString(BookmarksLoader.COLUMN_INDEX_TITLE),
229                    cursor.getString(BookmarksLoader.COLUMN_INDEX_URL),
230                    getBitmap(cursor, BookmarksLoader.COLUMN_INDEX_FAVICON),
231                    getBitmap(cursor, BookmarksLoader.COLUMN_INDEX_THUMBNAIL));
232            break;
233        }
234        case R.id.copy_url_context_menu_id:
235            copy(getUrl(adapter, childPosition));
236            break;
237        case R.id.homepage_context_menu_id: {
238            BrowserSettings.getInstance().setHomePage(getUrl(adapter, childPosition));
239            Toast.makeText(activity, R.string.homepage_set, Toast.LENGTH_LONG).show();
240            break;
241        }
242        // Only for the Most visited page
243        case R.id.save_to_bookmarks_menu_id: {
244            Cursor cursor = adapter.getItem(childPosition);
245            String name = cursor.getString(BookmarksLoader.COLUMN_INDEX_TITLE);
246            String url = cursor.getString(BookmarksLoader.COLUMN_INDEX_URL);
247            // If the site is bookmarked, the item becomes remove from
248            // bookmarks.
249            Bookmarks.removeFromBookmarks(activity, activity.getContentResolver(), url, name);
250            break;
251        }
252        default:
253            return false;
254        }
255        return true;
256    }
257
258    static Bitmap getBitmap(Cursor cursor, int columnIndex) {
259        byte[] data = cursor.getBlob(columnIndex);
260        if (data == null) {
261            return null;
262        }
263        return BitmapFactory.decodeByteArray(data, 0, data.length);
264    }
265
266    private MenuItem.OnMenuItemClickListener mContextItemClickListener =
267            new MenuItem.OnMenuItemClickListener() {
268        @Override
269        public boolean onMenuItemClick(MenuItem item) {
270            return onContextItemSelected(item);
271        }
272    };
273
274    @Override
275    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
276        inflater.inflate(R.menu.bookmark, menu);
277        if (!BrowserActivity.isTablet(getActivity())) {
278            MenuItem item = menu.findItem(R.id.add_bookmark);
279            item.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
280        }
281    }
282
283    @Override
284    public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
285        BookmarkContextMenuInfo info = (BookmarkContextMenuInfo) menuInfo;
286        BrowserBookmarksAdapter adapter = getChildAdapter(info.groupPosition);
287        Cursor cursor = adapter.getItem(info.childPosition);
288        if (!canEdit(cursor)) {
289            return;
290        }
291        boolean isFolder
292                = cursor.getInt(BookmarksLoader.COLUMN_INDEX_IS_FOLDER) != 0;
293
294        final Activity activity = getActivity();
295        MenuInflater inflater = activity.getMenuInflater();
296        inflater.inflate(R.menu.bookmarkscontext, menu);
297        if (isFolder) {
298            menu.setGroupVisible(R.id.FOLDER_CONTEXT_MENU, true);
299        } else {
300            menu.setGroupVisible(R.id.BOOKMARK_CONTEXT_MENU, true);
301            if (mDisableNewWindow) {
302                menu.findItem(R.id.new_window_context_menu_id).setVisible(false);
303            }
304        }
305        BookmarkItem header = new BookmarkItem(activity);
306        populateBookmarkItem(cursor, header, isFolder);
307        menu.setHeaderView(header);
308
309        int count = menu.size();
310        for (int i = 0; i < count; i++) {
311            menu.getItem(i).setOnMenuItemClickListener(mContextItemClickListener);
312        }
313    }
314
315    boolean canEdit(Cursor c) {
316        String unique = c.getString(BookmarksLoader.COLUMN_INDEX_SERVER_UNIQUE);
317        return !ChromeSyncColumns.FOLDER_NAME_OTHER_BOOKMARKS.equals(unique);
318    }
319
320    private void populateBookmarkItem(Cursor cursor, BookmarkItem item, boolean isFolder) {
321        item.setName(cursor.getString(BookmarksLoader.COLUMN_INDEX_TITLE));
322        if (isFolder) {
323            item.setUrl(null);
324            Bitmap bitmap =
325                BitmapFactory.decodeResource(getResources(), R.drawable.ic_folder_holo_dark);
326            item.setFavicon(bitmap);
327            new LookupBookmarkCount(getActivity(), item)
328                    .execute(cursor.getLong(BookmarksLoader.COLUMN_INDEX_ID));
329        } else {
330            String url = cursor.getString(BookmarksLoader.COLUMN_INDEX_URL);
331            item.setUrl(url);
332            Bitmap bitmap = getBitmap(cursor, BookmarksLoader.COLUMN_INDEX_FAVICON);
333            if (bitmap == null) {
334                bitmap = CombinedBookmarkHistoryView.getIconListenerSet().getFavicon(url);
335            }
336            item.setFavicon(bitmap);
337        }
338    }
339
340    /**
341     *  Create a new BrowserBookmarksPage.
342     */
343    @Override
344    public void onCreate(Bundle icicle) {
345        super.onCreate(icicle);
346        SharedPreferences prefs = PreferenceManager
347            .getDefaultSharedPreferences(getActivity());
348        mCurrentView = prefs.getInt(PREF_SELECTED_VIEW, getDefaultView());
349        // TODO: Support list view
350        mCurrentView = VIEW_THUMBNAILS;
351
352        Bundle args = getArguments();
353        mDisableNewWindow = args == null ? false : args.getBoolean(EXTRA_DISABLE_WINDOW, false);
354
355        setHasOptionsMenu(true);
356    }
357
358    @Override
359    public View onCreateView(LayoutInflater inflater, ViewGroup container,
360            Bundle savedInstanceState) {
361        mRoot = inflater.inflate(R.layout.bookmarks, container, false);
362        mEmptyView = mRoot.findViewById(android.R.id.empty);
363
364        mGrid = (BookmarkExpandableGridView) mRoot.findViewById(R.id.grid);
365        mGrid.setOnChildClickListener(this);
366        mGrid.setColumnWidthFromLayout(R.layout.bookmark_thumbnail);
367        mGrid.setBreadcrumbController(this);
368        mList = (ListView) mRoot.findViewById(R.id.list);
369        // TODO: mList.setOnItemClickListener(this);
370        setEnableContextMenu(mEnableContextMenu);
371        mDragHandler = new BookmarkDragHandler(getActivity(), mDragController,
372                mGrid.getDragAdapter());
373
374        // Start the loaders
375        LoaderManager lm = getLoaderManager();
376        lm.restartLoader(LOADER_ACCOUNTS, null, this);
377
378        // Add our own listener in case there are favicons that have yet to be loaded.
379        CombinedBookmarkHistoryView.getIconListenerSet().addListener(this);
380
381        return mRoot;
382    }
383
384     private int getDefaultView() {
385        if (BrowserActivity.isTablet(getActivity())) {
386            return VIEW_THUMBNAILS;
387        }
388        return VIEW_LIST;
389    }
390
391    @Override
392    public void onDestroyView() {
393        super.onDestroyView();
394        mGrid.setBreadcrumbController(null);
395        LoaderManager lm = getLoaderManager();
396        lm.destroyLoader(LOADER_ACCOUNTS);
397        for (int id : mBookmarkAdapters.keySet()) {
398            lm.destroyLoader(id);
399        }
400        mBookmarkAdapters.clear();
401
402        CombinedBookmarkHistoryView.getIconListenerSet().removeListener(this);
403    }
404
405    @Override
406    public void onReceivedIcon(String url, Bitmap icon) {
407        // A new favicon has been loaded, so let anything attached to the adapter know about it
408        // so new icons will be loaded.
409        // TODO: Notify all of data set changed
410        // TODO: Wait, is this even needed? Won't this trigger a DB change anyway?
411    }
412
413    private BrowserBookmarksAdapter getChildAdapter(int groupPosition) {
414        if (mCurrentView == VIEW_THUMBNAILS) {
415            return mGrid.getChildAdapter(groupPosition);
416        } else {
417            // TODO: Support expandable list
418            return null;
419        }
420    }
421
422    private BreadCrumbView getBreadCrumbs(int groupPosition) {
423        if (mCurrentView == VIEW_THUMBNAILS) {
424            return mGrid.getBreadCrumbs(groupPosition);
425        } else {
426            // TODO: Support expandable list
427            return null;
428        }
429    }
430
431    @Override
432    public boolean onChildClick(ExpandableListView parent, View v,
433            int groupPosition, int childPosition, long id) {
434        BrowserBookmarksAdapter adapter = getChildAdapter(groupPosition);
435        Cursor cursor = adapter.getItem(childPosition);
436        boolean isFolder = cursor.getInt(BookmarksLoader.COLUMN_INDEX_IS_FOLDER) != 0;
437        if (mCallbacks != null &&
438                mCallbacks.onBookmarkSelected(cursor, isFolder)) {
439            return true;
440        }
441
442        // TODO: Folder stuff
443        if (isFolder) {
444            String title = cursor.getString(BookmarksLoader.COLUMN_INDEX_TITLE);
445            Uri uri = ContentUris.withAppendedId(
446                    BrowserContract.Bookmarks.CONTENT_URI_DEFAULT_FOLDER, id);
447            BreadCrumbView crumbs = getBreadCrumbs(groupPosition);
448            if (crumbs != null) {
449                // update crumbs
450                crumbs.pushView(title, uri);
451            }
452            loadFolder(groupPosition, uri);
453        }
454        return true;
455    }
456
457    /* package */ static Intent createShortcutIntent(Context context, Cursor cursor) {
458        String url = cursor.getString(BookmarksLoader.COLUMN_INDEX_URL);
459        String title = cursor.getString(BookmarksLoader.COLUMN_INDEX_TITLE);
460        Bitmap touchIcon = getBitmap(cursor, BookmarksLoader.COLUMN_INDEX_TOUCH_ICON);
461        Bitmap favicon = getBitmap(cursor, BookmarksLoader.COLUMN_INDEX_FAVICON);
462        return BookmarkUtils.createAddToHomeIntent(context, url, title, touchIcon, favicon);
463    }
464
465    private void loadUrl(BrowserBookmarksAdapter adapter, int position) {
466        if (mCallbacks != null && adapter != null) {
467            mCallbacks.onBookmarkSelected(adapter.getItem(position), false);
468        }
469    }
470
471    private void openInNewWindow(BrowserBookmarksAdapter adapter, int position) {
472        if (mCallbacks != null) {
473            Cursor c = adapter.getItem(position);
474            boolean isFolder = c.getInt(BookmarksLoader.COLUMN_INDEX_IS_FOLDER) == 1;
475            if (isFolder) {
476                long id = c.getLong(BookmarksLoader.COLUMN_INDEX_ID);
477                new OpenAllInTabsTask(id).execute();
478            } else {
479                mCallbacks.onOpenInNewWindow(c);
480            }
481        }
482    }
483
484    class OpenAllInTabsTask extends AsyncTask<Void, Void, Cursor> {
485        long mFolderId;
486        public OpenAllInTabsTask(long id) {
487            mFolderId = id;
488        }
489
490        @Override
491        protected Cursor doInBackground(Void... params) {
492            Context c = getActivity();
493            if (c == null) return null;
494            return c.getContentResolver().query(BookmarkUtils.getBookmarksUri(c),
495                    BookmarksLoader.PROJECTION, BrowserContract.Bookmarks.PARENT + "=?",
496                    new String[] { Long.toString(mFolderId) }, null);
497        }
498
499        @Override
500        protected void onPostExecute(Cursor result) {
501            if (mCallbacks != null) {
502                while (result.moveToNext()) {
503                    mCallbacks.onOpenInNewWindow(result);
504                }
505            }
506        }
507
508    }
509
510    private void editBookmark(BrowserBookmarksAdapter adapter, int position) {
511        Intent intent = new Intent(getActivity(), AddBookmarkPage.class);
512        Cursor cursor = adapter.getItem(position);
513        Bundle item = new Bundle();
514        item.putString(BrowserContract.Bookmarks.TITLE,
515                cursor.getString(BookmarksLoader.COLUMN_INDEX_TITLE));
516        item.putString(BrowserContract.Bookmarks.URL,
517                cursor.getString(BookmarksLoader.COLUMN_INDEX_URL));
518        byte[] data = cursor.getBlob(BookmarksLoader.COLUMN_INDEX_FAVICON);
519        if (data != null) {
520            item.putParcelable(BrowserContract.Bookmarks.FAVICON,
521                    BitmapFactory.decodeByteArray(data, 0, data.length));
522        }
523        item.putLong(BrowserContract.Bookmarks._ID,
524                cursor.getLong(BookmarksLoader.COLUMN_INDEX_ID));
525        item.putLong(BrowserContract.Bookmarks.PARENT,
526                cursor.getLong(BookmarksLoader.COLUMN_INDEX_PARENT));
527        intent.putExtra(AddBookmarkPage.EXTRA_EDIT_BOOKMARK, item);
528        intent.putExtra(AddBookmarkPage.EXTRA_IS_FOLDER,
529                cursor.getInt(BookmarksLoader.COLUMN_INDEX_IS_FOLDER) == 1);
530        startActivity(intent);
531    }
532
533    private void displayRemoveBookmarkDialog(BrowserBookmarksAdapter adapter,
534            int position) {
535        // Put up a dialog asking if the user really wants to
536        // delete the bookmark
537        Cursor cursor = adapter.getItem(position);
538        long id = cursor.getLong(BookmarksLoader.COLUMN_INDEX_ID);
539        String title = cursor.getString(BookmarksLoader.COLUMN_INDEX_TITLE);
540        Context context = getActivity();
541        BookmarkUtils.displayRemoveBookmarkDialog(id, title, context, null);
542    }
543
544    private String getUrl(BrowserBookmarksAdapter adapter, int position) {
545        return getUrl(adapter.getItem(position));
546    }
547
548    /* package */ static String getUrl(Cursor c) {
549        return c.getString(BookmarksLoader.COLUMN_INDEX_URL);
550    }
551
552    private void copy(CharSequence text) {
553        ClipboardManager cm = (ClipboardManager) getActivity().getSystemService(
554                Context.CLIPBOARD_SERVICE);
555        cm.setPrimaryClip(ClipData.newRawUri(null, Uri.parse(text.toString())));
556    }
557
558    @Override
559    public boolean onOptionsItemSelected(MenuItem item) {
560        switch (item.getItemId()) {
561        case R.id.thumbnail_view:
562            selectView(VIEW_THUMBNAILS);
563            return true;
564        case R.id.list_view:
565            selectView(VIEW_LIST);
566            return true;
567        }
568        return super.onOptionsItemSelected(item);
569    }
570
571    @Override
572    public void onConfigurationChanged(Configuration newConfig) {
573        super.onConfigurationChanged(newConfig);
574        Resources res = getActivity().getResources();
575        int horizontalSpacing = (int) res.getDimension(R.dimen.combo_horizontalSpacing);
576        mGrid.setHorizontalSpacing(horizontalSpacing);
577        mGrid.setColumnWidthFromLayout(R.layout.bookmark_thumbnail);
578        int paddingLeftRight = (int) res.getDimension(R.dimen.combo_paddingLeftRight);
579        int paddingTop = (int) res.getDimension(R.dimen.combo_paddingTop);
580        mRoot.setPadding(paddingLeftRight, paddingTop,
581                paddingLeftRight, 0);
582        getActivity().invalidateOptionsMenu();
583    }
584
585    @Override
586    public void onPrepareOptionsMenu(Menu menu) {
587        super.onPrepareOptionsMenu(menu);
588        menu.findItem(R.id.list_view).setVisible(mCurrentView != VIEW_LIST);
589        menu.findItem(R.id.thumbnail_view).setVisible(mCurrentView != VIEW_THUMBNAILS);
590    }
591
592    void selectView(int view) {
593        // TODO: Support list view
594        view = mCurrentView;
595        if (view == mCurrentView) {
596            return;
597        }
598        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getActivity());
599        Editor edit = prefs.edit();
600        edit.putInt(PREF_SELECTED_VIEW, mCurrentView);
601        edit.apply();
602        if (mEmptyView.getVisibility() == View.VISIBLE) {
603            return;
604        }
605        setupBookmarkView();
606    }
607
608    private void setupBookmarkView() {
609        // TODO: Support list view
610//        mAdapter.selectView(mCurrentView);
611//        switch (mCurrentView) {
612//        case VIEW_THUMBNAILS:
613//            mList.setAdapter(null);
614//            SharedPreferences prefs = PreferenceManager
615//                    .getDefaultSharedPreferences(getActivity());
616//            String accountName = prefs.getString(PREF_ACCOUNT_NAME, null);
617//            mGrid.addAccount(accountName, mAdapter);
618//            mGrid.setVisibility(View.VISIBLE);
619//            mList.setVisibility(View.GONE);
620//            break;
621//        case VIEW_LIST:
622//            mGrid.clearAccounts();
623//            if (mList.getAdapter() != mAdapter) {
624//                mList.setAdapter(mAdapter);
625//            }
626//            mGrid.setVisibility(View.GONE);
627//            mList.setVisibility(View.VISIBLE);
628//            break;
629//        }
630    }
631
632    /**
633     * BreadCrumb controller callback
634     */
635    @Override
636    public void onTop(BreadCrumbView view, int level, Object data) {
637        int groupPosition = (Integer) view.getTag(R.id.group_position);
638        Uri uri = (Uri) data;
639        if (uri == null) {
640            // top level
641            uri = BrowserContract.Bookmarks.CONTENT_URI_DEFAULT_FOLDER;
642        }
643        loadFolder(groupPosition, uri);
644    }
645
646    /**
647     * @param uri
648     */
649    private void loadFolder(int groupPosition, Uri uri) {
650        LoaderManager manager = getLoaderManager();
651        // This assumes groups are ordered the same as loaders
652        BookmarksLoader loader = (BookmarksLoader) ((Loader<?>)
653                manager.getLoader(LOADER_BOOKMARKS + groupPosition));
654        loader.setUri(uri);
655        loader.forceLoad();
656    }
657
658    @Override
659    public boolean onMenuItemClick(MenuItem item) {
660        switch (item.getItemId()) {
661        case R.id.list_view:
662            selectView(BrowserBookmarksPage.VIEW_LIST);
663            return true;
664        case R.id.thumbnail_view:
665            selectView(BrowserBookmarksPage.VIEW_THUMBNAILS);
666            return true;
667        }
668        return false;
669    }
670
671    public boolean onBackPressed() {
672        return false;
673    }
674
675    public void setCallbackListener(BookmarksPageCallbacks callbackListener) {
676        mCallbacks = callbackListener;
677    }
678
679    public void setEnableContextMenu(boolean enable) {
680        mEnableContextMenu = enable;
681        if (mGrid != null) {
682            if (mEnableContextMenu) {
683                registerForContextMenu(mGrid);
684            } else {
685                unregisterForContextMenu(mGrid);
686                mGrid.setLongClickable(false);
687            }
688        }
689        if (mList != null) {
690            if (mEnableContextMenu) {
691                registerForContextMenu(mList);
692            } else {
693                unregisterForContextMenu(mList);
694                mList.setLongClickable(false);
695            }
696        }
697    }
698
699    private BookmarkDragController mDragController = new BookmarkDragController() {
700
701        @Override
702        public boolean startDrag(Cursor item) {
703            return canEdit(item);
704        }
705
706        @Override
707        public ViewGroup getActionModeView(ActionMode mode,
708                BookmarkDragState state) {
709            LayoutInflater inflater = LayoutInflater.from(getActivity());
710            LinearLayout view = (LinearLayout) inflater.inflate(
711                    R.layout.bookmarks_drag_actionmode, null);
712            view.setShowDividers(LinearLayout.SHOW_DIVIDER_MIDDLE);
713            ExtraDragState extraState = (ExtraDragState) state.extraState;
714            BrowserBookmarksAdapter adapter = getChildAdapter(extraState.groupPosition);
715            Cursor c = adapter.getItem(extraState.childPosition);
716            boolean isFolder = c.getInt(BookmarksLoader.COLUMN_INDEX_IS_FOLDER) != 0;
717            if (isFolder) {
718                view.findViewById(R.id.open_context_menu_id).setVisibility(View.GONE);
719                ImageView iv = (ImageView) view.findViewById(
720                        R.id.new_window_context_menu_id);
721                iv.setImageResource(R.drawable.ic_windows_holo_dark);
722            }
723            return view;
724        }
725
726        @Override
727        public void actionItemClicked(View v, BookmarkDragState state) {
728            if (v.getId() == R.id.info) {
729                if (mCurrentView == VIEW_THUMBNAILS) {
730                    mGrid.showContextMenuForState(state);
731                } else {
732                    // TODO: Support expandable list
733                }
734            } else {
735                ExtraDragState extraState = (ExtraDragState) state.extraState;
736                handleContextItem(v.getId(), extraState.groupPosition,
737                        extraState.childPosition);
738            }
739        }
740    };
741
742    private static class LookupBookmarkCount extends AsyncTask<Long, Void, Integer> {
743        Context mContext;
744        BookmarkItem mHeader;
745
746        public LookupBookmarkCount(Context context, BookmarkItem header) {
747            mContext = context;
748            mHeader = header;
749        }
750
751        @Override
752        protected Integer doInBackground(Long... params) {
753            if (params.length != 1) {
754                throw new IllegalArgumentException("Missing folder id!");
755            }
756            Uri uri = BookmarkUtils.getBookmarksUri(mContext);
757            Cursor c = mContext.getContentResolver().query(uri,
758                    null, BrowserContract.Bookmarks.PARENT + "=?",
759                    new String[] {params[0].toString()}, null);
760            return c.getCount();
761        }
762
763        @Override
764        protected void onPostExecute(Integer result) {
765            if (result > 0) {
766                mHeader.setUrl(mContext.getString(R.string.contextheader_folder_bookmarkcount,
767                        result));
768            } else if (result == 0) {
769                mHeader.setUrl(mContext.getString(R.string.contextheader_folder_empty));
770            }
771        }
772    }
773
774    static class AccountsLoader extends CursorLoader {
775
776        static String[] ACCOUNTS_PROJECTION = new String[] {
777            Accounts.ACCOUNT_NAME,
778            Accounts.ACCOUNT_TYPE
779        };
780
781        public AccountsLoader(Context context) {
782            super(context, Accounts.CONTENT_URI, ACCOUNTS_PROJECTION, null, null,
783                    Accounts.ACCOUNT_NAME + " ASC");
784        }
785
786    }
787}
788