/WebVox/src/com/marvin/webvox/BrowserBookmarksAdapter.java
Java | 594 lines | 425 code | 55 blank | 114 comment | 98 complexity | 9f501918f7ff826ad29976ca12e641c8 MD5 | raw file
1/* 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.marvin.webvox; 18 19import com.marvin.webvox.R; 20 21import android.content.ContentResolver; 22import android.content.ContentUris; 23import android.content.ContentValues; 24import android.database.ContentObserver; 25import android.database.Cursor; 26import android.database.DataSetObserver; 27import android.graphics.Bitmap; 28import android.graphics.BitmapFactory; 29import android.net.Uri; 30import android.os.Bundle; 31import android.os.Handler; 32import android.provider.Browser; 33import android.provider.Browser.BookmarkColumns; 34import android.view.KeyEvent; 35import android.view.LayoutInflater; 36import android.view.View; 37import android.view.ViewGroup; 38import android.webkit.WebIconDatabase; 39import android.webkit.WebIconDatabase.IconListener; 40import android.webkit.WebView; 41import android.widget.BaseAdapter; 42import android.widget.ImageView; 43import android.widget.TextView; 44 45import java.io.ByteArrayOutputStream; 46 47class BrowserBookmarksAdapter extends BaseAdapter { 48 49 private String mCurrentPage; 50 private String mCurrentTitle; 51 private Bitmap mCurrentThumbnail; 52 private Cursor mCursor; 53 private int mCount; 54 private BrowserBookmarksPage mBookmarksPage; 55 private ContentResolver mContentResolver; 56 private boolean mDataValid; 57 private BookmarkViewMode mViewMode; 58 private boolean mMostVisited; 59 private boolean mNeedsOffset; 60 private int mExtraOffset; 61 62 // Implementation of WebIconDatabase.IconListener 63 private class IconReceiver implements IconListener { 64 public void onReceivedIcon(String url, Bitmap icon) { 65 updateBookmarkFavicon(mContentResolver, null, url, icon); 66 } 67 } 68 69 // Instance of IconReceiver 70 private final IconReceiver mIconReceiver = new IconReceiver(); 71 72 /** 73 * Create a new BrowserBookmarksAdapter. 74 * @param b BrowserBookmarksPage that instantiated this. 75 * Necessary so it will adjust its focus 76 * appropriately after a search. 77 */ 78 public BrowserBookmarksAdapter(BrowserBookmarksPage b, String curPage, 79 String curTitle, Bitmap curThumbnail, boolean createShortcut, 80 boolean mostVisited) { 81 mNeedsOffset = !(createShortcut || mostVisited); 82 mMostVisited = mostVisited; 83 mExtraOffset = mNeedsOffset ? 1 : 0; 84 mBookmarksPage = b; 85 mCurrentPage = b.getResources().getString(R.string.current_page) 86 + curPage; 87 mCurrentTitle = curTitle; 88 mCurrentThumbnail = curThumbnail; 89 mContentResolver = b.getContentResolver(); 90 mViewMode = BookmarkViewMode.LIST; 91 92 String whereClause; 93 // FIXME: Should have a default sort order that the user selects. 94 String orderBy = Browser.BookmarkColumns.VISITS + " DESC"; 95 if (mostVisited) { 96 whereClause = Browser.BookmarkColumns.VISITS + " != 0"; 97 } else { 98 whereClause = Browser.BookmarkColumns.BOOKMARK + " != 0"; 99 } 100 mCursor = b.managedQuery(Browser.BOOKMARKS_URI, 101 Browser.HISTORY_PROJECTION, whereClause, null, orderBy); 102 mCursor.registerContentObserver(new ChangeObserver()); 103 mCursor.registerDataSetObserver(new MyDataSetObserver()); 104 105 mDataValid = true; 106 notifyDataSetChanged(); 107 108 mCount = mCursor.getCount() + mExtraOffset; 109 110 // FIXME: This requires another query of the database after the 111 // managedQuery. Can we optimize this? 112 Browser.requestAllIcons(mContentResolver, 113 Browser.BookmarkColumns.FAVICON + " is NULL AND " + 114 Browser.BookmarkColumns.BOOKMARK + " == 1", mIconReceiver); 115 } 116 117 /** 118 * Return a hashmap with one row's Title, Url, and favicon. 119 * @param position Position in the list. 120 * @return Bundle Stores title, url of row position, favicon, and id 121 * for the url. Return a blank map if position is out of 122 * range. 123 */ 124 public Bundle getRow(int position) { 125 Bundle map = new Bundle(); 126 if (position < mExtraOffset || position >= mCount) { 127 return map; 128 } 129 mCursor.moveToPosition(position- mExtraOffset); 130 String url = mCursor.getString(Browser.HISTORY_PROJECTION_URL_INDEX); 131 map.putString(Browser.BookmarkColumns.TITLE, 132 mCursor.getString(Browser.HISTORY_PROJECTION_TITLE_INDEX)); 133 map.putString(Browser.BookmarkColumns.URL, url); 134 byte[] data = mCursor.getBlob(Browser.HISTORY_PROJECTION_FAVICON_INDEX); 135 if (data != null) { 136 map.putParcelable(Browser.BookmarkColumns.FAVICON, 137 BitmapFactory.decodeByteArray(data, 0, data.length)); 138 } 139 map.putInt("id", mCursor.getInt(Browser.HISTORY_PROJECTION_ID_INDEX)); 140 return map; 141 } 142 143 /** 144 * Update a row in the database with new information. 145 * Requeries the database if the information has changed. 146 * @param map Bundle storing id, title and url of new information 147 */ 148 public void updateRow(Bundle map) { 149 150 // Find the record 151 int id = map.getInt("id"); 152 int position = -1; 153 for (mCursor.moveToFirst(); !mCursor.isAfterLast(); mCursor.moveToNext()) { 154 if (mCursor.getInt(Browser.HISTORY_PROJECTION_ID_INDEX) == id) { 155 position = mCursor.getPosition(); 156 break; 157 } 158 } 159 if (position < 0) { 160 return; 161 } 162 163 mCursor.moveToPosition(position); 164 ContentValues values = new ContentValues(); 165 String title = map.getString(Browser.BookmarkColumns.TITLE); 166 if (!title.equals(mCursor 167 .getString(Browser.HISTORY_PROJECTION_TITLE_INDEX))) { 168 values.put(Browser.BookmarkColumns.TITLE, title); 169 } 170 String url = map.getString(Browser.BookmarkColumns.URL); 171 if (!url.equals(mCursor. 172 getString(Browser.HISTORY_PROJECTION_URL_INDEX))) { 173 values.put(Browser.BookmarkColumns.URL, url); 174 } 175 176 if (map.getBoolean("invalidateThumbnail") == true) { 177// values.put(Browser.BookmarkColumns.THUMBNAIL, new byte[0]); 178 } 179 if (values.size() > 0 180 && mContentResolver.update(Browser.BOOKMARKS_URI, values, 181 "_id = " + id, null) != -1) { 182 refreshList(); 183 } 184 } 185 186 /** 187 * Delete a row from the database. Requeries the database. 188 * Does nothing if the provided position is out of range. 189 * @param position Position in the list. 190 */ 191 public void deleteRow(int position) { 192 if (position < mExtraOffset || position >= getCount()) { 193 return; 194 } 195 mCursor.moveToPosition(position- mExtraOffset); 196 String url = mCursor.getString(Browser.HISTORY_PROJECTION_URL_INDEX); 197 String title = mCursor.getString(Browser.HISTORY_PROJECTION_TITLE_INDEX); 198 Bookmarks.removeFromBookmarks(null, mContentResolver, url, title); 199 refreshList(); 200 } 201 202 /** 203 * Delete all bookmarks from the db. Requeries the database. 204 * All bookmarks with become visited URLs or if never visited 205 * are removed 206 */ 207 public void deleteAllRows() { 208 StringBuilder deleteIds = null; 209 StringBuilder convertIds = null; 210 211 for (mCursor.moveToFirst(); !mCursor.isAfterLast(); mCursor.moveToNext()) { 212 String url = mCursor.getString(Browser.HISTORY_PROJECTION_URL_INDEX); 213 WebIconDatabase.getInstance().releaseIconForPageUrl(url); 214 int id = mCursor.getInt(Browser.HISTORY_PROJECTION_ID_INDEX); 215 int numVisits = mCursor.getInt(Browser.HISTORY_PROJECTION_VISITS_INDEX); 216 if (0 == numVisits) { 217 if (deleteIds == null) { 218 deleteIds = new StringBuilder(); 219 deleteIds.append("( "); 220 } else { 221 deleteIds.append(" OR ( "); 222 } 223 deleteIds.append(BookmarkColumns._ID); 224 deleteIds.append(" = "); 225 deleteIds.append(id); 226 deleteIds.append(" )"); 227 } else { 228 // It is no longer a bookmark, but it is still a visited site. 229 if (convertIds == null) { 230 convertIds = new StringBuilder(); 231 convertIds.append("( "); 232 } else { 233 convertIds.append(" OR ( "); 234 } 235 convertIds.append(BookmarkColumns._ID); 236 convertIds.append(" = "); 237 convertIds.append(id); 238 convertIds.append(" )"); 239 } 240 } 241 242 if (deleteIds != null) { 243 mContentResolver.delete(Browser.BOOKMARKS_URI, deleteIds.toString(), 244 null); 245 } 246 if (convertIds != null) { 247 ContentValues values = new ContentValues(); 248 values.put(Browser.BookmarkColumns.BOOKMARK, 0); 249 mContentResolver.update(Browser.BOOKMARKS_URI, values, 250 convertIds.toString(), null); 251 } 252 refreshList(); 253 } 254 255 /** 256 * Refresh list to recognize a change in the database. 257 */ 258 public void refreshList() { 259 mCursor.requery(); 260 mCount = mCursor.getCount() + mExtraOffset; 261 notifyDataSetChanged(); 262 } 263 264 /** 265 * Update the bookmark's favicon. This is a convenience method for updating 266 * a bookmark favicon for the originalUrl and url of the passed in WebView. 267 * @param cr The ContentResolver to use. 268 * @param originalUrl The original url before any redirects. 269 * @param url The current url. 270 * @param favicon The favicon bitmap to write to the db. 271 */ 272 /* package */ static void updateBookmarkFavicon(ContentResolver cr, 273 String originalUrl, String url, Bitmap favicon) { 274 final Cursor c = queryBookmarksForUrl(cr, originalUrl, url, true); 275 if (c == null) { 276 return; 277 } 278 boolean succeed = c.moveToFirst(); 279 ContentValues values = null; 280 while (succeed) { 281 if (values == null) { 282 final ByteArrayOutputStream os = new ByteArrayOutputStream(); 283 favicon.compress(Bitmap.CompressFormat.PNG, 100, os); 284 values = new ContentValues(); 285 values.put(Browser.BookmarkColumns.FAVICON, os.toByteArray()); 286 } 287 cr.update(ContentUris.withAppendedId(Browser.BOOKMARKS_URI, c 288 .getInt(0)), values, null, null); 289 succeed = c.moveToNext(); 290 } 291 c.close(); 292 } 293 294 /* package */ static Cursor queryBookmarksForUrl(ContentResolver cr, 295 String originalUrl, String url, boolean onlyBookmarks) { 296 if (cr == null || url == null) { 297 return null; 298 } 299 300 // If originalUrl is null, just set it to url. 301 if (originalUrl == null) { 302 originalUrl = url; 303 } 304 305 // Look for both the original url and the actual url. This takes in to 306 // account redirects. 307 String originalUrlNoQuery = removeQuery(originalUrl); 308 String urlNoQuery = removeQuery(url); 309 originalUrl = originalUrlNoQuery + '?'; 310 url = urlNoQuery + '?'; 311 312 // Use NoQuery to search for the base url (i.e. if the url is 313 // http://www.yahoo.com/?rs=1, search for http://www.yahoo.com) 314 // Use url to match the base url with other queries (i.e. if the url is 315 // http://www.google.com/m, search for 316 // http://www.google.com/m?some_query) 317 final String[] selArgs = new String[] { 318 originalUrlNoQuery, urlNoQuery, originalUrl, url }; 319 String where = BookmarkColumns.URL + " == ? OR " 320 + BookmarkColumns.URL + " == ? OR " 321 + BookmarkColumns.URL + " GLOB ? || '*' OR " 322 + BookmarkColumns.URL + " GLOB ? || '*'"; 323 if (onlyBookmarks) { 324 where = "(" + where + ") AND " + BookmarkColumns.BOOKMARK + " == 1"; 325 } 326 final String[] projection = 327 new String[] { Browser.BookmarkColumns._ID }; 328 return cr.query(Browser.BOOKMARKS_URI, projection, where, selArgs, 329 null); 330 } 331 332 // Strip the query from the given url. 333 private static String removeQuery(String url) { 334 if (url == null) { 335 return null; 336 } 337 int query = url.indexOf('?'); 338 String noQuery = url; 339 if (query != -1) { 340 noQuery = url.substring(0, query); 341 } 342 return noQuery; 343 } 344 345 /** 346 * How many items should be displayed in the list. 347 * @return Count of items. 348 */ 349 public int getCount() { 350 if (mDataValid) { 351 return mCount; 352 } else { 353 return 0; 354 } 355 } 356 357 public boolean areAllItemsEnabled() { 358 return true; 359 } 360 361 public boolean isEnabled(int position) { 362 return true; 363 } 364 365 /** 366 * Get the data associated with the specified position in the list. 367 * @param position Index of the item whose data we want. 368 * @return The data at the specified position. 369 */ 370 public Object getItem(int position) { 371 return null; 372 } 373 374 /** 375 * Get the row id associated with the specified position in the list. 376 * @param position Index of the item whose row id we want. 377 * @return The id of the item at the specified position. 378 */ 379 public long getItemId(int position) { 380 return position; 381 } 382 383 /* package */ void switchViewMode(BookmarkViewMode viewMode) { 384 mViewMode = viewMode; 385 } 386 387 /* package */ void populateBookmarkItem(BookmarkItem b, int position) { 388 mCursor.moveToPosition(position - mExtraOffset); 389 String url = mCursor.getString(Browser.HISTORY_PROJECTION_URL_INDEX); 390 b.setUrl(url); 391 b.setName(mCursor.getString(Browser.HISTORY_PROJECTION_TITLE_INDEX)); 392 byte[] data = mCursor.getBlob(Browser.HISTORY_PROJECTION_FAVICON_INDEX); 393 Bitmap bitmap = null; 394 if (data == null) { 395 bitmap = CombinedBookmarkHistoryActivity.getIconListenerSet() 396 .getFavicon(url); 397 } else { 398 bitmap = BitmapFactory.decodeByteArray(data, 0, data.length); 399 } 400 b.setFavicon(bitmap); 401 } 402 403 /** 404 * Get a View that displays the data at the specified position 405 * in the list. 406 * @param position Index of the item whose view we want. 407 * @return A View corresponding to the data at the specified position. 408 */ 409 public View getView(int position, View convertView, ViewGroup parent) { 410 if (!mDataValid) { 411 throw new IllegalStateException( 412 "this should only be called when the cursor is valid"); 413 } 414 if (position < 0 || position > mCount) { 415 throw new AssertionError( 416 "BrowserBookmarksAdapter tried to get a view out of range"); 417 } 418 if (mViewMode == BookmarkViewMode.GRID) { 419 if (convertView == null || convertView instanceof AddNewBookmark 420 || convertView instanceof BookmarkItem) { 421 LayoutInflater factory = LayoutInflater.from(mBookmarksPage); 422 convertView 423 = factory.inflate(R.layout.bookmark_thumbnail, null); 424 } 425 View holder = convertView.findViewById(R.id.holder); 426 ImageView thumb = (ImageView) convertView.findViewById(R.id.thumb); 427 TextView tv = (TextView) convertView.findViewById(R.id.label); 428 429 if (0 == position && mNeedsOffset) { 430 // This is to create a bookmark for the current page. 431 holder.setVisibility(View.VISIBLE); 432 tv.setText(mCurrentTitle); 433 434 if (mCurrentThumbnail != null) { 435 thumb.setImageBitmap(mCurrentThumbnail); 436 } else { 437 thumb.setImageResource( 438 R.drawable.browser_thumbnail); 439 } 440 return convertView; 441 } 442 holder.setVisibility(View.GONE); 443 mCursor.moveToPosition(position - mExtraOffset); 444 tv.setText(mCursor.getString( 445 Browser.HISTORY_PROJECTION_TITLE_INDEX)); 446// Bitmap thumbnail = getBitmap(Browser.HISTORY_PROJECTION_THUMBNAIL_INDEX, position); 447// if (thumbnail == null) { 448// thumb.setImageResource(R.drawable.browser_thumbnail); 449// } else { 450// thumb.setImageBitmap(thumbnail); 451// } 452 453 return convertView; 454 455 } 456 if (position == 0 && mNeedsOffset) { 457 AddNewBookmark b; 458 if (convertView instanceof AddNewBookmark) { 459 b = (AddNewBookmark) convertView; 460 } else { 461 b = new AddNewBookmark(mBookmarksPage); 462 } 463 b.setUrl(mCurrentPage); 464 return b; 465 } 466 if (mMostVisited) { 467 if (convertView == null || !(convertView instanceof HistoryItem)) { 468 convertView = new HistoryItem(mBookmarksPage); 469 } 470 } else { 471 if (convertView == null || !(convertView instanceof BookmarkItem)) { 472 convertView = new BookmarkItem(mBookmarksPage); 473 } 474 } 475 bind((BookmarkItem) convertView, position); 476 if (mMostVisited) { 477 ((HistoryItem) convertView).setIsBookmark( 478 getIsBookmark(position)); 479 } 480 return convertView; 481 } 482 483 /** 484 * Return the title for this item in the list. 485 */ 486 public String getTitle(int position) { 487 return getString(Browser.HISTORY_PROJECTION_TITLE_INDEX, position); 488 } 489 490 /** 491 * Return the Url for this item in the list. 492 */ 493 public String getUrl(int position) { 494 return getString(Browser.HISTORY_PROJECTION_URL_INDEX, position); 495 } 496 497 /** 498 * Return the favicon for this item in the list. 499 */ 500 public Bitmap getFavicon(int position) { 501 return getBitmap(Browser.HISTORY_PROJECTION_FAVICON_INDEX, position); 502 } 503 504 public Bitmap getTouchIcon(int position) { 505// return getBitmap(Browser.HISTORY_PROJECTION_TOUCH_ICON_INDEX, position); 506 return getBitmap(Browser.HISTORY_PROJECTION_FAVICON_INDEX, position); 507 } 508 509 private Bitmap getBitmap(int cursorIndex, int position) { 510 if (position < mExtraOffset || position > mCount) { 511 return null; 512 } 513 mCursor.moveToPosition(position - mExtraOffset); 514 byte[] data = mCursor.getBlob(cursorIndex); 515 if (data == null) { 516 return null; 517 } 518 return BitmapFactory.decodeByteArray(data, 0, data.length); 519 } 520 521 /** 522 * Return whether or not this item represents a bookmarked site. 523 */ 524 public boolean getIsBookmark(int position) { 525 if (position < mExtraOffset || position > mCount) { 526 return false; 527 } 528 mCursor.moveToPosition(position - mExtraOffset); 529 return (1 == mCursor.getInt(Browser.HISTORY_PROJECTION_BOOKMARK_INDEX)); 530 } 531 532 /** 533 * Private helper function to return the title or url. 534 */ 535 private String getString(int cursorIndex, int position) { 536 if (position < mExtraOffset || position > mCount) { 537 return ""; 538 } 539 mCursor.moveToPosition(position- mExtraOffset); 540 return mCursor.getString(cursorIndex); 541 } 542 543 private void bind(BookmarkItem b, int position) { 544 mCursor.moveToPosition(position- mExtraOffset); 545 546 String title = mCursor.getString(Browser.HISTORY_PROJECTION_TITLE_INDEX); 547 if (title.length() > BrowserSettings.MAX_TEXTVIEW_LEN) { 548 title = title.substring(0, BrowserSettings.MAX_TEXTVIEW_LEN); 549 } 550 b.setName(title); 551 String url = mCursor.getString(Browser.HISTORY_PROJECTION_URL_INDEX); 552 if (url.length() > BrowserSettings.MAX_TEXTVIEW_LEN) { 553 url = url.substring(0, BrowserSettings.MAX_TEXTVIEW_LEN); 554 } 555 b.setUrl(url); 556 byte[] data = mCursor.getBlob(Browser.HISTORY_PROJECTION_FAVICON_INDEX); 557 if (data != null) { 558 b.setFavicon(BitmapFactory.decodeByteArray(data, 0, data.length)); 559 } else { 560 b.setFavicon(CombinedBookmarkHistoryActivity.getIconListenerSet() 561 .getFavicon(url)); 562 } 563 } 564 565 private class ChangeObserver extends ContentObserver { 566 public ChangeObserver() { 567 super(new Handler()); 568 } 569 570 @Override 571 public boolean deliverSelfNotifications() { 572 return true; 573 } 574 575 @Override 576 public void onChange(boolean selfChange) { 577 refreshList(); 578 } 579 } 580 581 private class MyDataSetObserver extends DataSetObserver { 582 @Override 583 public void onChanged() { 584 mDataValid = true; 585 notifyDataSetChanged(); 586 } 587 588 @Override 589 public void onInvalidated() { 590 mDataValid = false; 591 notifyDataSetInvalidated(); 592 } 593 } 594}