blob: 7560c78af23d5b475e3a5714b7a7079657f3374e [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;
Patrick Scottf49ecd62010-04-09 12:43:26 -040036import android.os.AsyncTask;
The Android Open Source Project0c908882009-03-03 19:32:16 -080037import android.os.Bundle;
38import android.os.Handler;
39import android.os.Message;
40import android.os.ServiceManager;
41import android.provider.Browser;
42import android.text.IClipboard;
43import android.util.Log;
44import android.view.ContextMenu;
The Android Open Source Project0c908882009-03-03 19:32:16 -080045import android.view.Menu;
46import android.view.MenuInflater;
47import android.view.MenuItem;
48import android.view.View;
49import android.view.ViewGroup;
50import android.view.ContextMenu.ContextMenuInfo;
Patrick Scottc1cf63a2010-03-09 16:02:08 -050051import android.webkit.WebIconDatabase.IconListener;
The Android Open Source Project0c908882009-03-03 19:32:16 -080052import android.widget.AdapterView;
Leon Scroggins89c6d362009-07-15 16:54:37 -040053import android.widget.GridView;
The Android Open Source Project0c908882009-03-03 19:32:16 -080054import android.widget.ListView;
Leon Scrogginsfeb941d2009-05-28 17:27:38 -040055import android.widget.Toast;
The Android Open Source Project0c908882009-03-03 19:32:16 -080056
Ben Murdoch328ea872009-09-16 13:33:29 +010057/*package*/ enum BookmarkViewMode { NONE, GRID, LIST }
The Android Open Source Project0c908882009-03-03 19:32:16 -080058/**
59 * View showing the user's bookmarks in the browser.
60 */
Nicolas Catania095292f2010-03-15 09:00:14 -070061public class BrowserBookmarksPage extends Activity implements
The Android Open Source Project0c908882009-03-03 19:32:16 -080062 View.OnCreateContextMenuListener {
63
Ben Murdoch328ea872009-09-16 13:33:29 +010064 private BookmarkViewMode mViewMode = BookmarkViewMode.NONE;
Leon Scroggins89c6d362009-07-15 16:54:37 -040065 private GridView mGridPage;
Leon Scrogginsea002572009-11-24 15:21:18 -050066 private ListView mVerticalList;
The Android Open Source Project0c908882009-03-03 19:32:16 -080067 private BrowserBookmarksAdapter mBookmarksAdapter;
68 private static final int BOOKMARKS_SAVE = 1;
Leon Scroggins190095d2009-08-17 17:01:38 -040069 private boolean mDisableNewWindow;
The Android Open Source Project0c908882009-03-03 19:32:16 -080070 private BookmarkItem mContextHeader;
71 private AddNewBookmark mAddHeader;
72 private boolean mCanceled = false;
73 private boolean mCreateShortcut;
Leon Scrogginsa5d669e2009-08-05 14:07:58 -040074 private boolean mMostVisited;
75 private View mEmptyView;
Patrick Scott152794b2010-03-30 13:57:29 -040076 private int mIconSize;
The Android Open Source Project0c908882009-03-03 19:32:16 -080077 // XXX: There is no public string defining this intent so if Home changes
78 // the value, we have to update this string.
79 private static final String INSTALL_SHORTCUT =
80 "com.android.launcher.action.INSTALL_SHORTCUT";
Nicolas Catania095292f2010-03-15 09:00:14 -070081
The Android Open Source Project0c908882009-03-03 19:32:16 -080082 private final static String LOGTAG = "browser";
Ben Murdoch328ea872009-09-16 13:33:29 +010083 private final static String PREF_BOOKMARK_VIEW_MODE = "pref_bookmark_view_mode";
84 private final static String PREF_MOST_VISITED_VIEW_MODE = "pref_most_visited_view_mode";
The Android Open Source Project0c908882009-03-03 19:32:16 -080085
86 @Override
87 public boolean onContextItemSelected(MenuItem item) {
88 // It is possible that the view has been canceled when we get to
Nicolas Catania095292f2010-03-15 09:00:14 -070089 // this point as back has a higher priority
The Android Open Source Project0c908882009-03-03 19:32:16 -080090 if (mCanceled) {
91 return true;
92 }
Nicolas Catania095292f2010-03-15 09:00:14 -070093 AdapterView.AdapterContextMenuInfo i =
The Android Open Source Project0c908882009-03-03 19:32:16 -080094 (AdapterView.AdapterContextMenuInfo)item.getMenuInfo();
95 // If we have no menu info, we can't tell which item was selected.
96 if (i == null) {
97 return true;
98 }
Nicolas Catania095292f2010-03-15 09:00:14 -070099
The Android Open Source Project0c908882009-03-03 19:32:16 -0800100 switch (item.getItemId()) {
101 case R.id.new_context_menu_id:
102 saveCurrentPage();
103 break;
104 case R.id.open_context_menu_id:
105 loadUrl(i.position);
106 break;
107 case R.id.edit_context_menu_id:
108 editBookmark(i.position);
109 break;
110 case R.id.shortcut_context_menu_id:
Patrick Scott3918d442009-08-04 13:22:29 -0400111 final Intent send = createShortcutIntent(i.position);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800112 send.setAction(INSTALL_SHORTCUT);
113 sendBroadcast(send);
114 break;
115 case R.id.delete_context_menu_id:
Leon Scrogginsa5d669e2009-08-05 14:07:58 -0400116 if (mMostVisited) {
117 Browser.deleteFromHistory(getContentResolver(),
118 getUrl(i.position));
119 refreshList();
120 } else {
121 displayRemoveBookmarkDialog(i.position);
122 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800123 break;
124 case R.id.new_window_context_menu_id:
125 openInNewWindow(i.position);
126 break;
Leon Scrogginsa5d669e2009-08-05 14:07:58 -0400127 case R.id.share_link_context_menu_id:
Leon Scroggins96afcb12009-12-10 12:35:56 -0500128 BrowserActivity.sharePage(BrowserBookmarksPage.this,
129 mBookmarksAdapter.getTitle(i.position), getUrl(i.position),
130 getFavicon(i.position),
131 mBookmarksAdapter.getScreenshot(i.position));
The Android Open Source Project0c908882009-03-03 19:32:16 -0800132 break;
133 case R.id.copy_url_context_menu_id:
134 copy(getUrl(i.position));
Leon Scrogginsfeb941d2009-05-28 17:27:38 -0400135 break;
136 case R.id.homepage_context_menu_id:
137 BrowserSettings.getInstance().setHomePage(this,
138 getUrl(i.position));
139 Toast.makeText(this, R.string.homepage_set,
140 Toast.LENGTH_LONG).show();
141 break;
Leon Scrogginsa5d669e2009-08-05 14:07:58 -0400142 // Only for the Most visited page
143 case R.id.save_to_bookmarks_menu_id:
Leon Scrogginsc1f57592009-08-14 14:16:10 -0400144 boolean isBookmark;
145 String name;
146 String url;
Ben Murdoch328ea872009-09-16 13:33:29 +0100147 if (mViewMode == BookmarkViewMode.GRID) {
Leon Scrogginsc1f57592009-08-14 14:16:10 -0400148 isBookmark = mBookmarksAdapter.getIsBookmark(i.position);
149 name = mBookmarksAdapter.getTitle(i.position);
150 url = mBookmarksAdapter.getUrl(i.position);
151 } else {
152 HistoryItem historyItem = ((HistoryItem) i.targetView);
153 isBookmark = historyItem.isBookmark();
154 name = historyItem.getName();
155 url = historyItem.getUrl();
156 }
Leon Scrogginsa5d669e2009-08-05 14:07:58 -0400157 // If the site is bookmarked, the item becomes remove from
158 // bookmarks.
Leon Scrogginsc1f57592009-08-14 14:16:10 -0400159 if (isBookmark) {
Andrei Popescuc9526192009-09-23 15:52:16 +0100160 Bookmarks.removeFromBookmarks(this, getContentResolver(), url, name);
Leon Scrogginsa5d669e2009-08-05 14:07:58 -0400161 } else {
Leon Scrogginsc1f57592009-08-14 14:16:10 -0400162 Browser.saveBookmark(this, name, url);
Leon Scrogginsa5d669e2009-08-05 14:07:58 -0400163 }
164 break;
The Android Open Source Project0c908882009-03-03 19:32:16 -0800165 default:
166 return super.onContextItemSelected(item);
167 }
168 return true;
169 }
170
171 @Override
172 public void onCreateContextMenu(ContextMenu menu, View v,
173 ContextMenuInfo menuInfo) {
Nicolas Catania095292f2010-03-15 09:00:14 -0700174 AdapterView.AdapterContextMenuInfo i =
The Android Open Source Project0c908882009-03-03 19:32:16 -0800175 (AdapterView.AdapterContextMenuInfo) menuInfo;
176
177 MenuInflater inflater = getMenuInflater();
Leon Scrogginsa5d669e2009-08-05 14:07:58 -0400178 if (mMostVisited) {
179 inflater.inflate(R.menu.historycontext, menu);
180 } else {
181 inflater.inflate(R.menu.bookmarkscontext, menu);
182 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800183
Leon Scrogginsa5d669e2009-08-05 14:07:58 -0400184 if (0 == i.position && !mMostVisited) {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800185 menu.setGroupVisible(R.id.CONTEXT_MENU, false);
186 if (mAddHeader == null) {
187 mAddHeader = new AddNewBookmark(BrowserBookmarksPage.this);
188 } else if (mAddHeader.getParent() != null) {
189 ((ViewGroup) mAddHeader.getParent()).
190 removeView(mAddHeader);
191 }
Leon Scroggins892df312009-07-14 14:48:02 -0400192 mAddHeader.setUrl(getIntent().getStringExtra("url"));
The Android Open Source Project0c908882009-03-03 19:32:16 -0800193 menu.setHeaderView(mAddHeader);
194 return;
195 }
Leon Scrogginsa5d669e2009-08-05 14:07:58 -0400196 if (mMostVisited) {
Ben Murdoch328ea872009-09-16 13:33:29 +0100197 if ((mViewMode == BookmarkViewMode.LIST
198 && ((HistoryItem) i.targetView).isBookmark())
Leon Scrogginsa5d669e2009-08-05 14:07:58 -0400199 || mBookmarksAdapter.getIsBookmark(i.position)) {
200 MenuItem item = menu.findItem(
201 R.id.save_to_bookmarks_menu_id);
202 item.setTitle(R.string.remove_from_bookmarks);
203 }
204 } else {
205 // The historycontext menu has no ADD_MENU group.
206 menu.setGroupVisible(R.id.ADD_MENU, false);
207 }
Leon Scroggins190095d2009-08-17 17:01:38 -0400208 if (mDisableNewWindow) {
Leon Scroggins892df312009-07-14 14:48:02 -0400209 menu.findItem(R.id.new_window_context_menu_id).setVisible(
210 false);
211 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800212 if (mContextHeader == null) {
213 mContextHeader = new BookmarkItem(BrowserBookmarksPage.this);
214 } else if (mContextHeader.getParent() != null) {
215 ((ViewGroup) mContextHeader.getParent()).
216 removeView(mContextHeader);
217 }
Ben Murdoch328ea872009-09-16 13:33:29 +0100218 if (mViewMode == BookmarkViewMode.GRID) {
Leon Scroggins892df312009-07-14 14:48:02 -0400219 mBookmarksAdapter.populateBookmarkItem(mContextHeader,
220 i.position);
221 } else {
222 BookmarkItem b = (BookmarkItem) i.targetView;
223 b.copyTo(mContextHeader);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800224 }
Leon Scroggins892df312009-07-14 14:48:02 -0400225 menu.setHeaderView(mContextHeader);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800226 }
227
228 /**
229 * Create a new BrowserBookmarksPage.
Nicolas Catania095292f2010-03-15 09:00:14 -0700230 */
The Android Open Source Project0c908882009-03-03 19:32:16 -0800231 @Override
232 protected void onCreate(Bundle icicle) {
233 super.onCreate(icicle);
234
Patrick Scott152794b2010-03-30 13:57:29 -0400235 // Grab the app icon size as a resource.
236 mIconSize = getResources().getDimensionPixelSize(
237 android.R.dimen.app_icon_size);
238
Patrick Scottf49ecd62010-04-09 12:43:26 -0400239 Intent intent = getIntent();
240 if (Intent.ACTION_CREATE_SHORTCUT.equals(intent.getAction())) {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800241 mCreateShortcut = true;
242 }
Patrick Scottf49ecd62010-04-09 12:43:26 -0400243 mDisableNewWindow = intent.getBooleanExtra("disable_new_window",
Leon Scroggins190095d2009-08-17 17:01:38 -0400244 false);
Patrick Scottf49ecd62010-04-09 12:43:26 -0400245 mMostVisited = intent.getBooleanExtra("mostVisited", false);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800246
Leon Scrogginsa5d669e2009-08-05 14:07:58 -0400247 if (mCreateShortcut) {
248 setTitle(R.string.browser_bookmarks_page_bookmarks_text);
249 }
Leon Scrogginsd87f85e2009-08-18 14:13:31 -0400250
251 setContentView(R.layout.empty_history);
252 mEmptyView = findViewById(R.id.empty_view);
253 mEmptyView.setVisibility(View.GONE);
254
Ben Murdoch328ea872009-09-16 13:33:29 +0100255 SharedPreferences p = getPreferences(MODE_PRIVATE);
256
257 // See if the user has set a preference for the view mode of their
258 // bookmarks. Otherwise default to grid mode.
259 BookmarkViewMode preference = BookmarkViewMode.NONE;
260 if (mMostVisited) {
Leon Scrogginsb3968bb2009-10-16 09:04:16 -0400261 // For the most visited page, only use list mode.
262 preference = BookmarkViewMode.LIST;
Ben Murdoch328ea872009-09-16 13:33:29 +0100263 } else {
264 preference = BookmarkViewMode.values()[p.getInt(
265 PREF_BOOKMARK_VIEW_MODE, BookmarkViewMode.GRID.ordinal())];
266 }
267 switchViewMode(preference);
Patrick Scottf49ecd62010-04-09 12:43:26 -0400268
269 final boolean createShortcut = mCreateShortcut;
270 final boolean mostVisited = mMostVisited;
271 final String url = intent.getStringExtra("url");
272 final String title = intent.getStringExtra("title");
273 final Bitmap thumbnail =
274 (Bitmap) intent.getParcelableExtra("thumbnail");
275 new AsyncTask<Void, Void, Void>() {
276 @Override
277 protected Void doInBackground(Void... unused) {
278 BrowserBookmarksAdapter adapter = new BrowserBookmarksAdapter(
279 BrowserBookmarksPage.this,
280 url,
281 title,
282 thumbnail,
283 createShortcut,
284 mostVisited);
285 mHandler.obtainMessage(ADAPTER_CREATED, adapter).sendToTarget();
286 return null;
287 }
288 }.execute();
Leon Scroggins892df312009-07-14 14:48:02 -0400289 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800290
Nicolas Catania095292f2010-03-15 09:00:14 -0700291 @Override
292 protected void onDestroy() {
293 mHandler.removeCallbacksAndMessages(null);
294 super.onDestroy();
295 }
296
Leon Scroggins892df312009-07-14 14:48:02 -0400297 /**
298 * Set the ContentView to be either the grid of thumbnails or the vertical
Ben Murdoch328ea872009-09-16 13:33:29 +0100299 * list.
Leon Scroggins892df312009-07-14 14:48:02 -0400300 */
Leon Scrogginsea002572009-11-24 15:21:18 -0500301 private void switchViewMode(BookmarkViewMode viewMode) {
302 if (mViewMode == viewMode) {
Ben Murdoch328ea872009-09-16 13:33:29 +0100303 return;
304 }
305
Leon Scrogginsea002572009-11-24 15:21:18 -0500306 mViewMode = viewMode;
Ben Murdoch328ea872009-09-16 13:33:29 +0100307
308 // Update the preferences to make the new view mode sticky.
309 Editor ed = getPreferences(MODE_PRIVATE).edit();
310 if (mMostVisited) {
311 ed.putInt(PREF_MOST_VISITED_VIEW_MODE, mViewMode.ordinal());
312 } else {
313 ed.putInt(PREF_BOOKMARK_VIEW_MODE, mViewMode.ordinal());
314 }
315 ed.commit();
316
Leon Scrogginsea002572009-11-24 15:21:18 -0500317 if (mBookmarksAdapter != null) {
318 mBookmarksAdapter.switchViewMode(viewMode);
319 }
Ben Murdoch328ea872009-09-16 13:33:29 +0100320 if (mViewMode == BookmarkViewMode.GRID) {
Leon Scroggins892df312009-07-14 14:48:02 -0400321 if (mGridPage == null) {
Leon Scroggins89c6d362009-07-15 16:54:37 -0400322 mGridPage = new GridView(this);
Leon Scrogginsea002572009-11-24 15:21:18 -0500323 if (mBookmarksAdapter != null) {
324 mGridPage.setAdapter(mBookmarksAdapter);
325 }
Leon Scroggins892df312009-07-14 14:48:02 -0400326 mGridPage.setOnItemClickListener(mListener);
Leon Scroggins89c6d362009-07-15 16:54:37 -0400327 mGridPage.setNumColumns(GridView.AUTO_FIT);
Leon Scrogginsf8551612009-09-24 16:06:02 -0400328 mGridPage.setColumnWidth(
329 BrowserActivity.getDesiredThumbnailWidth(this));
Leon Scroggins89c6d362009-07-15 16:54:37 -0400330 mGridPage.setFocusable(true);
331 mGridPage.setFocusableInTouchMode(true);
332 mGridPage.setSelector(android.R.drawable.gallery_thumb);
Leon Scrogginsf8551612009-09-24 16:06:02 -0400333 float density = getResources().getDisplayMetrics().density;
334 mGridPage.setVerticalSpacing((int) (14 * density));
335 mGridPage.setHorizontalSpacing((int) (8 * density));
336 mGridPage.setStretchMode(GridView.STRETCH_SPACING);
Leon Scrogginsbbe6d5b2009-09-28 12:01:00 -0400337 mGridPage.setScrollBarStyle(View.SCROLLBARS_INSIDE_INSET);
Leon Scrogginsf8551612009-09-24 16:06:02 -0400338 mGridPage.setDrawSelectorOnTop(true);
Leon Scrogginsa5d669e2009-08-05 14:07:58 -0400339 if (mMostVisited) {
340 mGridPage.setEmptyView(mEmptyView);
341 }
Leon Scroggins892df312009-07-14 14:48:02 -0400342 if (!mCreateShortcut) {
343 mGridPage.setOnCreateContextMenuListener(this);
344 }
345 }
Leon Scrogginsd87f85e2009-08-18 14:13:31 -0400346 addContentView(mGridPage, FULL_SCREEN_PARAMS);
347 if (mVerticalList != null) {
348 ViewGroup parent = (ViewGroup) mVerticalList.getParent();
349 if (parent != null) {
350 parent.removeView(mVerticalList);
351 }
352 }
Leon Scroggins892df312009-07-14 14:48:02 -0400353 } else {
354 if (null == mVerticalList) {
Leon Scrogginsd87f85e2009-08-18 14:13:31 -0400355 ListView listView = new ListView(this);
Leon Scrogginsea002572009-11-24 15:21:18 -0500356 if (mBookmarksAdapter != null) {
357 listView.setAdapter(mBookmarksAdapter);
358 }
Leon Scroggins892df312009-07-14 14:48:02 -0400359 listView.setDrawSelectorOnTop(false);
360 listView.setVerticalScrollBarEnabled(true);
361 listView.setOnItemClickListener(mListener);
Leon Scrogginsa5d669e2009-08-05 14:07:58 -0400362 if (mMostVisited) {
363 listView.setEmptyView(mEmptyView);
364 }
Leon Scroggins892df312009-07-14 14:48:02 -0400365 if (!mCreateShortcut) {
366 listView.setOnCreateContextMenuListener(this);
367 }
Leon Scrogginsd87f85e2009-08-18 14:13:31 -0400368 mVerticalList = listView;
Leon Scroggins892df312009-07-14 14:48:02 -0400369 }
Leon Scrogginsd87f85e2009-08-18 14:13:31 -0400370 addContentView(mVerticalList, FULL_SCREEN_PARAMS);
371 if (mGridPage != null) {
372 ViewGroup parent = (ViewGroup) mGridPage.getParent();
373 if (parent != null) {
374 parent.removeView(mGridPage);
375 }
376 }
Leon Scrogginsa5d669e2009-08-05 14:07:58 -0400377 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800378 }
379
Leon Scrogginsd87f85e2009-08-18 14:13:31 -0400380 private static final ViewGroup.LayoutParams FULL_SCREEN_PARAMS
381 = new ViewGroup.LayoutParams(
Romain Guy15b8ec62010-01-08 15:06:43 -0800382 ViewGroup.LayoutParams.MATCH_PARENT,
383 ViewGroup.LayoutParams.MATCH_PARENT);
Leon Scrogginsd87f85e2009-08-18 14:13:31 -0400384
The Android Open Source Project0c908882009-03-03 19:32:16 -0800385 private static final int SAVE_CURRENT_PAGE = 1000;
Patrick Scottf49ecd62010-04-09 12:43:26 -0400386 private static final int ADAPTER_CREATED = 1001;
The Android Open Source Project0c908882009-03-03 19:32:16 -0800387 private final Handler mHandler = new Handler() {
388 @Override
389 public void handleMessage(Message msg) {
Leon Scrogginsea002572009-11-24 15:21:18 -0500390 switch (msg.what) {
391 case SAVE_CURRENT_PAGE:
392 saveCurrentPage();
393 break;
Patrick Scottf49ecd62010-04-09 12:43:26 -0400394 case ADAPTER_CREATED:
395 mBookmarksAdapter = (BrowserBookmarksAdapter) msg.obj;
Leon Scrogginsea002572009-11-24 15:21:18 -0500396 mBookmarksAdapter.switchViewMode(mViewMode);
397 if (mGridPage != null) {
398 mGridPage.setAdapter(mBookmarksAdapter);
399 }
400 if (mVerticalList != null) {
401 mVerticalList.setAdapter(mBookmarksAdapter);
402 }
Patrick Scottc1cf63a2010-03-09 16:02:08 -0500403 // Add our own listener in case there are favicons that
404 // have yet to be loaded.
405 if (mMostVisited) {
406 IconListener listener = new IconListener() {
407 public void onReceivedIcon(String url,
408 Bitmap icon) {
409 if (mGridPage != null) {
410 mGridPage.setAdapter(mBookmarksAdapter);
411 }
412 if (mVerticalList != null) {
413 mVerticalList.setAdapter(mBookmarksAdapter);
414 }
415 }
416 };
417 CombinedBookmarkHistoryActivity.getIconListenerSet()
418 .addListener(listener);
419 }
Leon Scrogginsea002572009-11-24 15:21:18 -0500420 break;
The Android Open Source Project0c908882009-03-03 19:32:16 -0800421 }
422 }
423 };
424
425 private AdapterView.OnItemClickListener mListener = new AdapterView.OnItemClickListener() {
426 public void onItemClick(AdapterView parent, View v, int position, long id) {
427 // It is possible that the view has been canceled when we get to
Nicolas Catania095292f2010-03-15 09:00:14 -0700428 // this point as back has a higher priority
The Android Open Source Project0c908882009-03-03 19:32:16 -0800429 if (mCanceled) {
Leon Scroggins892df312009-07-14 14:48:02 -0400430 android.util.Log.e(LOGTAG, "item clicked when dismissing");
The Android Open Source Project0c908882009-03-03 19:32:16 -0800431 return;
432 }
433 if (!mCreateShortcut) {
Leon Scrogginsa5d669e2009-08-05 14:07:58 -0400434 if (0 == position && !mMostVisited) {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800435 // XXX: Work-around for a framework issue.
436 mHandler.sendEmptyMessage(SAVE_CURRENT_PAGE);
437 } else {
438 loadUrl(position);
439 }
440 } else {
Patrick Scott3918d442009-08-04 13:22:29 -0400441 final Intent intent = createShortcutIntent(position);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800442 setResultToParent(RESULT_OK, intent);
443 finish();
444 }
445 }
446 };
447
Patrick Scott3918d442009-08-04 13:22:29 -0400448 private Intent createShortcutIntent(int position) {
449 String url = getUrl(position);
450 String title = getBookmarkTitle(position);
451 Bitmap touchIcon = getTouchIcon(position);
452
The Android Open Source Project0c908882009-03-03 19:32:16 -0800453 final Intent i = new Intent();
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700454 final Intent shortcutIntent = new Intent(Intent.ACTION_VIEW,
455 Uri.parse(url));
456 long urlHash = url.hashCode();
457 long uniqueId = (urlHash << 32) | shortcutIntent.hashCode();
458 shortcutIntent.putExtra(Browser.EXTRA_APPLICATION_ID,
459 Long.toString(uniqueId));
460 i.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800461 i.putExtra(Intent.EXTRA_SHORTCUT_NAME, title);
Patrick Scott3918d442009-08-04 13:22:29 -0400462 // Use the apple-touch-icon if available
463 if (touchIcon != null) {
Patrick Scott152794b2010-03-30 13:57:29 -0400464 // Make a copy so we can modify the pixels. We can't use
465 // createScaledBitmap or copy since they will preserve the config
466 // and lose the ability to add alpha.
467 Bitmap bm = Bitmap.createBitmap(mIconSize, mIconSize,
468 Bitmap.Config.ARGB_8888);
469 Canvas canvas = new Canvas(bm);
470 Rect src = new Rect(0, 0, touchIcon.getWidth(),
471 touchIcon.getHeight());
472 Rect dest = new Rect(0, 0, bm.getWidth(), bm.getHeight());
473
474 // Paint used for scaling the bitmap and drawing the rounded rect.
475 Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
476 paint.setFilterBitmap(true);
477 canvas.drawBitmap(touchIcon, src, dest, paint);
Patrick Scotte09761e2009-03-24 20:43:37 -0700478
Patrick Scott3918d442009-08-04 13:22:29 -0400479 // Construct a path from a round rect. This will allow drawing with
480 // an inverse fill so we can punch a hole using the round rect.
481 Path path = new Path();
482 path.setFillType(Path.FillType.INVERSE_WINDING);
Patrick Scott152794b2010-03-30 13:57:29 -0400483 RectF rect = new RectF(0, 0, bm.getWidth(), bm.getHeight());
Patrick Scott59ce8302009-09-18 16:29:38 -0400484 rect.inset(1, 1);
485 path.addRoundRect(rect, 8f, 8f, Path.Direction.CW);
Patrick Scotte09761e2009-03-24 20:43:37 -0700486
Patrick Scott152794b2010-03-30 13:57:29 -0400487 // Reuse the paint and clear the outside of the rectangle.
Patrick Scott3918d442009-08-04 13:22:29 -0400488 paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
489 canvas.drawPath(path, paint);
Patrick Scotte09761e2009-03-24 20:43:37 -0700490
Patrick Scott152794b2010-03-30 13:57:29 -0400491 i.putExtra(Intent.EXTRA_SHORTCUT_ICON, bm);
Patrick Scott3918d442009-08-04 13:22:29 -0400492 } else {
493 Bitmap favicon = getFavicon(position);
494 if (favicon == null) {
495 i.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE,
496 Intent.ShortcutIconResource.fromContext(
497 BrowserBookmarksPage.this,
498 R.drawable.ic_launcher_shortcut_browser_bookmark));
499 } else {
500 Bitmap icon = BitmapFactory.decodeResource(getResources(),
Patrick Scottc0fdde92010-02-25 14:40:11 -0500501 R.drawable.ic_launcher_shortcut_browser_bookmark_icon);
Patrick Scott3918d442009-08-04 13:22:29 -0400502
503 // Make a copy of the regular icon so we can modify the pixels.
504 Bitmap copy = icon.copy(Bitmap.Config.ARGB_8888, true);
505 Canvas canvas = new Canvas(copy);
506
507 // Make a Paint for the white background rectangle and for
508 // filtering the favicon.
509 Paint p = new Paint(Paint.ANTI_ALIAS_FLAG
510 | Paint.FILTER_BITMAP_FLAG);
511 p.setStyle(Paint.Style.FILL_AND_STROKE);
512 p.setColor(Color.WHITE);
513
Patrick Scottc0fdde92010-02-25 14:40:11 -0500514 final float density =
515 getResources().getDisplayMetrics().density;
Patrick Scott3918d442009-08-04 13:22:29 -0400516 // Create a rectangle that is slightly wider than the favicon
Patrick Scott4aff3aa2009-10-05 09:32:46 -0400517 final float iconSize = 16 * density; // 16x16 favicon
Patrick Scottc0fdde92010-02-25 14:40:11 -0500518 final float padding = 2 * density; // white padding around icon
Patrick Scott3918d442009-08-04 13:22:29 -0400519 final float rectSize = iconSize + 2 * padding;
Patrick Scottc0fdde92010-02-25 14:40:11 -0500520
521 final Rect iconBounds =
522 new Rect(0, 0, icon.getWidth(), icon.getHeight());
523 final float x = iconBounds.exactCenterX() - (rectSize / 2);
524 // Note: Subtract 2 dip from the y position since the box is
525 // slightly higher than center. Use padding since it is already
526 // 2 * density.
527 final float y = iconBounds.exactCenterY() - (rectSize / 2)
528 - padding;
529 RectF r = new RectF(x, y, x + rectSize, y + rectSize);
Patrick Scott3918d442009-08-04 13:22:29 -0400530
531 // Draw a white rounded rectangle behind the favicon
532 canvas.drawRoundRect(r, 2, 2, p);
533
534 // Draw the favicon in the same rectangle as the rounded
535 // rectangle but inset by the padding
536 // (results in a 16x16 favicon).
537 r.inset(padding, padding);
538 canvas.drawBitmap(favicon, null, r, p);
539 i.putExtra(Intent.EXTRA_SHORTCUT_ICON, copy);
540 }
Patrick Scotte09761e2009-03-24 20:43:37 -0700541 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800542 // Do not allow duplicate items
543 i.putExtra("duplicate", false);
544 return i;
545 }
546
547 private void saveCurrentPage() {
548 Intent i = new Intent(BrowserBookmarksPage.this,
549 AddBookmarkPage.class);
550 i.putExtras(getIntent());
551 startActivityForResult(i, BOOKMARKS_SAVE);
552 }
553
554 private void loadUrl(int position) {
555 Intent intent = (new Intent()).setAction(getUrl(position));
556 setResultToParent(RESULT_OK, intent);
557 finish();
558 }
559
560 @Override
561 public boolean onCreateOptionsMenu(Menu menu) {
562 boolean result = super.onCreateOptionsMenu(menu);
Leon Scrogginsb3968bb2009-10-16 09:04:16 -0400563 if (!mCreateShortcut && !mMostVisited) {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800564 MenuInflater inflater = getMenuInflater();
565 inflater.inflate(R.menu.bookmarks, menu);
566 return true;
567 }
568 return result;
569 }
570
571 @Override
Leon Scroggins0c786502009-08-04 16:04:55 -0400572 public boolean onPrepareOptionsMenu(Menu menu) {
Leon Scroggins8382d992009-08-19 11:25:14 -0400573 boolean result = super.onPrepareOptionsMenu(menu);
Leon Scrogginsea002572009-11-24 15:21:18 -0500574 if (mCreateShortcut || mMostVisited || mBookmarksAdapter == null
Leon Scrogginsb3968bb2009-10-16 09:04:16 -0400575 || mBookmarksAdapter.getCount() == 0) {
Leon Scrogginsa5d669e2009-08-05 14:07:58 -0400576 // No need to show the menu if there are no items.
Leon Scroggins8382d992009-08-19 11:25:14 -0400577 return result;
Leon Scrogginsa5d669e2009-08-05 14:07:58 -0400578 }
Leon Scrogginsfdd10d72009-09-25 13:01:45 -0400579 MenuItem switchItem = menu.findItem(R.id.switch_mode_menu_id);
580 int titleResId;
581 int iconResId;
582 if (mViewMode == BookmarkViewMode.GRID) {
583 titleResId = R.string.switch_to_list;
584 iconResId = R.drawable.ic_menu_list;
585 } else {
586 titleResId = R.string.switch_to_thumbnails;
587 iconResId = R.drawable.ic_menu_thumbnail;
588 }
589 switchItem.setTitle(titleResId);
590 switchItem.setIcon(iconResId);
Leon Scroggins0c786502009-08-04 16:04:55 -0400591 return true;
592 }
593
594 @Override
The Android Open Source Project0c908882009-03-03 19:32:16 -0800595 public boolean onOptionsItemSelected(MenuItem item) {
596 switch (item.getItemId()) {
Leon Scroggins892df312009-07-14 14:48:02 -0400597 case R.id.new_context_menu_id:
598 saveCurrentPage();
599 break;
600
601 case R.id.switch_mode_menu_id:
Ben Murdoch328ea872009-09-16 13:33:29 +0100602 if (mViewMode == BookmarkViewMode.GRID) {
603 switchViewMode(BookmarkViewMode.LIST);
604 } else {
605 switchViewMode(BookmarkViewMode.GRID);
606 }
Leon Scroggins892df312009-07-14 14:48:02 -0400607 break;
608
609 default:
610 return super.onOptionsItemSelected(item);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800611 }
612 return true;
613 }
614
615 private void openInNewWindow(int position) {
616 Bundle b = new Bundle();
617 b.putBoolean("new_window", true);
618 setResultToParent(RESULT_OK,
619 (new Intent()).setAction(getUrl(position)).putExtras(b));
620
621 finish();
622 }
Nicolas Catania095292f2010-03-15 09:00:14 -0700623
The Android Open Source Project0c908882009-03-03 19:32:16 -0800624
625 private void editBookmark(int position) {
Nicolas Catania095292f2010-03-15 09:00:14 -0700626 Intent intent = new Intent(BrowserBookmarksPage.this,
The Android Open Source Project0c908882009-03-03 19:32:16 -0800627 AddBookmarkPage.class);
628 intent.putExtra("bookmark", getRow(position));
629 startActivityForResult(intent, BOOKMARKS_SAVE);
630 }
631
632 @Override
633 protected void onActivityResult(int requestCode, int resultCode,
634 Intent data) {
635 switch(requestCode) {
636 case BOOKMARKS_SAVE:
637 if (resultCode == RESULT_OK) {
638 Bundle extras;
639 if (data != null && (extras = data.getExtras()) != null) {
640 // If there are extras, then we need to save
641 // the edited bookmark. This is done in updateRow()
642 String title = extras.getString("title");
643 String url = extras.getString("url");
644 if (title != null && url != null) {
645 mBookmarksAdapter.updateRow(extras);
646 }
647 } else {
648 // extras == null then a new bookmark was added to
649 // the database.
650 refreshList();
651 }
652 }
653 break;
654 default:
655 break;
656 }
657 }
Nicolas Catania095292f2010-03-15 09:00:14 -0700658
The Android Open Source Project0c908882009-03-03 19:32:16 -0800659 private void displayRemoveBookmarkDialog(int position) {
660 // Put up a dialog asking if the user really wants to
661 // delete the bookmark
662 final int deletePos = position;
663 new AlertDialog.Builder(this)
664 .setTitle(R.string.delete_bookmark)
665 .setIcon(android.R.drawable.ic_dialog_alert)
666 .setMessage(getText(R.string.delete_bookmark_warning).toString().replace(
667 "%s", getBookmarkTitle(deletePos)))
Nicolas Catania095292f2010-03-15 09:00:14 -0700668 .setPositiveButton(R.string.ok,
The Android Open Source Project0c908882009-03-03 19:32:16 -0800669 new DialogInterface.OnClickListener() {
670 public void onClick(DialogInterface dialog, int whichButton) {
671 deleteBookmark(deletePos);
672 }
673 })
674 .setNegativeButton(R.string.cancel, null)
675 .show();
676 }
677
678 /**
679 * Refresh the shown list after the database has changed.
680 */
Leon Scroggins892df312009-07-14 14:48:02 -0400681 private void refreshList() {
Grace Kloba0b82a6f2010-04-20 13:46:42 -0700682 if (mBookmarksAdapter == null) return;
The Android Open Source Project0c908882009-03-03 19:32:16 -0800683 mBookmarksAdapter.refreshList();
684 }
Nicolas Catania095292f2010-03-15 09:00:14 -0700685
The Android Open Source Project0c908882009-03-03 19:32:16 -0800686 /**
687 * Return a hashmap representing the currently highlighted row.
688 */
689 public Bundle getRow(int position) {
Leon Scrogginsea002572009-11-24 15:21:18 -0500690 return mBookmarksAdapter == null ? null
691 : mBookmarksAdapter.getRow(position);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800692 }
693
694 /**
695 * Return the url of the currently highlighted row.
696 */
697 public String getUrl(int position) {
Leon Scrogginsea002572009-11-24 15:21:18 -0500698 return mBookmarksAdapter == null ? null
699 : mBookmarksAdapter.getUrl(position);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800700 }
701
Patrick Scotte09761e2009-03-24 20:43:37 -0700702 /**
703 * Return the favicon of the currently highlighted row.
704 */
705 public Bitmap getFavicon(int position) {
Leon Scrogginsea002572009-11-24 15:21:18 -0500706 return mBookmarksAdapter == null ? null
707 : mBookmarksAdapter.getFavicon(position);
Patrick Scotte09761e2009-03-24 20:43:37 -0700708 }
709
Patrick Scott3918d442009-08-04 13:22:29 -0400710 private Bitmap getTouchIcon(int position) {
Leon Scrogginsea002572009-11-24 15:21:18 -0500711 return mBookmarksAdapter == null ? null
712 : mBookmarksAdapter.getTouchIcon(position);
Patrick Scott3918d442009-08-04 13:22:29 -0400713 }
714
The Android Open Source Project0c908882009-03-03 19:32:16 -0800715 private void copy(CharSequence text) {
716 try {
717 IClipboard clip = IClipboard.Stub.asInterface(ServiceManager.getService("clipboard"));
718 if (clip != null) {
719 clip.setClipboardText(text);
720 }
721 } catch (android.os.RemoteException e) {
722 Log.e(LOGTAG, "Copy failed", e);
723 }
724 }
Nicolas Catania095292f2010-03-15 09:00:14 -0700725
The Android Open Source Project0c908882009-03-03 19:32:16 -0800726 public String getBookmarkTitle(int position) {
Leon Scrogginsea002572009-11-24 15:21:18 -0500727 return mBookmarksAdapter == null ? null
728 : mBookmarksAdapter.getTitle(position);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800729 }
730
731 /**
732 * Delete the currently highlighted row.
733 */
734 public void deleteBookmark(int position) {
Leon Scrogginsea002572009-11-24 15:21:18 -0500735 if (mBookmarksAdapter == null) return;
The Android Open Source Project0c908882009-03-03 19:32:16 -0800736 mBookmarksAdapter.deleteRow(position);
737 }
Grace Kloba5942df02009-09-18 11:48:29 -0700738
739 @Override
740 public void onBackPressed() {
741 setResultToParent(RESULT_CANCELED, null);
742 mCanceled = true;
743 super.onBackPressed();
The Android Open Source Project0c908882009-03-03 19:32:16 -0800744 }
745
Leon Scrogginsfde97462010-01-11 13:06:21 -0500746 // This Activity is generally a sub-Activity of
747 // CombinedBookmarkHistoryActivity. In that situation, we need to pass our
748 // result code up to our parent. However, if someone calls this Activity
749 // directly, then this has no parent, and it needs to set it on itself.
The Android Open Source Project0c908882009-03-03 19:32:16 -0800750 private void setResultToParent(int resultCode, Intent data) {
Leon Scrogginsfde97462010-01-11 13:06:21 -0500751 Activity parent = getParent();
752 if (parent == null) {
753 setResult(resultCode, data);
754 } else {
755 ((CombinedBookmarkHistoryActivity) parent).setResultFromChild(
756 resultCode, data);
757 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800758 }
759}