BrowserBookmarksPage.java revision 0c90888c75eed12f6e2e14a9044faf50bd4af8ed
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.graphics.Bitmap; 24import android.net.Uri; 25import android.os.Bundle; 26import android.os.Handler; 27import android.os.Message; 28import android.os.ServiceManager; 29import android.provider.Browser; 30import android.text.IClipboard; 31import android.util.Log; 32import android.view.ContextMenu; 33import android.view.KeyEvent; 34import android.view.Menu; 35import android.view.MenuInflater; 36import android.view.MenuItem; 37import android.view.View; 38import android.view.ViewGroup; 39import android.view.ContextMenu.ContextMenuInfo; 40import android.widget.AdapterView; 41import android.widget.ListView; 42 43/** 44 * View showing the user's bookmarks in the browser. 45 */ 46public class BrowserBookmarksPage extends Activity implements 47 View.OnCreateContextMenuListener { 48 49 private BrowserBookmarksAdapter mBookmarksAdapter; 50 private static final int BOOKMARKS_SAVE = 1; 51 private boolean mMaxTabsOpen; 52 private BookmarkItem mContextHeader; 53 private AddNewBookmark mAddHeader; 54 private boolean mCanceled = false; 55 private boolean mCreateShortcut; 56 // XXX: There is no public string defining this intent so if Home changes 57 // the value, we have to update this string. 58 private static final String INSTALL_SHORTCUT = 59 "com.android.launcher.action.INSTALL_SHORTCUT"; 60 61 private final static String LOGTAG = "browser"; 62 63 64 @Override 65 public boolean onContextItemSelected(MenuItem item) { 66 // It is possible that the view has been canceled when we get to 67 // this point as back has a higher priority 68 if (mCanceled) { 69 return true; 70 } 71 AdapterView.AdapterContextMenuInfo i = 72 (AdapterView.AdapterContextMenuInfo)item.getMenuInfo(); 73 // If we have no menu info, we can't tell which item was selected. 74 if (i == null) { 75 return true; 76 } 77 78 switch (item.getItemId()) { 79 case R.id.new_context_menu_id: 80 saveCurrentPage(); 81 break; 82 case R.id.open_context_menu_id: 83 loadUrl(i.position); 84 break; 85 case R.id.edit_context_menu_id: 86 editBookmark(i.position); 87 break; 88 case R.id.shortcut_context_menu_id: 89 final Intent send = createShortcutIntent(getUrl(i.position), 90 getBookmarkTitle(i.position)); 91 send.setAction(INSTALL_SHORTCUT); 92 sendBroadcast(send); 93 break; 94 case R.id.delete_context_menu_id: 95 displayRemoveBookmarkDialog(i.position); 96 break; 97 case R.id.new_window_context_menu_id: 98 openInNewWindow(i.position); 99 break; 100 case R.id.send_context_menu_id: 101 Browser.sendString(BrowserBookmarksPage.this, getUrl(i.position)); 102 break; 103 case R.id.copy_url_context_menu_id: 104 copy(getUrl(i.position)); 105 106 default: 107 return super.onContextItemSelected(item); 108 } 109 return true; 110 } 111 112 @Override 113 public void onCreateContextMenu(ContextMenu menu, View v, 114 ContextMenuInfo menuInfo) { 115 AdapterView.AdapterContextMenuInfo i = 116 (AdapterView.AdapterContextMenuInfo) menuInfo; 117 118 MenuInflater inflater = getMenuInflater(); 119 inflater.inflate(R.menu.bookmarkscontext, menu); 120 121 if (0 == i.position) { 122 menu.setGroupVisible(R.id.CONTEXT_MENU, false); 123 if (mAddHeader == null) { 124 mAddHeader = new AddNewBookmark(BrowserBookmarksPage.this); 125 } else if (mAddHeader.getParent() != null) { 126 ((ViewGroup) mAddHeader.getParent()). 127 removeView(mAddHeader); 128 } 129 ((AddNewBookmark) i.targetView).copyTo(mAddHeader); 130 menu.setHeaderView(mAddHeader); 131 return; 132 } 133 menu.setGroupVisible(R.id.ADD_MENU, false); 134 BookmarkItem b = (BookmarkItem) i.targetView; 135 if (mContextHeader == null) { 136 mContextHeader = new BookmarkItem(BrowserBookmarksPage.this); 137 } else if (mContextHeader.getParent() != null) { 138 ((ViewGroup) mContextHeader.getParent()). 139 removeView(mContextHeader); 140 } 141 b.copyTo(mContextHeader); 142 menu.setHeaderView(mContextHeader); 143 144 if (mMaxTabsOpen) { 145 menu.findItem(R.id.new_window_context_menu_id).setVisible( 146 false); 147 } 148 } 149 150 /** 151 * Create a new BrowserBookmarksPage. 152 */ 153 @Override 154 protected void onCreate(Bundle icicle) { 155 super.onCreate(icicle); 156 157 setContentView(R.layout.browser_bookmarks_page); 158 setTitle(R.string.browser_bookmarks_page_bookmarks_text); 159 160 if (Intent.ACTION_CREATE_SHORTCUT.equals(getIntent().getAction())) { 161 mCreateShortcut = true; 162 } 163 164 mBookmarksAdapter = new BrowserBookmarksAdapter(this, 165 getIntent().getStringExtra("url"), mCreateShortcut); 166 mMaxTabsOpen = getIntent().getBooleanExtra("maxTabsOpen", false); 167 168 ListView listView = (ListView) findViewById(R.id.list); 169 listView.setAdapter(mBookmarksAdapter); 170 listView.setDrawSelectorOnTop(false); 171 listView.setVerticalScrollBarEnabled(true); 172 listView.setOnItemClickListener(mListener); 173 174 if (!mCreateShortcut) { 175 listView.setOnCreateContextMenuListener(this); 176 } 177 } 178 179 private static final int SAVE_CURRENT_PAGE = 1000; 180 private final Handler mHandler = new Handler() { 181 @Override 182 public void handleMessage(Message msg) { 183 if (msg.what == SAVE_CURRENT_PAGE) { 184 saveCurrentPage(); 185 } 186 } 187 }; 188 189 private AdapterView.OnItemClickListener mListener = new AdapterView.OnItemClickListener() { 190 public void onItemClick(AdapterView parent, View v, int position, long id) { 191 // It is possible that the view has been canceled when we get to 192 // this point as back has a higher priority 193 if (mCanceled) { 194 android.util.Log.e("browser", "item clicked when dismising"); 195 return; 196 } 197 if (!mCreateShortcut) { 198 if (0 == position) { 199 // XXX: Work-around for a framework issue. 200 mHandler.sendEmptyMessage(SAVE_CURRENT_PAGE); 201 } else { 202 loadUrl(position); 203 } 204 } else { 205 final Intent intent = createShortcutIntent(getUrl(position), 206 getBookmarkTitle(position)); 207 setResultToParent(RESULT_OK, intent); 208 finish(); 209 } 210 } 211 }; 212 213 private Intent createShortcutIntent(String url, String title) { 214 final Intent i = new Intent(); 215 i.putExtra(Intent.EXTRA_SHORTCUT_INTENT, new Intent(Intent.ACTION_VIEW, 216 Uri.parse(url))); 217 i.putExtra(Intent.EXTRA_SHORTCUT_NAME, title); 218 i.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, 219 Intent.ShortcutIconResource.fromContext(BrowserBookmarksPage.this, 220 R.drawable.ic_launcher_browser)); 221 // Do not allow duplicate items 222 i.putExtra("duplicate", false); 223 return i; 224 } 225 226 private void saveCurrentPage() { 227 Intent i = new Intent(BrowserBookmarksPage.this, 228 AddBookmarkPage.class); 229 i.putExtras(getIntent()); 230 startActivityForResult(i, BOOKMARKS_SAVE); 231 } 232 233 private void loadUrl(int position) { 234 Intent intent = (new Intent()).setAction(getUrl(position)); 235 setResultToParent(RESULT_OK, intent); 236 finish(); 237 } 238 239 @Override 240 public boolean onCreateOptionsMenu(Menu menu) { 241 boolean result = super.onCreateOptionsMenu(menu); 242 if (!mCreateShortcut) { 243 MenuInflater inflater = getMenuInflater(); 244 inflater.inflate(R.menu.bookmarks, menu); 245 return true; 246 } 247 return result; 248 } 249 250 @Override 251 public boolean onOptionsItemSelected(MenuItem item) { 252 switch (item.getItemId()) { 253 case R.id.new_context_menu_id: 254 saveCurrentPage(); 255 break; 256 257 default: 258 return super.onOptionsItemSelected(item); 259 } 260 return true; 261 } 262 263 private void openInNewWindow(int position) { 264 Bundle b = new Bundle(); 265 b.putBoolean("new_window", true); 266 setResultToParent(RESULT_OK, 267 (new Intent()).setAction(getUrl(position)).putExtras(b)); 268 269 finish(); 270 } 271 272 273 private void editBookmark(int position) { 274 Intent intent = new Intent(BrowserBookmarksPage.this, 275 AddBookmarkPage.class); 276 intent.putExtra("bookmark", getRow(position)); 277 startActivityForResult(intent, BOOKMARKS_SAVE); 278 } 279 280 @Override 281 protected void onActivityResult(int requestCode, int resultCode, 282 Intent data) { 283 switch(requestCode) { 284 case BOOKMARKS_SAVE: 285 if (resultCode == RESULT_OK) { 286 Bundle extras; 287 if (data != null && (extras = data.getExtras()) != null) { 288 // If there are extras, then we need to save 289 // the edited bookmark. This is done in updateRow() 290 String title = extras.getString("title"); 291 String url = extras.getString("url"); 292 if (title != null && url != null) { 293 mBookmarksAdapter.updateRow(extras); 294 } 295 } else { 296 // extras == null then a new bookmark was added to 297 // the database. 298 refreshList(); 299 } 300 } 301 break; 302 default: 303 break; 304 } 305 } 306 307 private void displayRemoveBookmarkDialog(int position) { 308 // Put up a dialog asking if the user really wants to 309 // delete the bookmark 310 final int deletePos = position; 311 new AlertDialog.Builder(this) 312 .setTitle(R.string.delete_bookmark) 313 .setIcon(android.R.drawable.ic_dialog_alert) 314 .setMessage(getText(R.string.delete_bookmark_warning).toString().replace( 315 "%s", getBookmarkTitle(deletePos))) 316 .setPositiveButton(R.string.ok, 317 new DialogInterface.OnClickListener() { 318 public void onClick(DialogInterface dialog, int whichButton) { 319 deleteBookmark(deletePos); 320 } 321 }) 322 .setNegativeButton(R.string.cancel, null) 323 .show(); 324 } 325 326 /** 327 * Refresh the shown list after the database has changed. 328 */ 329 public void refreshList() { 330 mBookmarksAdapter.refreshList(); 331 } 332 333 /** 334 * Return a hashmap representing the currently highlighted row. 335 */ 336 public Bundle getRow(int position) { 337 return mBookmarksAdapter.getRow(position); 338 } 339 340 /** 341 * Return the url of the currently highlighted row. 342 */ 343 public String getUrl(int position) { 344 return mBookmarksAdapter.getUrl(position); 345 } 346 347 private void copy(CharSequence text) { 348 try { 349 IClipboard clip = IClipboard.Stub.asInterface(ServiceManager.getService("clipboard")); 350 if (clip != null) { 351 clip.setClipboardText(text); 352 } 353 } catch (android.os.RemoteException e) { 354 Log.e(LOGTAG, "Copy failed", e); 355 } 356 } 357 358 public String getBookmarkTitle(int position) { 359 return mBookmarksAdapter.getTitle(position); 360 } 361 362 /** 363 * Delete the currently highlighted row. 364 */ 365 public void deleteBookmark(int position) { 366 mBookmarksAdapter.deleteRow(position); 367 } 368 369 public boolean dispatchKeyEvent(KeyEvent event) { 370 if (event.getKeyCode() == KeyEvent.KEYCODE_BACK && event.isDown()) { 371 setResultToParent(RESULT_CANCELED, null); 372 mCanceled = true; 373 } 374 return super.dispatchKeyEvent(event); 375 } 376 377 // This Activity is generally a sub-Activity of CombinedHistoryActivity. In 378 // that situation, we need to pass our result code up to our parent. 379 // However, if someone calls this Activity directly, then this has no 380 // parent, and it needs to set it on itself. 381 private void setResultToParent(int resultCode, Intent data) { 382 Activity a = getParent() == null ? this : getParent(); 383 a.setResult(resultCode, data); 384 } 385} 386