/WebVox/src/com/marvin/webvox/BrowserBookmarksAdapter.java

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