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.commit(); 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