diff options
author | John Reck <jreck@google.com> | 2010-10-29 11:42:55 -0700 |
---|---|---|
committer | John Reck <jreck@google.com> | 2010-11-01 13:24:43 -0700 |
commit | df63d2ff2e9881706eef78796d05078d0ffb7f60 (patch) | |
tree | 22ae1d5b09227bf05ff4cd501723fa6af9715a5a /src | |
parent | 0187f3352b9f8989c70b4842c0d4b528f5c796cd (diff) | |
download | packages_apps_Browser-df63d2ff2e9881706eef78796d05078d0ffb7f60.zip packages_apps_Browser-df63d2ff2e9881706eef78796d05078d0ffb7f60.tar.gz packages_apps_Browser-df63d2ff2e9881706eef78796d05078d0ffb7f60.tar.bz2 |
New bookmark list widget
Bug: 3144077 and 2988059
This replaces the old stack based widget with a list based one. It
also updates whenever the bookmarks changes rather than periodically
Change-Id: Ie37978918bab441bf31a6131360e308cd2bc0f4b
Diffstat (limited to 'src')
5 files changed, 453 insertions, 262 deletions
diff --git a/src/com/android/browser/provider/BrowserProvider2.java b/src/com/android/browser/provider/BrowserProvider2.java index c909d70..37b8eef 100644 --- a/src/com/android/browser/provider/BrowserProvider2.java +++ b/src/com/android/browser/provider/BrowserProvider2.java @@ -94,6 +94,7 @@ public class BrowserProvider2 extends SQLiteContentProvider { public static final long FIXED_ID_ROOT = 1; + // BookmarkListWidgetService.ORDER_BY_CLAUSE has a copy of this default sort order static final String DEFAULT_BOOKMARKS_SORT_ORDER = "position ASC, _id ASC"; static final UriMatcher URI_MATCHER = new UriMatcher(UriMatcher.NO_MATCH); diff --git a/src/com/android/browser/widget/BookmarkListWidgetProvider.java b/src/com/android/browser/widget/BookmarkListWidgetProvider.java new file mode 100644 index 0000000..04f7b07 --- /dev/null +++ b/src/com/android/browser/widget/BookmarkListWidgetProvider.java @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.browser.widget; + +import com.android.browser.BrowserActivity; +import com.android.browser.R; + +import android.app.PendingIntent; +import android.appwidget.AppWidgetManager; +import android.appwidget.AppWidgetProvider; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.net.Uri; +import android.util.Log; +import android.widget.RemoteViews; + +/** + * Widget that shows a preview of the user's bookmarks. + */ +public class BookmarkListWidgetProvider extends AppWidgetProvider { + static final String ACTION_BOOKMARK_APPWIDGET_UPDATE = + "com.android.browser.BOOKMARK_APPWIDGET_UPDATE"; + + /** + * {@inheritDoc} + */ + @Override + public void onReceive(Context context, Intent intent) { + // Handle bookmark-specific updates ourselves because they might be + // coming in without extras, which AppWidgetProvider then blocks. + final String action = intent.getAction(); + if (ACTION_BOOKMARK_APPWIDGET_UPDATE.equals(action)) { + AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context); + performUpdate(context, appWidgetManager, + appWidgetManager.getAppWidgetIds(getComponentName(context))); + } else { + super.onReceive(context, intent); + } + } + + @Override + public void onUpdate(Context context, AppWidgetManager mngr, int[] ids) { + performUpdate(context, mngr, ids); + } + + @Override + public void onEnabled(Context context) { + // Start the backing service + context.startService(new Intent(context, BookmarkListWidgetService.class)); + } + + @Override + public void onDisabled(Context context) { + // Stop the backing service + context.stopService(new Intent(context, BookmarkListWidgetService.class)); + } + + @Override + public void onDeleted(Context context, int[] appWidgetIds) { + super.onDeleted(context, appWidgetIds); + context.startService(new Intent(BookmarkListWidgetService.ACTION_REMOVE_FACTORIES, + null, context, BookmarkListWidgetService.class) + .putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds)); + } + + private void performUpdate(Context context, + AppWidgetManager appWidgetManager, int[] appWidgetIds) { + // Update the widgets + for (int appWidgetId : appWidgetIds) { + Intent updateIntent = new Intent(context, BookmarkListWidgetService.class); + updateIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId); + updateIntent.setData(Uri.parse(updateIntent.toUri(Intent.URI_INTENT_SCHEME))); + RemoteViews views = new RemoteViews(context.getPackageName(), + R.layout.bookmarklistwidget); + views.setRemoteAdapter(R.id.bookmarks_list, updateIntent); + appWidgetManager.notifyAppWidgetViewDataChanged(appWidgetId, R.id.bookmarks_list); + Intent ic = new Intent(context, BookmarkListWidgetService.class); + views.setPendingIntentTemplate(R.id.bookmarks_list, + PendingIntent.getService(context, 0, ic, + PendingIntent.FLAG_UPDATE_CURRENT)); + Intent launch = new Intent(context, BrowserActivity.class); + views.setOnClickPendingIntent(R.id.header, PendingIntent + .getActivity(context, 0, launch, PendingIntent.FLAG_CANCEL_CURRENT)); + appWidgetManager.updateAppWidget(appWidgetId, views); + } + } + + /** + * Build {@link ComponentName} describing this specific + * {@link AppWidgetProvider} + */ + static ComponentName getComponentName(Context context) { + return new ComponentName(context, BookmarkListWidgetProvider.class); + } +} diff --git a/src/com/android/browser/widget/BookmarkListWidgetService.java b/src/com/android/browser/widget/BookmarkListWidgetService.java new file mode 100644 index 0000000..1b3b1f4 --- /dev/null +++ b/src/com/android/browser/widget/BookmarkListWidgetService.java @@ -0,0 +1,342 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.browser.widget; + +import com.android.browser.R; +import com.android.browser.provider.BrowserProvider2; + +import android.appwidget.AppWidgetManager; +import android.content.Context; +import android.content.Intent; +import android.database.ContentObserver; +import android.database.Cursor; +import android.graphics.Bitmap; +import android.graphics.Bitmap.Config; +import android.graphics.BitmapFactory; +import android.graphics.BitmapFactory.Options; +import android.net.Uri; +import android.os.Binder; +import android.os.Handler; +import android.os.HandlerThread; +import android.provider.BrowserContract; +import android.provider.BrowserContract.Bookmarks; +import android.text.TextUtils; +import android.util.Log; +import android.widget.RemoteViews; +import android.widget.RemoteViewsService; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class BookmarkListWidgetService extends RemoteViewsService { + + static final String TAG = "BookmarkListWidgetService"; + static final boolean USE_FOLDERS = true; + + static final String ACTION_REMOVE_FACTORIES + = "com.android.browser.widget.REMOVE_FACTORIES"; + static final String ACTION_CHANGE_FOLDER + = "com.android.browser.widget.CHANGE_FOLDER"; + + private static final String[] PROJECTION = new String[] { + BrowserContract.Bookmarks._ID, + BrowserContract.Bookmarks.TITLE, + BrowserContract.Bookmarks.URL, + BrowserContract.Bookmarks.FAVICON, + BrowserContract.Bookmarks.IS_FOLDER, + BrowserContract.Bookmarks.PARENT}; + + // Ordering merged with DEFAULT_BOOKMARK_SORT_ORDER from BrowserProvider2 + private static final String ORDER_BY_CLAUSE = + Bookmarks.IS_FOLDER + " DESC, position ASC, _id ASC"; + + private Map<Integer, BookmarkFactory> mFactories; + private Handler mUiHandler; + private HandlerThread mBackgroundThread; + private BookmarksObserver mBookmarksObserver; + + @Override + public void onCreate() { + super.onCreate(); + mFactories = new HashMap<Integer, BookmarkFactory>(); + mUiHandler = new Handler(); + mBookmarksObserver = new BookmarksObserver(mUiHandler); + getContentResolver().registerContentObserver( + BrowserContract.AUTHORITY_URI, true, mBookmarksObserver); + } + + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + String action = intent.getAction(); + if (Intent.ACTION_VIEW.equals(action)) { + Intent view = new Intent(intent); + view.setComponent(null); + startActivity(view); + } else if (ACTION_REMOVE_FACTORIES.equals(action)) { + int[] ids = intent.getIntArrayExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS); + if (ids != null) { + for (int id : ids) { + mFactories.remove(id); + } + } + } else if (ACTION_CHANGE_FOLDER.equals(action)) { + int widgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1); + long folderId = intent.getLongExtra(Bookmarks._ID, -1); + BookmarkFactory fac = mFactories.get(widgetId); + if (fac != null && folderId >= 0) { + fac.setFolder(folderId); + AppWidgetManager.getInstance(this).notifyAppWidgetViewDataChanged(widgetId, R.id.bookmarks_list); + } + } + return START_STICKY; + } + + @Override + public void onDestroy() { + super.onDestroy(); + getContentResolver().unregisterContentObserver(mBookmarksObserver); + mBackgroundThread.quit(); + } + + private class BookmarksObserver extends ContentObserver { + public BookmarksObserver(Handler handler) { + super(handler); + } + + @Override + public void onChange(boolean selfChange) { + super.onChange(selfChange); + + // Update all the bookmark widgets + sendBroadcast(new Intent( + BookmarkListWidgetProvider.ACTION_BOOKMARK_APPWIDGET_UPDATE, + null, BookmarkListWidgetService.this, + BookmarkListWidgetProvider.class)); + } + } + + @Override + public RemoteViewsFactory onGetViewFactory(Intent intent) { + int widgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1); + if (widgetId < 0) { + Log.w(TAG, "Missing EXTRA_APPWIDGET_ID!"); + return null; + } else { + BookmarkFactory fac = new BookmarkFactory(this, widgetId); + mFactories.put(widgetId, fac); + return fac; + } + } + + static class BookmarkFactory implements RemoteViewsService.RemoteViewsFactory { + private List<RenderResult> mBookmarks; + private Context mContext; + private int mWidgetId; + private long mFolderId = BrowserProvider2.FIXED_ID_ROOT; + + public BookmarkFactory(Context context, int widgetId) { + mContext = context; + mWidgetId = widgetId; + } + + void setFolder(long folderId) { + mFolderId = folderId; + } + + @Override + public int getCount() { + if (mBookmarks == null) + return 0; + return mBookmarks.size(); + } + + @Override + public long getItemId(int position) { + return position; + } + + @Override + public RemoteViews getLoadingView() { + return null; + } + + @Override + public RemoteViews getViewAt(int position) { + if (position < 0 || position >= getCount()) { + return null; + } + + RenderResult res = mBookmarks.get(position); + + RemoteViews views = new RemoteViews( + mContext.getPackageName(), R.layout.bookmarklistwidget_item); + Intent fillin; + if (res.mIsFolder) { + long nfi = res.mId; + if (nfi == mFolderId) nfi = res.mParentId; + fillin = new Intent(ACTION_CHANGE_FOLDER, null, + mContext, BookmarkListWidgetService.class) + .putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, mWidgetId) + .putExtra(Bookmarks._ID, nfi); + } else { + fillin = new Intent(Intent.ACTION_VIEW) + .setData(Uri.parse(res.mUrl)) + .addCategory(Intent.CATEGORY_BROWSABLE); + } + views.setOnClickFillInIntent(R.id.list_item, fillin); + // Set the title of the bookmark. Use the url as a backup. + String displayTitle = res.mTitle; + if (TextUtils.isEmpty(displayTitle)) { + // The browser always requires a title for bookmarks, but jic... + displayTitle = res.mUrl; + } + views.setTextViewText(R.id.label, displayTitle); + views.setDrawableParameters(R.id.list_item, true, 0, -1, null, -1); + if (res.mIsFolder) { + if (res.mId == mFolderId) { + views.setDrawableParameters(R.id.list_item, true, 140, -1, null, -1); + views.setImageViewResource(R.id.thumb, R.drawable.ic_back_normal); + } else { + views.setImageViewResource(R.id.thumb, R.drawable.ic_folder); + } + } else { + if (res.mBitmap != null) { + views.setImageViewBitmap(R.id.thumb, res.mBitmap); + } else { + views.setImageViewResource(R.id.thumb, + R.drawable.browser_thumbnail); + } + } + return views; + } + + @Override + public int getViewTypeCount() { + return 1; + } + + @Override + public boolean hasStableIds() { + return false; + } + + @Override + public void onCreate() { + loadData(); + } + + @Override + public void onDestroy() { + recycleBitmaps(); + } + + @Override + public void onDataSetChanged() { + loadData(); + } + + void loadData() { + // Reset identity since this could be an IPC call + long token = Binder.clearCallingIdentity(); + update(); + Binder.restoreCallingIdentity(token); + } + + void update() { + recycleBitmaps(); + String where; + if (USE_FOLDERS) { + where = String.format("%s == %d", Bookmarks.PARENT, mFolderId); + if (mFolderId != BrowserProvider2.FIXED_ID_ROOT) { + where = String.format("%s OR %s == %d", where, + Bookmarks._ID, mFolderId); + } + } else { + where = Bookmarks.IS_FOLDER + " == 0"; + } + Cursor c = null; + try { + c = mContext.getContentResolver().query( + BrowserContract.Bookmarks.CONTENT_URI, PROJECTION, + where, null, ORDER_BY_CLAUSE); + if (c != null) { + mBookmarks = new ArrayList<RenderResult>(c.getCount()); + while (c.moveToNext()) { + long id = c.getLong(0); + String title = c.getString(1); + String url = c.getString(2); + RenderResult res = new RenderResult(id, title, url); + byte[] blob = c.getBlob(3); + if (blob != null) { + // RemoteViews require a valid bitmap config + Options options = new Options(); + options.inPreferredConfig = Config.ARGB_8888; + res.mBitmap = BitmapFactory.decodeByteArray( + blob, 0, blob.length, options); + } + res.mIsFolder = c.getInt(4) != 0; + res.mParentId = c.getLong(5); + if (res.mId == mFolderId) { + // Make sure this is first + mBookmarks.add(0, res); + } else { + mBookmarks.add(res); + } + } + } + } catch (IllegalStateException e) { + Log.e(TAG, "update bookmark widget", e); + } finally { + if (c != null) { + c.close(); + } + } + } + + private void recycleBitmaps() { + // Do a bit of house cleaning for the system + if (mBookmarks != null) { + for (RenderResult res : mBookmarks) { + if (res.mBitmap != null) { + res.mBitmap.recycle(); + res.mBitmap = null; + } + } + } + } + } + + // Class containing the rendering information for a specific bookmark. + private static class RenderResult { + final String mTitle; + final String mUrl; + Bitmap mBitmap; + boolean mIsFolder; + long mParentId; + long mId; + + RenderResult(long id, String title, String url) { + mId = id; + mTitle = title; + mUrl = url; + } + + } + +} diff --git a/src/com/android/browser/widget/BookmarkStackWidgetProvider.java b/src/com/android/browser/widget/BookmarkStackWidgetProvider.java deleted file mode 100644 index 0684c61..0000000 --- a/src/com/android/browser/widget/BookmarkStackWidgetProvider.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.browser.widget; - -import android.appwidget.AppWidgetManager; -import android.appwidget.AppWidgetProvider; -import android.content.Context; -import android.content.Intent; - -/** - * Widget that shows a preview of the user's bookmarks. - */ -public class BookmarkStackWidgetProvider extends AppWidgetProvider { - - @Override - public void onUpdate(Context context, AppWidgetManager mngr, int[] ids) { - context.startService(new Intent(BookmarkStackWidgetService.UPDATE, null, - context, BookmarkStackWidgetService.class)); - } - - @Override - public void onEnabled(Context context) { - context.startService(new Intent(context, BookmarkStackWidgetService.class)); - } - - @Override - public void onDisabled(Context context) { - context.stopService(new Intent(context, BookmarkStackWidgetService.class)); - } - -} diff --git a/src/com/android/browser/widget/BookmarkStackWidgetService.java b/src/com/android/browser/widget/BookmarkStackWidgetService.java deleted file mode 100644 index 83b07df..0000000 --- a/src/com/android/browser/widget/BookmarkStackWidgetService.java +++ /dev/null @@ -1,217 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.browser.widget; - -import com.android.browser.R; - -import android.app.PendingIntent; -import android.appwidget.AppWidgetManager; -import android.content.ComponentName; -import android.content.Intent; -import android.database.Cursor; -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; -import android.net.Uri; -import android.os.Handler; -import android.os.Message; -import android.provider.BrowserContract; -import android.text.TextUtils; -import android.util.Log; -import android.view.View; -import android.widget.RemoteViews; -import android.widget.RemoteViewsService; - -import java.util.ArrayList; -import java.util.List; - -public class BookmarkStackWidgetService extends RemoteViewsService { - - private static final String LOGTAG = "browserwidget"; - - /** Force the bookmarks to be re-rendered. */ - public static final String UPDATE = "com.android.browser.widget.UPDATE"; - - /** the adapter intent action */ - public static final String ADAPTER = "com.android.browser.widget.ADAPTER"; - - private static final String[] PROJECTION = new String[] { - BrowserContract.Bookmarks._ID, - BrowserContract.Bookmarks.TITLE, - BrowserContract.Bookmarks.URL, - BrowserContract.Bookmarks.THUMBNAIL }; - - private static final String WHERE_CLAUSE = BrowserContract.Bookmarks.IS_FOLDER + - " == 0"; - - // No id specified. - private static final int NO_ID = -1; - - private static final int MSG_UPDATE = 0; - - List<RenderResult> mBookmarks; - - private final Handler mHandler = new Handler() { - @Override - public void handleMessage(Message msg) { - switch (msg.what) { - case MSG_UPDATE: - updateWidget(); - break; - default: - break; - } - } - }; - - @Override - public int onStartCommand(Intent intent, int flags, int startId) { - if ((intent == null) || (intent.getAction() != null) && UPDATE.equals(intent.getAction())) { - mHandler.sendEmptyMessage(MSG_UPDATE); - } - return START_STICKY; - } - - private void updateWidget() { - RemoteViews views = new RemoteViews(getPackageName(), - R.layout.bookmarkstackwidget); - Intent vi = new Intent(Intent.ACTION_VIEW); - vi.addCategory(Intent.CATEGORY_BROWSABLE); - views.setPendingIntentTemplate(R.id.stackwidget_stack, - PendingIntent.getActivity(BookmarkStackWidgetService.this, 0, vi, - PendingIntent.FLAG_CANCEL_CURRENT)); - Intent adapter = new Intent(BookmarkStackWidgetService.ADAPTER, null, - this, BookmarkStackWidgetService.class); - views.setRemoteAdapter(R.id.stackwidget_stack, adapter); - AppWidgetManager.getInstance(this).updateAppWidget( - new ComponentName(this, BookmarkStackWidgetProvider.class), - views); - } - - @Override - public RemoteViewsFactory onGetViewFactory(Intent intent) { - return mViewFactory; - } - - RemoteViewsService.RemoteViewsFactory mViewFactory = new RemoteViewsFactory () { - - Intent mFillIntent; - - @Override - public int getCount() { - return mBookmarks.size(); - } - - @Override - public long getItemId(int position) { - return position; - } - - @Override - public RemoteViews getLoadingView() { - return null; - } - - @Override - public RemoteViews getViewAt(int position) { - RenderResult res = mBookmarks.get(position); - RemoteViews views = new RemoteViews(getPackageName(), - R.layout.bookmarkstackwidget_item); - mFillIntent.setData(Uri.parse(res.mUrl)); - views.setOnClickFillInIntent(R.id.stack_item, mFillIntent); - // Set the title of the bookmark. Use the url as a backup. - String displayTitle = res.mTitle; - if (TextUtils.isEmpty(displayTitle)) { - displayTitle = res.mUrl; - } - views.setTextViewText(R.id.label, displayTitle); - if (res.mBitmap != null) { - views.setImageViewBitmap(R.id.thumb, res.mBitmap); - views.setViewVisibility(R.id.label, View.GONE); - } - return views; - } - - @Override - public int getViewTypeCount() { - return 1; - } - - @Override - public boolean hasStableIds() { - return false; - } - - @Override - public void onCreate() { - mFillIntent = new Intent(); - update(); - } - - @Override - public void onDestroy() { - } - - public void update() { - mBookmarks = new ArrayList<RenderResult>(); - // Look up all the bookmarks - Cursor c = null; - try { - c = getContentResolver().query(BrowserContract.Bookmarks.CONTENT_URI, - PROJECTION, WHERE_CLAUSE, null, null); - if (c != null) { - while (c.moveToNext()) { - int id = c.getInt(0); - String title = c.getString(1); - String url = c.getString(2); - RenderResult res = new RenderResult(id, title, url); - byte[] blob = c.getBlob(3); - if (blob != null) { - res.mBitmap = BitmapFactory.decodeByteArray(blob, 0, blob.length); - } - mBookmarks.add(res); - } - } - } catch (IllegalStateException e) { - Log.e(LOGTAG, "update bookmark widget", e); - } finally { - if (c != null) { - c.close(); - } - } - } - - @Override - public void onDataSetChanged() { - } - }; - - // Class containing the rendering information for a specific bookmark. - private static class RenderResult { - final int mId; - final String mTitle; - final String mUrl; - Bitmap mBitmap; - - RenderResult(int id, String title, String url) { - mId = id; - mTitle = title; - mUrl = url; - } - - } - -} |