BrowserBookmarksPage.java revision 83c0151b266f4789cb1728294eac98e3dba0ed53
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.provider.BrowserContract.ChromeSyncColumns;
41import android.view.ActionMode;
42import android.view.ContextMenu;
43import android.view.ContextMenu.ContextMenuInfo;
44import android.view.LayoutInflater;
45import android.view.MenuInflater;
46import android.view.MenuItem;
47import android.view.View;
48import android.view.ViewGroup;
49import android.widget.ExpandableListView;
50import android.widget.ExpandableListView.OnChildClickListener;
51import android.widget.ImageView;
52import android.widget.LinearLayout;
53import android.widget.Toast;
54
55import com.android.browser.BookmarkDragHandler.BookmarkDragController;
56import com.android.browser.BookmarkDragHandler.BookmarkDragState;
57import com.android.browser.provider.BrowserProvider2;
58import com.android.browser.view.BookmarkExpandableView;
59import com.android.browser.view.BookmarkExpandableView.BookmarkContextMenuInfo;
60
61import org.json.JSONException;
62import org.json.JSONObject;
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(String... urls);
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        OnChildClickListener {
79
80    public static class ExtraDragState {
81        public int childPosition;
82        public int groupPosition;
83    }
84
85    static final String LOGTAG = "browser";
86
87    static final int LOADER_ACCOUNTS = 1;
88    static final int LOADER_BOOKMARKS = 100;
89
90    static final String EXTRA_DISABLE_WINDOW = "disable_new_window";
91    static final String PREF_GROUP_STATE = "bbp_group_state";
92
93    static final String ACCOUNT_TYPE = "account_type";
94    static final String ACCOUNT_NAME = "account_name";
95
96    public static final int VIEW_THUMBNAILS = 1;
97    public static final int VIEW_LIST = 2;
98
99    BookmarksPageCallbacks mCallbacks;
100    View mRoot;
101    BookmarkExpandableView mGrid;
102    boolean mDisableNewWindow;
103    boolean mEnableContextMenu = true;
104    View mEmptyView;
105    View mHeader;
106    HashMap<Integer, BrowserBookmarksAdapter> mBookmarkAdapters = new HashMap<Integer, BrowserBookmarksAdapter>();
107    BookmarkDragHandler mDragHandler;
108    JSONObject mState;
109
110    @Override
111    public Loader<Cursor> onCreateLoader(int id, Bundle args) {
112        if (id == LOADER_ACCOUNTS) {
113            return new AccountsLoader(getActivity());
114        } else if (id >= LOADER_BOOKMARKS) {
115            String accountType = args.getString(ACCOUNT_TYPE);
116            String accountName = args.getString(ACCOUNT_NAME);
117            BookmarksLoader bl = new BookmarksLoader(getActivity(),
118                    accountType, accountName);
119            return bl;
120        } else {
121            throw new UnsupportedOperationException("Unknown loader id " + id);
122        }
123    }
124
125    @Override
126    public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
127        if (loader.getId() == LOADER_ACCOUNTS) {
128            LoaderManager lm = getLoaderManager();
129            int id = LOADER_BOOKMARKS;
130            while (cursor.moveToNext()) {
131                String accountName = cursor.getString(0);
132                String accountType = cursor.getString(1);
133                Bundle args = new Bundle();
134                args.putString(ACCOUNT_NAME, accountName);
135                args.putString(ACCOUNT_TYPE, accountType);
136                BrowserBookmarksAdapter adapter = new BrowserBookmarksAdapter(
137                        getActivity(), VIEW_THUMBNAILS);
138                mBookmarkAdapters.put(id, adapter);
139                boolean expand = true;
140                try {
141                    expand = mState.getBoolean(accountName != null ? accountName
142                            : BookmarkExpandableView.LOCAL_ACCOUNT_NAME);
143                } catch (JSONException e) {} // no state for accountName
144                mGrid.addAccount(accountName, adapter, expand);
145                lm.restartLoader(id, args, this);
146                id++;
147            }
148            // TODO: Figure out what a reload of these means
149            // Currently, a reload is triggered whenever bookmarks change
150            // This is less than ideal
151            // It also causes UI flickering as a new adapter is created
152            // instead of re-using an existing one when the account_name is the
153            // same.
154            // For now, this is a one-shot load
155            getLoaderManager().destroyLoader(LOADER_ACCOUNTS);
156        } else if (loader.getId() >= LOADER_BOOKMARKS) {
157            BrowserBookmarksAdapter adapter = mBookmarkAdapters.get(loader.getId());
158            adapter.changeCursor(cursor);
159        }
160    }
161
162    @Override
163    public void onLoaderReset(Loader<Cursor> loader) {
164    }
165
166    @Override
167    public boolean onContextItemSelected(MenuItem item) {
168        if (!(item.getMenuInfo() instanceof BookmarkContextMenuInfo)) {
169            return false;
170        }
171        BookmarkContextMenuInfo i = (BookmarkContextMenuInfo) item.getMenuInfo();
172        // If we have no menu info, we can't tell which item was selected.
173        if (i == null) {
174            return false;
175        }
176
177        if (handleContextItem(item.getItemId(), i.groupPosition, i.childPosition)) {
178            return true;
179        }
180        return super.onContextItemSelected(item);
181    }
182
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 onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
256        BookmarkContextMenuInfo info = (BookmarkContextMenuInfo) menuInfo;
257        BrowserBookmarksAdapter adapter = getChildAdapter(info.groupPosition);
258        Cursor cursor = adapter.getItem(info.childPosition);
259        if (!canEdit(cursor)) {
260            return;
261        }
262        boolean isFolder
263                = cursor.getInt(BookmarksLoader.COLUMN_INDEX_IS_FOLDER) != 0;
264
265        final Activity activity = getActivity();
266        MenuInflater inflater = activity.getMenuInflater();
267        inflater.inflate(R.menu.bookmarkscontext, menu);
268        if (isFolder) {
269            menu.setGroupVisible(R.id.FOLDER_CONTEXT_MENU, true);
270        } else {
271            menu.setGroupVisible(R.id.BOOKMARK_CONTEXT_MENU, true);
272            if (mDisableNewWindow) {
273                menu.findItem(R.id.new_window_context_menu_id).setVisible(false);
274            }
275        }
276        BookmarkItem header = new BookmarkItem(activity);
277        header.setEnableScrolling(true);
278        populateBookmarkItem(cursor, header, isFolder);
279        menu.setHeaderView(header);
280
281        int count = menu.size();
282        for (int i = 0; i < count; i++) {
283            menu.getItem(i).setOnMenuItemClickListener(mContextItemClickListener);
284        }
285    }
286
287    boolean canEdit(Cursor c) {
288        String unique = c.getString(BookmarksLoader.COLUMN_INDEX_SERVER_UNIQUE);
289        return !ChromeSyncColumns.FOLDER_NAME_OTHER_BOOKMARKS.equals(unique);
290    }
291
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        // TODO: Folder stuff
426        if (isFolder) {
427            String title = cursor.getString(BookmarksLoader.COLUMN_INDEX_TITLE);
428            Uri uri = ContentUris.withAppendedId(
429                    BrowserContract.Bookmarks.CONTENT_URI_DEFAULT_FOLDER, id);
430            BreadCrumbView crumbs = getBreadCrumbs(groupPosition);
431            if (crumbs != null) {
432                // update crumbs
433                crumbs.pushView(title, uri);
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    }
567
568    /**
569     * @param uri
570     */
571    private void loadFolder(int groupPosition, Uri uri) {
572        LoaderManager manager = getLoaderManager();
573        // This assumes groups are ordered the same as loaders
574        BookmarksLoader loader = (BookmarksLoader) ((Loader<?>)
575                manager.getLoader(LOADER_BOOKMARKS + groupPosition));
576        loader.setUri(uri);
577        loader.forceLoad();
578    }
579
580    public void setCallbackListener(BookmarksPageCallbacks callbackListener) {
581        mCallbacks = callbackListener;
582    }
583
584    public void setEnableContextMenu(boolean enable) {
585        mEnableContextMenu = enable;
586        if (mGrid != null) {
587            if (mEnableContextMenu) {
588                registerForContextMenu(mGrid);
589            } else {
590                unregisterForContextMenu(mGrid);
591                mGrid.setLongClickable(false);
592            }
593        }
594    }
595
596    private BookmarkDragController mDragController = new BookmarkDragController() {
597
598        @Override
599        public boolean startDrag(Cursor item) {
600            return canEdit(item);
601        }
602
603        @Override
604        public ViewGroup getActionModeView(ActionMode mode,
605                BookmarkDragState state) {
606            LayoutInflater inflater = LayoutInflater.from(getActivity());
607            LinearLayout view = (LinearLayout) inflater.inflate(
608                    R.layout.bookmarks_drag_actionmode, null);
609            view.setShowDividers(LinearLayout.SHOW_DIVIDER_MIDDLE);
610            ExtraDragState extraState = (ExtraDragState) state.extraState;
611            BrowserBookmarksAdapter adapter = getChildAdapter(extraState.groupPosition);
612            Cursor c = adapter.getItem(extraState.childPosition);
613            boolean isFolder = c.getInt(BookmarksLoader.COLUMN_INDEX_IS_FOLDER) != 0;
614            if (isFolder) {
615                view.findViewById(R.id.open_context_menu_id).setVisibility(View.GONE);
616                ImageView iv = (ImageView) view.findViewById(
617                        R.id.new_window_context_menu_id);
618                iv.setImageResource(R.drawable.ic_windows_holo_dark);
619            }
620            return view;
621        }
622
623        @Override
624        public void actionItemClicked(View v, BookmarkDragState state) {
625            if (v.getId() == R.id.info) {
626                mGrid.showContextMenuForState(state);
627            } else {
628                ExtraDragState extraState = (ExtraDragState) state.extraState;
629                handleContextItem(v.getId(), extraState.groupPosition,
630                        extraState.childPosition);
631            }
632        }
633    };
634
635    private static class LookupBookmarkCount extends AsyncTask<Long, Void, Integer> {
636        Context mContext;
637        BookmarkItem mHeader;
638
639        public LookupBookmarkCount(Context context, BookmarkItem header) {
640            mContext = context.getApplicationContext();
641            mHeader = header;
642        }
643
644        @Override
645        protected Integer doInBackground(Long... params) {
646            if (params.length != 1) {
647                throw new IllegalArgumentException("Missing folder id!");
648            }
649            Uri uri = BookmarkUtils.getBookmarksUri(mContext);
650            Cursor c = mContext.getContentResolver().query(uri,
651                    null, BrowserContract.Bookmarks.PARENT + "=?",
652                    new String[] {params[0].toString()}, null);
653            return c.getCount();
654        }
655
656        @Override
657        protected void onPostExecute(Integer result) {
658            if (result > 0) {
659                mHeader.setUrl(mContext.getString(R.string.contextheader_folder_bookmarkcount,
660                        result));
661            } else if (result == 0) {
662                mHeader.setUrl(mContext.getString(R.string.contextheader_folder_empty));
663            }
664        }
665    }
666
667    static class AccountsLoader extends CursorLoader {
668
669        static String[] ACCOUNTS_PROJECTION = new String[] {
670            Accounts.ACCOUNT_NAME,
671            Accounts.ACCOUNT_TYPE
672        };
673
674        public AccountsLoader(Context context) {
675            super(context, Accounts.CONTENT_URI
676                    .buildUpon()
677                    .appendQueryParameter(BrowserProvider2.PARAM_ALLOW_EMPTY_ACCOUNTS, "false")
678                    .build(),
679                    ACCOUNTS_PROJECTION, null, null, null);
680        }
681
682    }
683}
684