BrowserBookmarksPage.java revision 152794b84cd885cd0f219dd6baf6f167c128eae4
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.content.DialogInterface;
22import android.content.Intent;
23import android.content.SharedPreferences;
24import android.content.SharedPreferences.Editor;
25import android.graphics.Bitmap;
26import android.graphics.BitmapFactory;
27import android.graphics.Canvas;
28import android.graphics.Color;
29import android.graphics.Paint;
30import android.graphics.Path;
31import android.graphics.PorterDuff;
32import android.graphics.PorterDuffXfermode;
33import android.graphics.Rect;
34import android.graphics.RectF;
35import android.net.Uri;
36import android.os.Bundle;
37import android.os.Handler;
38import android.os.Message;
39import android.os.ServiceManager;
40import android.provider.Browser;
41import android.text.IClipboard;
42import android.util.Log;
43import android.view.ContextMenu;
44import android.view.Menu;
45import android.view.MenuInflater;
46import android.view.MenuItem;
47import android.view.View;
48import android.view.ViewGroup;
49import android.view.ContextMenu.ContextMenuInfo;
50import android.webkit.WebIconDatabase.IconListener;
51import android.widget.AdapterView;
52import android.widget.GridView;
53import android.widget.ListView;
54import android.widget.Toast;
55
56/*package*/ enum BookmarkViewMode { NONE, GRID, LIST }
57/**
58 *  View showing the user's bookmarks in the browser.
59 */
60public class BrowserBookmarksPage extends Activity implements
61        View.OnCreateContextMenuListener {
62
63    private BookmarkViewMode        mViewMode = BookmarkViewMode.NONE;
64    private GridView                mGridPage;
65    private ListView                mVerticalList;
66    private BrowserBookmarksAdapter mBookmarksAdapter;
67    private static final int        BOOKMARKS_SAVE = 1;
68    private boolean                 mDisableNewWindow;
69    private BookmarkItem            mContextHeader;
70    private AddNewBookmark          mAddHeader;
71    private boolean                 mCanceled = false;
72    private boolean                 mCreateShortcut;
73    private boolean                 mMostVisited;
74    private View                    mEmptyView;
75    private int                     mIconSize;
76    // XXX: There is no public string defining this intent so if Home changes
77    // the value, we have to update this string.
78    private static final String     INSTALL_SHORTCUT =
79            "com.android.launcher.action.INSTALL_SHORTCUT";
80
81    private final static String LOGTAG = "browser";
82    private final static String PREF_BOOKMARK_VIEW_MODE = "pref_bookmark_view_mode";
83    private final static String PREF_MOST_VISITED_VIEW_MODE = "pref_most_visited_view_mode";
84
85    @Override
86    public boolean onContextItemSelected(MenuItem item) {
87        // It is possible that the view has been canceled when we get to
88        // this point as back has a higher priority
89        if (mCanceled) {
90            return true;
91        }
92        AdapterView.AdapterContextMenuInfo i =
93            (AdapterView.AdapterContextMenuInfo)item.getMenuInfo();
94        // If we have no menu info, we can't tell which item was selected.
95        if (i == null) {
96            return true;
97        }
98
99        switch (item.getItemId()) {
100        case R.id.new_context_menu_id:
101            saveCurrentPage();
102            break;
103        case R.id.open_context_menu_id:
104            loadUrl(i.position);
105            break;
106        case R.id.edit_context_menu_id:
107            editBookmark(i.position);
108            break;
109        case R.id.shortcut_context_menu_id:
110            final Intent send = createShortcutIntent(i.position);
111            send.setAction(INSTALL_SHORTCUT);
112            sendBroadcast(send);
113            break;
114        case R.id.delete_context_menu_id:
115            if (mMostVisited) {
116                Browser.deleteFromHistory(getContentResolver(),
117                        getUrl(i.position));
118                refreshList();
119            } else {
120                displayRemoveBookmarkDialog(i.position);
121            }
122            break;
123        case R.id.new_window_context_menu_id:
124            openInNewWindow(i.position);
125            break;
126        case R.id.share_link_context_menu_id:
127            BrowserActivity.sharePage(BrowserBookmarksPage.this,
128                    mBookmarksAdapter.getTitle(i.position), getUrl(i.position),
129                    getFavicon(i.position),
130                    mBookmarksAdapter.getScreenshot(i.position));
131            break;
132        case R.id.copy_url_context_menu_id:
133            copy(getUrl(i.position));
134            break;
135        case R.id.homepage_context_menu_id:
136            BrowserSettings.getInstance().setHomePage(this,
137                    getUrl(i.position));
138            Toast.makeText(this, R.string.homepage_set,
139                    Toast.LENGTH_LONG).show();
140            break;
141        // Only for the Most visited page
142        case R.id.save_to_bookmarks_menu_id:
143            boolean isBookmark;
144            String name;
145            String url;
146            if (mViewMode == BookmarkViewMode.GRID) {
147                isBookmark = mBookmarksAdapter.getIsBookmark(i.position);
148                name = mBookmarksAdapter.getTitle(i.position);
149                url = mBookmarksAdapter.getUrl(i.position);
150            } else {
151                HistoryItem historyItem = ((HistoryItem) i.targetView);
152                isBookmark = historyItem.isBookmark();
153                name = historyItem.getName();
154                url = historyItem.getUrl();
155            }
156            // If the site is bookmarked, the item becomes remove from
157            // bookmarks.
158            if (isBookmark) {
159                Bookmarks.removeFromBookmarks(this, getContentResolver(), url, name);
160            } else {
161                Browser.saveBookmark(this, name, url);
162            }
163            break;
164        default:
165            return super.onContextItemSelected(item);
166        }
167        return true;
168    }
169
170    @Override
171    public void onCreateContextMenu(ContextMenu menu, View v,
172                ContextMenuInfo menuInfo) {
173            AdapterView.AdapterContextMenuInfo i =
174                    (AdapterView.AdapterContextMenuInfo) menuInfo;
175
176            MenuInflater inflater = getMenuInflater();
177            if (mMostVisited) {
178                inflater.inflate(R.menu.historycontext, menu);
179            } else {
180                inflater.inflate(R.menu.bookmarkscontext, menu);
181            }
182
183            if (0 == i.position && !mMostVisited) {
184                menu.setGroupVisible(R.id.CONTEXT_MENU, false);
185                if (mAddHeader == null) {
186                    mAddHeader = new AddNewBookmark(BrowserBookmarksPage.this);
187                } else if (mAddHeader.getParent() != null) {
188                    ((ViewGroup) mAddHeader.getParent()).
189                            removeView(mAddHeader);
190                }
191                mAddHeader.setUrl(getIntent().getStringExtra("url"));
192                menu.setHeaderView(mAddHeader);
193                return;
194            }
195            if (mMostVisited) {
196                if ((mViewMode == BookmarkViewMode.LIST
197                        && ((HistoryItem) i.targetView).isBookmark())
198                        || mBookmarksAdapter.getIsBookmark(i.position)) {
199                    MenuItem item = menu.findItem(
200                            R.id.save_to_bookmarks_menu_id);
201                    item.setTitle(R.string.remove_from_bookmarks);
202                }
203            } else {
204                // The historycontext menu has no ADD_MENU group.
205                menu.setGroupVisible(R.id.ADD_MENU, false);
206            }
207            if (mDisableNewWindow) {
208                menu.findItem(R.id.new_window_context_menu_id).setVisible(
209                        false);
210            }
211            if (mContextHeader == null) {
212                mContextHeader = new BookmarkItem(BrowserBookmarksPage.this);
213            } else if (mContextHeader.getParent() != null) {
214                ((ViewGroup) mContextHeader.getParent()).
215                        removeView(mContextHeader);
216            }
217            if (mViewMode == BookmarkViewMode.GRID) {
218                mBookmarksAdapter.populateBookmarkItem(mContextHeader,
219                        i.position);
220            } else {
221                BookmarkItem b = (BookmarkItem) i.targetView;
222                b.copyTo(mContextHeader);
223            }
224            menu.setHeaderView(mContextHeader);
225        }
226
227    /**
228     *  Create a new BrowserBookmarksPage.
229     */
230    @Override
231    protected void onCreate(Bundle icicle) {
232        super.onCreate(icicle);
233
234        // Grab the app icon size as a resource.
235        mIconSize = getResources().getDimensionPixelSize(
236                android.R.dimen.app_icon_size);
237
238        if (Intent.ACTION_CREATE_SHORTCUT.equals(getIntent().getAction())) {
239            mCreateShortcut = true;
240        }
241        mDisableNewWindow = getIntent().getBooleanExtra("disable_new_window",
242                false);
243        mMostVisited = getIntent().getBooleanExtra("mostVisited", false);
244
245        if (mCreateShortcut) {
246            setTitle(R.string.browser_bookmarks_page_bookmarks_text);
247        }
248        mHandler.obtainMessage(CREATE_ADAPTER).sendToTarget();
249
250        setContentView(R.layout.empty_history);
251        mEmptyView = findViewById(R.id.empty_view);
252        mEmptyView.setVisibility(View.GONE);
253
254        SharedPreferences p = getPreferences(MODE_PRIVATE);
255
256        // See if the user has set a preference for the view mode of their
257        // bookmarks. Otherwise default to grid mode.
258        BookmarkViewMode preference = BookmarkViewMode.NONE;
259        if (mMostVisited) {
260            // For the most visited page, only use list mode.
261            preference = BookmarkViewMode.LIST;
262        } else {
263            preference = BookmarkViewMode.values()[p.getInt(
264                    PREF_BOOKMARK_VIEW_MODE, BookmarkViewMode.GRID.ordinal())];
265        }
266        switchViewMode(preference);
267    }
268
269    @Override
270    protected void onDestroy() {
271        mHandler.removeCallbacksAndMessages(null);
272        super.onDestroy();
273    }
274
275    /**
276     *  Set the ContentView to be either the grid of thumbnails or the vertical
277     *  list.
278     */
279    private void switchViewMode(BookmarkViewMode viewMode) {
280        if (mViewMode == viewMode) {
281            return;
282        }
283
284        mViewMode = viewMode;
285
286        // Update the preferences to make the new view mode sticky.
287        Editor ed = getPreferences(MODE_PRIVATE).edit();
288        if (mMostVisited) {
289            ed.putInt(PREF_MOST_VISITED_VIEW_MODE, mViewMode.ordinal());
290        } else {
291            ed.putInt(PREF_BOOKMARK_VIEW_MODE, mViewMode.ordinal());
292        }
293        ed.commit();
294
295        if (mBookmarksAdapter != null) {
296            mBookmarksAdapter.switchViewMode(viewMode);
297        }
298        if (mViewMode == BookmarkViewMode.GRID) {
299            if (mGridPage == null) {
300                mGridPage = new GridView(this);
301                if (mBookmarksAdapter != null) {
302                    mGridPage.setAdapter(mBookmarksAdapter);
303                }
304                mGridPage.setOnItemClickListener(mListener);
305                mGridPage.setNumColumns(GridView.AUTO_FIT);
306                mGridPage.setColumnWidth(
307                        BrowserActivity.getDesiredThumbnailWidth(this));
308                mGridPage.setFocusable(true);
309                mGridPage.setFocusableInTouchMode(true);
310                mGridPage.setSelector(android.R.drawable.gallery_thumb);
311                float density = getResources().getDisplayMetrics().density;
312                mGridPage.setVerticalSpacing((int) (14 * density));
313                mGridPage.setHorizontalSpacing((int) (8 * density));
314                mGridPage.setStretchMode(GridView.STRETCH_SPACING);
315                mGridPage.setScrollBarStyle(View.SCROLLBARS_INSIDE_INSET);
316                mGridPage.setDrawSelectorOnTop(true);
317                if (mMostVisited) {
318                    mGridPage.setEmptyView(mEmptyView);
319                }
320                if (!mCreateShortcut) {
321                    mGridPage.setOnCreateContextMenuListener(this);
322                }
323            }
324            addContentView(mGridPage, FULL_SCREEN_PARAMS);
325            if (mVerticalList != null) {
326                ViewGroup parent = (ViewGroup) mVerticalList.getParent();
327                if (parent != null) {
328                    parent.removeView(mVerticalList);
329                }
330            }
331        } else {
332            if (null == mVerticalList) {
333                ListView listView = new ListView(this);
334                if (mBookmarksAdapter != null) {
335                    listView.setAdapter(mBookmarksAdapter);
336                }
337                listView.setDrawSelectorOnTop(false);
338                listView.setVerticalScrollBarEnabled(true);
339                listView.setOnItemClickListener(mListener);
340                if (mMostVisited) {
341                    listView.setEmptyView(mEmptyView);
342                }
343                if (!mCreateShortcut) {
344                    listView.setOnCreateContextMenuListener(this);
345                }
346                mVerticalList = listView;
347            }
348            addContentView(mVerticalList, FULL_SCREEN_PARAMS);
349            if (mGridPage != null) {
350                ViewGroup parent = (ViewGroup) mGridPage.getParent();
351                if (parent != null) {
352                    parent.removeView(mGridPage);
353                }
354            }
355        }
356    }
357
358    private static final ViewGroup.LayoutParams FULL_SCREEN_PARAMS
359            = new ViewGroup.LayoutParams(
360            ViewGroup.LayoutParams.MATCH_PARENT,
361            ViewGroup.LayoutParams.MATCH_PARENT);
362
363    private static final int SAVE_CURRENT_PAGE = 1000;
364    private static final int CREATE_ADAPTER = 1001;
365    private final Handler mHandler = new Handler() {
366        @Override
367        public void handleMessage(Message msg) {
368            switch (msg.what) {
369                case SAVE_CURRENT_PAGE:
370                    saveCurrentPage();
371                    break;
372                case CREATE_ADAPTER:
373                    Intent intent = getIntent();
374                    mBookmarksAdapter = new BrowserBookmarksAdapter(
375                            BrowserBookmarksPage.this,
376                            intent.getStringExtra("url"),
377                            intent.getStringExtra("title"),
378                            (Bitmap) intent.getParcelableExtra("thumbnail"),
379                            mCreateShortcut,
380                            mMostVisited);
381                    mBookmarksAdapter.switchViewMode(mViewMode);
382                    if (mGridPage != null) {
383                        mGridPage.setAdapter(mBookmarksAdapter);
384                    }
385                    if (mVerticalList != null) {
386                        mVerticalList.setAdapter(mBookmarksAdapter);
387                    }
388                    // Add our own listener in case there are favicons that
389                    // have yet to be loaded.
390                    if (mMostVisited) {
391                        IconListener listener = new IconListener() {
392                            public void onReceivedIcon(String url,
393                                    Bitmap icon) {
394                                if (mGridPage != null) {
395                                    mGridPage.setAdapter(mBookmarksAdapter);
396                                }
397                                if (mVerticalList != null) {
398                                    mVerticalList.setAdapter(mBookmarksAdapter);
399                                }
400                            }
401                        };
402                        CombinedBookmarkHistoryActivity.getIconListenerSet()
403                                .addListener(listener);
404                    }
405                    break;
406            }
407        }
408    };
409
410    private AdapterView.OnItemClickListener mListener = new AdapterView.OnItemClickListener() {
411        public void onItemClick(AdapterView parent, View v, int position, long id) {
412            // It is possible that the view has been canceled when we get to
413            // this point as back has a higher priority
414            if (mCanceled) {
415                android.util.Log.e(LOGTAG, "item clicked when dismissing");
416                return;
417            }
418            if (!mCreateShortcut) {
419                if (0 == position && !mMostVisited) {
420                    // XXX: Work-around for a framework issue.
421                    mHandler.sendEmptyMessage(SAVE_CURRENT_PAGE);
422                } else {
423                    loadUrl(position);
424                }
425            } else {
426                final Intent intent = createShortcutIntent(position);
427                setResultToParent(RESULT_OK, intent);
428                finish();
429            }
430        }
431    };
432
433    private Intent createShortcutIntent(int position) {
434        String url = getUrl(position);
435        String title = getBookmarkTitle(position);
436        Bitmap touchIcon = getTouchIcon(position);
437
438        final Intent i = new Intent();
439        final Intent shortcutIntent = new Intent(Intent.ACTION_VIEW,
440                Uri.parse(url));
441        long urlHash = url.hashCode();
442        long uniqueId = (urlHash << 32) | shortcutIntent.hashCode();
443        shortcutIntent.putExtra(Browser.EXTRA_APPLICATION_ID,
444                Long.toString(uniqueId));
445        i.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent);
446        i.putExtra(Intent.EXTRA_SHORTCUT_NAME, title);
447        // Use the apple-touch-icon if available
448        if (touchIcon != null) {
449            // Make a copy so we can modify the pixels.  We can't use
450            // createScaledBitmap or copy since they will preserve the config
451            // and lose the ability to add alpha.
452            Bitmap bm = Bitmap.createBitmap(mIconSize, mIconSize,
453                    Bitmap.Config.ARGB_8888);
454            Canvas canvas = new Canvas(bm);
455            Rect src = new Rect(0, 0, touchIcon.getWidth(),
456                                touchIcon.getHeight());
457            Rect dest = new Rect(0, 0, bm.getWidth(), bm.getHeight());
458
459            // Paint used for scaling the bitmap and drawing the rounded rect.
460            Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
461            paint.setFilterBitmap(true);
462            canvas.drawBitmap(touchIcon, src, dest, paint);
463
464            // Construct a path from a round rect. This will allow drawing with
465            // an inverse fill so we can punch a hole using the round rect.
466            Path path = new Path();
467            path.setFillType(Path.FillType.INVERSE_WINDING);
468            RectF rect = new RectF(0, 0, bm.getWidth(), bm.getHeight());
469            rect.inset(1, 1);
470            path.addRoundRect(rect, 8f, 8f, Path.Direction.CW);
471
472            // Reuse the paint and clear the outside of the rectangle.
473            paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
474            canvas.drawPath(path, paint);
475
476            i.putExtra(Intent.EXTRA_SHORTCUT_ICON, bm);
477        } else {
478            Bitmap favicon = getFavicon(position);
479            if (favicon == null) {
480                i.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE,
481                        Intent.ShortcutIconResource.fromContext(
482                                BrowserBookmarksPage.this,
483                                R.drawable.ic_launcher_shortcut_browser_bookmark));
484            } else {
485                Bitmap icon = BitmapFactory.decodeResource(getResources(),
486                        R.drawable.ic_launcher_shortcut_browser_bookmark_icon);
487
488                // Make a copy of the regular icon so we can modify the pixels.
489                Bitmap copy = icon.copy(Bitmap.Config.ARGB_8888, true);
490                Canvas canvas = new Canvas(copy);
491
492                // Make a Paint for the white background rectangle and for
493                // filtering the favicon.
494                Paint p = new Paint(Paint.ANTI_ALIAS_FLAG
495                        | Paint.FILTER_BITMAP_FLAG);
496                p.setStyle(Paint.Style.FILL_AND_STROKE);
497                p.setColor(Color.WHITE);
498
499                final float density =
500                        getResources().getDisplayMetrics().density;
501                // Create a rectangle that is slightly wider than the favicon
502                final float iconSize = 16 * density; // 16x16 favicon
503                final float padding = 2 * density; // white padding around icon
504                final float rectSize = iconSize + 2 * padding;
505
506                final Rect iconBounds =
507                        new Rect(0, 0, icon.getWidth(), icon.getHeight());
508                final float x = iconBounds.exactCenterX() - (rectSize / 2);
509                // Note: Subtract 2 dip from the y position since the box is
510                // slightly higher than center. Use padding since it is already
511                // 2 * density.
512                final float y = iconBounds.exactCenterY() - (rectSize / 2)
513                        - padding;
514                RectF r = new RectF(x, y, x + rectSize, y + rectSize);
515
516                // Draw a white rounded rectangle behind the favicon
517                canvas.drawRoundRect(r, 2, 2, p);
518
519                // Draw the favicon in the same rectangle as the rounded
520                // rectangle but inset by the padding
521                // (results in a 16x16 favicon).
522                r.inset(padding, padding);
523                canvas.drawBitmap(favicon, null, r, p);
524                i.putExtra(Intent.EXTRA_SHORTCUT_ICON, copy);
525            }
526        }
527        // Do not allow duplicate items
528        i.putExtra("duplicate", false);
529        return i;
530    }
531
532    private void saveCurrentPage() {
533        Intent i = new Intent(BrowserBookmarksPage.this,
534                AddBookmarkPage.class);
535        i.putExtras(getIntent());
536        startActivityForResult(i, BOOKMARKS_SAVE);
537    }
538
539    private void loadUrl(int position) {
540        Intent intent = (new Intent()).setAction(getUrl(position));
541        setResultToParent(RESULT_OK, intent);
542        finish();
543    }
544
545    @Override
546    public boolean onCreateOptionsMenu(Menu menu) {
547        boolean result = super.onCreateOptionsMenu(menu);
548        if (!mCreateShortcut && !mMostVisited) {
549            MenuInflater inflater = getMenuInflater();
550            inflater.inflate(R.menu.bookmarks, menu);
551            return true;
552        }
553        return result;
554    }
555
556    @Override
557    public boolean onPrepareOptionsMenu(Menu menu) {
558        boolean result = super.onPrepareOptionsMenu(menu);
559        if (mCreateShortcut || mMostVisited || mBookmarksAdapter == null
560                || mBookmarksAdapter.getCount() == 0) {
561            // No need to show the menu if there are no items.
562            return result;
563        }
564        MenuItem switchItem = menu.findItem(R.id.switch_mode_menu_id);
565        int titleResId;
566        int iconResId;
567        if (mViewMode == BookmarkViewMode.GRID) {
568            titleResId = R.string.switch_to_list;
569            iconResId = R.drawable.ic_menu_list;
570        } else {
571            titleResId = R.string.switch_to_thumbnails;
572            iconResId = R.drawable.ic_menu_thumbnail;
573        }
574        switchItem.setTitle(titleResId);
575        switchItem.setIcon(iconResId);
576        return true;
577    }
578
579    @Override
580    public boolean onOptionsItemSelected(MenuItem item) {
581        switch (item.getItemId()) {
582        case R.id.new_context_menu_id:
583            saveCurrentPage();
584            break;
585
586        case R.id.switch_mode_menu_id:
587            if (mViewMode == BookmarkViewMode.GRID) {
588                switchViewMode(BookmarkViewMode.LIST);
589            } else {
590                switchViewMode(BookmarkViewMode.GRID);
591            }
592            break;
593
594        default:
595            return super.onOptionsItemSelected(item);
596        }
597        return true;
598    }
599
600    private void openInNewWindow(int position) {
601        Bundle b = new Bundle();
602        b.putBoolean("new_window", true);
603        setResultToParent(RESULT_OK,
604                (new Intent()).setAction(getUrl(position)).putExtras(b));
605
606        finish();
607    }
608
609
610    private void editBookmark(int position) {
611        Intent intent = new Intent(BrowserBookmarksPage.this,
612            AddBookmarkPage.class);
613        intent.putExtra("bookmark", getRow(position));
614        startActivityForResult(intent, BOOKMARKS_SAVE);
615    }
616
617    @Override
618    protected void onActivityResult(int requestCode, int resultCode,
619                                    Intent data) {
620        switch(requestCode) {
621            case BOOKMARKS_SAVE:
622                if (resultCode == RESULT_OK) {
623                    Bundle extras;
624                    if (data != null && (extras = data.getExtras()) != null) {
625                        // If there are extras, then we need to save
626                        // the edited bookmark. This is done in updateRow()
627                        String title = extras.getString("title");
628                        String url = extras.getString("url");
629                        if (title != null && url != null) {
630                            mBookmarksAdapter.updateRow(extras);
631                        }
632                    } else {
633                        // extras == null then a new bookmark was added to
634                        // the database.
635                        refreshList();
636                    }
637                }
638                break;
639            default:
640                break;
641        }
642    }
643
644    private void displayRemoveBookmarkDialog(int position) {
645        // Put up a dialog asking if the user really wants to
646        // delete the bookmark
647        final int deletePos = position;
648        new AlertDialog.Builder(this)
649                .setTitle(R.string.delete_bookmark)
650                .setIcon(android.R.drawable.ic_dialog_alert)
651                .setMessage(getText(R.string.delete_bookmark_warning).toString().replace(
652                        "%s", getBookmarkTitle(deletePos)))
653                .setPositiveButton(R.string.ok,
654                        new DialogInterface.OnClickListener() {
655                            public void onClick(DialogInterface dialog, int whichButton) {
656                                deleteBookmark(deletePos);
657                            }
658                        })
659                .setNegativeButton(R.string.cancel, null)
660                .show();
661    }
662
663    /**
664     *  Refresh the shown list after the database has changed.
665     */
666    private void refreshList() {
667        mBookmarksAdapter.refreshList();
668    }
669
670    /**
671     *  Return a hashmap representing the currently highlighted row.
672     */
673    public Bundle getRow(int position) {
674        return mBookmarksAdapter == null ? null
675                : mBookmarksAdapter.getRow(position);
676    }
677
678    /**
679     *  Return the url of the currently highlighted row.
680     */
681    public String getUrl(int position) {
682        return mBookmarksAdapter == null ? null
683                : mBookmarksAdapter.getUrl(position);
684    }
685
686    /**
687     * Return the favicon of the currently highlighted row.
688     */
689    public Bitmap getFavicon(int position) {
690        return mBookmarksAdapter == null ? null
691                : mBookmarksAdapter.getFavicon(position);
692    }
693
694    private Bitmap getTouchIcon(int position) {
695        return mBookmarksAdapter == null ? null
696                : mBookmarksAdapter.getTouchIcon(position);
697    }
698
699    private void copy(CharSequence text) {
700        try {
701            IClipboard clip = IClipboard.Stub.asInterface(ServiceManager.getService("clipboard"));
702            if (clip != null) {
703                clip.setClipboardText(text);
704            }
705        } catch (android.os.RemoteException e) {
706            Log.e(LOGTAG, "Copy failed", e);
707        }
708    }
709
710    public String getBookmarkTitle(int position) {
711        return mBookmarksAdapter == null ? null
712                : mBookmarksAdapter.getTitle(position);
713    }
714
715    /**
716     *  Delete the currently highlighted row.
717     */
718    public void deleteBookmark(int position) {
719        if (mBookmarksAdapter == null) return;
720        mBookmarksAdapter.deleteRow(position);
721    }
722
723    @Override
724    public void onBackPressed() {
725        setResultToParent(RESULT_CANCELED, null);
726        mCanceled = true;
727        super.onBackPressed();
728    }
729
730    // This Activity is generally a sub-Activity of
731    // CombinedBookmarkHistoryActivity. In that situation, we need to pass our
732    // result code up to our parent. However, if someone calls this Activity
733    // directly, then this has no parent, and it needs to set it on itself.
734    private void setResultToParent(int resultCode, Intent data) {
735        Activity parent = getParent();
736        if (parent == null) {
737            setResult(resultCode, data);
738        } else {
739            ((CombinedBookmarkHistoryActivity) parent).setResultFromChild(
740                    resultCode, data);
741        }
742    }
743}
744