blob: 7dd85aae42e8d4d8c4019137a30ff215d4014f8a [file] [log] [blame]
The Android Open Source Project0c908882009-03-03 19:32:16 -08001/*
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;
Ben Murdoch328ea872009-09-16 13:33:29 +010023import android.content.SharedPreferences;
24import android.content.SharedPreferences.Editor;
The Android Open Source Project0c908882009-03-03 19:32:16 -080025import android.graphics.Bitmap;
Patrick Scotte09761e2009-03-24 20:43:37 -070026import android.graphics.BitmapFactory;
27import android.graphics.Canvas;
28import android.graphics.Color;
29import android.graphics.Paint;
Patrick Scott3918d442009-08-04 13:22:29 -040030import android.graphics.Path;
31import android.graphics.PorterDuff;
32import android.graphics.PorterDuffXfermode;
Patrick Scottc0fdde92010-02-25 14:40:11 -050033import android.graphics.Rect;
Patrick Scotte09761e2009-03-24 20:43:37 -070034import android.graphics.RectF;
The Android Open Source Project0c908882009-03-03 19:32:16 -080035import 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;
The Android Open Source Project0c908882009-03-03 19:32:16 -080044import android.view.Menu;
45import android.view.MenuInflater;
46import android.view.MenuItem;
47import android.view.View;
48import android.view.ViewGroup;
49import android.view.ContextMenu.ContextMenuInfo;
Patrick Scottc1cf63a2010-03-09 16:02:08 -050050import android.webkit.WebIconDatabase.IconListener;
The Android Open Source Project0c908882009-03-03 19:32:16 -080051import android.widget.AdapterView;
Leon Scroggins89c6d362009-07-15 16:54:37 -040052import android.widget.GridView;
The Android Open Source Project0c908882009-03-03 19:32:16 -080053import android.widget.ListView;
Leon Scrogginsfeb941d2009-05-28 17:27:38 -040054import android.widget.Toast;
The Android Open Source Project0c908882009-03-03 19:32:16 -080055
Ben Murdoch328ea872009-09-16 13:33:29 +010056/*package*/ enum BookmarkViewMode { NONE, GRID, LIST }
The Android Open Source Project0c908882009-03-03 19:32:16 -080057/**
58 * View showing the user's bookmarks in the browser.
59 */
Nicolas Catania095292f2010-03-15 09:00:14 -070060public class BrowserBookmarksPage extends Activity implements
The Android Open Source Project0c908882009-03-03 19:32:16 -080061 View.OnCreateContextMenuListener {
62
Ben Murdoch328ea872009-09-16 13:33:29 +010063 private BookmarkViewMode mViewMode = BookmarkViewMode.NONE;
Leon Scroggins89c6d362009-07-15 16:54:37 -040064 private GridView mGridPage;
Leon Scrogginsea002572009-11-24 15:21:18 -050065 private ListView mVerticalList;
The Android Open Source Project0c908882009-03-03 19:32:16 -080066 private BrowserBookmarksAdapter mBookmarksAdapter;
67 private static final int BOOKMARKS_SAVE = 1;
Leon Scroggins190095d2009-08-17 17:01:38 -040068 private boolean mDisableNewWindow;
The Android Open Source Project0c908882009-03-03 19:32:16 -080069 private BookmarkItem mContextHeader;
70 private AddNewBookmark mAddHeader;
71 private boolean mCanceled = false;
72 private boolean mCreateShortcut;
Leon Scrogginsa5d669e2009-08-05 14:07:58 -040073 private boolean mMostVisited;
74 private View mEmptyView;
Patrick Scott152794b2010-03-30 13:57:29 -040075 private int mIconSize;
The Android Open Source Project0c908882009-03-03 19:32:16 -080076 // 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";
Nicolas Catania095292f2010-03-15 09:00:14 -070080
The Android Open Source Project0c908882009-03-03 19:32:16 -080081 private final static String LOGTAG = "browser";
Ben Murdoch328ea872009-09-16 13:33:29 +010082 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";
The Android Open Source Project0c908882009-03-03 19:32:16 -080084
85 @Override
86 public boolean onContextItemSelected(MenuItem item) {
87 // It is possible that the view has been canceled when we get to
Nicolas Catania095292f2010-03-15 09:00:14 -070088 // this point as back has a higher priority
The Android Open Source Project0c908882009-03-03 19:32:16 -080089 if (mCanceled) {
90 return true;
91 }
Nicolas Catania095292f2010-03-15 09:00:14 -070092 AdapterView.AdapterContextMenuInfo i =
The Android Open Source Project0c908882009-03-03 19:32:16 -080093 (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 }
Nicolas Catania095292f2010-03-15 09:00:14 -070098
The Android Open Source Project0c908882009-03-03 19:32:16 -080099 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:
Patrick Scott3918d442009-08-04 13:22:29 -0400110 final Intent send = createShortcutIntent(i.position);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800111 send.setAction(INSTALL_SHORTCUT);
112 sendBroadcast(send);
113 break;
114 case R.id.delete_context_menu_id:
Leon Scrogginsa5d669e2009-08-05 14:07:58 -0400115 if (mMostVisited) {
116 Browser.deleteFromHistory(getContentResolver(),
117 getUrl(i.position));
118 refreshList();
119 } else {
120 displayRemoveBookmarkDialog(i.position);
121 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800122 break;
123 case R.id.new_window_context_menu_id:
124 openInNewWindow(i.position);
125 break;
Leon Scrogginsa5d669e2009-08-05 14:07:58 -0400126 case R.id.share_link_context_menu_id:
Leon Scroggins96afcb12009-12-10 12:35:56 -0500127 BrowserActivity.sharePage(BrowserBookmarksPage.this,
128 mBookmarksAdapter.getTitle(i.position), getUrl(i.position),
129 getFavicon(i.position),
130 mBookmarksAdapter.getScreenshot(i.position));
The Android Open Source Project0c908882009-03-03 19:32:16 -0800131 break;
132 case R.id.copy_url_context_menu_id:
133 copy(getUrl(i.position));
Leon Scrogginsfeb941d2009-05-28 17:27:38 -0400134 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;
Leon Scrogginsa5d669e2009-08-05 14:07:58 -0400141 // Only for the Most visited page
142 case R.id.save_to_bookmarks_menu_id:
Leon Scrogginsc1f57592009-08-14 14:16:10 -0400143 boolean isBookmark;
144 String name;
145 String url;
Ben Murdoch328ea872009-09-16 13:33:29 +0100146 if (mViewMode == BookmarkViewMode.GRID) {
Leon Scrogginsc1f57592009-08-14 14:16:10 -0400147 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 }
Leon Scrogginsa5d669e2009-08-05 14:07:58 -0400156 // If the site is bookmarked, the item becomes remove from
157 // bookmarks.
Leon Scrogginsc1f57592009-08-14 14:16:10 -0400158 if (isBookmark) {
Andrei Popescuc9526192009-09-23 15:52:16 +0100159 Bookmarks.removeFromBookmarks(this, getContentResolver(), url, name);
Leon Scrogginsa5d669e2009-08-05 14:07:58 -0400160 } else {
Leon Scrogginsc1f57592009-08-14 14:16:10 -0400161 Browser.saveBookmark(this, name, url);
Leon Scrogginsa5d669e2009-08-05 14:07:58 -0400162 }
163 break;
The Android Open Source Project0c908882009-03-03 19:32:16 -0800164 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) {
Nicolas Catania095292f2010-03-15 09:00:14 -0700173 AdapterView.AdapterContextMenuInfo i =
The Android Open Source Project0c908882009-03-03 19:32:16 -0800174 (AdapterView.AdapterContextMenuInfo) menuInfo;
175
176 MenuInflater inflater = getMenuInflater();
Leon Scrogginsa5d669e2009-08-05 14:07:58 -0400177 if (mMostVisited) {
178 inflater.inflate(R.menu.historycontext, menu);
179 } else {
180 inflater.inflate(R.menu.bookmarkscontext, menu);
181 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800182
Leon Scrogginsa5d669e2009-08-05 14:07:58 -0400183 if (0 == i.position && !mMostVisited) {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800184 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 }
Leon Scroggins892df312009-07-14 14:48:02 -0400191 mAddHeader.setUrl(getIntent().getStringExtra("url"));
The Android Open Source Project0c908882009-03-03 19:32:16 -0800192 menu.setHeaderView(mAddHeader);
193 return;
194 }
Leon Scrogginsa5d669e2009-08-05 14:07:58 -0400195 if (mMostVisited) {
Ben Murdoch328ea872009-09-16 13:33:29 +0100196 if ((mViewMode == BookmarkViewMode.LIST
197 && ((HistoryItem) i.targetView).isBookmark())
Leon Scrogginsa5d669e2009-08-05 14:07:58 -0400198 || 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 }
Leon Scroggins190095d2009-08-17 17:01:38 -0400207 if (mDisableNewWindow) {
Leon Scroggins892df312009-07-14 14:48:02 -0400208 menu.findItem(R.id.new_window_context_menu_id).setVisible(
209 false);
210 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800211 if (mContextHeader == null) {
212 mContextHeader = new BookmarkItem(BrowserBookmarksPage.this);
213 } else if (mContextHeader.getParent() != null) {
214 ((ViewGroup) mContextHeader.getParent()).
215 removeView(mContextHeader);
216 }
Ben Murdoch328ea872009-09-16 13:33:29 +0100217 if (mViewMode == BookmarkViewMode.GRID) {
Leon Scroggins892df312009-07-14 14:48:02 -0400218 mBookmarksAdapter.populateBookmarkItem(mContextHeader,
219 i.position);
220 } else {
221 BookmarkItem b = (BookmarkItem) i.targetView;
222 b.copyTo(mContextHeader);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800223 }
Leon Scroggins892df312009-07-14 14:48:02 -0400224 menu.setHeaderView(mContextHeader);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800225 }
226
227 /**
228 * Create a new BrowserBookmarksPage.
Nicolas Catania095292f2010-03-15 09:00:14 -0700229 */
The Android Open Source Project0c908882009-03-03 19:32:16 -0800230 @Override
231 protected void onCreate(Bundle icicle) {
232 super.onCreate(icicle);
233
Patrick Scott152794b2010-03-30 13:57:29 -0400234 // Grab the app icon size as a resource.
235 mIconSize = getResources().getDimensionPixelSize(
236 android.R.dimen.app_icon_size);
237
The Android Open Source Project0c908882009-03-03 19:32:16 -0800238 if (Intent.ACTION_CREATE_SHORTCUT.equals(getIntent().getAction())) {
239 mCreateShortcut = true;
240 }
Leon Scroggins190095d2009-08-17 17:01:38 -0400241 mDisableNewWindow = getIntent().getBooleanExtra("disable_new_window",
242 false);
Leon Scrogginsa5d669e2009-08-05 14:07:58 -0400243 mMostVisited = getIntent().getBooleanExtra("mostVisited", false);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800244
Leon Scrogginsa5d669e2009-08-05 14:07:58 -0400245 if (mCreateShortcut) {
246 setTitle(R.string.browser_bookmarks_page_bookmarks_text);
247 }
Leon Scrogginsea002572009-11-24 15:21:18 -0500248 mHandler.obtainMessage(CREATE_ADAPTER).sendToTarget();
Leon Scrogginsd87f85e2009-08-18 14:13:31 -0400249
250 setContentView(R.layout.empty_history);
251 mEmptyView = findViewById(R.id.empty_view);
252 mEmptyView.setVisibility(View.GONE);
253
Ben Murdoch328ea872009-09-16 13:33:29 +0100254 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) {
Leon Scrogginsb3968bb2009-10-16 09:04:16 -0400260 // For the most visited page, only use list mode.
261 preference = BookmarkViewMode.LIST;
Ben Murdoch328ea872009-09-16 13:33:29 +0100262 } else {
263 preference = BookmarkViewMode.values()[p.getInt(
264 PREF_BOOKMARK_VIEW_MODE, BookmarkViewMode.GRID.ordinal())];
265 }
266 switchViewMode(preference);
Leon Scroggins892df312009-07-14 14:48:02 -0400267 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800268
Nicolas Catania095292f2010-03-15 09:00:14 -0700269 @Override
270 protected void onDestroy() {
271 mHandler.removeCallbacksAndMessages(null);
272 super.onDestroy();
273 }
274
Leon Scroggins892df312009-07-14 14:48:02 -0400275 /**
276 * Set the ContentView to be either the grid of thumbnails or the vertical
Ben Murdoch328ea872009-09-16 13:33:29 +0100277 * list.
Leon Scroggins892df312009-07-14 14:48:02 -0400278 */
Leon Scrogginsea002572009-11-24 15:21:18 -0500279 private void switchViewMode(BookmarkViewMode viewMode) {
280 if (mViewMode == viewMode) {
Ben Murdoch328ea872009-09-16 13:33:29 +0100281 return;
282 }
283
Leon Scrogginsea002572009-11-24 15:21:18 -0500284 mViewMode = viewMode;
Ben Murdoch328ea872009-09-16 13:33:29 +0100285
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
Leon Scrogginsea002572009-11-24 15:21:18 -0500295 if (mBookmarksAdapter != null) {
296 mBookmarksAdapter.switchViewMode(viewMode);
297 }
Ben Murdoch328ea872009-09-16 13:33:29 +0100298 if (mViewMode == BookmarkViewMode.GRID) {
Leon Scroggins892df312009-07-14 14:48:02 -0400299 if (mGridPage == null) {
Leon Scroggins89c6d362009-07-15 16:54:37 -0400300 mGridPage = new GridView(this);
Leon Scrogginsea002572009-11-24 15:21:18 -0500301 if (mBookmarksAdapter != null) {
302 mGridPage.setAdapter(mBookmarksAdapter);
303 }
Leon Scroggins892df312009-07-14 14:48:02 -0400304 mGridPage.setOnItemClickListener(mListener);
Leon Scroggins89c6d362009-07-15 16:54:37 -0400305 mGridPage.setNumColumns(GridView.AUTO_FIT);
Leon Scrogginsf8551612009-09-24 16:06:02 -0400306 mGridPage.setColumnWidth(
307 BrowserActivity.getDesiredThumbnailWidth(this));
Leon Scroggins89c6d362009-07-15 16:54:37 -0400308 mGridPage.setFocusable(true);
309 mGridPage.setFocusableInTouchMode(true);
310 mGridPage.setSelector(android.R.drawable.gallery_thumb);
Leon Scrogginsf8551612009-09-24 16:06:02 -0400311 float density = getResources().getDisplayMetrics().density;
312 mGridPage.setVerticalSpacing((int) (14 * density));
313 mGridPage.setHorizontalSpacing((int) (8 * density));
314 mGridPage.setStretchMode(GridView.STRETCH_SPACING);
Leon Scrogginsbbe6d5b2009-09-28 12:01:00 -0400315 mGridPage.setScrollBarStyle(View.SCROLLBARS_INSIDE_INSET);
Leon Scrogginsf8551612009-09-24 16:06:02 -0400316 mGridPage.setDrawSelectorOnTop(true);
Leon Scrogginsa5d669e2009-08-05 14:07:58 -0400317 if (mMostVisited) {
318 mGridPage.setEmptyView(mEmptyView);
319 }
Leon Scroggins892df312009-07-14 14:48:02 -0400320 if (!mCreateShortcut) {
321 mGridPage.setOnCreateContextMenuListener(this);
322 }
323 }
Leon Scrogginsd87f85e2009-08-18 14:13:31 -0400324 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 }
Leon Scroggins892df312009-07-14 14:48:02 -0400331 } else {
332 if (null == mVerticalList) {
Leon Scrogginsd87f85e2009-08-18 14:13:31 -0400333 ListView listView = new ListView(this);
Leon Scrogginsea002572009-11-24 15:21:18 -0500334 if (mBookmarksAdapter != null) {
335 listView.setAdapter(mBookmarksAdapter);
336 }
Leon Scroggins892df312009-07-14 14:48:02 -0400337 listView.setDrawSelectorOnTop(false);
338 listView.setVerticalScrollBarEnabled(true);
339 listView.setOnItemClickListener(mListener);
Leon Scrogginsa5d669e2009-08-05 14:07:58 -0400340 if (mMostVisited) {
341 listView.setEmptyView(mEmptyView);
342 }
Leon Scroggins892df312009-07-14 14:48:02 -0400343 if (!mCreateShortcut) {
344 listView.setOnCreateContextMenuListener(this);
345 }
Leon Scrogginsd87f85e2009-08-18 14:13:31 -0400346 mVerticalList = listView;
Leon Scroggins892df312009-07-14 14:48:02 -0400347 }
Leon Scrogginsd87f85e2009-08-18 14:13:31 -0400348 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 }
Leon Scrogginsa5d669e2009-08-05 14:07:58 -0400355 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800356 }
357
Leon Scrogginsd87f85e2009-08-18 14:13:31 -0400358 private static final ViewGroup.LayoutParams FULL_SCREEN_PARAMS
359 = new ViewGroup.LayoutParams(
Romain Guy15b8ec62010-01-08 15:06:43 -0800360 ViewGroup.LayoutParams.MATCH_PARENT,
361 ViewGroup.LayoutParams.MATCH_PARENT);
Leon Scrogginsd87f85e2009-08-18 14:13:31 -0400362
The Android Open Source Project0c908882009-03-03 19:32:16 -0800363 private static final int SAVE_CURRENT_PAGE = 1000;
Leon Scrogginsea002572009-11-24 15:21:18 -0500364 private static final int CREATE_ADAPTER = 1001;
The Android Open Source Project0c908882009-03-03 19:32:16 -0800365 private final Handler mHandler = new Handler() {
366 @Override
367 public void handleMessage(Message msg) {
Leon Scrogginsea002572009-11-24 15:21:18 -0500368 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 }
Patrick Scottc1cf63a2010-03-09 16:02:08 -0500388 // 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 }
Leon Scrogginsea002572009-11-24 15:21:18 -0500405 break;
The Android Open Source Project0c908882009-03-03 19:32:16 -0800406 }
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
Nicolas Catania095292f2010-03-15 09:00:14 -0700413 // this point as back has a higher priority
The Android Open Source Project0c908882009-03-03 19:32:16 -0800414 if (mCanceled) {
Leon Scroggins892df312009-07-14 14:48:02 -0400415 android.util.Log.e(LOGTAG, "item clicked when dismissing");
The Android Open Source Project0c908882009-03-03 19:32:16 -0800416 return;
417 }
418 if (!mCreateShortcut) {
Leon Scrogginsa5d669e2009-08-05 14:07:58 -0400419 if (0 == position && !mMostVisited) {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800420 // XXX: Work-around for a framework issue.
421 mHandler.sendEmptyMessage(SAVE_CURRENT_PAGE);
422 } else {
423 loadUrl(position);
424 }
425 } else {
Patrick Scott3918d442009-08-04 13:22:29 -0400426 final Intent intent = createShortcutIntent(position);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800427 setResultToParent(RESULT_OK, intent);
428 finish();
429 }
430 }
431 };
432
Patrick Scott3918d442009-08-04 13:22:29 -0400433 private Intent createShortcutIntent(int position) {
434 String url = getUrl(position);
435 String title = getBookmarkTitle(position);
436 Bitmap touchIcon = getTouchIcon(position);
437
The Android Open Source Project0c908882009-03-03 19:32:16 -0800438 final Intent i = new Intent();
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700439 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);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800446 i.putExtra(Intent.EXTRA_SHORTCUT_NAME, title);
Patrick Scott3918d442009-08-04 13:22:29 -0400447 // Use the apple-touch-icon if available
448 if (touchIcon != null) {
Patrick Scott152794b2010-03-30 13:57:29 -0400449 // 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);
Patrick Scotte09761e2009-03-24 20:43:37 -0700463
Patrick Scott3918d442009-08-04 13:22:29 -0400464 // 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);
Patrick Scott152794b2010-03-30 13:57:29 -0400468 RectF rect = new RectF(0, 0, bm.getWidth(), bm.getHeight());
Patrick Scott59ce8302009-09-18 16:29:38 -0400469 rect.inset(1, 1);
470 path.addRoundRect(rect, 8f, 8f, Path.Direction.CW);
Patrick Scotte09761e2009-03-24 20:43:37 -0700471
Patrick Scott152794b2010-03-30 13:57:29 -0400472 // Reuse the paint and clear the outside of the rectangle.
Patrick Scott3918d442009-08-04 13:22:29 -0400473 paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
474 canvas.drawPath(path, paint);
Patrick Scotte09761e2009-03-24 20:43:37 -0700475
Patrick Scott152794b2010-03-30 13:57:29 -0400476 i.putExtra(Intent.EXTRA_SHORTCUT_ICON, bm);
Patrick Scott3918d442009-08-04 13:22:29 -0400477 } 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(),
Patrick Scottc0fdde92010-02-25 14:40:11 -0500486 R.drawable.ic_launcher_shortcut_browser_bookmark_icon);
Patrick Scott3918d442009-08-04 13:22:29 -0400487
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
Patrick Scottc0fdde92010-02-25 14:40:11 -0500499 final float density =
500 getResources().getDisplayMetrics().density;
Patrick Scott3918d442009-08-04 13:22:29 -0400501 // Create a rectangle that is slightly wider than the favicon
Patrick Scott4aff3aa2009-10-05 09:32:46 -0400502 final float iconSize = 16 * density; // 16x16 favicon
Patrick Scottc0fdde92010-02-25 14:40:11 -0500503 final float padding = 2 * density; // white padding around icon
Patrick Scott3918d442009-08-04 13:22:29 -0400504 final float rectSize = iconSize + 2 * padding;
Patrick Scottc0fdde92010-02-25 14:40:11 -0500505
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);
Patrick Scott3918d442009-08-04 13:22:29 -0400515
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 }
Patrick Scotte09761e2009-03-24 20:43:37 -0700526 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800527 // 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);
Leon Scrogginsb3968bb2009-10-16 09:04:16 -0400548 if (!mCreateShortcut && !mMostVisited) {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800549 MenuInflater inflater = getMenuInflater();
550 inflater.inflate(R.menu.bookmarks, menu);
551 return true;
552 }
553 return result;
554 }
555
556 @Override
Leon Scroggins0c786502009-08-04 16:04:55 -0400557 public boolean onPrepareOptionsMenu(Menu menu) {
Leon Scroggins8382d992009-08-19 11:25:14 -0400558 boolean result = super.onPrepareOptionsMenu(menu);
Leon Scrogginsea002572009-11-24 15:21:18 -0500559 if (mCreateShortcut || mMostVisited || mBookmarksAdapter == null
Leon Scrogginsb3968bb2009-10-16 09:04:16 -0400560 || mBookmarksAdapter.getCount() == 0) {
Leon Scrogginsa5d669e2009-08-05 14:07:58 -0400561 // No need to show the menu if there are no items.
Leon Scroggins8382d992009-08-19 11:25:14 -0400562 return result;
Leon Scrogginsa5d669e2009-08-05 14:07:58 -0400563 }
Leon Scrogginsfdd10d72009-09-25 13:01:45 -0400564 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);
Leon Scroggins0c786502009-08-04 16:04:55 -0400576 return true;
577 }
578
579 @Override
The Android Open Source Project0c908882009-03-03 19:32:16 -0800580 public boolean onOptionsItemSelected(MenuItem item) {
581 switch (item.getItemId()) {
Leon Scroggins892df312009-07-14 14:48:02 -0400582 case R.id.new_context_menu_id:
583 saveCurrentPage();
584 break;
585
586 case R.id.switch_mode_menu_id:
Ben Murdoch328ea872009-09-16 13:33:29 +0100587 if (mViewMode == BookmarkViewMode.GRID) {
588 switchViewMode(BookmarkViewMode.LIST);
589 } else {
590 switchViewMode(BookmarkViewMode.GRID);
591 }
Leon Scroggins892df312009-07-14 14:48:02 -0400592 break;
593
594 default:
595 return super.onOptionsItemSelected(item);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800596 }
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 }
Nicolas Catania095292f2010-03-15 09:00:14 -0700608
The Android Open Source Project0c908882009-03-03 19:32:16 -0800609
610 private void editBookmark(int position) {
Nicolas Catania095292f2010-03-15 09:00:14 -0700611 Intent intent = new Intent(BrowserBookmarksPage.this,
The Android Open Source Project0c908882009-03-03 19:32:16 -0800612 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 }
Nicolas Catania095292f2010-03-15 09:00:14 -0700643
The Android Open Source Project0c908882009-03-03 19:32:16 -0800644 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)))
Nicolas Catania095292f2010-03-15 09:00:14 -0700653 .setPositiveButton(R.string.ok,
The Android Open Source Project0c908882009-03-03 19:32:16 -0800654 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 */
Leon Scroggins892df312009-07-14 14:48:02 -0400666 private void refreshList() {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800667 mBookmarksAdapter.refreshList();
668 }
Nicolas Catania095292f2010-03-15 09:00:14 -0700669
The Android Open Source Project0c908882009-03-03 19:32:16 -0800670 /**
671 * Return a hashmap representing the currently highlighted row.
672 */
673 public Bundle getRow(int position) {
Leon Scrogginsea002572009-11-24 15:21:18 -0500674 return mBookmarksAdapter == null ? null
675 : mBookmarksAdapter.getRow(position);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800676 }
677
678 /**
679 * Return the url of the currently highlighted row.
680 */
681 public String getUrl(int position) {
Leon Scrogginsea002572009-11-24 15:21:18 -0500682 return mBookmarksAdapter == null ? null
683 : mBookmarksAdapter.getUrl(position);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800684 }
685
Patrick Scotte09761e2009-03-24 20:43:37 -0700686 /**
687 * Return the favicon of the currently highlighted row.
688 */
689 public Bitmap getFavicon(int position) {
Leon Scrogginsea002572009-11-24 15:21:18 -0500690 return mBookmarksAdapter == null ? null
691 : mBookmarksAdapter.getFavicon(position);
Patrick Scotte09761e2009-03-24 20:43:37 -0700692 }
693
Patrick Scott3918d442009-08-04 13:22:29 -0400694 private Bitmap getTouchIcon(int position) {
Leon Scrogginsea002572009-11-24 15:21:18 -0500695 return mBookmarksAdapter == null ? null
696 : mBookmarksAdapter.getTouchIcon(position);
Patrick Scott3918d442009-08-04 13:22:29 -0400697 }
698
The Android Open Source Project0c908882009-03-03 19:32:16 -0800699 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 }
Nicolas Catania095292f2010-03-15 09:00:14 -0700709
The Android Open Source Project0c908882009-03-03 19:32:16 -0800710 public String getBookmarkTitle(int position) {
Leon Scrogginsea002572009-11-24 15:21:18 -0500711 return mBookmarksAdapter == null ? null
712 : mBookmarksAdapter.getTitle(position);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800713 }
714
715 /**
716 * Delete the currently highlighted row.
717 */
718 public void deleteBookmark(int position) {
Leon Scrogginsea002572009-11-24 15:21:18 -0500719 if (mBookmarksAdapter == null) return;
The Android Open Source Project0c908882009-03-03 19:32:16 -0800720 mBookmarksAdapter.deleteRow(position);
721 }
Grace Kloba5942df02009-09-18 11:48:29 -0700722
723 @Override
724 public void onBackPressed() {
725 setResultToParent(RESULT_CANCELED, null);
726 mCanceled = true;
727 super.onBackPressed();
The Android Open Source Project0c908882009-03-03 19:32:16 -0800728 }
729
Leon Scrogginsfde97462010-01-11 13:06:21 -0500730 // 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.
The Android Open Source Project0c908882009-03-03 19:32:16 -0800734 private void setResultToParent(int resultCode, Intent data) {
Leon Scrogginsfde97462010-01-11 13:06:21 -0500735 Activity parent = getParent();
736 if (parent == null) {
737 setResult(resultCode, data);
738 } else {
739 ((CombinedBookmarkHistoryActivity) parent).setResultFromChild(
740 resultCode, data);
741 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800742 }
743}