blob: 1c58beb79fe957cf2bba0dca1bbe830bb3363eb7 [file] [log] [blame]
The Android Open Source Project0c908882009-03-03 19:32:16 -08001/*
2 * Copyright (C) 2006 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.browser;
18
19import android.app.Activity;
20import android.app.AlertDialog;
21import android.content.DialogInterface;
22import android.content.Intent;
Ben Murdoch328ea872009-09-16 13:33:29 +010023import android.content.SharedPreferences;
24import android.content.SharedPreferences.Editor;
The Android Open Source Project0c908882009-03-03 19:32:16 -080025import android.graphics.Bitmap;
Patrick Scotte09761e2009-03-24 20:43:37 -070026import android.graphics.BitmapFactory;
27import android.graphics.Canvas;
28import android.graphics.Color;
29import android.graphics.Paint;
Patrick Scott3918d442009-08-04 13:22:29 -040030import android.graphics.Path;
31import android.graphics.PorterDuff;
32import android.graphics.PorterDuffXfermode;
Patrick Scottc0fdde92010-02-25 14:40:11 -050033import android.graphics.Rect;
Patrick Scotte09761e2009-03-24 20:43:37 -070034import android.graphics.RectF;
The Android Open Source Project0c908882009-03-03 19:32:16 -080035import android.net.Uri;
36import android.os.Bundle;
37import android.os.Handler;
38import android.os.Message;
39import android.os.ServiceManager;
40import android.provider.Browser;
41import android.text.IClipboard;
42import android.util.Log;
43import android.view.ContextMenu;
The Android Open Source Project0c908882009-03-03 19:32:16 -080044import android.view.Menu;
45import android.view.MenuInflater;
46import android.view.MenuItem;
47import android.view.View;
48import android.view.ViewGroup;
49import android.view.ContextMenu.ContextMenuInfo;
Patrick Scottc1cf63a2010-03-09 16:02:08 -050050import android.webkit.WebIconDatabase.IconListener;
The Android Open Source Project0c908882009-03-03 19:32:16 -080051import android.widget.AdapterView;
Leon Scroggins89c6d362009-07-15 16:54:37 -040052import android.widget.GridView;
The Android Open Source Project0c908882009-03-03 19:32:16 -080053import android.widget.ListView;
Leon Scrogginsfeb941d2009-05-28 17:27:38 -040054import android.widget.Toast;
The Android Open Source Project0c908882009-03-03 19:32:16 -080055
Ben Murdoch328ea872009-09-16 13:33:29 +010056/*package*/ enum BookmarkViewMode { NONE, GRID, LIST }
The Android Open Source Project0c908882009-03-03 19:32:16 -080057/**
58 * View showing the user's bookmarks in the browser.
59 */
Nicolas Catania095292f2010-03-15 09:00:14 -070060public class BrowserBookmarksPage extends Activity implements
The Android Open Source Project0c908882009-03-03 19:32:16 -080061 View.OnCreateContextMenuListener {
62
Ben Murdoch328ea872009-09-16 13:33:29 +010063 private BookmarkViewMode mViewMode = BookmarkViewMode.NONE;
Leon Scroggins89c6d362009-07-15 16:54:37 -040064 private GridView mGridPage;
Leon Scrogginsea002572009-11-24 15:21:18 -050065 private ListView mVerticalList;
The Android Open Source Project0c908882009-03-03 19:32:16 -080066 private BrowserBookmarksAdapter mBookmarksAdapter;
67 private static final int BOOKMARKS_SAVE = 1;
Leon Scroggins190095d2009-08-17 17:01:38 -040068 private boolean mDisableNewWindow;
The Android Open Source Project0c908882009-03-03 19:32:16 -080069 private BookmarkItem mContextHeader;
70 private AddNewBookmark mAddHeader;
71 private boolean mCanceled = false;
72 private boolean mCreateShortcut;
Leon Scrogginsa5d669e2009-08-05 14:07:58 -040073 private boolean mMostVisited;
74 private View mEmptyView;
The Android Open Source Project0c908882009-03-03 19:32:16 -080075 // XXX: There is no public string defining this intent so if Home changes
76 // the value, we have to update this string.
77 private static final String INSTALL_SHORTCUT =
78 "com.android.launcher.action.INSTALL_SHORTCUT";
Nicolas Catania095292f2010-03-15 09:00:14 -070079
The Android Open Source Project0c908882009-03-03 19:32:16 -080080 private final static String LOGTAG = "browser";
Ben Murdoch328ea872009-09-16 13:33:29 +010081 private final static String PREF_BOOKMARK_VIEW_MODE = "pref_bookmark_view_mode";
82 private final static String PREF_MOST_VISITED_VIEW_MODE = "pref_most_visited_view_mode";
The Android Open Source Project0c908882009-03-03 19:32:16 -080083
84 @Override
85 public boolean onContextItemSelected(MenuItem item) {
86 // It is possible that the view has been canceled when we get to
Nicolas Catania095292f2010-03-15 09:00:14 -070087 // this point as back has a higher priority
The Android Open Source Project0c908882009-03-03 19:32:16 -080088 if (mCanceled) {
89 return true;
90 }
Nicolas Catania095292f2010-03-15 09:00:14 -070091 AdapterView.AdapterContextMenuInfo i =
The Android Open Source Project0c908882009-03-03 19:32:16 -080092 (AdapterView.AdapterContextMenuInfo)item.getMenuInfo();
93 // If we have no menu info, we can't tell which item was selected.
94 if (i == null) {
95 return true;
96 }
Nicolas Catania095292f2010-03-15 09:00:14 -070097
The Android Open Source Project0c908882009-03-03 19:32:16 -080098 switch (item.getItemId()) {
99 case R.id.new_context_menu_id:
100 saveCurrentPage();
101 break;
102 case R.id.open_context_menu_id:
103 loadUrl(i.position);
104 break;
105 case R.id.edit_context_menu_id:
106 editBookmark(i.position);
107 break;
108 case R.id.shortcut_context_menu_id:
Patrick Scott3918d442009-08-04 13:22:29 -0400109 final Intent send = createShortcutIntent(i.position);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800110 send.setAction(INSTALL_SHORTCUT);
111 sendBroadcast(send);
112 break;
113 case R.id.delete_context_menu_id:
Leon Scrogginsa5d669e2009-08-05 14:07:58 -0400114 if (mMostVisited) {
115 Browser.deleteFromHistory(getContentResolver(),
116 getUrl(i.position));
117 refreshList();
118 } else {
119 displayRemoveBookmarkDialog(i.position);
120 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800121 break;
122 case R.id.new_window_context_menu_id:
123 openInNewWindow(i.position);
124 break;
Leon Scrogginsa5d669e2009-08-05 14:07:58 -0400125 case R.id.share_link_context_menu_id:
Leon Scroggins96afcb12009-12-10 12:35:56 -0500126 BrowserActivity.sharePage(BrowserBookmarksPage.this,
127 mBookmarksAdapter.getTitle(i.position), getUrl(i.position),
128 getFavicon(i.position),
129 mBookmarksAdapter.getScreenshot(i.position));
The Android Open Source Project0c908882009-03-03 19:32:16 -0800130 break;
131 case R.id.copy_url_context_menu_id:
132 copy(getUrl(i.position));
Leon Scrogginsfeb941d2009-05-28 17:27:38 -0400133 break;
134 case R.id.homepage_context_menu_id:
135 BrowserSettings.getInstance().setHomePage(this,
136 getUrl(i.position));
137 Toast.makeText(this, R.string.homepage_set,
138 Toast.LENGTH_LONG).show();
139 break;
Leon Scrogginsa5d669e2009-08-05 14:07:58 -0400140 // Only for the Most visited page
141 case R.id.save_to_bookmarks_menu_id:
Leon Scrogginsc1f57592009-08-14 14:16:10 -0400142 boolean isBookmark;
143 String name;
144 String url;
Ben Murdoch328ea872009-09-16 13:33:29 +0100145 if (mViewMode == BookmarkViewMode.GRID) {
Leon Scrogginsc1f57592009-08-14 14:16:10 -0400146 isBookmark = mBookmarksAdapter.getIsBookmark(i.position);
147 name = mBookmarksAdapter.getTitle(i.position);
148 url = mBookmarksAdapter.getUrl(i.position);
149 } else {
150 HistoryItem historyItem = ((HistoryItem) i.targetView);
151 isBookmark = historyItem.isBookmark();
152 name = historyItem.getName();
153 url = historyItem.getUrl();
154 }
Leon Scrogginsa5d669e2009-08-05 14:07:58 -0400155 // If the site is bookmarked, the item becomes remove from
156 // bookmarks.
Leon Scrogginsc1f57592009-08-14 14:16:10 -0400157 if (isBookmark) {
Andrei Popescuc9526192009-09-23 15:52:16 +0100158 Bookmarks.removeFromBookmarks(this, getContentResolver(), url, name);
Leon Scrogginsa5d669e2009-08-05 14:07:58 -0400159 } else {
Leon Scrogginsc1f57592009-08-14 14:16:10 -0400160 Browser.saveBookmark(this, name, url);
Leon Scrogginsa5d669e2009-08-05 14:07:58 -0400161 }
162 break;
The Android Open Source Project0c908882009-03-03 19:32:16 -0800163 default:
164 return super.onContextItemSelected(item);
165 }
166 return true;
167 }
168
169 @Override
170 public void onCreateContextMenu(ContextMenu menu, View v,
171 ContextMenuInfo menuInfo) {
Nicolas Catania095292f2010-03-15 09:00:14 -0700172 AdapterView.AdapterContextMenuInfo i =
The Android Open Source Project0c908882009-03-03 19:32:16 -0800173 (AdapterView.AdapterContextMenuInfo) menuInfo;
174
175 MenuInflater inflater = getMenuInflater();
Leon Scrogginsa5d669e2009-08-05 14:07:58 -0400176 if (mMostVisited) {
177 inflater.inflate(R.menu.historycontext, menu);
178 } else {
179 inflater.inflate(R.menu.bookmarkscontext, menu);
180 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800181
Leon Scrogginsa5d669e2009-08-05 14:07:58 -0400182 if (0 == i.position && !mMostVisited) {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800183 menu.setGroupVisible(R.id.CONTEXT_MENU, false);
184 if (mAddHeader == null) {
185 mAddHeader = new AddNewBookmark(BrowserBookmarksPage.this);
186 } else if (mAddHeader.getParent() != null) {
187 ((ViewGroup) mAddHeader.getParent()).
188 removeView(mAddHeader);
189 }
Leon Scroggins892df312009-07-14 14:48:02 -0400190 mAddHeader.setUrl(getIntent().getStringExtra("url"));
The Android Open Source Project0c908882009-03-03 19:32:16 -0800191 menu.setHeaderView(mAddHeader);
192 return;
193 }
Leon Scrogginsa5d669e2009-08-05 14:07:58 -0400194 if (mMostVisited) {
Ben Murdoch328ea872009-09-16 13:33:29 +0100195 if ((mViewMode == BookmarkViewMode.LIST
196 && ((HistoryItem) i.targetView).isBookmark())
Leon Scrogginsa5d669e2009-08-05 14:07:58 -0400197 || mBookmarksAdapter.getIsBookmark(i.position)) {
198 MenuItem item = menu.findItem(
199 R.id.save_to_bookmarks_menu_id);
200 item.setTitle(R.string.remove_from_bookmarks);
201 }
202 } else {
203 // The historycontext menu has no ADD_MENU group.
204 menu.setGroupVisible(R.id.ADD_MENU, false);
205 }
Leon Scroggins190095d2009-08-17 17:01:38 -0400206 if (mDisableNewWindow) {
Leon Scroggins892df312009-07-14 14:48:02 -0400207 menu.findItem(R.id.new_window_context_menu_id).setVisible(
208 false);
209 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800210 if (mContextHeader == null) {
211 mContextHeader = new BookmarkItem(BrowserBookmarksPage.this);
212 } else if (mContextHeader.getParent() != null) {
213 ((ViewGroup) mContextHeader.getParent()).
214 removeView(mContextHeader);
215 }
Ben Murdoch328ea872009-09-16 13:33:29 +0100216 if (mViewMode == BookmarkViewMode.GRID) {
Leon Scroggins892df312009-07-14 14:48:02 -0400217 mBookmarksAdapter.populateBookmarkItem(mContextHeader,
218 i.position);
219 } else {
220 BookmarkItem b = (BookmarkItem) i.targetView;
221 b.copyTo(mContextHeader);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800222 }
Leon Scroggins892df312009-07-14 14:48:02 -0400223 menu.setHeaderView(mContextHeader);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800224 }
225
226 /**
227 * Create a new BrowserBookmarksPage.
Nicolas Catania095292f2010-03-15 09:00:14 -0700228 */
The Android Open Source Project0c908882009-03-03 19:32:16 -0800229 @Override
230 protected void onCreate(Bundle icicle) {
231 super.onCreate(icicle);
232
The Android Open Source Project0c908882009-03-03 19:32:16 -0800233 if (Intent.ACTION_CREATE_SHORTCUT.equals(getIntent().getAction())) {
234 mCreateShortcut = true;
235 }
Leon Scroggins190095d2009-08-17 17:01:38 -0400236 mDisableNewWindow = getIntent().getBooleanExtra("disable_new_window",
237 false);
Leon Scrogginsa5d669e2009-08-05 14:07:58 -0400238 mMostVisited = getIntent().getBooleanExtra("mostVisited", false);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800239
Leon Scrogginsa5d669e2009-08-05 14:07:58 -0400240 if (mCreateShortcut) {
241 setTitle(R.string.browser_bookmarks_page_bookmarks_text);
242 }
Leon Scrogginsea002572009-11-24 15:21:18 -0500243 mHandler.obtainMessage(CREATE_ADAPTER).sendToTarget();
Leon Scrogginsd87f85e2009-08-18 14:13:31 -0400244
245 setContentView(R.layout.empty_history);
246 mEmptyView = findViewById(R.id.empty_view);
247 mEmptyView.setVisibility(View.GONE);
248
Ben Murdoch328ea872009-09-16 13:33:29 +0100249 SharedPreferences p = getPreferences(MODE_PRIVATE);
250
251 // See if the user has set a preference for the view mode of their
252 // bookmarks. Otherwise default to grid mode.
253 BookmarkViewMode preference = BookmarkViewMode.NONE;
254 if (mMostVisited) {
Leon Scrogginsb3968bb2009-10-16 09:04:16 -0400255 // For the most visited page, only use list mode.
256 preference = BookmarkViewMode.LIST;
Ben Murdoch328ea872009-09-16 13:33:29 +0100257 } else {
258 preference = BookmarkViewMode.values()[p.getInt(
259 PREF_BOOKMARK_VIEW_MODE, BookmarkViewMode.GRID.ordinal())];
260 }
261 switchViewMode(preference);
Leon Scroggins892df312009-07-14 14:48:02 -0400262 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800263
Nicolas Catania095292f2010-03-15 09:00:14 -0700264 @Override
265 protected void onDestroy() {
266 mHandler.removeCallbacksAndMessages(null);
267 super.onDestroy();
268 }
269
Leon Scroggins892df312009-07-14 14:48:02 -0400270 /**
271 * Set the ContentView to be either the grid of thumbnails or the vertical
Ben Murdoch328ea872009-09-16 13:33:29 +0100272 * list.
Leon Scroggins892df312009-07-14 14:48:02 -0400273 */
Leon Scrogginsea002572009-11-24 15:21:18 -0500274 private void switchViewMode(BookmarkViewMode viewMode) {
275 if (mViewMode == viewMode) {
Ben Murdoch328ea872009-09-16 13:33:29 +0100276 return;
277 }
278
Leon Scrogginsea002572009-11-24 15:21:18 -0500279 mViewMode = viewMode;
Ben Murdoch328ea872009-09-16 13:33:29 +0100280
281 // Update the preferences to make the new view mode sticky.
282 Editor ed = getPreferences(MODE_PRIVATE).edit();
283 if (mMostVisited) {
284 ed.putInt(PREF_MOST_VISITED_VIEW_MODE, mViewMode.ordinal());
285 } else {
286 ed.putInt(PREF_BOOKMARK_VIEW_MODE, mViewMode.ordinal());
287 }
288 ed.commit();
289
Leon Scrogginsea002572009-11-24 15:21:18 -0500290 if (mBookmarksAdapter != null) {
291 mBookmarksAdapter.switchViewMode(viewMode);
292 }
Ben Murdoch328ea872009-09-16 13:33:29 +0100293 if (mViewMode == BookmarkViewMode.GRID) {
Leon Scroggins892df312009-07-14 14:48:02 -0400294 if (mGridPage == null) {
Leon Scroggins89c6d362009-07-15 16:54:37 -0400295 mGridPage = new GridView(this);
Leon Scrogginsea002572009-11-24 15:21:18 -0500296 if (mBookmarksAdapter != null) {
297 mGridPage.setAdapter(mBookmarksAdapter);
298 }
Leon Scroggins892df312009-07-14 14:48:02 -0400299 mGridPage.setOnItemClickListener(mListener);
Leon Scroggins89c6d362009-07-15 16:54:37 -0400300 mGridPage.setNumColumns(GridView.AUTO_FIT);
Leon Scrogginsf8551612009-09-24 16:06:02 -0400301 mGridPage.setColumnWidth(
302 BrowserActivity.getDesiredThumbnailWidth(this));
Leon Scroggins89c6d362009-07-15 16:54:37 -0400303 mGridPage.setFocusable(true);
304 mGridPage.setFocusableInTouchMode(true);
305 mGridPage.setSelector(android.R.drawable.gallery_thumb);
Leon Scrogginsf8551612009-09-24 16:06:02 -0400306 float density = getResources().getDisplayMetrics().density;
307 mGridPage.setVerticalSpacing((int) (14 * density));
308 mGridPage.setHorizontalSpacing((int) (8 * density));
309 mGridPage.setStretchMode(GridView.STRETCH_SPACING);
Leon Scrogginsbbe6d5b2009-09-28 12:01:00 -0400310 mGridPage.setScrollBarStyle(View.SCROLLBARS_INSIDE_INSET);
Leon Scrogginsf8551612009-09-24 16:06:02 -0400311 mGridPage.setDrawSelectorOnTop(true);
Leon Scrogginsa5d669e2009-08-05 14:07:58 -0400312 if (mMostVisited) {
313 mGridPage.setEmptyView(mEmptyView);
314 }
Leon Scroggins892df312009-07-14 14:48:02 -0400315 if (!mCreateShortcut) {
316 mGridPage.setOnCreateContextMenuListener(this);
317 }
318 }
Leon Scrogginsd87f85e2009-08-18 14:13:31 -0400319 addContentView(mGridPage, FULL_SCREEN_PARAMS);
320 if (mVerticalList != null) {
321 ViewGroup parent = (ViewGroup) mVerticalList.getParent();
322 if (parent != null) {
323 parent.removeView(mVerticalList);
324 }
325 }
Leon Scroggins892df312009-07-14 14:48:02 -0400326 } else {
327 if (null == mVerticalList) {
Leon Scrogginsd87f85e2009-08-18 14:13:31 -0400328 ListView listView = new ListView(this);
Leon Scrogginsea002572009-11-24 15:21:18 -0500329 if (mBookmarksAdapter != null) {
330 listView.setAdapter(mBookmarksAdapter);
331 }
Leon Scroggins892df312009-07-14 14:48:02 -0400332 listView.setDrawSelectorOnTop(false);
333 listView.setVerticalScrollBarEnabled(true);
334 listView.setOnItemClickListener(mListener);
Leon Scrogginsa5d669e2009-08-05 14:07:58 -0400335 if (mMostVisited) {
336 listView.setEmptyView(mEmptyView);
337 }
Leon Scroggins892df312009-07-14 14:48:02 -0400338 if (!mCreateShortcut) {
339 listView.setOnCreateContextMenuListener(this);
340 }
Leon Scrogginsd87f85e2009-08-18 14:13:31 -0400341 mVerticalList = listView;
Leon Scroggins892df312009-07-14 14:48:02 -0400342 }
Leon Scrogginsd87f85e2009-08-18 14:13:31 -0400343 addContentView(mVerticalList, FULL_SCREEN_PARAMS);
344 if (mGridPage != null) {
345 ViewGroup parent = (ViewGroup) mGridPage.getParent();
346 if (parent != null) {
347 parent.removeView(mGridPage);
348 }
349 }
Leon Scrogginsa5d669e2009-08-05 14:07:58 -0400350 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800351 }
352
Leon Scrogginsd87f85e2009-08-18 14:13:31 -0400353 private static final ViewGroup.LayoutParams FULL_SCREEN_PARAMS
354 = new ViewGroup.LayoutParams(
Romain Guy15b8ec62010-01-08 15:06:43 -0800355 ViewGroup.LayoutParams.MATCH_PARENT,
356 ViewGroup.LayoutParams.MATCH_PARENT);
Leon Scrogginsd87f85e2009-08-18 14:13:31 -0400357
The Android Open Source Project0c908882009-03-03 19:32:16 -0800358 private static final int SAVE_CURRENT_PAGE = 1000;
Leon Scrogginsea002572009-11-24 15:21:18 -0500359 private static final int CREATE_ADAPTER = 1001;
The Android Open Source Project0c908882009-03-03 19:32:16 -0800360 private final Handler mHandler = new Handler() {
361 @Override
362 public void handleMessage(Message msg) {
Leon Scrogginsea002572009-11-24 15:21:18 -0500363 switch (msg.what) {
364 case SAVE_CURRENT_PAGE:
365 saveCurrentPage();
366 break;
367 case CREATE_ADAPTER:
368 Intent intent = getIntent();
369 mBookmarksAdapter = new BrowserBookmarksAdapter(
370 BrowserBookmarksPage.this,
371 intent.getStringExtra("url"),
372 intent.getStringExtra("title"),
373 (Bitmap) intent.getParcelableExtra("thumbnail"),
374 mCreateShortcut,
375 mMostVisited);
376 mBookmarksAdapter.switchViewMode(mViewMode);
377 if (mGridPage != null) {
378 mGridPage.setAdapter(mBookmarksAdapter);
379 }
380 if (mVerticalList != null) {
381 mVerticalList.setAdapter(mBookmarksAdapter);
382 }
Patrick Scottc1cf63a2010-03-09 16:02:08 -0500383 // Add our own listener in case there are favicons that
384 // have yet to be loaded.
385 if (mMostVisited) {
386 IconListener listener = new IconListener() {
387 public void onReceivedIcon(String url,
388 Bitmap icon) {
389 if (mGridPage != null) {
390 mGridPage.setAdapter(mBookmarksAdapter);
391 }
392 if (mVerticalList != null) {
393 mVerticalList.setAdapter(mBookmarksAdapter);
394 }
395 }
396 };
397 CombinedBookmarkHistoryActivity.getIconListenerSet()
398 .addListener(listener);
399 }
Leon Scrogginsea002572009-11-24 15:21:18 -0500400 break;
The Android Open Source Project0c908882009-03-03 19:32:16 -0800401 }
402 }
403 };
404
405 private AdapterView.OnItemClickListener mListener = new AdapterView.OnItemClickListener() {
406 public void onItemClick(AdapterView parent, View v, int position, long id) {
407 // It is possible that the view has been canceled when we get to
Nicolas Catania095292f2010-03-15 09:00:14 -0700408 // this point as back has a higher priority
The Android Open Source Project0c908882009-03-03 19:32:16 -0800409 if (mCanceled) {
Leon Scroggins892df312009-07-14 14:48:02 -0400410 android.util.Log.e(LOGTAG, "item clicked when dismissing");
The Android Open Source Project0c908882009-03-03 19:32:16 -0800411 return;
412 }
413 if (!mCreateShortcut) {
Leon Scrogginsa5d669e2009-08-05 14:07:58 -0400414 if (0 == position && !mMostVisited) {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800415 // XXX: Work-around for a framework issue.
416 mHandler.sendEmptyMessage(SAVE_CURRENT_PAGE);
417 } else {
418 loadUrl(position);
419 }
420 } else {
Patrick Scott3918d442009-08-04 13:22:29 -0400421 final Intent intent = createShortcutIntent(position);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800422 setResultToParent(RESULT_OK, intent);
423 finish();
424 }
425 }
426 };
427
Patrick Scott3918d442009-08-04 13:22:29 -0400428 private Intent createShortcutIntent(int position) {
429 String url = getUrl(position);
430 String title = getBookmarkTitle(position);
431 Bitmap touchIcon = getTouchIcon(position);
432
The Android Open Source Project0c908882009-03-03 19:32:16 -0800433 final Intent i = new Intent();
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700434 final Intent shortcutIntent = new Intent(Intent.ACTION_VIEW,
435 Uri.parse(url));
436 long urlHash = url.hashCode();
437 long uniqueId = (urlHash << 32) | shortcutIntent.hashCode();
438 shortcutIntent.putExtra(Browser.EXTRA_APPLICATION_ID,
439 Long.toString(uniqueId));
440 i.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800441 i.putExtra(Intent.EXTRA_SHORTCUT_NAME, title);
Patrick Scott3918d442009-08-04 13:22:29 -0400442 // Use the apple-touch-icon if available
443 if (touchIcon != null) {
444 // Make a copy so we can modify the pixels.
445 Bitmap copy = touchIcon.copy(Bitmap.Config.ARGB_8888, true);
Patrick Scotte09761e2009-03-24 20:43:37 -0700446 Canvas canvas = new Canvas(copy);
447
Patrick Scott3918d442009-08-04 13:22:29 -0400448 // Construct a path from a round rect. This will allow drawing with
449 // an inverse fill so we can punch a hole using the round rect.
450 Path path = new Path();
451 path.setFillType(Path.FillType.INVERSE_WINDING);
Patrick Scott59ce8302009-09-18 16:29:38 -0400452 RectF rect = new RectF(0, 0, touchIcon.getWidth(),
453 touchIcon.getHeight());
454 rect.inset(1, 1);
455 path.addRoundRect(rect, 8f, 8f, Path.Direction.CW);
Patrick Scotte09761e2009-03-24 20:43:37 -0700456
Patrick Scott3918d442009-08-04 13:22:29 -0400457 // Construct a paint that clears the outside of the rectangle and
458 // draw.
459 Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
460 paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
461 canvas.drawPath(path, paint);
Patrick Scotte09761e2009-03-24 20:43:37 -0700462
Patrick Scotte09761e2009-03-24 20:43:37 -0700463 i.putExtra(Intent.EXTRA_SHORTCUT_ICON, copy);
Patrick Scott3918d442009-08-04 13:22:29 -0400464 } else {
465 Bitmap favicon = getFavicon(position);
466 if (favicon == null) {
467 i.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE,
468 Intent.ShortcutIconResource.fromContext(
469 BrowserBookmarksPage.this,
470 R.drawable.ic_launcher_shortcut_browser_bookmark));
471 } else {
472 Bitmap icon = BitmapFactory.decodeResource(getResources(),
Patrick Scottc0fdde92010-02-25 14:40:11 -0500473 R.drawable.ic_launcher_shortcut_browser_bookmark_icon);
Patrick Scott3918d442009-08-04 13:22:29 -0400474
475 // Make a copy of the regular icon so we can modify the pixels.
476 Bitmap copy = icon.copy(Bitmap.Config.ARGB_8888, true);
477 Canvas canvas = new Canvas(copy);
478
479 // Make a Paint for the white background rectangle and for
480 // filtering the favicon.
481 Paint p = new Paint(Paint.ANTI_ALIAS_FLAG
482 | Paint.FILTER_BITMAP_FLAG);
483 p.setStyle(Paint.Style.FILL_AND_STROKE);
484 p.setColor(Color.WHITE);
485
Patrick Scottc0fdde92010-02-25 14:40:11 -0500486 final float density =
487 getResources().getDisplayMetrics().density;
Patrick Scott3918d442009-08-04 13:22:29 -0400488 // Create a rectangle that is slightly wider than the favicon
Patrick Scott4aff3aa2009-10-05 09:32:46 -0400489 final float iconSize = 16 * density; // 16x16 favicon
Patrick Scottc0fdde92010-02-25 14:40:11 -0500490 final float padding = 2 * density; // white padding around icon
Patrick Scott3918d442009-08-04 13:22:29 -0400491 final float rectSize = iconSize + 2 * padding;
Patrick Scottc0fdde92010-02-25 14:40:11 -0500492
493 final Rect iconBounds =
494 new Rect(0, 0, icon.getWidth(), icon.getHeight());
495 final float x = iconBounds.exactCenterX() - (rectSize / 2);
496 // Note: Subtract 2 dip from the y position since the box is
497 // slightly higher than center. Use padding since it is already
498 // 2 * density.
499 final float y = iconBounds.exactCenterY() - (rectSize / 2)
500 - padding;
501 RectF r = new RectF(x, y, x + rectSize, y + rectSize);
Patrick Scott3918d442009-08-04 13:22:29 -0400502
503 // Draw a white rounded rectangle behind the favicon
504 canvas.drawRoundRect(r, 2, 2, p);
505
506 // Draw the favicon in the same rectangle as the rounded
507 // rectangle but inset by the padding
508 // (results in a 16x16 favicon).
509 r.inset(padding, padding);
510 canvas.drawBitmap(favicon, null, r, p);
511 i.putExtra(Intent.EXTRA_SHORTCUT_ICON, copy);
512 }
Patrick Scotte09761e2009-03-24 20:43:37 -0700513 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800514 // Do not allow duplicate items
515 i.putExtra("duplicate", false);
516 return i;
517 }
518
519 private void saveCurrentPage() {
520 Intent i = new Intent(BrowserBookmarksPage.this,
521 AddBookmarkPage.class);
522 i.putExtras(getIntent());
523 startActivityForResult(i, BOOKMARKS_SAVE);
524 }
525
526 private void loadUrl(int position) {
527 Intent intent = (new Intent()).setAction(getUrl(position));
528 setResultToParent(RESULT_OK, intent);
529 finish();
530 }
531
532 @Override
533 public boolean onCreateOptionsMenu(Menu menu) {
534 boolean result = super.onCreateOptionsMenu(menu);
Leon Scrogginsb3968bb2009-10-16 09:04:16 -0400535 if (!mCreateShortcut && !mMostVisited) {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800536 MenuInflater inflater = getMenuInflater();
537 inflater.inflate(R.menu.bookmarks, menu);
538 return true;
539 }
540 return result;
541 }
542
543 @Override
Leon Scroggins0c786502009-08-04 16:04:55 -0400544 public boolean onPrepareOptionsMenu(Menu menu) {
Leon Scroggins8382d992009-08-19 11:25:14 -0400545 boolean result = super.onPrepareOptionsMenu(menu);
Leon Scrogginsea002572009-11-24 15:21:18 -0500546 if (mCreateShortcut || mMostVisited || mBookmarksAdapter == null
Leon Scrogginsb3968bb2009-10-16 09:04:16 -0400547 || mBookmarksAdapter.getCount() == 0) {
Leon Scrogginsa5d669e2009-08-05 14:07:58 -0400548 // No need to show the menu if there are no items.
Leon Scroggins8382d992009-08-19 11:25:14 -0400549 return result;
Leon Scrogginsa5d669e2009-08-05 14:07:58 -0400550 }
Leon Scrogginsfdd10d72009-09-25 13:01:45 -0400551 MenuItem switchItem = menu.findItem(R.id.switch_mode_menu_id);
552 int titleResId;
553 int iconResId;
554 if (mViewMode == BookmarkViewMode.GRID) {
555 titleResId = R.string.switch_to_list;
556 iconResId = R.drawable.ic_menu_list;
557 } else {
558 titleResId = R.string.switch_to_thumbnails;
559 iconResId = R.drawable.ic_menu_thumbnail;
560 }
561 switchItem.setTitle(titleResId);
562 switchItem.setIcon(iconResId);
Leon Scroggins0c786502009-08-04 16:04:55 -0400563 return true;
564 }
565
566 @Override
The Android Open Source Project0c908882009-03-03 19:32:16 -0800567 public boolean onOptionsItemSelected(MenuItem item) {
568 switch (item.getItemId()) {
Leon Scroggins892df312009-07-14 14:48:02 -0400569 case R.id.new_context_menu_id:
570 saveCurrentPage();
571 break;
572
573 case R.id.switch_mode_menu_id:
Ben Murdoch328ea872009-09-16 13:33:29 +0100574 if (mViewMode == BookmarkViewMode.GRID) {
575 switchViewMode(BookmarkViewMode.LIST);
576 } else {
577 switchViewMode(BookmarkViewMode.GRID);
578 }
Leon Scroggins892df312009-07-14 14:48:02 -0400579 break;
580
581 default:
582 return super.onOptionsItemSelected(item);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800583 }
584 return true;
585 }
586
587 private void openInNewWindow(int position) {
588 Bundle b = new Bundle();
589 b.putBoolean("new_window", true);
590 setResultToParent(RESULT_OK,
591 (new Intent()).setAction(getUrl(position)).putExtras(b));
592
593 finish();
594 }
Nicolas Catania095292f2010-03-15 09:00:14 -0700595
The Android Open Source Project0c908882009-03-03 19:32:16 -0800596
597 private void editBookmark(int position) {
Nicolas Catania095292f2010-03-15 09:00:14 -0700598 Intent intent = new Intent(BrowserBookmarksPage.this,
The Android Open Source Project0c908882009-03-03 19:32:16 -0800599 AddBookmarkPage.class);
600 intent.putExtra("bookmark", getRow(position));
601 startActivityForResult(intent, BOOKMARKS_SAVE);
602 }
603
604 @Override
605 protected void onActivityResult(int requestCode, int resultCode,
606 Intent data) {
607 switch(requestCode) {
608 case BOOKMARKS_SAVE:
609 if (resultCode == RESULT_OK) {
610 Bundle extras;
611 if (data != null && (extras = data.getExtras()) != null) {
612 // If there are extras, then we need to save
613 // the edited bookmark. This is done in updateRow()
614 String title = extras.getString("title");
615 String url = extras.getString("url");
616 if (title != null && url != null) {
617 mBookmarksAdapter.updateRow(extras);
618 }
619 } else {
620 // extras == null then a new bookmark was added to
621 // the database.
622 refreshList();
623 }
624 }
625 break;
626 default:
627 break;
628 }
629 }
Nicolas Catania095292f2010-03-15 09:00:14 -0700630
The Android Open Source Project0c908882009-03-03 19:32:16 -0800631 private void displayRemoveBookmarkDialog(int position) {
632 // Put up a dialog asking if the user really wants to
633 // delete the bookmark
634 final int deletePos = position;
635 new AlertDialog.Builder(this)
636 .setTitle(R.string.delete_bookmark)
637 .setIcon(android.R.drawable.ic_dialog_alert)
638 .setMessage(getText(R.string.delete_bookmark_warning).toString().replace(
639 "%s", getBookmarkTitle(deletePos)))
Nicolas Catania095292f2010-03-15 09:00:14 -0700640 .setPositiveButton(R.string.ok,
The Android Open Source Project0c908882009-03-03 19:32:16 -0800641 new DialogInterface.OnClickListener() {
642 public void onClick(DialogInterface dialog, int whichButton) {
643 deleteBookmark(deletePos);
644 }
645 })
646 .setNegativeButton(R.string.cancel, null)
647 .show();
648 }
649
650 /**
651 * Refresh the shown list after the database has changed.
652 */
Leon Scroggins892df312009-07-14 14:48:02 -0400653 private void refreshList() {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800654 mBookmarksAdapter.refreshList();
655 }
Nicolas Catania095292f2010-03-15 09:00:14 -0700656
The Android Open Source Project0c908882009-03-03 19:32:16 -0800657 /**
658 * Return a hashmap representing the currently highlighted row.
659 */
660 public Bundle getRow(int position) {
Leon Scrogginsea002572009-11-24 15:21:18 -0500661 return mBookmarksAdapter == null ? null
662 : mBookmarksAdapter.getRow(position);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800663 }
664
665 /**
666 * Return the url of the currently highlighted row.
667 */
668 public String getUrl(int position) {
Leon Scrogginsea002572009-11-24 15:21:18 -0500669 return mBookmarksAdapter == null ? null
670 : mBookmarksAdapter.getUrl(position);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800671 }
672
Patrick Scotte09761e2009-03-24 20:43:37 -0700673 /**
674 * Return the favicon of the currently highlighted row.
675 */
676 public Bitmap getFavicon(int position) {
Leon Scrogginsea002572009-11-24 15:21:18 -0500677 return mBookmarksAdapter == null ? null
678 : mBookmarksAdapter.getFavicon(position);
Patrick Scotte09761e2009-03-24 20:43:37 -0700679 }
680
Patrick Scott3918d442009-08-04 13:22:29 -0400681 private Bitmap getTouchIcon(int position) {
Leon Scrogginsea002572009-11-24 15:21:18 -0500682 return mBookmarksAdapter == null ? null
683 : mBookmarksAdapter.getTouchIcon(position);
Patrick Scott3918d442009-08-04 13:22:29 -0400684 }
685
The Android Open Source Project0c908882009-03-03 19:32:16 -0800686 private void copy(CharSequence text) {
687 try {
688 IClipboard clip = IClipboard.Stub.asInterface(ServiceManager.getService("clipboard"));
689 if (clip != null) {
690 clip.setClipboardText(text);
691 }
692 } catch (android.os.RemoteException e) {
693 Log.e(LOGTAG, "Copy failed", e);
694 }
695 }
Nicolas Catania095292f2010-03-15 09:00:14 -0700696
The Android Open Source Project0c908882009-03-03 19:32:16 -0800697 public String getBookmarkTitle(int position) {
Leon Scrogginsea002572009-11-24 15:21:18 -0500698 return mBookmarksAdapter == null ? null
699 : mBookmarksAdapter.getTitle(position);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800700 }
701
702 /**
703 * Delete the currently highlighted row.
704 */
705 public void deleteBookmark(int position) {
Leon Scrogginsea002572009-11-24 15:21:18 -0500706 if (mBookmarksAdapter == null) return;
The Android Open Source Project0c908882009-03-03 19:32:16 -0800707 mBookmarksAdapter.deleteRow(position);
708 }
Grace Kloba5942df02009-09-18 11:48:29 -0700709
710 @Override
711 public void onBackPressed() {
712 setResultToParent(RESULT_CANCELED, null);
713 mCanceled = true;
714 super.onBackPressed();
The Android Open Source Project0c908882009-03-03 19:32:16 -0800715 }
716
Leon Scrogginsfde97462010-01-11 13:06:21 -0500717 // This Activity is generally a sub-Activity of
718 // CombinedBookmarkHistoryActivity. In that situation, we need to pass our
719 // result code up to our parent. However, if someone calls this Activity
720 // directly, then this has no parent, and it needs to set it on itself.
The Android Open Source Project0c908882009-03-03 19:32:16 -0800721 private void setResultToParent(int resultCode, Intent data) {
Leon Scrogginsfde97462010-01-11 13:06:21 -0500722 Activity parent = getParent();
723 if (parent == null) {
724 setResult(resultCode, data);
725 } else {
726 ((CombinedBookmarkHistoryActivity) parent).setResultFromChild(
727 resultCode, data);
728 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800729 }
730}