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