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