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