blob: 050b24b6c37c19b770d35ef972817782101a7dec [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;
23import android.graphics.Bitmap;
24import android.net.Uri;
25import android.os.Bundle;
26import android.os.Handler;
27import android.os.Message;
28import android.os.ServiceManager;
29import android.provider.Browser;
30import android.text.IClipboard;
31import android.util.Log;
32import android.view.ContextMenu;
33import android.view.KeyEvent;
34import android.view.Menu;
35import android.view.MenuInflater;
36import android.view.MenuItem;
37import android.view.View;
38import android.view.ViewGroup;
39import android.view.ContextMenu.ContextMenuInfo;
40import android.widget.AdapterView;
41import android.widget.ListView;
42
43/**
44 * View showing the user's bookmarks in the browser.
45 */
46public class BrowserBookmarksPage extends Activity implements
47 View.OnCreateContextMenuListener {
48
49 private BrowserBookmarksAdapter mBookmarksAdapter;
50 private static final int BOOKMARKS_SAVE = 1;
51 private boolean mMaxTabsOpen;
52 private BookmarkItem mContextHeader;
53 private AddNewBookmark mAddHeader;
54 private boolean mCanceled = false;
55 private boolean mCreateShortcut;
56 // XXX: There is no public string defining this intent so if Home changes
57 // the value, we have to update this string.
58 private static final String INSTALL_SHORTCUT =
59 "com.android.launcher.action.INSTALL_SHORTCUT";
60
61 private final static String LOGTAG = "browser";
62
63
64 @Override
65 public boolean onContextItemSelected(MenuItem item) {
66 // It is possible that the view has been canceled when we get to
67 // this point as back has a higher priority
68 if (mCanceled) {
69 return true;
70 }
71 AdapterView.AdapterContextMenuInfo i =
72 (AdapterView.AdapterContextMenuInfo)item.getMenuInfo();
73 // If we have no menu info, we can't tell which item was selected.
74 if (i == null) {
75 return true;
76 }
77
78 switch (item.getItemId()) {
79 case R.id.new_context_menu_id:
80 saveCurrentPage();
81 break;
82 case R.id.open_context_menu_id:
83 loadUrl(i.position);
84 break;
85 case R.id.edit_context_menu_id:
86 editBookmark(i.position);
87 break;
88 case R.id.shortcut_context_menu_id:
89 final Intent send = createShortcutIntent(getUrl(i.position),
90 getBookmarkTitle(i.position));
91 send.setAction(INSTALL_SHORTCUT);
92 sendBroadcast(send);
93 break;
94 case R.id.delete_context_menu_id:
95 displayRemoveBookmarkDialog(i.position);
96 break;
97 case R.id.new_window_context_menu_id:
98 openInNewWindow(i.position);
99 break;
100 case R.id.send_context_menu_id:
101 Browser.sendString(BrowserBookmarksPage.this, getUrl(i.position));
102 break;
103 case R.id.copy_url_context_menu_id:
104 copy(getUrl(i.position));
105
106 default:
107 return super.onContextItemSelected(item);
108 }
109 return true;
110 }
111
112 @Override
113 public void onCreateContextMenu(ContextMenu menu, View v,
114 ContextMenuInfo menuInfo) {
115 AdapterView.AdapterContextMenuInfo i =
116 (AdapterView.AdapterContextMenuInfo) menuInfo;
117
118 MenuInflater inflater = getMenuInflater();
119 inflater.inflate(R.menu.bookmarkscontext, menu);
120
121 if (0 == i.position) {
122 menu.setGroupVisible(R.id.CONTEXT_MENU, false);
123 if (mAddHeader == null) {
124 mAddHeader = new AddNewBookmark(BrowserBookmarksPage.this);
125 } else if (mAddHeader.getParent() != null) {
126 ((ViewGroup) mAddHeader.getParent()).
127 removeView(mAddHeader);
128 }
129 ((AddNewBookmark) i.targetView).copyTo(mAddHeader);
130 menu.setHeaderView(mAddHeader);
131 return;
132 }
133 menu.setGroupVisible(R.id.ADD_MENU, false);
134 BookmarkItem b = (BookmarkItem) i.targetView;
135 if (mContextHeader == null) {
136 mContextHeader = new BookmarkItem(BrowserBookmarksPage.this);
137 } else if (mContextHeader.getParent() != null) {
138 ((ViewGroup) mContextHeader.getParent()).
139 removeView(mContextHeader);
140 }
141 b.copyTo(mContextHeader);
142 menu.setHeaderView(mContextHeader);
143
144 if (mMaxTabsOpen) {
145 menu.findItem(R.id.new_window_context_menu_id).setVisible(
146 false);
147 }
148 }
149
150 /**
151 * Create a new BrowserBookmarksPage.
152 */
153 @Override
154 protected void onCreate(Bundle icicle) {
155 super.onCreate(icicle);
156
157 setContentView(R.layout.browser_bookmarks_page);
158 setTitle(R.string.browser_bookmarks_page_bookmarks_text);
159
160 if (Intent.ACTION_CREATE_SHORTCUT.equals(getIntent().getAction())) {
161 mCreateShortcut = true;
162 }
163
164 mBookmarksAdapter = new BrowserBookmarksAdapter(this,
165 getIntent().getStringExtra("url"), mCreateShortcut);
166 mMaxTabsOpen = getIntent().getBooleanExtra("maxTabsOpen", false);
167
168 ListView listView = (ListView) findViewById(R.id.list);
169 listView.setAdapter(mBookmarksAdapter);
170 listView.setDrawSelectorOnTop(false);
171 listView.setVerticalScrollBarEnabled(true);
172 listView.setOnItemClickListener(mListener);
173
174 if (!mCreateShortcut) {
175 listView.setOnCreateContextMenuListener(this);
176 }
177 }
178
179 private static final int SAVE_CURRENT_PAGE = 1000;
180 private final Handler mHandler = new Handler() {
181 @Override
182 public void handleMessage(Message msg) {
183 if (msg.what == SAVE_CURRENT_PAGE) {
184 saveCurrentPage();
185 }
186 }
187 };
188
189 private AdapterView.OnItemClickListener mListener = new AdapterView.OnItemClickListener() {
190 public void onItemClick(AdapterView parent, View v, int position, long id) {
191 // It is possible that the view has been canceled when we get to
192 // this point as back has a higher priority
193 if (mCanceled) {
194 android.util.Log.e("browser", "item clicked when dismising");
195 return;
196 }
197 if (!mCreateShortcut) {
198 if (0 == position) {
199 // XXX: Work-around for a framework issue.
200 mHandler.sendEmptyMessage(SAVE_CURRENT_PAGE);
201 } else {
202 loadUrl(position);
203 }
204 } else {
205 final Intent intent = createShortcutIntent(getUrl(position),
206 getBookmarkTitle(position));
207 setResultToParent(RESULT_OK, intent);
208 finish();
209 }
210 }
211 };
212
213 private Intent createShortcutIntent(String url, String title) {
214 final Intent i = new Intent();
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700215 final Intent shortcutIntent = new Intent(Intent.ACTION_VIEW,
216 Uri.parse(url));
217 long urlHash = url.hashCode();
218 long uniqueId = (urlHash << 32) | shortcutIntent.hashCode();
219 shortcutIntent.putExtra(Browser.EXTRA_APPLICATION_ID,
220 Long.toString(uniqueId));
221 i.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800222 i.putExtra(Intent.EXTRA_SHORTCUT_NAME, title);
223 i.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE,
224 Intent.ShortcutIconResource.fromContext(BrowserBookmarksPage.this,
Romain Guydb6c3912009-03-24 18:08:40 -0700225 R.drawable.ic_launcher_shortcut_browser_bookmark));
The Android Open Source Project0c908882009-03-03 19:32:16 -0800226 // Do not allow duplicate items
227 i.putExtra("duplicate", false);
228 return i;
229 }
230
231 private void saveCurrentPage() {
232 Intent i = new Intent(BrowserBookmarksPage.this,
233 AddBookmarkPage.class);
234 i.putExtras(getIntent());
235 startActivityForResult(i, BOOKMARKS_SAVE);
236 }
237
238 private void loadUrl(int position) {
239 Intent intent = (new Intent()).setAction(getUrl(position));
240 setResultToParent(RESULT_OK, intent);
241 finish();
242 }
243
244 @Override
245 public boolean onCreateOptionsMenu(Menu menu) {
246 boolean result = super.onCreateOptionsMenu(menu);
247 if (!mCreateShortcut) {
248 MenuInflater inflater = getMenuInflater();
249 inflater.inflate(R.menu.bookmarks, menu);
250 return true;
251 }
252 return result;
253 }
254
255 @Override
256 public boolean onOptionsItemSelected(MenuItem item) {
257 switch (item.getItemId()) {
258 case R.id.new_context_menu_id:
259 saveCurrentPage();
260 break;
261
262 default:
263 return super.onOptionsItemSelected(item);
264 }
265 return true;
266 }
267
268 private void openInNewWindow(int position) {
269 Bundle b = new Bundle();
270 b.putBoolean("new_window", true);
271 setResultToParent(RESULT_OK,
272 (new Intent()).setAction(getUrl(position)).putExtras(b));
273
274 finish();
275 }
276
277
278 private void editBookmark(int position) {
279 Intent intent = new Intent(BrowserBookmarksPage.this,
280 AddBookmarkPage.class);
281 intent.putExtra("bookmark", getRow(position));
282 startActivityForResult(intent, BOOKMARKS_SAVE);
283 }
284
285 @Override
286 protected void onActivityResult(int requestCode, int resultCode,
287 Intent data) {
288 switch(requestCode) {
289 case BOOKMARKS_SAVE:
290 if (resultCode == RESULT_OK) {
291 Bundle extras;
292 if (data != null && (extras = data.getExtras()) != null) {
293 // If there are extras, then we need to save
294 // the edited bookmark. This is done in updateRow()
295 String title = extras.getString("title");
296 String url = extras.getString("url");
297 if (title != null && url != null) {
298 mBookmarksAdapter.updateRow(extras);
299 }
300 } else {
301 // extras == null then a new bookmark was added to
302 // the database.
303 refreshList();
304 }
305 }
306 break;
307 default:
308 break;
309 }
310 }
311
312 private void displayRemoveBookmarkDialog(int position) {
313 // Put up a dialog asking if the user really wants to
314 // delete the bookmark
315 final int deletePos = position;
316 new AlertDialog.Builder(this)
317 .setTitle(R.string.delete_bookmark)
318 .setIcon(android.R.drawable.ic_dialog_alert)
319 .setMessage(getText(R.string.delete_bookmark_warning).toString().replace(
320 "%s", getBookmarkTitle(deletePos)))
321 .setPositiveButton(R.string.ok,
322 new DialogInterface.OnClickListener() {
323 public void onClick(DialogInterface dialog, int whichButton) {
324 deleteBookmark(deletePos);
325 }
326 })
327 .setNegativeButton(R.string.cancel, null)
328 .show();
329 }
330
331 /**
332 * Refresh the shown list after the database has changed.
333 */
334 public void refreshList() {
335 mBookmarksAdapter.refreshList();
336 }
337
338 /**
339 * Return a hashmap representing the currently highlighted row.
340 */
341 public Bundle getRow(int position) {
342 return mBookmarksAdapter.getRow(position);
343 }
344
345 /**
346 * Return the url of the currently highlighted row.
347 */
348 public String getUrl(int position) {
349 return mBookmarksAdapter.getUrl(position);
350 }
351
352 private void copy(CharSequence text) {
353 try {
354 IClipboard clip = IClipboard.Stub.asInterface(ServiceManager.getService("clipboard"));
355 if (clip != null) {
356 clip.setClipboardText(text);
357 }
358 } catch (android.os.RemoteException e) {
359 Log.e(LOGTAG, "Copy failed", e);
360 }
361 }
362
363 public String getBookmarkTitle(int position) {
364 return mBookmarksAdapter.getTitle(position);
365 }
366
367 /**
368 * Delete the currently highlighted row.
369 */
370 public void deleteBookmark(int position) {
371 mBookmarksAdapter.deleteRow(position);
372 }
373
374 public boolean dispatchKeyEvent(KeyEvent event) {
375 if (event.getKeyCode() == KeyEvent.KEYCODE_BACK && event.isDown()) {
376 setResultToParent(RESULT_CANCELED, null);
377 mCanceled = true;
378 }
379 return super.dispatchKeyEvent(event);
380 }
381
382 // This Activity is generally a sub-Activity of CombinedHistoryActivity. In
383 // that situation, we need to pass our result code up to our parent.
384 // However, if someone calls this Activity directly, then this has no
385 // parent, and it needs to set it on itself.
386 private void setResultToParent(int resultCode, Intent data) {
387 Activity a = getParent() == null ? this : getParent();
388 a.setResult(resultCode, data);
389 }
390}