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