PageRenderTime 68ms CodeModel.GetById 2ms app.highlight 60ms RepoModel.GetById 1ms app.codeStats 0ms

/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
 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}