OSDN Git Service

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