blob: 1183b707dbb9587e400874b263022f943a71bb75 [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;
50import android.widget.AdapterView;
Leon Scroggins89c6d362009-07-15 16:54:37 -040051import android.widget.GridView;
The Android Open Source Project0c908882009-03-03 19:32:16 -080052import android.widget.ListView;
Leon Scrogginsfeb941d2009-05-28 17:27:38 -040053import android.widget.Toast;
The Android Open Source Project0c908882009-03-03 19:32:16 -080054
Ben Murdoch328ea872009-09-16 13:33:29 +010055/*package*/ enum BookmarkViewMode { NONE, GRID, LIST }
The Android Open Source Project0c908882009-03-03 19:32:16 -080056/**
57 * View showing the user's bookmarks in the browser.
58 */
59public class BrowserBookmarksPage extends Activity implements
60 View.OnCreateContextMenuListener {
61
Ben Murdoch328ea872009-09-16 13:33:29 +010062 private BookmarkViewMode mViewMode = BookmarkViewMode.NONE;
Leon Scroggins89c6d362009-07-15 16:54:37 -040063 private GridView mGridPage;
Leon Scrogginsea002572009-11-24 15:21:18 -050064 private ListView mVerticalList;
The Android Open Source Project0c908882009-03-03 19:32:16 -080065 private BrowserBookmarksAdapter mBookmarksAdapter;
66 private static final int BOOKMARKS_SAVE = 1;
Leon Scroggins190095d2009-08-17 17:01:38 -040067 private boolean mDisableNewWindow;
The Android Open Source Project0c908882009-03-03 19:32:16 -080068 private BookmarkItem mContextHeader;
69 private AddNewBookmark mAddHeader;
70 private boolean mCanceled = false;
71 private boolean mCreateShortcut;
Leon Scrogginsa5d669e2009-08-05 14:07:58 -040072 private boolean mMostVisited;
73 private View mEmptyView;
The Android Open Source Project0c908882009-03-03 19:32:16 -080074 // XXX: There is no public string defining this intent so if Home changes
75 // the value, we have to update this string.
76 private static final String INSTALL_SHORTCUT =
77 "com.android.launcher.action.INSTALL_SHORTCUT";
78
79 private final static String LOGTAG = "browser";
Ben Murdoch328ea872009-09-16 13:33:29 +010080 private final static String PREF_BOOKMARK_VIEW_MODE = "pref_bookmark_view_mode";
81 private final static String PREF_MOST_VISITED_VIEW_MODE = "pref_most_visited_view_mode";
The Android Open Source Project0c908882009-03-03 19:32:16 -080082
83 @Override
84 public boolean onContextItemSelected(MenuItem item) {
85 // It is possible that the view has been canceled when we get to
86 // this point as back has a higher priority
87 if (mCanceled) {
88 return true;
89 }
90 AdapterView.AdapterContextMenuInfo i =
91 (AdapterView.AdapterContextMenuInfo)item.getMenuInfo();
92 // If we have no menu info, we can't tell which item was selected.
93 if (i == null) {
94 return true;
95 }
96
97 switch (item.getItemId()) {
98 case R.id.new_context_menu_id:
99 saveCurrentPage();
100 break;
101 case R.id.open_context_menu_id:
102 loadUrl(i.position);
103 break;
104 case R.id.edit_context_menu_id:
105 editBookmark(i.position);
106 break;
107 case R.id.shortcut_context_menu_id:
Patrick Scott3918d442009-08-04 13:22:29 -0400108 final Intent send = createShortcutIntent(i.position);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800109 send.setAction(INSTALL_SHORTCUT);
110 sendBroadcast(send);
111 break;
112 case R.id.delete_context_menu_id:
Leon Scrogginsa5d669e2009-08-05 14:07:58 -0400113 if (mMostVisited) {
114 Browser.deleteFromHistory(getContentResolver(),
115 getUrl(i.position));
116 refreshList();
117 } else {
118 displayRemoveBookmarkDialog(i.position);
119 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800120 break;
121 case R.id.new_window_context_menu_id:
122 openInNewWindow(i.position);
123 break;
Leon Scrogginsa5d669e2009-08-05 14:07:58 -0400124 case R.id.share_link_context_menu_id:
Leon Scroggins96afcb12009-12-10 12:35:56 -0500125 BrowserActivity.sharePage(BrowserBookmarksPage.this,
126 mBookmarksAdapter.getTitle(i.position), getUrl(i.position),
127 getFavicon(i.position),
128 mBookmarksAdapter.getScreenshot(i.position));
The Android Open Source Project0c908882009-03-03 19:32:16 -0800129 break;
130 case R.id.copy_url_context_menu_id:
131 copy(getUrl(i.position));
Leon Scrogginsfeb941d2009-05-28 17:27:38 -0400132 break;
133 case R.id.homepage_context_menu_id:
134 BrowserSettings.getInstance().setHomePage(this,
135 getUrl(i.position));
136 Toast.makeText(this, R.string.homepage_set,
137 Toast.LENGTH_LONG).show();
138 break;
Leon Scrogginsa5d669e2009-08-05 14:07:58 -0400139 // Only for the Most visited page
140 case R.id.save_to_bookmarks_menu_id:
Leon Scrogginsc1f57592009-08-14 14:16:10 -0400141 boolean isBookmark;
142 String name;
143 String url;
Ben Murdoch328ea872009-09-16 13:33:29 +0100144 if (mViewMode == BookmarkViewMode.GRID) {
Leon Scrogginsc1f57592009-08-14 14:16:10 -0400145 isBookmark = mBookmarksAdapter.getIsBookmark(i.position);
146 name = mBookmarksAdapter.getTitle(i.position);
147 url = mBookmarksAdapter.getUrl(i.position);
148 } else {
149 HistoryItem historyItem = ((HistoryItem) i.targetView);
150 isBookmark = historyItem.isBookmark();
151 name = historyItem.getName();
152 url = historyItem.getUrl();
153 }
Leon Scrogginsa5d669e2009-08-05 14:07:58 -0400154 // If the site is bookmarked, the item becomes remove from
155 // bookmarks.
Leon Scrogginsc1f57592009-08-14 14:16:10 -0400156 if (isBookmark) {
Andrei Popescuc9526192009-09-23 15:52:16 +0100157 Bookmarks.removeFromBookmarks(this, getContentResolver(), url, name);
Leon Scrogginsa5d669e2009-08-05 14:07:58 -0400158 } else {
Leon Scrogginsc1f57592009-08-14 14:16:10 -0400159 Browser.saveBookmark(this, name, url);
Leon Scrogginsa5d669e2009-08-05 14:07:58 -0400160 }
161 break;
The Android Open Source Project0c908882009-03-03 19:32:16 -0800162 default:
163 return super.onContextItemSelected(item);
164 }
165 return true;
166 }
167
168 @Override
169 public void onCreateContextMenu(ContextMenu menu, View v,
170 ContextMenuInfo menuInfo) {
171 AdapterView.AdapterContextMenuInfo i =
172 (AdapterView.AdapterContextMenuInfo) menuInfo;
173
174 MenuInflater inflater = getMenuInflater();
Leon Scrogginsa5d669e2009-08-05 14:07:58 -0400175 if (mMostVisited) {
176 inflater.inflate(R.menu.historycontext, menu);
177 } else {
178 inflater.inflate(R.menu.bookmarkscontext, menu);
179 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800180
Leon Scrogginsa5d669e2009-08-05 14:07:58 -0400181 if (0 == i.position && !mMostVisited) {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800182 menu.setGroupVisible(R.id.CONTEXT_MENU, false);
183 if (mAddHeader == null) {
184 mAddHeader = new AddNewBookmark(BrowserBookmarksPage.this);
185 } else if (mAddHeader.getParent() != null) {
186 ((ViewGroup) mAddHeader.getParent()).
187 removeView(mAddHeader);
188 }
Leon Scroggins892df312009-07-14 14:48:02 -0400189 mAddHeader.setUrl(getIntent().getStringExtra("url"));
The Android Open Source Project0c908882009-03-03 19:32:16 -0800190 menu.setHeaderView(mAddHeader);
191 return;
192 }
Leon Scrogginsa5d669e2009-08-05 14:07:58 -0400193 if (mMostVisited) {
Ben Murdoch328ea872009-09-16 13:33:29 +0100194 if ((mViewMode == BookmarkViewMode.LIST
195 && ((HistoryItem) i.targetView).isBookmark())
Leon Scrogginsa5d669e2009-08-05 14:07:58 -0400196 || mBookmarksAdapter.getIsBookmark(i.position)) {
197 MenuItem item = menu.findItem(
198 R.id.save_to_bookmarks_menu_id);
199 item.setTitle(R.string.remove_from_bookmarks);
200 }
201 } else {
202 // The historycontext menu has no ADD_MENU group.
203 menu.setGroupVisible(R.id.ADD_MENU, false);
204 }
Leon Scroggins190095d2009-08-17 17:01:38 -0400205 if (mDisableNewWindow) {
Leon Scroggins892df312009-07-14 14:48:02 -0400206 menu.findItem(R.id.new_window_context_menu_id).setVisible(
207 false);
208 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800209 if (mContextHeader == null) {
210 mContextHeader = new BookmarkItem(BrowserBookmarksPage.this);
211 } else if (mContextHeader.getParent() != null) {
212 ((ViewGroup) mContextHeader.getParent()).
213 removeView(mContextHeader);
214 }
Ben Murdoch328ea872009-09-16 13:33:29 +0100215 if (mViewMode == BookmarkViewMode.GRID) {
Leon Scroggins892df312009-07-14 14:48:02 -0400216 mBookmarksAdapter.populateBookmarkItem(mContextHeader,
217 i.position);
218 } else {
219 BookmarkItem b = (BookmarkItem) i.targetView;
220 b.copyTo(mContextHeader);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800221 }
Leon Scroggins892df312009-07-14 14:48:02 -0400222 menu.setHeaderView(mContextHeader);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800223 }
224
225 /**
226 * Create a new BrowserBookmarksPage.
227 */
228 @Override
229 protected void onCreate(Bundle icicle) {
230 super.onCreate(icicle);
231
The Android Open Source Project0c908882009-03-03 19:32:16 -0800232 if (Intent.ACTION_CREATE_SHORTCUT.equals(getIntent().getAction())) {
233 mCreateShortcut = true;
234 }
Leon Scroggins190095d2009-08-17 17:01:38 -0400235 mDisableNewWindow = getIntent().getBooleanExtra("disable_new_window",
236 false);
Leon Scrogginsa5d669e2009-08-05 14:07:58 -0400237 mMostVisited = getIntent().getBooleanExtra("mostVisited", false);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800238
Leon Scrogginsa5d669e2009-08-05 14:07:58 -0400239 if (mCreateShortcut) {
240 setTitle(R.string.browser_bookmarks_page_bookmarks_text);
241 }
Leon Scrogginsea002572009-11-24 15:21:18 -0500242 mHandler.obtainMessage(CREATE_ADAPTER).sendToTarget();
Leon Scrogginsd87f85e2009-08-18 14:13:31 -0400243
244 setContentView(R.layout.empty_history);
245 mEmptyView = findViewById(R.id.empty_view);
246 mEmptyView.setVisibility(View.GONE);
247
Ben Murdoch328ea872009-09-16 13:33:29 +0100248 SharedPreferences p = getPreferences(MODE_PRIVATE);
249
250 // See if the user has set a preference for the view mode of their
251 // bookmarks. Otherwise default to grid mode.
252 BookmarkViewMode preference = BookmarkViewMode.NONE;
253 if (mMostVisited) {
Leon Scrogginsb3968bb2009-10-16 09:04:16 -0400254 // For the most visited page, only use list mode.
255 preference = BookmarkViewMode.LIST;
Ben Murdoch328ea872009-09-16 13:33:29 +0100256 } else {
257 preference = BookmarkViewMode.values()[p.getInt(
258 PREF_BOOKMARK_VIEW_MODE, BookmarkViewMode.GRID.ordinal())];
259 }
260 switchViewMode(preference);
Leon Scroggins892df312009-07-14 14:48:02 -0400261 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800262
Leon Scroggins892df312009-07-14 14:48:02 -0400263 /**
264 * Set the ContentView to be either the grid of thumbnails or the vertical
Ben Murdoch328ea872009-09-16 13:33:29 +0100265 * list.
Leon Scroggins892df312009-07-14 14:48:02 -0400266 */
Leon Scrogginsea002572009-11-24 15:21:18 -0500267 private void switchViewMode(BookmarkViewMode viewMode) {
268 if (mViewMode == viewMode) {
Ben Murdoch328ea872009-09-16 13:33:29 +0100269 return;
270 }
271
Leon Scrogginsea002572009-11-24 15:21:18 -0500272 mViewMode = viewMode;
Ben Murdoch328ea872009-09-16 13:33:29 +0100273
274 // Update the preferences to make the new view mode sticky.
275 Editor ed = getPreferences(MODE_PRIVATE).edit();
276 if (mMostVisited) {
277 ed.putInt(PREF_MOST_VISITED_VIEW_MODE, mViewMode.ordinal());
278 } else {
279 ed.putInt(PREF_BOOKMARK_VIEW_MODE, mViewMode.ordinal());
280 }
281 ed.commit();
282
Leon Scrogginsea002572009-11-24 15:21:18 -0500283 if (mBookmarksAdapter != null) {
284 mBookmarksAdapter.switchViewMode(viewMode);
285 }
Ben Murdoch328ea872009-09-16 13:33:29 +0100286 if (mViewMode == BookmarkViewMode.GRID) {
Leon Scroggins892df312009-07-14 14:48:02 -0400287 if (mGridPage == null) {
Leon Scroggins89c6d362009-07-15 16:54:37 -0400288 mGridPage = new GridView(this);
Leon Scrogginsea002572009-11-24 15:21:18 -0500289 if (mBookmarksAdapter != null) {
290 mGridPage.setAdapter(mBookmarksAdapter);
291 }
Leon Scroggins892df312009-07-14 14:48:02 -0400292 mGridPage.setOnItemClickListener(mListener);
Leon Scroggins89c6d362009-07-15 16:54:37 -0400293 mGridPage.setNumColumns(GridView.AUTO_FIT);
Leon Scrogginsf8551612009-09-24 16:06:02 -0400294 mGridPage.setColumnWidth(
295 BrowserActivity.getDesiredThumbnailWidth(this));
Leon Scroggins89c6d362009-07-15 16:54:37 -0400296 mGridPage.setFocusable(true);
297 mGridPage.setFocusableInTouchMode(true);
298 mGridPage.setSelector(android.R.drawable.gallery_thumb);
Leon Scrogginsf8551612009-09-24 16:06:02 -0400299 float density = getResources().getDisplayMetrics().density;
300 mGridPage.setVerticalSpacing((int) (14 * density));
301 mGridPage.setHorizontalSpacing((int) (8 * density));
302 mGridPage.setStretchMode(GridView.STRETCH_SPACING);
Leon Scrogginsbbe6d5b2009-09-28 12:01:00 -0400303 mGridPage.setScrollBarStyle(View.SCROLLBARS_INSIDE_INSET);
Leon Scrogginsf8551612009-09-24 16:06:02 -0400304 mGridPage.setDrawSelectorOnTop(true);
Leon Scrogginsa5d669e2009-08-05 14:07:58 -0400305 if (mMostVisited) {
306 mGridPage.setEmptyView(mEmptyView);
307 }
Leon Scroggins892df312009-07-14 14:48:02 -0400308 if (!mCreateShortcut) {
309 mGridPage.setOnCreateContextMenuListener(this);
310 }
311 }
Leon Scrogginsd87f85e2009-08-18 14:13:31 -0400312 addContentView(mGridPage, FULL_SCREEN_PARAMS);
313 if (mVerticalList != null) {
314 ViewGroup parent = (ViewGroup) mVerticalList.getParent();
315 if (parent != null) {
316 parent.removeView(mVerticalList);
317 }
318 }
Leon Scroggins892df312009-07-14 14:48:02 -0400319 } else {
320 if (null == mVerticalList) {
Leon Scrogginsd87f85e2009-08-18 14:13:31 -0400321 ListView listView = new ListView(this);
Leon Scrogginsea002572009-11-24 15:21:18 -0500322 if (mBookmarksAdapter != null) {
323 listView.setAdapter(mBookmarksAdapter);
324 }
Leon Scroggins892df312009-07-14 14:48:02 -0400325 listView.setDrawSelectorOnTop(false);
326 listView.setVerticalScrollBarEnabled(true);
327 listView.setOnItemClickListener(mListener);
Leon Scrogginsa5d669e2009-08-05 14:07:58 -0400328 if (mMostVisited) {
329 listView.setEmptyView(mEmptyView);
330 }
Leon Scroggins892df312009-07-14 14:48:02 -0400331 if (!mCreateShortcut) {
332 listView.setOnCreateContextMenuListener(this);
333 }
Leon Scrogginsd87f85e2009-08-18 14:13:31 -0400334 mVerticalList = listView;
Leon Scroggins892df312009-07-14 14:48:02 -0400335 }
Leon Scrogginsd87f85e2009-08-18 14:13:31 -0400336 addContentView(mVerticalList, FULL_SCREEN_PARAMS);
337 if (mGridPage != null) {
338 ViewGroup parent = (ViewGroup) mGridPage.getParent();
339 if (parent != null) {
340 parent.removeView(mGridPage);
341 }
342 }
Leon Scrogginsa5d669e2009-08-05 14:07:58 -0400343 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800344 }
345
Leon Scrogginsd87f85e2009-08-18 14:13:31 -0400346 private static final ViewGroup.LayoutParams FULL_SCREEN_PARAMS
347 = new ViewGroup.LayoutParams(
Romain Guy15b8ec62010-01-08 15:06:43 -0800348 ViewGroup.LayoutParams.MATCH_PARENT,
349 ViewGroup.LayoutParams.MATCH_PARENT);
Leon Scrogginsd87f85e2009-08-18 14:13:31 -0400350
The Android Open Source Project0c908882009-03-03 19:32:16 -0800351 private static final int SAVE_CURRENT_PAGE = 1000;
Leon Scrogginsea002572009-11-24 15:21:18 -0500352 private static final int CREATE_ADAPTER = 1001;
The Android Open Source Project0c908882009-03-03 19:32:16 -0800353 private final Handler mHandler = new Handler() {
354 @Override
355 public void handleMessage(Message msg) {
Leon Scrogginsea002572009-11-24 15:21:18 -0500356 switch (msg.what) {
357 case SAVE_CURRENT_PAGE:
358 saveCurrentPage();
359 break;
360 case CREATE_ADAPTER:
361 Intent intent = getIntent();
362 mBookmarksAdapter = new BrowserBookmarksAdapter(
363 BrowserBookmarksPage.this,
364 intent.getStringExtra("url"),
365 intent.getStringExtra("title"),
366 (Bitmap) intent.getParcelableExtra("thumbnail"),
367 mCreateShortcut,
368 mMostVisited);
369 mBookmarksAdapter.switchViewMode(mViewMode);
370 if (mGridPage != null) {
371 mGridPage.setAdapter(mBookmarksAdapter);
372 }
373 if (mVerticalList != null) {
374 mVerticalList.setAdapter(mBookmarksAdapter);
375 }
376 break;
The Android Open Source Project0c908882009-03-03 19:32:16 -0800377 }
378 }
379 };
380
381 private AdapterView.OnItemClickListener mListener = new AdapterView.OnItemClickListener() {
382 public void onItemClick(AdapterView parent, View v, int position, long id) {
383 // It is possible that the view has been canceled when we get to
384 // this point as back has a higher priority
385 if (mCanceled) {
Leon Scroggins892df312009-07-14 14:48:02 -0400386 android.util.Log.e(LOGTAG, "item clicked when dismissing");
The Android Open Source Project0c908882009-03-03 19:32:16 -0800387 return;
388 }
389 if (!mCreateShortcut) {
Leon Scrogginsa5d669e2009-08-05 14:07:58 -0400390 if (0 == position && !mMostVisited) {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800391 // XXX: Work-around for a framework issue.
392 mHandler.sendEmptyMessage(SAVE_CURRENT_PAGE);
393 } else {
394 loadUrl(position);
395 }
396 } else {
Patrick Scott3918d442009-08-04 13:22:29 -0400397 final Intent intent = createShortcutIntent(position);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800398 setResultToParent(RESULT_OK, intent);
399 finish();
400 }
401 }
402 };
403
Patrick Scott3918d442009-08-04 13:22:29 -0400404 private Intent createShortcutIntent(int position) {
405 String url = getUrl(position);
406 String title = getBookmarkTitle(position);
407 Bitmap touchIcon = getTouchIcon(position);
408
The Android Open Source Project0c908882009-03-03 19:32:16 -0800409 final Intent i = new Intent();
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700410 final Intent shortcutIntent = new Intent(Intent.ACTION_VIEW,
411 Uri.parse(url));
412 long urlHash = url.hashCode();
413 long uniqueId = (urlHash << 32) | shortcutIntent.hashCode();
414 shortcutIntent.putExtra(Browser.EXTRA_APPLICATION_ID,
415 Long.toString(uniqueId));
416 i.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800417 i.putExtra(Intent.EXTRA_SHORTCUT_NAME, title);
Patrick Scott3918d442009-08-04 13:22:29 -0400418 // Use the apple-touch-icon if available
419 if (touchIcon != null) {
420 // Make a copy so we can modify the pixels.
421 Bitmap copy = touchIcon.copy(Bitmap.Config.ARGB_8888, true);
Patrick Scotte09761e2009-03-24 20:43:37 -0700422 Canvas canvas = new Canvas(copy);
423
Patrick Scott3918d442009-08-04 13:22:29 -0400424 // Construct a path from a round rect. This will allow drawing with
425 // an inverse fill so we can punch a hole using the round rect.
426 Path path = new Path();
427 path.setFillType(Path.FillType.INVERSE_WINDING);
Patrick Scott59ce8302009-09-18 16:29:38 -0400428 RectF rect = new RectF(0, 0, touchIcon.getWidth(),
429 touchIcon.getHeight());
430 rect.inset(1, 1);
431 path.addRoundRect(rect, 8f, 8f, Path.Direction.CW);
Patrick Scotte09761e2009-03-24 20:43:37 -0700432
Patrick Scott3918d442009-08-04 13:22:29 -0400433 // Construct a paint that clears the outside of the rectangle and
434 // draw.
435 Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
436 paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
437 canvas.drawPath(path, paint);
Patrick Scotte09761e2009-03-24 20:43:37 -0700438
Patrick Scotte09761e2009-03-24 20:43:37 -0700439 i.putExtra(Intent.EXTRA_SHORTCUT_ICON, copy);
Patrick Scott3918d442009-08-04 13:22:29 -0400440 } else {
441 Bitmap favicon = getFavicon(position);
442 if (favicon == null) {
443 i.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE,
444 Intent.ShortcutIconResource.fromContext(
445 BrowserBookmarksPage.this,
446 R.drawable.ic_launcher_shortcut_browser_bookmark));
447 } else {
448 Bitmap icon = BitmapFactory.decodeResource(getResources(),
Patrick Scottc0fdde92010-02-25 14:40:11 -0500449 R.drawable.ic_launcher_shortcut_browser_bookmark_icon);
Patrick Scott3918d442009-08-04 13:22:29 -0400450
451 // Make a copy of the regular icon so we can modify the pixels.
452 Bitmap copy = icon.copy(Bitmap.Config.ARGB_8888, true);
453 Canvas canvas = new Canvas(copy);
454
455 // Make a Paint for the white background rectangle and for
456 // filtering the favicon.
457 Paint p = new Paint(Paint.ANTI_ALIAS_FLAG
458 | Paint.FILTER_BITMAP_FLAG);
459 p.setStyle(Paint.Style.FILL_AND_STROKE);
460 p.setColor(Color.WHITE);
461
Patrick Scottc0fdde92010-02-25 14:40:11 -0500462 final float density =
463 getResources().getDisplayMetrics().density;
Patrick Scott3918d442009-08-04 13:22:29 -0400464 // Create a rectangle that is slightly wider than the favicon
Patrick Scott4aff3aa2009-10-05 09:32:46 -0400465 final float iconSize = 16 * density; // 16x16 favicon
Patrick Scottc0fdde92010-02-25 14:40:11 -0500466 final float padding = 2 * density; // white padding around icon
Patrick Scott3918d442009-08-04 13:22:29 -0400467 final float rectSize = iconSize + 2 * padding;
Patrick Scottc0fdde92010-02-25 14:40:11 -0500468
469 final Rect iconBounds =
470 new Rect(0, 0, icon.getWidth(), icon.getHeight());
471 final float x = iconBounds.exactCenterX() - (rectSize / 2);
472 // Note: Subtract 2 dip from the y position since the box is
473 // slightly higher than center. Use padding since it is already
474 // 2 * density.
475 final float y = iconBounds.exactCenterY() - (rectSize / 2)
476 - padding;
477 RectF r = new RectF(x, y, x + rectSize, y + rectSize);
Patrick Scott3918d442009-08-04 13:22:29 -0400478
479 // Draw a white rounded rectangle behind the favicon
480 canvas.drawRoundRect(r, 2, 2, p);
481
482 // Draw the favicon in the same rectangle as the rounded
483 // rectangle but inset by the padding
484 // (results in a 16x16 favicon).
485 r.inset(padding, padding);
486 canvas.drawBitmap(favicon, null, r, p);
487 i.putExtra(Intent.EXTRA_SHORTCUT_ICON, copy);
488 }
Patrick Scotte09761e2009-03-24 20:43:37 -0700489 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800490 // Do not allow duplicate items
491 i.putExtra("duplicate", false);
492 return i;
493 }
494
495 private void saveCurrentPage() {
496 Intent i = new Intent(BrowserBookmarksPage.this,
497 AddBookmarkPage.class);
498 i.putExtras(getIntent());
499 startActivityForResult(i, BOOKMARKS_SAVE);
500 }
501
502 private void loadUrl(int position) {
503 Intent intent = (new Intent()).setAction(getUrl(position));
504 setResultToParent(RESULT_OK, intent);
505 finish();
506 }
507
508 @Override
509 public boolean onCreateOptionsMenu(Menu menu) {
510 boolean result = super.onCreateOptionsMenu(menu);
Leon Scrogginsb3968bb2009-10-16 09:04:16 -0400511 if (!mCreateShortcut && !mMostVisited) {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800512 MenuInflater inflater = getMenuInflater();
513 inflater.inflate(R.menu.bookmarks, menu);
514 return true;
515 }
516 return result;
517 }
518
519 @Override
Leon Scroggins0c786502009-08-04 16:04:55 -0400520 public boolean onPrepareOptionsMenu(Menu menu) {
Leon Scroggins8382d992009-08-19 11:25:14 -0400521 boolean result = super.onPrepareOptionsMenu(menu);
Leon Scrogginsea002572009-11-24 15:21:18 -0500522 if (mCreateShortcut || mMostVisited || mBookmarksAdapter == null
Leon Scrogginsb3968bb2009-10-16 09:04:16 -0400523 || mBookmarksAdapter.getCount() == 0) {
Leon Scrogginsa5d669e2009-08-05 14:07:58 -0400524 // No need to show the menu if there are no items.
Leon Scroggins8382d992009-08-19 11:25:14 -0400525 return result;
Leon Scrogginsa5d669e2009-08-05 14:07:58 -0400526 }
Leon Scrogginsfdd10d72009-09-25 13:01:45 -0400527 MenuItem switchItem = menu.findItem(R.id.switch_mode_menu_id);
528 int titleResId;
529 int iconResId;
530 if (mViewMode == BookmarkViewMode.GRID) {
531 titleResId = R.string.switch_to_list;
532 iconResId = R.drawable.ic_menu_list;
533 } else {
534 titleResId = R.string.switch_to_thumbnails;
535 iconResId = R.drawable.ic_menu_thumbnail;
536 }
537 switchItem.setTitle(titleResId);
538 switchItem.setIcon(iconResId);
Leon Scroggins0c786502009-08-04 16:04:55 -0400539 return true;
540 }
541
542 @Override
The Android Open Source Project0c908882009-03-03 19:32:16 -0800543 public boolean onOptionsItemSelected(MenuItem item) {
544 switch (item.getItemId()) {
Leon Scroggins892df312009-07-14 14:48:02 -0400545 case R.id.new_context_menu_id:
546 saveCurrentPage();
547 break;
548
549 case R.id.switch_mode_menu_id:
Ben Murdoch328ea872009-09-16 13:33:29 +0100550 if (mViewMode == BookmarkViewMode.GRID) {
551 switchViewMode(BookmarkViewMode.LIST);
552 } else {
553 switchViewMode(BookmarkViewMode.GRID);
554 }
Leon Scroggins892df312009-07-14 14:48:02 -0400555 break;
556
557 default:
558 return super.onOptionsItemSelected(item);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800559 }
560 return true;
561 }
562
563 private void openInNewWindow(int position) {
564 Bundle b = new Bundle();
565 b.putBoolean("new_window", true);
566 setResultToParent(RESULT_OK,
567 (new Intent()).setAction(getUrl(position)).putExtras(b));
568
569 finish();
570 }
571
572
573 private void editBookmark(int position) {
574 Intent intent = new Intent(BrowserBookmarksPage.this,
575 AddBookmarkPage.class);
576 intent.putExtra("bookmark", getRow(position));
577 startActivityForResult(intent, BOOKMARKS_SAVE);
578 }
579
580 @Override
581 protected void onActivityResult(int requestCode, int resultCode,
582 Intent data) {
583 switch(requestCode) {
584 case BOOKMARKS_SAVE:
585 if (resultCode == RESULT_OK) {
586 Bundle extras;
587 if (data != null && (extras = data.getExtras()) != null) {
588 // If there are extras, then we need to save
589 // the edited bookmark. This is done in updateRow()
590 String title = extras.getString("title");
591 String url = extras.getString("url");
592 if (title != null && url != null) {
593 mBookmarksAdapter.updateRow(extras);
594 }
595 } else {
596 // extras == null then a new bookmark was added to
597 // the database.
598 refreshList();
599 }
600 }
601 break;
602 default:
603 break;
604 }
605 }
606
607 private void displayRemoveBookmarkDialog(int position) {
608 // Put up a dialog asking if the user really wants to
609 // delete the bookmark
610 final int deletePos = position;
611 new AlertDialog.Builder(this)
612 .setTitle(R.string.delete_bookmark)
613 .setIcon(android.R.drawable.ic_dialog_alert)
614 .setMessage(getText(R.string.delete_bookmark_warning).toString().replace(
615 "%s", getBookmarkTitle(deletePos)))
616 .setPositiveButton(R.string.ok,
617 new DialogInterface.OnClickListener() {
618 public void onClick(DialogInterface dialog, int whichButton) {
619 deleteBookmark(deletePos);
620 }
621 })
622 .setNegativeButton(R.string.cancel, null)
623 .show();
624 }
625
626 /**
627 * Refresh the shown list after the database has changed.
628 */
Leon Scroggins892df312009-07-14 14:48:02 -0400629 private void refreshList() {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800630 mBookmarksAdapter.refreshList();
631 }
632
633 /**
634 * Return a hashmap representing the currently highlighted row.
635 */
636 public Bundle getRow(int position) {
Leon Scrogginsea002572009-11-24 15:21:18 -0500637 return mBookmarksAdapter == null ? null
638 : mBookmarksAdapter.getRow(position);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800639 }
640
641 /**
642 * Return the url of the currently highlighted row.
643 */
644 public String getUrl(int position) {
Leon Scrogginsea002572009-11-24 15:21:18 -0500645 return mBookmarksAdapter == null ? null
646 : mBookmarksAdapter.getUrl(position);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800647 }
648
Patrick Scotte09761e2009-03-24 20:43:37 -0700649 /**
650 * Return the favicon of the currently highlighted row.
651 */
652 public Bitmap getFavicon(int position) {
Leon Scrogginsea002572009-11-24 15:21:18 -0500653 return mBookmarksAdapter == null ? null
654 : mBookmarksAdapter.getFavicon(position);
Patrick Scotte09761e2009-03-24 20:43:37 -0700655 }
656
Patrick Scott3918d442009-08-04 13:22:29 -0400657 private Bitmap getTouchIcon(int position) {
Leon Scrogginsea002572009-11-24 15:21:18 -0500658 return mBookmarksAdapter == null ? null
659 : mBookmarksAdapter.getTouchIcon(position);
Patrick Scott3918d442009-08-04 13:22:29 -0400660 }
661
The Android Open Source Project0c908882009-03-03 19:32:16 -0800662 private void copy(CharSequence text) {
663 try {
664 IClipboard clip = IClipboard.Stub.asInterface(ServiceManager.getService("clipboard"));
665 if (clip != null) {
666 clip.setClipboardText(text);
667 }
668 } catch (android.os.RemoteException e) {
669 Log.e(LOGTAG, "Copy failed", e);
670 }
671 }
672
673 public String getBookmarkTitle(int position) {
Leon Scrogginsea002572009-11-24 15:21:18 -0500674 return mBookmarksAdapter == null ? null
675 : mBookmarksAdapter.getTitle(position);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800676 }
677
678 /**
679 * Delete the currently highlighted row.
680 */
681 public void deleteBookmark(int position) {
Leon Scrogginsea002572009-11-24 15:21:18 -0500682 if (mBookmarksAdapter == null) return;
The Android Open Source Project0c908882009-03-03 19:32:16 -0800683 mBookmarksAdapter.deleteRow(position);
684 }
Grace Kloba5942df02009-09-18 11:48:29 -0700685
686 @Override
687 public void onBackPressed() {
688 setResultToParent(RESULT_CANCELED, null);
689 mCanceled = true;
690 super.onBackPressed();
The Android Open Source Project0c908882009-03-03 19:32:16 -0800691 }
692
Leon Scrogginsfde97462010-01-11 13:06:21 -0500693 // This Activity is generally a sub-Activity of
694 // CombinedBookmarkHistoryActivity. In that situation, we need to pass our
695 // result code up to our parent. However, if someone calls this Activity
696 // directly, then this has no parent, and it needs to set it on itself.
The Android Open Source Project0c908882009-03-03 19:32:16 -0800697 private void setResultToParent(int resultCode, Intent data) {
Leon Scrogginsfde97462010-01-11 13:06:21 -0500698 Activity parent = getParent();
699 if (parent == null) {
700 setResult(resultCode, data);
701 } else {
702 ((CombinedBookmarkHistoryActivity) parent).setResultFromChild(
703 resultCode, data);
704 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800705 }
706}