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