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