blob: d835f84ba475222e5ba1272c0a38effec139d493 [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 Scotte09761e2009-03-24 20:43:37 -070033import android.graphics.RectF;
The Android Open Source Project0c908882009-03-03 19:32:16 -080034import android.net.Uri;
35import android.os.Bundle;
36import android.os.Handler;
37import android.os.Message;
38import android.os.ServiceManager;
39import android.provider.Browser;
40import android.text.IClipboard;
41import android.util.Log;
42import android.view.ContextMenu;
The Android Open Source Project0c908882009-03-03 19:32:16 -080043import android.view.Menu;
44import android.view.MenuInflater;
45import android.view.MenuItem;
46import android.view.View;
47import android.view.ViewGroup;
48import android.view.ContextMenu.ContextMenuInfo;
49import android.widget.AdapterView;
Leon Scroggins89c6d362009-07-15 16:54:37 -040050import android.widget.GridView;
The Android Open Source Project0c908882009-03-03 19:32:16 -080051import android.widget.ListView;
Leon Scrogginsfeb941d2009-05-28 17:27:38 -040052import android.widget.Toast;
The Android Open Source Project0c908882009-03-03 19:32:16 -080053
Ben Murdoch328ea872009-09-16 13:33:29 +010054/*package*/ enum BookmarkViewMode { NONE, GRID, LIST }
The Android Open Source Project0c908882009-03-03 19:32:16 -080055/**
56 * View showing the user's bookmarks in the browser.
57 */
58public class BrowserBookmarksPage extends Activity implements
59 View.OnCreateContextMenuListener {
60
Ben Murdoch328ea872009-09-16 13:33:29 +010061 private BookmarkViewMode mViewMode = BookmarkViewMode.NONE;
Leon Scroggins89c6d362009-07-15 16:54:37 -040062 private GridView mGridPage;
Leon Scrogginsea002572009-11-24 15:21:18 -050063 private ListView mVerticalList;
The Android Open Source Project0c908882009-03-03 19:32:16 -080064 private BrowserBookmarksAdapter mBookmarksAdapter;
65 private static final int BOOKMARKS_SAVE = 1;
Leon Scroggins190095d2009-08-17 17:01:38 -040066 private boolean mDisableNewWindow;
The Android Open Source Project0c908882009-03-03 19:32:16 -080067 private BookmarkItem mContextHeader;
68 private AddNewBookmark mAddHeader;
69 private boolean mCanceled = false;
70 private boolean mCreateShortcut;
Leon Scrogginsa5d669e2009-08-05 14:07:58 -040071 private boolean mMostVisited;
72 private View mEmptyView;
The Android Open Source Project0c908882009-03-03 19:32:16 -080073 // XXX: There is no public string defining this intent so if Home changes
74 // the value, we have to update this string.
75 private static final String INSTALL_SHORTCUT =
76 "com.android.launcher.action.INSTALL_SHORTCUT";
77
78 private final static String LOGTAG = "browser";
Ben Murdoch328ea872009-09-16 13:33:29 +010079 private final static String PREF_BOOKMARK_VIEW_MODE = "pref_bookmark_view_mode";
80 private final static String PREF_MOST_VISITED_VIEW_MODE = "pref_most_visited_view_mode";
The Android Open Source Project0c908882009-03-03 19:32:16 -080081
82 @Override
83 public boolean onContextItemSelected(MenuItem item) {
84 // It is possible that the view has been canceled when we get to
85 // this point as back has a higher priority
86 if (mCanceled) {
87 return true;
88 }
89 AdapterView.AdapterContextMenuInfo i =
90 (AdapterView.AdapterContextMenuInfo)item.getMenuInfo();
91 // If we have no menu info, we can't tell which item was selected.
92 if (i == null) {
93 return true;
94 }
95
96 switch (item.getItemId()) {
97 case R.id.new_context_menu_id:
98 saveCurrentPage();
99 break;
100 case R.id.open_context_menu_id:
101 loadUrl(i.position);
102 break;
103 case R.id.edit_context_menu_id:
104 editBookmark(i.position);
105 break;
106 case R.id.shortcut_context_menu_id:
Patrick Scott3918d442009-08-04 13:22:29 -0400107 final Intent send = createShortcutIntent(i.position);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800108 send.setAction(INSTALL_SHORTCUT);
109 sendBroadcast(send);
110 break;
111 case R.id.delete_context_menu_id:
Leon Scrogginsa5d669e2009-08-05 14:07:58 -0400112 if (mMostVisited) {
113 Browser.deleteFromHistory(getContentResolver(),
114 getUrl(i.position));
115 refreshList();
116 } else {
117 displayRemoveBookmarkDialog(i.position);
118 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800119 break;
120 case R.id.new_window_context_menu_id:
121 openInNewWindow(i.position);
122 break;
Leon Scrogginsa5d669e2009-08-05 14:07:58 -0400123 case R.id.share_link_context_menu_id:
Leon Scroggins96afcb12009-12-10 12:35:56 -0500124 BrowserActivity.sharePage(BrowserBookmarksPage.this,
125 mBookmarksAdapter.getTitle(i.position), getUrl(i.position),
126 getFavicon(i.position),
127 mBookmarksAdapter.getScreenshot(i.position));
The Android Open Source Project0c908882009-03-03 19:32:16 -0800128 break;
129 case R.id.copy_url_context_menu_id:
130 copy(getUrl(i.position));
Leon Scrogginsfeb941d2009-05-28 17:27:38 -0400131 break;
132 case R.id.homepage_context_menu_id:
133 BrowserSettings.getInstance().setHomePage(this,
134 getUrl(i.position));
135 Toast.makeText(this, R.string.homepage_set,
136 Toast.LENGTH_LONG).show();
137 break;
Leon Scrogginsa5d669e2009-08-05 14:07:58 -0400138 // Only for the Most visited page
139 case R.id.save_to_bookmarks_menu_id:
Leon Scrogginsc1f57592009-08-14 14:16:10 -0400140 boolean isBookmark;
141 String name;
142 String url;
Ben Murdoch328ea872009-09-16 13:33:29 +0100143 if (mViewMode == BookmarkViewMode.GRID) {
Leon Scrogginsc1f57592009-08-14 14:16:10 -0400144 isBookmark = mBookmarksAdapter.getIsBookmark(i.position);
145 name = mBookmarksAdapter.getTitle(i.position);
146 url = mBookmarksAdapter.getUrl(i.position);
147 } else {
148 HistoryItem historyItem = ((HistoryItem) i.targetView);
149 isBookmark = historyItem.isBookmark();
150 name = historyItem.getName();
151 url = historyItem.getUrl();
152 }
Leon Scrogginsa5d669e2009-08-05 14:07:58 -0400153 // If the site is bookmarked, the item becomes remove from
154 // bookmarks.
Leon Scrogginsc1f57592009-08-14 14:16:10 -0400155 if (isBookmark) {
Andrei Popescuc9526192009-09-23 15:52:16 +0100156 Bookmarks.removeFromBookmarks(this, getContentResolver(), url, name);
Leon Scrogginsa5d669e2009-08-05 14:07:58 -0400157 } else {
Leon Scrogginsc1f57592009-08-14 14:16:10 -0400158 Browser.saveBookmark(this, name, url);
Leon Scrogginsa5d669e2009-08-05 14:07:58 -0400159 }
160 break;
The Android Open Source Project0c908882009-03-03 19:32:16 -0800161 default:
162 return super.onContextItemSelected(item);
163 }
164 return true;
165 }
166
167 @Override
168 public void onCreateContextMenu(ContextMenu menu, View v,
169 ContextMenuInfo menuInfo) {
170 AdapterView.AdapterContextMenuInfo i =
171 (AdapterView.AdapterContextMenuInfo) menuInfo;
172
173 MenuInflater inflater = getMenuInflater();
Leon Scrogginsa5d669e2009-08-05 14:07:58 -0400174 if (mMostVisited) {
175 inflater.inflate(R.menu.historycontext, menu);
176 } else {
177 inflater.inflate(R.menu.bookmarkscontext, menu);
178 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800179
Leon Scrogginsa5d669e2009-08-05 14:07:58 -0400180 if (0 == i.position && !mMostVisited) {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800181 menu.setGroupVisible(R.id.CONTEXT_MENU, false);
182 if (mAddHeader == null) {
183 mAddHeader = new AddNewBookmark(BrowserBookmarksPage.this);
184 } else if (mAddHeader.getParent() != null) {
185 ((ViewGroup) mAddHeader.getParent()).
186 removeView(mAddHeader);
187 }
Leon Scroggins892df312009-07-14 14:48:02 -0400188 mAddHeader.setUrl(getIntent().getStringExtra("url"));
The Android Open Source Project0c908882009-03-03 19:32:16 -0800189 menu.setHeaderView(mAddHeader);
190 return;
191 }
Leon Scrogginsa5d669e2009-08-05 14:07:58 -0400192 if (mMostVisited) {
Ben Murdoch328ea872009-09-16 13:33:29 +0100193 if ((mViewMode == BookmarkViewMode.LIST
194 && ((HistoryItem) i.targetView).isBookmark())
Leon Scrogginsa5d669e2009-08-05 14:07:58 -0400195 || mBookmarksAdapter.getIsBookmark(i.position)) {
196 MenuItem item = menu.findItem(
197 R.id.save_to_bookmarks_menu_id);
198 item.setTitle(R.string.remove_from_bookmarks);
199 }
200 } else {
201 // The historycontext menu has no ADD_MENU group.
202 menu.setGroupVisible(R.id.ADD_MENU, false);
203 }
Leon Scroggins190095d2009-08-17 17:01:38 -0400204 if (mDisableNewWindow) {
Leon Scroggins892df312009-07-14 14:48:02 -0400205 menu.findItem(R.id.new_window_context_menu_id).setVisible(
206 false);
207 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800208 if (mContextHeader == null) {
209 mContextHeader = new BookmarkItem(BrowserBookmarksPage.this);
210 } else if (mContextHeader.getParent() != null) {
211 ((ViewGroup) mContextHeader.getParent()).
212 removeView(mContextHeader);
213 }
Ben Murdoch328ea872009-09-16 13:33:29 +0100214 if (mViewMode == BookmarkViewMode.GRID) {
Leon Scroggins892df312009-07-14 14:48:02 -0400215 mBookmarksAdapter.populateBookmarkItem(mContextHeader,
216 i.position);
217 } else {
218 BookmarkItem b = (BookmarkItem) i.targetView;
219 b.copyTo(mContextHeader);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800220 }
Leon Scroggins892df312009-07-14 14:48:02 -0400221 menu.setHeaderView(mContextHeader);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800222 }
223
224 /**
225 * Create a new BrowserBookmarksPage.
226 */
227 @Override
228 protected void onCreate(Bundle icicle) {
229 super.onCreate(icicle);
230
The Android Open Source Project0c908882009-03-03 19:32:16 -0800231 if (Intent.ACTION_CREATE_SHORTCUT.equals(getIntent().getAction())) {
232 mCreateShortcut = true;
233 }
Leon Scroggins190095d2009-08-17 17:01:38 -0400234 mDisableNewWindow = getIntent().getBooleanExtra("disable_new_window",
235 false);
Leon Scrogginsa5d669e2009-08-05 14:07:58 -0400236 mMostVisited = getIntent().getBooleanExtra("mostVisited", false);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800237
Leon Scrogginsa5d669e2009-08-05 14:07:58 -0400238 if (mCreateShortcut) {
239 setTitle(R.string.browser_bookmarks_page_bookmarks_text);
240 }
Leon Scrogginsea002572009-11-24 15:21:18 -0500241 mHandler.obtainMessage(CREATE_ADAPTER).sendToTarget();
Leon Scrogginsd87f85e2009-08-18 14:13:31 -0400242
243 setContentView(R.layout.empty_history);
244 mEmptyView = findViewById(R.id.empty_view);
245 mEmptyView.setVisibility(View.GONE);
246
Ben Murdoch328ea872009-09-16 13:33:29 +0100247 SharedPreferences p = getPreferences(MODE_PRIVATE);
248
249 // See if the user has set a preference for the view mode of their
250 // bookmarks. Otherwise default to grid mode.
251 BookmarkViewMode preference = BookmarkViewMode.NONE;
252 if (mMostVisited) {
Leon Scrogginsb3968bb2009-10-16 09:04:16 -0400253 // For the most visited page, only use list mode.
254 preference = BookmarkViewMode.LIST;
Ben Murdoch328ea872009-09-16 13:33:29 +0100255 } else {
256 preference = BookmarkViewMode.values()[p.getInt(
257 PREF_BOOKMARK_VIEW_MODE, BookmarkViewMode.GRID.ordinal())];
258 }
259 switchViewMode(preference);
Leon Scroggins892df312009-07-14 14:48:02 -0400260 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800261
Leon Scroggins892df312009-07-14 14:48:02 -0400262 /**
263 * Set the ContentView to be either the grid of thumbnails or the vertical
Ben Murdoch328ea872009-09-16 13:33:29 +0100264 * list.
Leon Scroggins892df312009-07-14 14:48:02 -0400265 */
Leon Scrogginsea002572009-11-24 15:21:18 -0500266 private void switchViewMode(BookmarkViewMode viewMode) {
267 if (mViewMode == viewMode) {
Ben Murdoch328ea872009-09-16 13:33:29 +0100268 return;
269 }
270
Leon Scrogginsea002572009-11-24 15:21:18 -0500271 mViewMode = viewMode;
Ben Murdoch328ea872009-09-16 13:33:29 +0100272
273 // Update the preferences to make the new view mode sticky.
274 Editor ed = getPreferences(MODE_PRIVATE).edit();
275 if (mMostVisited) {
276 ed.putInt(PREF_MOST_VISITED_VIEW_MODE, mViewMode.ordinal());
277 } else {
278 ed.putInt(PREF_BOOKMARK_VIEW_MODE, mViewMode.ordinal());
279 }
280 ed.commit();
281
Leon Scrogginsea002572009-11-24 15:21:18 -0500282 if (mBookmarksAdapter != null) {
283 mBookmarksAdapter.switchViewMode(viewMode);
284 }
Ben Murdoch328ea872009-09-16 13:33:29 +0100285 if (mViewMode == BookmarkViewMode.GRID) {
Leon Scroggins892df312009-07-14 14:48:02 -0400286 if (mGridPage == null) {
Leon Scroggins89c6d362009-07-15 16:54:37 -0400287 mGridPage = new GridView(this);
Leon Scrogginsea002572009-11-24 15:21:18 -0500288 if (mBookmarksAdapter != null) {
289 mGridPage.setAdapter(mBookmarksAdapter);
290 }
Leon Scroggins892df312009-07-14 14:48:02 -0400291 mGridPage.setOnItemClickListener(mListener);
Leon Scroggins89c6d362009-07-15 16:54:37 -0400292 mGridPage.setNumColumns(GridView.AUTO_FIT);
Leon Scrogginsf8551612009-09-24 16:06:02 -0400293 mGridPage.setColumnWidth(
294 BrowserActivity.getDesiredThumbnailWidth(this));
Leon Scroggins89c6d362009-07-15 16:54:37 -0400295 mGridPage.setFocusable(true);
296 mGridPage.setFocusableInTouchMode(true);
297 mGridPage.setSelector(android.R.drawable.gallery_thumb);
Leon Scrogginsf8551612009-09-24 16:06:02 -0400298 float density = getResources().getDisplayMetrics().density;
299 mGridPage.setVerticalSpacing((int) (14 * density));
300 mGridPage.setHorizontalSpacing((int) (8 * density));
301 mGridPage.setStretchMode(GridView.STRETCH_SPACING);
Leon Scrogginsbbe6d5b2009-09-28 12:01:00 -0400302 mGridPage.setScrollBarStyle(View.SCROLLBARS_INSIDE_INSET);
Leon Scrogginsf8551612009-09-24 16:06:02 -0400303 mGridPage.setDrawSelectorOnTop(true);
Leon Scrogginsa5d669e2009-08-05 14:07:58 -0400304 if (mMostVisited) {
305 mGridPage.setEmptyView(mEmptyView);
306 }
Leon Scroggins892df312009-07-14 14:48:02 -0400307 if (!mCreateShortcut) {
308 mGridPage.setOnCreateContextMenuListener(this);
309 }
310 }
Leon Scrogginsd87f85e2009-08-18 14:13:31 -0400311 addContentView(mGridPage, FULL_SCREEN_PARAMS);
312 if (mVerticalList != null) {
313 ViewGroup parent = (ViewGroup) mVerticalList.getParent();
314 if (parent != null) {
315 parent.removeView(mVerticalList);
316 }
317 }
Leon Scroggins892df312009-07-14 14:48:02 -0400318 } else {
319 if (null == mVerticalList) {
Leon Scrogginsd87f85e2009-08-18 14:13:31 -0400320 ListView listView = new ListView(this);
Leon Scrogginsea002572009-11-24 15:21:18 -0500321 if (mBookmarksAdapter != null) {
322 listView.setAdapter(mBookmarksAdapter);
323 }
Leon Scroggins892df312009-07-14 14:48:02 -0400324 listView.setDrawSelectorOnTop(false);
325 listView.setVerticalScrollBarEnabled(true);
326 listView.setOnItemClickListener(mListener);
Leon Scrogginsa5d669e2009-08-05 14:07:58 -0400327 if (mMostVisited) {
328 listView.setEmptyView(mEmptyView);
329 }
Leon Scroggins892df312009-07-14 14:48:02 -0400330 if (!mCreateShortcut) {
331 listView.setOnCreateContextMenuListener(this);
332 }
Leon Scrogginsd87f85e2009-08-18 14:13:31 -0400333 mVerticalList = listView;
Leon Scroggins892df312009-07-14 14:48:02 -0400334 }
Leon Scrogginsd87f85e2009-08-18 14:13:31 -0400335 addContentView(mVerticalList, FULL_SCREEN_PARAMS);
336 if (mGridPage != null) {
337 ViewGroup parent = (ViewGroup) mGridPage.getParent();
338 if (parent != null) {
339 parent.removeView(mGridPage);
340 }
341 }
Leon Scrogginsa5d669e2009-08-05 14:07:58 -0400342 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800343 }
344
Leon Scrogginsd87f85e2009-08-18 14:13:31 -0400345 private static final ViewGroup.LayoutParams FULL_SCREEN_PARAMS
346 = new ViewGroup.LayoutParams(
Romain Guy15b8ec62010-01-08 15:06:43 -0800347 ViewGroup.LayoutParams.MATCH_PARENT,
348 ViewGroup.LayoutParams.MATCH_PARENT);
Leon Scrogginsd87f85e2009-08-18 14:13:31 -0400349
The Android Open Source Project0c908882009-03-03 19:32:16 -0800350 private static final int SAVE_CURRENT_PAGE = 1000;
Leon Scrogginsea002572009-11-24 15:21:18 -0500351 private static final int CREATE_ADAPTER = 1001;
The Android Open Source Project0c908882009-03-03 19:32:16 -0800352 private final Handler mHandler = new Handler() {
353 @Override
354 public void handleMessage(Message msg) {
Leon Scrogginsea002572009-11-24 15:21:18 -0500355 switch (msg.what) {
356 case SAVE_CURRENT_PAGE:
357 saveCurrentPage();
358 break;
359 case CREATE_ADAPTER:
360 Intent intent = getIntent();
361 mBookmarksAdapter = new BrowserBookmarksAdapter(
362 BrowserBookmarksPage.this,
363 intent.getStringExtra("url"),
364 intent.getStringExtra("title"),
365 (Bitmap) intent.getParcelableExtra("thumbnail"),
366 mCreateShortcut,
367 mMostVisited);
368 mBookmarksAdapter.switchViewMode(mViewMode);
369 if (mGridPage != null) {
370 mGridPage.setAdapter(mBookmarksAdapter);
371 }
372 if (mVerticalList != null) {
373 mVerticalList.setAdapter(mBookmarksAdapter);
374 }
375 break;
The Android Open Source Project0c908882009-03-03 19:32:16 -0800376 }
377 }
378 };
379
380 private AdapterView.OnItemClickListener mListener = new AdapterView.OnItemClickListener() {
381 public void onItemClick(AdapterView parent, View v, int position, long id) {
382 // It is possible that the view has been canceled when we get to
383 // this point as back has a higher priority
384 if (mCanceled) {
Leon Scroggins892df312009-07-14 14:48:02 -0400385 android.util.Log.e(LOGTAG, "item clicked when dismissing");
The Android Open Source Project0c908882009-03-03 19:32:16 -0800386 return;
387 }
388 if (!mCreateShortcut) {
Leon Scrogginsa5d669e2009-08-05 14:07:58 -0400389 if (0 == position && !mMostVisited) {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800390 // XXX: Work-around for a framework issue.
391 mHandler.sendEmptyMessage(SAVE_CURRENT_PAGE);
392 } else {
393 loadUrl(position);
394 }
395 } else {
Patrick Scott3918d442009-08-04 13:22:29 -0400396 final Intent intent = createShortcutIntent(position);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800397 setResultToParent(RESULT_OK, intent);
398 finish();
399 }
400 }
401 };
402
Patrick Scott3918d442009-08-04 13:22:29 -0400403 private Intent createShortcutIntent(int position) {
404 String url = getUrl(position);
405 String title = getBookmarkTitle(position);
406 Bitmap touchIcon = getTouchIcon(position);
407
The Android Open Source Project0c908882009-03-03 19:32:16 -0800408 final Intent i = new Intent();
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700409 final Intent shortcutIntent = new Intent(Intent.ACTION_VIEW,
410 Uri.parse(url));
411 long urlHash = url.hashCode();
412 long uniqueId = (urlHash << 32) | shortcutIntent.hashCode();
413 shortcutIntent.putExtra(Browser.EXTRA_APPLICATION_ID,
414 Long.toString(uniqueId));
415 i.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800416 i.putExtra(Intent.EXTRA_SHORTCUT_NAME, title);
Patrick Scott3918d442009-08-04 13:22:29 -0400417 // Use the apple-touch-icon if available
418 if (touchIcon != null) {
419 // Make a copy so we can modify the pixels.
420 Bitmap copy = touchIcon.copy(Bitmap.Config.ARGB_8888, true);
Patrick Scotte09761e2009-03-24 20:43:37 -0700421 Canvas canvas = new Canvas(copy);
422
Patrick Scott3918d442009-08-04 13:22:29 -0400423 // Construct a path from a round rect. This will allow drawing with
424 // an inverse fill so we can punch a hole using the round rect.
425 Path path = new Path();
426 path.setFillType(Path.FillType.INVERSE_WINDING);
Patrick Scott59ce8302009-09-18 16:29:38 -0400427 RectF rect = new RectF(0, 0, touchIcon.getWidth(),
428 touchIcon.getHeight());
429 rect.inset(1, 1);
430 path.addRoundRect(rect, 8f, 8f, Path.Direction.CW);
Patrick Scotte09761e2009-03-24 20:43:37 -0700431
Patrick Scott3918d442009-08-04 13:22:29 -0400432 // Construct a paint that clears the outside of the rectangle and
433 // draw.
434 Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
435 paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
436 canvas.drawPath(path, paint);
Patrick Scotte09761e2009-03-24 20:43:37 -0700437
Patrick Scotte09761e2009-03-24 20:43:37 -0700438 i.putExtra(Intent.EXTRA_SHORTCUT_ICON, copy);
Patrick Scott3918d442009-08-04 13:22:29 -0400439 } else {
440 Bitmap favicon = getFavicon(position);
441 if (favicon == null) {
442 i.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE,
443 Intent.ShortcutIconResource.fromContext(
444 BrowserBookmarksPage.this,
445 R.drawable.ic_launcher_shortcut_browser_bookmark));
446 } else {
447 Bitmap icon = BitmapFactory.decodeResource(getResources(),
448 R.drawable.ic_launcher_shortcut_browser_bookmark);
449
450 // Make a copy of the regular icon so we can modify the pixels.
451 Bitmap copy = icon.copy(Bitmap.Config.ARGB_8888, true);
452 Canvas canvas = new Canvas(copy);
453
454 // Make a Paint for the white background rectangle and for
455 // filtering the favicon.
456 Paint p = new Paint(Paint.ANTI_ALIAS_FLAG
457 | Paint.FILTER_BITMAP_FLAG);
458 p.setStyle(Paint.Style.FILL_AND_STROKE);
459 p.setColor(Color.WHITE);
460
Patrick Scott4aff3aa2009-10-05 09:32:46 -0400461 float density = getResources().getDisplayMetrics().density;
Patrick Scott3918d442009-08-04 13:22:29 -0400462 // Create a rectangle that is slightly wider than the favicon
Patrick Scott4aff3aa2009-10-05 09:32:46 -0400463 final float iconSize = 16 * density; // 16x16 favicon
464 final float padding = 2; // white padding around icon
Patrick Scott3918d442009-08-04 13:22:29 -0400465 final float rectSize = iconSize + 2 * padding;
466 final float y = icon.getHeight() - rectSize;
467 RectF r = new RectF(0, y, rectSize, y + rectSize);
468
469 // Draw a white rounded rectangle behind the favicon
470 canvas.drawRoundRect(r, 2, 2, p);
471
472 // Draw the favicon in the same rectangle as the rounded
473 // rectangle but inset by the padding
474 // (results in a 16x16 favicon).
475 r.inset(padding, padding);
476 canvas.drawBitmap(favicon, null, r, p);
477 i.putExtra(Intent.EXTRA_SHORTCUT_ICON, copy);
478 }
Patrick Scotte09761e2009-03-24 20:43:37 -0700479 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800480 // Do not allow duplicate items
481 i.putExtra("duplicate", false);
482 return i;
483 }
484
485 private void saveCurrentPage() {
486 Intent i = new Intent(BrowserBookmarksPage.this,
487 AddBookmarkPage.class);
488 i.putExtras(getIntent());
489 startActivityForResult(i, BOOKMARKS_SAVE);
490 }
491
492 private void loadUrl(int position) {
493 Intent intent = (new Intent()).setAction(getUrl(position));
494 setResultToParent(RESULT_OK, intent);
495 finish();
496 }
497
498 @Override
499 public boolean onCreateOptionsMenu(Menu menu) {
500 boolean result = super.onCreateOptionsMenu(menu);
Leon Scrogginsb3968bb2009-10-16 09:04:16 -0400501 if (!mCreateShortcut && !mMostVisited) {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800502 MenuInflater inflater = getMenuInflater();
503 inflater.inflate(R.menu.bookmarks, menu);
504 return true;
505 }
506 return result;
507 }
508
509 @Override
Leon Scroggins0c786502009-08-04 16:04:55 -0400510 public boolean onPrepareOptionsMenu(Menu menu) {
Leon Scroggins8382d992009-08-19 11:25:14 -0400511 boolean result = super.onPrepareOptionsMenu(menu);
Leon Scrogginsea002572009-11-24 15:21:18 -0500512 if (mCreateShortcut || mMostVisited || mBookmarksAdapter == null
Leon Scrogginsb3968bb2009-10-16 09:04:16 -0400513 || mBookmarksAdapter.getCount() == 0) {
Leon Scrogginsa5d669e2009-08-05 14:07:58 -0400514 // No need to show the menu if there are no items.
Leon Scroggins8382d992009-08-19 11:25:14 -0400515 return result;
Leon Scrogginsa5d669e2009-08-05 14:07:58 -0400516 }
Leon Scrogginsfdd10d72009-09-25 13:01:45 -0400517 MenuItem switchItem = menu.findItem(R.id.switch_mode_menu_id);
518 int titleResId;
519 int iconResId;
520 if (mViewMode == BookmarkViewMode.GRID) {
521 titleResId = R.string.switch_to_list;
522 iconResId = R.drawable.ic_menu_list;
523 } else {
524 titleResId = R.string.switch_to_thumbnails;
525 iconResId = R.drawable.ic_menu_thumbnail;
526 }
527 switchItem.setTitle(titleResId);
528 switchItem.setIcon(iconResId);
Leon Scroggins0c786502009-08-04 16:04:55 -0400529 return true;
530 }
531
532 @Override
The Android Open Source Project0c908882009-03-03 19:32:16 -0800533 public boolean onOptionsItemSelected(MenuItem item) {
534 switch (item.getItemId()) {
Leon Scroggins892df312009-07-14 14:48:02 -0400535 case R.id.new_context_menu_id:
536 saveCurrentPage();
537 break;
538
539 case R.id.switch_mode_menu_id:
Ben Murdoch328ea872009-09-16 13:33:29 +0100540 if (mViewMode == BookmarkViewMode.GRID) {
541 switchViewMode(BookmarkViewMode.LIST);
542 } else {
543 switchViewMode(BookmarkViewMode.GRID);
544 }
Leon Scroggins892df312009-07-14 14:48:02 -0400545 break;
546
547 default:
548 return super.onOptionsItemSelected(item);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800549 }
550 return true;
551 }
552
553 private void openInNewWindow(int position) {
554 Bundle b = new Bundle();
555 b.putBoolean("new_window", true);
556 setResultToParent(RESULT_OK,
557 (new Intent()).setAction(getUrl(position)).putExtras(b));
558
559 finish();
560 }
561
562
563 private void editBookmark(int position) {
564 Intent intent = new Intent(BrowserBookmarksPage.this,
565 AddBookmarkPage.class);
566 intent.putExtra("bookmark", getRow(position));
567 startActivityForResult(intent, BOOKMARKS_SAVE);
568 }
569
570 @Override
571 protected void onActivityResult(int requestCode, int resultCode,
572 Intent data) {
573 switch(requestCode) {
574 case BOOKMARKS_SAVE:
575 if (resultCode == RESULT_OK) {
576 Bundle extras;
577 if (data != null && (extras = data.getExtras()) != null) {
578 // If there are extras, then we need to save
579 // the edited bookmark. This is done in updateRow()
580 String title = extras.getString("title");
581 String url = extras.getString("url");
582 if (title != null && url != null) {
583 mBookmarksAdapter.updateRow(extras);
584 }
585 } else {
586 // extras == null then a new bookmark was added to
587 // the database.
588 refreshList();
589 }
590 }
591 break;
592 default:
593 break;
594 }
595 }
596
597 private void displayRemoveBookmarkDialog(int position) {
598 // Put up a dialog asking if the user really wants to
599 // delete the bookmark
600 final int deletePos = position;
601 new AlertDialog.Builder(this)
602 .setTitle(R.string.delete_bookmark)
603 .setIcon(android.R.drawable.ic_dialog_alert)
604 .setMessage(getText(R.string.delete_bookmark_warning).toString().replace(
605 "%s", getBookmarkTitle(deletePos)))
606 .setPositiveButton(R.string.ok,
607 new DialogInterface.OnClickListener() {
608 public void onClick(DialogInterface dialog, int whichButton) {
609 deleteBookmark(deletePos);
610 }
611 })
612 .setNegativeButton(R.string.cancel, null)
613 .show();
614 }
615
616 /**
617 * Refresh the shown list after the database has changed.
618 */
Leon Scroggins892df312009-07-14 14:48:02 -0400619 private void refreshList() {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800620 mBookmarksAdapter.refreshList();
621 }
622
623 /**
624 * Return a hashmap representing the currently highlighted row.
625 */
626 public Bundle getRow(int position) {
Leon Scrogginsea002572009-11-24 15:21:18 -0500627 return mBookmarksAdapter == null ? null
628 : mBookmarksAdapter.getRow(position);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800629 }
630
631 /**
632 * Return the url of the currently highlighted row.
633 */
634 public String getUrl(int position) {
Leon Scrogginsea002572009-11-24 15:21:18 -0500635 return mBookmarksAdapter == null ? null
636 : mBookmarksAdapter.getUrl(position);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800637 }
638
Patrick Scotte09761e2009-03-24 20:43:37 -0700639 /**
640 * Return the favicon of the currently highlighted row.
641 */
642 public Bitmap getFavicon(int position) {
Leon Scrogginsea002572009-11-24 15:21:18 -0500643 return mBookmarksAdapter == null ? null
644 : mBookmarksAdapter.getFavicon(position);
Patrick Scotte09761e2009-03-24 20:43:37 -0700645 }
646
Patrick Scott3918d442009-08-04 13:22:29 -0400647 private Bitmap getTouchIcon(int position) {
Leon Scrogginsea002572009-11-24 15:21:18 -0500648 return mBookmarksAdapter == null ? null
649 : mBookmarksAdapter.getTouchIcon(position);
Patrick Scott3918d442009-08-04 13:22:29 -0400650 }
651
The Android Open Source Project0c908882009-03-03 19:32:16 -0800652 private void copy(CharSequence text) {
653 try {
654 IClipboard clip = IClipboard.Stub.asInterface(ServiceManager.getService("clipboard"));
655 if (clip != null) {
656 clip.setClipboardText(text);
657 }
658 } catch (android.os.RemoteException e) {
659 Log.e(LOGTAG, "Copy failed", e);
660 }
661 }
662
663 public String getBookmarkTitle(int position) {
Leon Scrogginsea002572009-11-24 15:21:18 -0500664 return mBookmarksAdapter == null ? null
665 : mBookmarksAdapter.getTitle(position);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800666 }
667
668 /**
669 * Delete the currently highlighted row.
670 */
671 public void deleteBookmark(int position) {
Leon Scrogginsea002572009-11-24 15:21:18 -0500672 if (mBookmarksAdapter == null) return;
The Android Open Source Project0c908882009-03-03 19:32:16 -0800673 mBookmarksAdapter.deleteRow(position);
674 }
Grace Kloba5942df02009-09-18 11:48:29 -0700675
676 @Override
677 public void onBackPressed() {
678 setResultToParent(RESULT_CANCELED, null);
679 mCanceled = true;
680 super.onBackPressed();
The Android Open Source Project0c908882009-03-03 19:32:16 -0800681 }
682
Leon Scrogginsfde97462010-01-11 13:06:21 -0500683 // This Activity is generally a sub-Activity of
684 // CombinedBookmarkHistoryActivity. In that situation, we need to pass our
685 // result code up to our parent. However, if someone calls this Activity
686 // directly, then this has no parent, and it needs to set it on itself.
The Android Open Source Project0c908882009-03-03 19:32:16 -0800687 private void setResultToParent(int resultCode, Intent data) {
Leon Scrogginsfde97462010-01-11 13:06:21 -0500688 Activity parent = getParent();
689 if (parent == null) {
690 setResult(resultCode, data);
691 } else {
692 ((CombinedBookmarkHistoryActivity) parent).setResultFromChild(
693 resultCode, data);
694 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800695 }
696}