BrowserBookmarksPage.java revision 7f6cf3e4109426164c6fdd11aba0c69622e2353c
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.AlertDialog;
21import android.app.Fragment;
22import android.app.LoaderManager;
23import android.content.ClipData;
24import android.content.ClipboardManager;
25import android.content.ContentResolver;
26import android.content.ContentUris;
27import android.content.ContentValues;
28import android.content.Context;
29import android.content.CursorLoader;
30import android.content.DialogInterface;
31import android.content.Intent;
32import android.content.Loader;
33import android.content.SharedPreferences;
34import android.database.Cursor;
35import android.graphics.Bitmap;
36import android.graphics.BitmapFactory;
37import android.net.Uri;
38import android.os.Bundle;
39import android.preference.PreferenceManager;
40import android.provider.BrowserContract;
41import android.provider.BrowserContract.Accounts;
42import android.text.TextUtils;
43import android.util.Pair;
44import android.view.ContextMenu;
45import android.view.ContextMenu.ContextMenuInfo;
46import android.view.LayoutInflater;
47import android.view.MenuInflater;
48import android.view.MenuItem;
49import android.view.View;
50import android.view.View.OnClickListener;
51import android.view.ViewGroup;
52import android.webkit.WebIconDatabase.IconListener;
53import android.widget.Adapter;
54import android.widget.AdapterView;
55import android.widget.AdapterView.OnItemClickListener;
56import android.widget.AdapterView.OnItemSelectedListener;
57import android.widget.ArrayAdapter;
58import android.widget.Button;
59import android.widget.GridView;
60import android.widget.Spinner;
61import android.widget.Toast;
62
63import java.util.ArrayList;
64import java.util.Stack;
65
66/**
67 *  View showing the user's bookmarks in the browser.
68 */
69public class BrowserBookmarksPage extends Fragment implements View.OnCreateContextMenuListener,
70        LoaderManager.LoaderCallbacks<Cursor>, OnItemClickListener, IconListener, OnClickListener,
71        OnItemSelectedListener {
72
73    static final int BOOKMARKS_SAVE = 1;
74    static final String LOGTAG = "browser";
75
76    static final int LOADER_BOOKMARKS = 1;
77    static final int LOADER_ACCOUNTS = 2;
78    static final int LOADER_ACCOUNTS_THEN_BOOKMARKS = 3;
79
80    static final String EXTRA_SHORTCUT = "create_shortcut";
81    static final String EXTRA_DISABLE_WINDOW = "disable_new_window";
82
83    public static final String PREF_ACCOUNT_TYPE = "acct_type";
84    public static final String PREF_ACCOUNT_NAME = "acct_name";
85
86    static final String DEFAULT_ACCOUNT = "local";
87
88    BookmarksHistoryCallbacks mCallbacks;
89    GridView mGrid;
90    Spinner mAccountSelector;
91    BrowserBookmarksAdapter mAdapter;
92    boolean mDisableNewWindow;
93    BookmarkItem mContextHeader;
94    boolean mCanceled = false;
95    boolean mCreateShortcut;
96    View mEmptyView;
97    View mContentView;
98    Stack<Pair<String, Uri>> mFolderStack = new Stack<Pair<String, Uri>>();
99    Button mUpButton;
100
101    @Override
102    public Loader<Cursor> onCreateLoader(int id, Bundle args) {
103        switch (id) {
104            case LOADER_BOOKMARKS: {
105                String accountType = null;
106                String accountName = null;
107                if (args != null) {
108                    accountType = args.getString(BookmarksLoader.ARG_ACCOUNT_TYPE);
109                    accountName = args.getString(BookmarksLoader.ARG_ACCOUNT_NAME);
110                }
111                return new BookmarksLoader(getActivity(), accountType, accountName);
112            }
113
114            case LOADER_ACCOUNTS:
115            case LOADER_ACCOUNTS_THEN_BOOKMARKS: {
116                return new CursorLoader(getActivity(), Accounts.CONTENT_URI,
117                        new String[] { Accounts.ACCOUNT_TYPE, Accounts.ACCOUNT_NAME }, null, null,
118                        null);
119            }
120        }
121        throw new UnsupportedOperationException("Unknown loader id " + id);
122    }
123
124    @Override
125    public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
126        switch (loader.getId()) {
127            case LOADER_BOOKMARKS: {
128                // Set the visibility of the empty vs. content views
129                if (cursor == null || cursor.getCount() == 0) {
130                    mEmptyView.setVisibility(View.VISIBLE);
131                    mGrid.setVisibility(View.GONE);
132                } else {
133                    mEmptyView.setVisibility(View.GONE);
134                    mGrid.setVisibility(View.VISIBLE);
135                }
136
137                // Fill in the "up" button if needed
138                BookmarksLoader bl = (BookmarksLoader) loader;
139                String path = bl.getUri().getPath();
140                boolean rootFolder =
141                    BrowserContract.Bookmarks.CONTENT_URI_DEFAULT_FOLDER.getPath().equals(path);
142                if (rootFolder) {
143                    mUpButton.setText(R.string.defaultBookmarksUpButton);
144                    mUpButton.setEnabled(false);
145                } else {
146                    mUpButton.setText(mFolderStack.peek().first);
147                    mUpButton.setEnabled(true);
148                }
149                mUpButton.setVisibility(View.VISIBLE);
150
151                // Give the new data to the adapter
152                mAdapter.changeCursor(cursor);
153
154                break;
155            }
156
157            case LOADER_ACCOUNTS:
158            case LOADER_ACCOUNTS_THEN_BOOKMARKS: {
159                SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(
160                        getActivity());
161                String storedAccountType = prefs.getString(PREF_ACCOUNT_TYPE, null);
162                String storedAccountName = prefs.getString(PREF_ACCOUNT_NAME, null);
163                String accountType =
164                        TextUtils.isEmpty(storedAccountType) ? DEFAULT_ACCOUNT : storedAccountType;
165                String accountName =
166                        TextUtils.isEmpty(storedAccountName) ? DEFAULT_ACCOUNT : storedAccountName;
167
168                Bundle args = null;
169                if (cursor == null || !cursor.moveToFirst()) {
170                    // No accounts, set the prefs to the default
171                    accountType = DEFAULT_ACCOUNT;
172                    accountName = DEFAULT_ACCOUNT;
173                    mAccountSelector.setVisibility(View.GONE);
174                } else {
175                    int accountPosition = -1;
176
177                    if (!DEFAULT_ACCOUNT.equals(accountType) &&
178                            !DEFAULT_ACCOUNT.equals(accountName)) {
179                        // Check to see if the account in prefs still exists
180                        cursor.moveToFirst();
181                        do {
182                            if (accountType.equals(cursor.getString(0))
183                                    && accountName.equals(cursor.getString(1))) {
184                                accountPosition = cursor.getPosition();
185                                break;
186                            }
187                        } while (cursor.moveToNext());
188                    }
189
190                    if (accountPosition == -1) {
191                        // No account is set in prefs and there is at least one,
192                        // so pick the first one as the default
193                        cursor.moveToFirst();
194                        accountType = cursor.getString(0);
195                        accountName = cursor.getString(1);
196                        accountPosition = 0;
197                    }
198
199                    args = new Bundle();
200                    args.putString(BookmarksLoader.ARG_ACCOUNT_TYPE, accountType);
201                    args.putString(BookmarksLoader.ARG_ACCOUNT_NAME, accountName);
202
203                    // Setup the account selector if there is more than 1 account
204                    if (cursor.getCount() > 1) {
205                        ArrayList<String> accounts = new ArrayList<String>();
206                        cursor.moveToFirst();
207                        do {
208                            accounts.add(cursor.getString(1));
209                        } while (cursor.moveToNext());
210
211                        mAccountSelector.setAdapter(new ArrayAdapter<String>(getActivity(),
212                                android.R.layout.simple_list_item_1, android.R.id.text1, accounts));
213                        mAccountSelector.setVisibility(View.VISIBLE);
214                        mAccountSelector.setSelection(accountPosition);
215                    }
216                }
217                if (!accountType.equals(storedAccountType)
218                        || !accountName.equals(storedAccountName)) {
219                    prefs.edit()
220                            .putString(PREF_ACCOUNT_TYPE, accountType)
221                            .putString(PREF_ACCOUNT_NAME, accountName)
222                            .apply();
223                }
224                if (loader.getId() == LOADER_ACCOUNTS_THEN_BOOKMARKS) {
225                    getLoaderManager().initLoader(LOADER_BOOKMARKS, args, this);
226                }
227
228                break;
229            }
230        }
231    }
232
233    @Override
234    public void onClick(View view) {
235        if (view == mUpButton) {
236            Pair<String, Uri> pair = mFolderStack.pop();
237            BookmarksLoader loader =
238                    (BookmarksLoader) ((Loader) getLoaderManager().getLoader(LOADER_BOOKMARKS));
239            loader.setUri(pair.second);
240            loader.forceLoad();
241        }
242    }
243
244    @Override
245    public boolean onContextItemSelected(MenuItem item) {
246        final Activity activity = getActivity();
247        // It is possible that the view has been canceled when we get to
248        // this point as back has a higher priority
249        if (mCanceled) {
250            return true;
251        }
252        AdapterView.AdapterContextMenuInfo i =
253            (AdapterView.AdapterContextMenuInfo)item.getMenuInfo();
254        // If we have no menu info, we can't tell which item was selected.
255        if (i == null) {
256            return true;
257        }
258
259        switch (item.getItemId()) {
260        case R.id.open_context_menu_id:
261            loadUrl(i.position);
262            break;
263        case R.id.edit_context_menu_id:
264            editBookmark(i.position);
265            break;
266        case R.id.shortcut_context_menu_id:
267            activity.sendBroadcast(createShortcutIntent(i.position));
268            break;
269        case R.id.delete_context_menu_id:
270            displayRemoveBookmarkDialog(i.position);
271            break;
272        case R.id.new_window_context_menu_id:
273            openInNewWindow(i.position);
274            break;
275        case R.id.share_link_context_menu_id: {
276            Cursor cursor = (Cursor) mAdapter.getItem(i.position);
277            BrowserActivity.sharePage(activity,
278                    cursor.getString(BookmarksLoader.COLUMN_INDEX_TITLE),
279                    cursor.getString(BookmarksLoader.COLUMN_INDEX_URL),
280                    getBitmap(cursor, BookmarksLoader.COLUMN_INDEX_FAVICON),
281                    getBitmap(cursor, BookmarksLoader.COLUMN_INDEX_THUMBNAIL));
282            break;
283        }
284        case R.id.copy_url_context_menu_id:
285            copy(getUrl(i.position));
286            break;
287        case R.id.homepage_context_menu_id: {
288            BrowserSettings.getInstance().setHomePage(activity, getUrl(i.position));
289            Toast.makeText(activity, R.string.homepage_set, Toast.LENGTH_LONG).show();
290            break;
291        }
292        // Only for the Most visited page
293        case R.id.save_to_bookmarks_menu_id: {
294            Cursor cursor = (Cursor) mAdapter.getItem(i.position);
295            String name = cursor.getString(BookmarksLoader.COLUMN_INDEX_TITLE);
296            String url = cursor.getString(BookmarksLoader.COLUMN_INDEX_URL);
297            // If the site is bookmarked, the item becomes remove from
298            // bookmarks.
299            Bookmarks.removeFromBookmarks(activity, activity.getContentResolver(), url, name);
300            break;
301        }
302        default:
303            return super.onContextItemSelected(item);
304        }
305        return true;
306    }
307
308    Bitmap getBitmap(Cursor cursor, int columnIndex) {
309        byte[] data = cursor.getBlob(columnIndex);
310        if (data == null) {
311            return null;
312        }
313        return BitmapFactory.decodeByteArray(data, 0, data.length);
314    }
315
316    @Override
317    public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
318        AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo;
319
320        final Activity activity = getActivity();
321        MenuInflater inflater = activity.getMenuInflater();
322        inflater.inflate(R.menu.bookmarkscontext, menu);
323
324        if (mDisableNewWindow) {
325            menu.findItem(R.id.new_window_context_menu_id).setVisible(false);
326        }
327
328        if (mContextHeader == null) {
329            mContextHeader = new BookmarkItem(activity);
330        } else if (mContextHeader.getParent() != null) {
331            ((ViewGroup) mContextHeader.getParent()).removeView(mContextHeader);
332        }
333
334        populateBookmarkItem(mAdapter, mContextHeader, info.position);
335
336        menu.setHeaderView(mContextHeader);
337    }
338
339    private void populateBookmarkItem(BrowserBookmarksAdapter adapter, BookmarkItem item,
340            int position) {
341        Cursor cursor = (Cursor) mAdapter.getItem(position);
342        String url = cursor.getString(BookmarksLoader.COLUMN_INDEX_URL);
343        item.setUrl(url);
344        item.setName(cursor.getString(BookmarksLoader.COLUMN_INDEX_TITLE));
345        Bitmap bitmap = getBitmap(cursor, BookmarksLoader.COLUMN_INDEX_FAVICON);
346        if (bitmap == null) {
347            bitmap = CombinedBookmarkHistoryActivity.getIconListenerSet().getFavicon(url);
348        }
349        item.setFavicon(bitmap);
350    }
351
352    /**
353     *  Create a new BrowserBookmarksPage.
354     */
355    @Override
356    public void onCreate(Bundle icicle) {
357        super.onCreate(icicle);
358
359        Bundle args = getArguments();
360        mCreateShortcut = args == null ? false : args.getBoolean("create_shortcut", false);
361        mDisableNewWindow = args == null ? false : args.getBoolean("disable_new_window", false);
362    }
363
364    @Override
365    public void onAttach(Activity activity) {
366        super.onAttach(activity);
367        mCallbacks = (BookmarksHistoryCallbacks) activity;
368    }
369
370    @Override
371    public View onCreateView(LayoutInflater inflater, ViewGroup container,
372            Bundle savedInstanceState) {
373        Context context = getActivity();
374
375        View root = inflater.inflate(R.layout.bookmarks, container, false);
376        mEmptyView = root.findViewById(android.R.id.empty);
377        mContentView = root.findViewById(android.R.id.content);
378
379        mGrid = (GridView) root.findViewById(R.id.grid);
380        mGrid.setOnItemClickListener(this);
381        mGrid.setColumnWidth(BrowserActivity.getDesiredThumbnailWidth(getActivity()));
382        if (!mCreateShortcut) {
383            mGrid.setOnCreateContextMenuListener(this);
384        }
385
386        mAccountSelector = (Spinner) root.findViewById(R.id.accounts);
387        mAccountSelector.setOnItemSelectedListener(this);
388        mAccountSelector.setVisibility(View.INVISIBLE);
389
390        mUpButton = (Button) root.findViewById(R.id.up);
391        mUpButton.setEnabled(false);
392        mUpButton.setOnClickListener(this);
393        mUpButton.setVisibility(View.GONE);
394
395        mAdapter = new BrowserBookmarksAdapter(getActivity());
396        mGrid.setAdapter(mAdapter);
397
398        // Start the loaders
399        LoaderManager lm = getLoaderManager();
400        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
401        String accountType = prefs.getString(PREF_ACCOUNT_TYPE, null);
402        String accountName = prefs.getString(PREF_ACCOUNT_NAME, null);
403        if (!TextUtils.isEmpty(accountType) && !TextUtils.isEmpty(accountName)) {
404            // There is an account set, load up that one
405            Bundle args = null;
406            if (!DEFAULT_ACCOUNT.equals(accountType) && !DEFAULT_ACCOUNT.equals(accountName)) {
407                args = new Bundle();
408                args.putString(BookmarksLoader.ARG_ACCOUNT_TYPE, accountType);
409                args.putString(BookmarksLoader.ARG_ACCOUNT_NAME, accountName);
410            }
411            lm.initLoader(LOADER_BOOKMARKS, args, this);
412            lm.initLoader(LOADER_ACCOUNTS, null, this);
413        } else {
414            // No account set, load them first
415            lm.initLoader(LOADER_ACCOUNTS_THEN_BOOKMARKS, null, this);
416        }
417
418        // Add our own listener in case there are favicons that have yet to be loaded.
419        CombinedBookmarkHistoryActivity.getIconListenerSet().addListener(this);
420
421        return root;
422    }
423
424    @Override
425    public void onReceivedIcon(String url, Bitmap icon) {
426        // A new favicon has been loaded, so let anything attached to the adapter know about it
427        // so new icons will be loaded.
428        mAdapter.notifyDataSetChanged();
429    }
430
431    @Override
432    public void onItemClick(AdapterView parent, View v, int position, long id) {
433        // It is possible that the view has been canceled when we get to
434        // this point as back has a higher priority
435        if (mCanceled) {
436            android.util.Log.e(LOGTAG, "item clicked when dismissing");
437            return;
438        }
439
440        if (mCreateShortcut) {
441            Intent intent = createShortcutIntent(position);
442            // the activity handles the intent in startActivityFromFragment
443            startActivity(intent);
444            return;
445        }
446
447        Cursor cursor = (Cursor) mAdapter.getItem(position);
448        boolean isFolder = cursor.getInt(BookmarksLoader.COLUMN_INDEX_IS_FOLDER) != 0;
449        if (!isFolder) {
450            mCallbacks.onUrlSelected(getUrl(position), false);
451        } else {
452            String title;
453            if (mFolderStack.size() != 0) {
454                title = cursor.getString(BookmarksLoader.COLUMN_INDEX_TITLE);
455            } else {
456                // TODO localize
457                title = "Bookmarks";
458            }
459            LoaderManager manager = getLoaderManager();
460            BookmarksLoader loader =
461                    (BookmarksLoader) ((Loader) manager.getLoader(LOADER_BOOKMARKS));
462            mFolderStack.push(new Pair(title, loader.getUri()));
463            Uri uri = ContentUris.withAppendedId(
464                    BrowserContract.Bookmarks.CONTENT_URI_DEFAULT_FOLDER, id);
465            loader.setUri(uri);
466            loader.forceLoad();
467        }
468    }
469
470    @Override
471    public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
472        Adapter adapter = parent.getAdapter();
473        String accountType = "com.google";
474        String accountName = adapter.getItem(position).toString();
475
476        // Remember the selection for later
477        PreferenceManager.getDefaultSharedPreferences(getActivity()).edit()
478                .putString(PREF_ACCOUNT_TYPE, accountType)
479                .putString(PREF_ACCOUNT_NAME, accountName)
480                .apply();
481
482        Bundle args = new Bundle();
483        args.putString(BookmarksLoader.ARG_ACCOUNT_TYPE, accountType);
484        args.putString(BookmarksLoader.ARG_ACCOUNT_NAME, accountName);
485        getLoaderManager().restartLoader(LOADER_BOOKMARKS, args, this);
486    }
487
488    @Override
489    public void onNothingSelected(AdapterView<?> parent) {
490        // Do nothing
491    }
492
493    private Intent createShortcutIntent(int position) {
494        Cursor cursor = (Cursor) mAdapter.getItem(position);
495        String url = cursor.getString(BookmarksLoader.COLUMN_INDEX_URL);
496        String title = cursor.getString(BookmarksLoader.COLUMN_INDEX_URL);
497        Bitmap touchIcon = getBitmap(cursor, BookmarksLoader.COLUMN_INDEX_TOUCH_ICON);
498        Bitmap favicon = getBitmap(cursor, BookmarksLoader.COLUMN_INDEX_FAVICON);
499        return BookmarkUtils.createAddToHomeIntent(getActivity(), url, title, touchIcon, favicon);
500    }
501
502    private void loadUrl(int position) {
503        mCallbacks.onUrlSelected(getUrl(position), false);
504    }
505
506    private void openInNewWindow(int position) {
507        mCallbacks.onUrlSelected(getUrl(position), true);
508    }
509
510    private void editBookmark(int position) {
511        Intent intent = new Intent(getActivity(), AddBookmarkPage.class);
512        Cursor cursor = (Cursor) mAdapter.getItem(position);
513        Bundle item = new Bundle();
514        item.putString(BrowserContract.Bookmarks.TITLE,
515                cursor.getString(BookmarksLoader.COLUMN_INDEX_TITLE));
516        item.putString(BrowserContract.Bookmarks.URL,
517                cursor.getString(BookmarksLoader.COLUMN_INDEX_URL));
518        byte[] data = cursor.getBlob(BookmarksLoader.COLUMN_INDEX_FAVICON);
519        if (data != null) {
520            item.putParcelable(BrowserContract.Bookmarks.FAVICON,
521                    BitmapFactory.decodeByteArray(data, 0, data.length));
522        }
523        item.putInt("id", cursor.getInt(BookmarksLoader.COLUMN_INDEX_ID));
524        intent.putExtra("bookmark", item);
525        startActivityForResult(intent, BOOKMARKS_SAVE);
526    }
527
528    @Override
529    public void onActivityResult(int requestCode, int resultCode, Intent data) {
530        switch(requestCode) {
531            case BOOKMARKS_SAVE:
532                if (resultCode == Activity.RESULT_OK) {
533                    Bundle extras;
534                    if (data != null && (extras = data.getExtras()) != null) {
535                        // If there are extras, then we need to save
536                        // the edited bookmark. This is done in updateRow()
537                        String title = extras.getString("title");
538                        String url = extras.getString("url");
539                        if (title != null && url != null) {
540                            updateRow(extras);
541                        }
542                    }
543                }
544                break;
545        }
546    }
547
548    /**
549     *  Update a row in the database with new information.
550     *  @param map  Bundle storing id, title and url of new information
551     */
552    public void updateRow(Bundle map) {
553
554        // Find the record
555        int id = map.getInt("id");
556        int position = -1;
557        Cursor cursor = mAdapter.getCursor();
558        for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {
559            if (cursor.getInt(BookmarksLoader.COLUMN_INDEX_ID) == id) {
560                position = cursor.getPosition();
561                break;
562            }
563        }
564        if (position < 0) {
565            return;
566        }
567
568        cursor.moveToPosition(position);
569        ContentValues values = new ContentValues();
570        String title = map.getString(BrowserContract.Bookmarks.TITLE);
571        if (!title.equals(cursor.getString(BookmarksLoader.COLUMN_INDEX_TITLE))) {
572            values.put(BrowserContract.Bookmarks.TITLE, title);
573        }
574        String url = map.getString(BrowserContract.Bookmarks.URL);
575        if (!url.equals(cursor.getString(BookmarksLoader.COLUMN_INDEX_URL))) {
576            values.put(BrowserContract.Bookmarks.URL, url);
577        }
578
579        if (map.getBoolean("invalidateThumbnail") == true) {
580            values.putNull(BrowserContract.Bookmarks.THUMBNAIL);
581        }
582
583        if (values.size() > 0) {
584            getActivity().getContentResolver().update(
585                    ContentUris.withAppendedId(BrowserContract.Bookmarks.CONTENT_URI, id),
586                    values, null, null);
587        }
588    }
589
590    private void displayRemoveBookmarkDialog(final int position) {
591        // Put up a dialog asking if the user really wants to
592        // delete the bookmark
593        Cursor cursor = (Cursor) mAdapter.getItem(position);
594        Context context = getActivity();
595        final ContentResolver resolver = context.getContentResolver();
596        final Uri uri = ContentUris.withAppendedId(BrowserContract.Bookmarks.CONTENT_URI,
597                cursor.getLong(BookmarksLoader.COLUMN_INDEX_ID));
598
599        new AlertDialog.Builder(context)
600                .setTitle(R.string.delete_bookmark)
601                .setIcon(android.R.drawable.ic_dialog_alert)
602                .setMessage(context.getString(R.string.delete_bookmark_warning,
603                        cursor.getString(BookmarksLoader.COLUMN_INDEX_TITLE)))
604                .setPositiveButton(R.string.ok,
605                        new DialogInterface.OnClickListener() {
606                            public void onClick(DialogInterface dialog, int whichButton) {
607                                resolver.delete(uri, null, null);
608                            }
609                        })
610                .setNegativeButton(R.string.cancel, null)
611                .show();
612    }
613
614    private String getUrl(int position) {
615        Cursor cursor = (Cursor) mAdapter.getItem(position);
616        return cursor.getString(BookmarksLoader.COLUMN_INDEX_URL);
617    }
618
619    private void copy(CharSequence text) {
620        ClipboardManager cm = (ClipboardManager) getActivity().getSystemService(
621                Context.CLIPBOARD_SERVICE);
622        cm.setPrimaryClip(ClipData.newRawUri(null, null, Uri.parse(text.toString())));
623    }
624}
625