BrowserHistoryPage.java revision c1cf63a4c15ad81f4d19e10574e9dba91f0b83e4
1/* 2 * Copyright (C) 2008 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.ExpandableListActivity; 21import android.content.Context; 22import android.content.Intent; 23import android.content.pm.PackageManager; 24import android.content.pm.ResolveInfo; 25import android.database.Cursor; 26import android.graphics.Bitmap; 27import android.graphics.BitmapFactory; 28import android.os.Bundle; 29import android.os.ServiceManager; 30import android.provider.Browser; 31import android.text.IClipboard; 32import android.util.Log; 33import android.view.ContextMenu; 34import android.view.Menu; 35import android.view.MenuInflater; 36import android.view.MenuItem; 37import android.view.View; 38import android.view.ViewGroup; 39import android.view.ViewGroup.LayoutParams; 40import android.view.ContextMenu.ContextMenuInfo; 41import android.view.ViewStub; 42import android.webkit.WebIconDatabase.IconListener; 43import android.widget.ExpandableListAdapter; 44import android.widget.ExpandableListView; 45import android.widget.ExpandableListView.ExpandableListContextMenuInfo; 46import android.widget.Toast; 47 48/** 49 * Activity for displaying the browser's history, divided into 50 * days of viewing. 51 */ 52public class BrowserHistoryPage extends ExpandableListActivity { 53 private HistoryAdapter mAdapter; 54 private boolean mDisableNewWindow; 55 private HistoryItem mContextHeader; 56 57 private final static String LOGTAG = "browser"; 58 59 // Implementation of WebIconDatabase.IconListener 60 private class IconReceiver implements IconListener { 61 public void onReceivedIcon(String url, Bitmap icon) { 62 setListAdapter(mAdapter); 63 } 64 } 65 // Instance of IconReceiver 66 private final IconReceiver mIconReceiver = new IconReceiver(); 67 68 /** 69 * Report back to the calling activity to load a site. 70 * @param url Site to load. 71 * @param newWindow True if the URL should be loaded in a new window 72 */ 73 private void loadUrl(String url, boolean newWindow) { 74 Intent intent = new Intent().setAction(url); 75 if (newWindow) { 76 Bundle b = new Bundle(); 77 b.putBoolean("new_window", true); 78 intent.putExtras(b); 79 } 80 setResultToParent(RESULT_OK, intent); 81 finish(); 82 } 83 84 private void copy(CharSequence text) { 85 try { 86 IClipboard clip = IClipboard.Stub.asInterface(ServiceManager.getService("clipboard")); 87 if (clip != null) { 88 clip.setClipboardText(text); 89 } 90 } catch (android.os.RemoteException e) { 91 Log.e(LOGTAG, "Copy failed", e); 92 } 93 } 94 95 @Override 96 protected void onCreate(Bundle icicle) { 97 super.onCreate(icicle); 98 setTitle(R.string.browser_history); 99 100 final String whereClause = Browser.BookmarkColumns.VISITS + " > 0" 101 // In AddBookmarkPage, where we save new bookmarks, we add 102 // three visits to newly created bookmarks, so that 103 // bookmarks that have not been visited will show up in the 104 // most visited, and higher in the goto search box. 105 // However, this puts the site in the history, unless we 106 // ignore sites with a DATE of 0, which the next line does. 107 + " AND " + Browser.BookmarkColumns.DATE + " > 0"; 108 final String orderBy = Browser.BookmarkColumns.DATE + " DESC"; 109 110 Cursor cursor = managedQuery( 111 Browser.BOOKMARKS_URI, 112 Browser.HISTORY_PROJECTION, 113 whereClause, null, orderBy); 114 115 mAdapter = new HistoryAdapter(this, cursor, 116 Browser.HISTORY_PROJECTION_DATE_INDEX); 117 setListAdapter(mAdapter); 118 final ExpandableListView list = getExpandableListView(); 119 list.setOnCreateContextMenuListener(this); 120 View v = new ViewStub(this, R.layout.empty_history); 121 addContentView(v, new LayoutParams(LayoutParams.MATCH_PARENT, 122 LayoutParams.MATCH_PARENT)); 123 list.setEmptyView(v); 124 // Do not post the runnable if there is nothing in the list. 125 if (list.getExpandableListAdapter().getGroupCount() > 0) { 126 list.post(new Runnable() { 127 public void run() { 128 // In case the history gets cleared before this event 129 // happens. 130 if (list.getExpandableListAdapter().getGroupCount() > 0) { 131 list.expandGroup(0); 132 } 133 } 134 }); 135 } 136 mDisableNewWindow = getIntent().getBooleanExtra("disable_new_window", 137 false); 138 139 // Register to receive icons in case they haven't all been loaded. 140 CombinedBookmarkHistoryActivity.getIconListenerSet() 141 .addListener(mIconReceiver); 142 143 Activity parent = getParent(); 144 if (null == parent 145 || !(parent instanceof CombinedBookmarkHistoryActivity)) { 146 throw new AssertionError("history page can only be viewed as a tab" 147 + "in CombinedBookmarkHistoryActivity"); 148 } 149 // initialize the result to canceled, so that if the user just presses 150 // back then it will have the correct result 151 setResultToParent(RESULT_CANCELED, null); 152 } 153 154 @Override 155 protected void onDestroy() { 156 super.onDestroy(); 157 CombinedBookmarkHistoryActivity.getIconListenerSet() 158 .removeListener(mIconReceiver); 159 } 160 161 @Override 162 public boolean onCreateOptionsMenu(Menu menu) { 163 super.onCreateOptionsMenu(menu); 164 MenuInflater inflater = getMenuInflater(); 165 inflater.inflate(R.menu.history, menu); 166 return true; 167 } 168 169 @Override 170 public boolean onPrepareOptionsMenu(Menu menu) { 171 menu.findItem(R.id.clear_history_menu_id).setVisible(Browser.canClearHistory(this.getContentResolver())); 172 return true; 173 } 174 175 @Override 176 public boolean onOptionsItemSelected(MenuItem item) { 177 switch (item.getItemId()) { 178 case R.id.clear_history_menu_id: 179 Browser.clearHistory(getContentResolver()); 180 // BrowserHistoryPage is always a child of 181 // CombinedBookmarkHistoryActivity 182 ((CombinedBookmarkHistoryActivity) getParent()) 183 .removeParentChildRelationShips(); 184 mAdapter.refreshData(); 185 return true; 186 187 default: 188 break; 189 } 190 return super.onOptionsItemSelected(item); 191 } 192 193 @Override 194 public void onCreateContextMenu(ContextMenu menu, View v, 195 ContextMenuInfo menuInfo) { 196 ExpandableListContextMenuInfo i = 197 (ExpandableListContextMenuInfo) menuInfo; 198 // Do not allow a context menu to come up from the group views. 199 if (!(i.targetView instanceof HistoryItem)) { 200 return; 201 } 202 203 // Inflate the menu 204 MenuInflater inflater = getMenuInflater(); 205 inflater.inflate(R.menu.historycontext, menu); 206 207 HistoryItem historyItem = (HistoryItem) i.targetView; 208 209 // Setup the header 210 if (mContextHeader == null) { 211 mContextHeader = new HistoryItem(this); 212 } else if (mContextHeader.getParent() != null) { 213 ((ViewGroup) mContextHeader.getParent()).removeView(mContextHeader); 214 } 215 historyItem.copyTo(mContextHeader); 216 menu.setHeaderView(mContextHeader); 217 218 // Only show open in new tab if it was not explicitly disabled 219 if (mDisableNewWindow) { 220 menu.findItem(R.id.new_window_context_menu_id).setVisible(false); 221 } 222 // For a bookmark, provide the option to remove it from bookmarks 223 if (historyItem.isBookmark()) { 224 MenuItem item = menu.findItem(R.id.save_to_bookmarks_menu_id); 225 item.setTitle(R.string.remove_from_bookmarks); 226 } 227 // decide whether to show the share link option 228 PackageManager pm = getPackageManager(); 229 Intent send = new Intent(Intent.ACTION_SEND); 230 send.setType("text/plain"); 231 ResolveInfo ri = pm.resolveActivity(send, PackageManager.MATCH_DEFAULT_ONLY); 232 menu.findItem(R.id.share_link_context_menu_id).setVisible(ri != null); 233 234 super.onCreateContextMenu(menu, v, menuInfo); 235 } 236 237 @Override 238 public boolean onContextItemSelected(MenuItem item) { 239 ExpandableListContextMenuInfo i = 240 (ExpandableListContextMenuInfo) item.getMenuInfo(); 241 HistoryItem historyItem = (HistoryItem) i.targetView; 242 String url = historyItem.getUrl(); 243 String title = historyItem.getName(); 244 switch (item.getItemId()) { 245 case R.id.open_context_menu_id: 246 loadUrl(url, false); 247 return true; 248 case R.id.new_window_context_menu_id: 249 loadUrl(url, true); 250 return true; 251 case R.id.save_to_bookmarks_menu_id: 252 if (historyItem.isBookmark()) { 253 Bookmarks.removeFromBookmarks(this, getContentResolver(), 254 url, title); 255 } else { 256 Browser.saveBookmark(this, title, url); 257 } 258 return true; 259 case R.id.share_link_context_menu_id: 260 Browser.sendString(this, url, 261 getText(R.string.choosertitle_sharevia).toString()); 262 return true; 263 case R.id.copy_url_context_menu_id: 264 copy(url); 265 return true; 266 case R.id.delete_context_menu_id: 267 Browser.deleteFromHistory(getContentResolver(), url); 268 mAdapter.refreshData(); 269 return true; 270 case R.id.homepage_context_menu_id: 271 BrowserSettings.getInstance().setHomePage(this, url); 272 Toast.makeText(this, R.string.homepage_set, 273 Toast.LENGTH_LONG).show(); 274 return true; 275 default: 276 break; 277 } 278 return super.onContextItemSelected(item); 279 } 280 281 @Override 282 public boolean onChildClick(ExpandableListView parent, View v, 283 int groupPosition, int childPosition, long id) { 284 if (v instanceof HistoryItem) { 285 loadUrl(((HistoryItem) v).getUrl(), false); 286 return true; 287 } 288 return false; 289 } 290 291 // This Activity is always a sub-Activity of 292 // CombinedBookmarkHistoryActivity. Therefore, we need to pass our 293 // result code up to our parent. 294 private void setResultToParent(int resultCode, Intent data) { 295 ((CombinedBookmarkHistoryActivity) getParent()).setResultFromChild( 296 resultCode, data); 297 } 298 299 private class HistoryAdapter extends DateSortedExpandableListAdapter { 300 HistoryAdapter(Context context, Cursor cursor, int index) { 301 super(context, cursor, index); 302 303 } 304 305 public View getChildView(int groupPosition, int childPosition, boolean isLastChild, 306 View convertView, ViewGroup parent) { 307 HistoryItem item; 308 if (null == convertView || !(convertView instanceof HistoryItem)) { 309 item = new HistoryItem(BrowserHistoryPage.this); 310 // Add padding on the left so it will be indented from the 311 // arrows on the group views. 312 item.setPadding(item.getPaddingLeft() + 10, 313 item.getPaddingTop(), 314 item.getPaddingRight(), 315 item.getPaddingBottom()); 316 } else { 317 item = (HistoryItem) convertView; 318 } 319 // Bail early if the Cursor is closed. 320 if (!moveCursorToChildPosition(groupPosition, childPosition)) { 321 return item; 322 } 323 item.setName(getString(Browser.HISTORY_PROJECTION_TITLE_INDEX)); 324 String url = getString(Browser.HISTORY_PROJECTION_URL_INDEX); 325 item.setUrl(url); 326 byte[] data = getBlob(Browser.HISTORY_PROJECTION_FAVICON_INDEX); 327 if (data != null) { 328 item.setFavicon(BitmapFactory.decodeByteArray(data, 0, 329 data.length)); 330 } else { 331 item.setFavicon(CombinedBookmarkHistoryActivity 332 .getIconListenerSet().getFavicon(url)); 333 } 334 item.setIsBookmark(1 == 335 getInt(Browser.HISTORY_PROJECTION_BOOKMARK_INDEX)); 336 return item; 337 } 338 } 339} 340