summaryrefslogtreecommitdiffstats
path: root/src/com/android/browser/widget
diff options
context:
space:
mode:
authorPatrick Scott <phanna@android.com>2009-12-08 08:51:33 -0500
committerPatrick Scott <phanna@android.com>2010-01-28 14:09:28 -0500
commit1c15ace5e630e361a48cff8f83fef207436bb80b (patch)
treeeba133f957002d0b54dedc2486acf9ea81471006 /src/com/android/browser/widget
parent1bc9c7464052c544ff5719bca614be3228f5985c (diff)
downloadpackages_apps_Browser-1c15ace5e630e361a48cff8f83fef207436bb80b.zip
packages_apps_Browser-1c15ace5e630e361a48cff8f83fef207436bb80b.tar.gz
packages_apps_Browser-1c15ace5e630e361a48cff8f83fef207436bb80b.tar.bz2
Demo widget using the UrlRendererService for server-side rendering of urls.
Diffstat (limited to 'src/com/android/browser/widget')
-rw-r--r--src/com/android/browser/widget/BookmarkWidgetProvider.java49
-rw-r--r--src/com/android/browser/widget/BookmarkWidgetService.java385
2 files changed, 434 insertions, 0 deletions
diff --git a/src/com/android/browser/widget/BookmarkWidgetProvider.java b/src/com/android/browser/widget/BookmarkWidgetProvider.java
new file mode 100644
index 0000000..62b48c0
--- /dev/null
+++ b/src/com/android/browser/widget/BookmarkWidgetProvider.java
@@ -0,0 +1,49 @@
+/*
+ * 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.app.PendingIntent;
+import android.appwidget.AppWidgetManager;
+import android.appwidget.AppWidgetProvider;
+import android.content.Context;
+import android.content.Intent;
+import android.util.Log;
+
+/**
+ * Widget that shows a preview of the user's bookmarks.
+ */
+public class BookmarkWidgetProvider extends AppWidgetProvider {
+
+ static final String TAG = "BookmarkWidgetProvider";
+
+ @Override
+ public void onUpdate(Context context, AppWidgetManager mngr, int[] ids) {
+ context.startService(new Intent(BookmarkWidgetService.UPDATE, null,
+ context, BookmarkWidgetService.class));
+ }
+
+ @Override
+ public void onEnabled(Context context) {
+ context.startService(new Intent(context, BookmarkWidgetService.class));
+ }
+
+ @Override
+ public void onDisabled(Context context) {
+ context.stopService(new Intent(context, BookmarkWidgetService.class));
+ }
+}
+
diff --git a/src/com/android/browser/widget/BookmarkWidgetService.java b/src/com/android/browser/widget/BookmarkWidgetService.java
new file mode 100644
index 0000000..1fd9163
--- /dev/null
+++ b/src/com/android/browser/widget/BookmarkWidgetService.java
@@ -0,0 +1,385 @@
+/*
+ * 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.app.PendingIntent;
+import android.app.Service;
+import android.appwidget.AppWidgetManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.database.Cursor;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.ParcelFileDescriptor;
+import android.provider.Browser;
+import android.provider.Browser.BookmarkColumns;
+import android.service.urlrenderer.UrlRenderer;
+import android.service.urlrenderer.UrlRendererService;
+import android.util.Log;
+import android.view.View;
+import android.widget.RemoteViews;
+
+import com.android.browser.R;
+
+import java.io.InputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+
+public class BookmarkWidgetService extends Service
+ implements UrlRenderer.Callback {
+
+ private static final String TAG = "BookmarkWidgetService";
+
+ /** Force the bookmarks to be re-renderer. */
+ public static final String UPDATE = "com.android.browser.widget.UPDATE";
+
+ /** Change the widget to the next bookmark. */
+ private static final String NEXT = "com.android.browser.widget.NEXT";
+
+ /** Change the widget to the previous bookmark. */
+ private static final String PREV = "com.android.browser.widget.PREV";
+
+ /** Id of the current item displayed in the widget. */
+ private static final String EXTRA_ID =
+ "com.android.browser.widget.extra.ID";
+
+ // XXX: Remove these magic numbers once the dimensions of the widget can be
+ // queried.
+ private static final int WIDTH = 306;
+ private static final int HEIGHT = 386;
+
+ // Limit the number of connection attempts.
+ private static final int MAX_SERVICE_RETRY_COUNT = 5;
+
+ // No id specified.
+ private static final int NO_ID = -1;
+
+ private static final int MSG_UPDATE = 0;
+ private final Handler mHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_UPDATE:
+ if (mRenderer != null) {
+ queryCursorAndRender();
+ } else {
+ if (++mServiceRetryCount <= MAX_SERVICE_RETRY_COUNT) {
+ // Service is not connected, try again in a second.
+ mHandler.sendEmptyMessageDelayed(MSG_UPDATE, 1000);
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ };
+
+ private final ServiceConnection mConnection = new ServiceConnection() {
+ public void onServiceConnected(ComponentName className,
+ IBinder service) {
+ mRenderer = new UrlRenderer(service);
+ }
+
+ public void onServiceDisconnected(ComponentName className) {
+ mRenderer = null;
+ }
+ };
+
+ // Id -> information map storing db ids and their result.
+ private final HashMap<Integer, RenderResult> mIdsToResults =
+ new HashMap<Integer, RenderResult>();
+
+ // List of ids in order
+ private final ArrayList<Integer> mIdList = new ArrayList<Integer>();
+
+ // Map of urls to ids for when a url is complete.
+ private final HashMap<String, Integer> mUrlsToIds =
+ new HashMap<String, Integer>();
+
+ // The current id used by the widget during an update.
+ private int mCurrentId = NO_ID;
+ // Class that contacts the service on the phone to render bookmarks.
+ private UrlRenderer mRenderer;
+ // Number of service retries. Stop trying to connect after
+ // MAX_SERVICE_RETRY_COUNT
+ private int mServiceRetryCount;
+
+ @Override
+ public void onCreate() {
+ bindService(new Intent(UrlRendererService.SERVICE_INTERFACE),
+ mConnection, Context.BIND_AUTO_CREATE);
+ }
+
+ @Override
+ public void onDestroy() {
+ unbindService(mConnection);
+ }
+
+ @Override
+ public android.os.IBinder onBind(Intent intent) {
+ return null;
+ }
+
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ final String action = intent.getAction();
+ if (UPDATE.equals(action)) {
+ mHandler.sendEmptyMessage(MSG_UPDATE);
+ } else if (PREV.equals(action) && mIdList.size() > 1) {
+ int prev = getPreviousId(intent);
+ if (prev == NO_ID) {
+ Log.d(TAG, "Could not determine previous id");
+ return START_NOT_STICKY;
+ }
+ RenderResult res = mIdsToResults.get(prev);
+ if (res != null) {
+ updateWidget(res);
+ }
+ } else if (NEXT.equals(action) && mIdList.size() > 1) {
+ int next = getNextId(intent);
+ if (next == NO_ID) {
+ Log.d(TAG, "Could not determine next id");
+ return START_NOT_STICKY;
+ }
+ RenderResult res = mIdsToResults.get(next);
+ if (res != null) {
+ updateWidget(res);
+ }
+ }
+ return START_STICKY;
+ }
+
+ private int getPreviousId(Intent intent) {
+ int listSize = mIdList.size();
+ // If the list contains 1 or fewer entries, return NO_ID so that the
+ // widget does not update.
+ if (listSize <= 1) {
+ return NO_ID;
+ }
+
+ int curr = intent.getIntExtra(EXTRA_ID, NO_ID);
+ if (curr == NO_ID) {
+ return NO_ID;
+ }
+
+ // Check if the current id is the beginning of the list so we can skip
+ // iterating through.
+ if (mIdList.get(0) == curr) {
+ return mIdList.get(listSize - 1);
+ }
+
+ // Search for the current id and remember the previous id.
+ int prev = NO_ID;
+ for (int id : mIdList) {
+ if (id == curr) {
+ break;
+ }
+ prev = id;
+ }
+ return prev;
+ }
+
+ private int getNextId(Intent intent) {
+ int listSize = mIdList.size();
+ // If the list contains 1 or fewer entries, return NO_ID so that the
+ // widget does not update.
+ if (listSize <= 1) {
+ return NO_ID;
+ }
+
+ int curr = intent.getIntExtra(EXTRA_ID, NO_ID);
+ if (curr == NO_ID) {
+ return NO_ID;
+ }
+
+ // Check if the current id is at the end of the list so we can skip
+ // iterating through.
+ if (mIdList.get(listSize - 1) == curr) {
+ return mIdList.get(0);
+ }
+
+ // Iterate through the ids. i is set to the current index + 1.
+ int i = 1;
+ for (int id : mIdList) {
+ if (id == curr) {
+ break;
+ }
+ i++;
+ }
+ return mIdList.get(i);
+ }
+
+ private void updateWidget(RenderResult res) {
+ RemoteViews views = new RemoteViews(getPackageName(),
+ R.layout.bookmarkwidget);
+
+ Intent prev = new Intent(PREV, null, this, BookmarkWidgetService.class);
+ prev.putExtra(EXTRA_ID, res.mId);
+ views.setOnClickPendingIntent(R.id.previous,
+ PendingIntent.getService(this, 0, prev,
+ PendingIntent.FLAG_CANCEL_CURRENT));
+
+ Intent next = new Intent(NEXT, null, this, BookmarkWidgetService.class);
+ next.putExtra(EXTRA_ID, res.mId);
+ views.setOnClickPendingIntent(R.id.next,
+ PendingIntent.getService(this, 0, next,
+ PendingIntent.FLAG_CANCEL_CURRENT));
+
+ // Set the title of the bookmark. Use the url as a backup.
+ String displayTitle = res.mTitle;
+ if (displayTitle == null) {
+ displayTitle = res.mUrl;
+ }
+ views.setTextViewText(R.id.title, displayTitle);
+
+ // Set the image or revert to the progress indicator.
+ if (res.mBitmap != null) {
+ views.setImageViewBitmap(R.id.image, res.mBitmap);
+ views.setViewVisibility(R.id.image, View.VISIBLE);
+ views.setViewVisibility(R.id.progress, View.GONE);
+ } else {
+ views.setViewVisibility(R.id.progress, View.VISIBLE);
+ views.setViewVisibility(R.id.image, View.GONE);
+ }
+
+ // Update the current id.
+ mCurrentId = res.mId;
+
+ AppWidgetManager.getInstance(this).updateAppWidget(
+ new ComponentName(this, BookmarkWidgetProvider.class),
+ views);
+ }
+
+ // Default WHERE clause is all bookmarks.
+ private static final String QUERY_WHERE =
+ BookmarkColumns.BOOKMARK + " == 1";
+ private static final String[] PROJECTION = new String[] {
+ BookmarkColumns._ID, BookmarkColumns.TITLE, BookmarkColumns.URL };
+
+ // 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;
+ }
+ }
+
+ private void queryCursorAndRender() {
+ // Clear the ordered list of ids and the map of ids to bitmaps.
+ mIdList.clear();
+ mIdsToResults.clear();
+
+ // Look up all the bookmarks
+ Cursor c = getContentResolver().query(Browser.BOOKMARKS_URI, PROJECTION,
+ QUERY_WHERE, null, null);
+ if (c != null) {
+ if (c.moveToFirst()) {
+ ArrayList<String> urls = new ArrayList<String>(c.getCount());
+ boolean sawCurrentId = false;
+ do {
+ int id = c.getInt(0);
+ String title = c.getString(1);
+ String url = c.getString(2);
+
+ // Linear list of ids to obtain the previous and next.
+ mIdList.add(id);
+
+ // Map the url to its db id for lookup when complete.
+ mUrlsToIds.put(url, id);
+
+ // Is this the current id?
+ if (mCurrentId == id) {
+ sawCurrentId = true;
+ }
+
+ // Store the current information to at least display the
+ // title.
+ RenderResult res = new RenderResult(id, title, url);
+ mIdsToResults.put(id, res);
+
+ // Add the url to our list to render.
+ urls.add(url);
+ } while (c.moveToNext());
+
+ // Request a rendering of the urls. XXX: Hard-coded dimensions
+ // until the view's orientation and size can be determined. Or
+ // in the future the image will be a picture that can be
+ // scaled/zoomed arbitrarily.
+ mRenderer.render(urls, WIDTH, HEIGHT, this);
+
+ // Set the current id to the very first id if we did not see
+ // the current id in the list (the bookmark could have been
+ // deleted or this is the first update).
+ if (!sawCurrentId) {
+ mCurrentId = mIdList.get(0);
+ }
+ }
+ c.close();
+ }
+ }
+
+ // UrlRenderer.Callback implementation
+ public void complete(String url, ParcelFileDescriptor result) {
+ int id = mUrlsToIds.get(url);
+ if (id == NO_ID) {
+ Log.d(TAG, "No matching id found during completion of "
+ + url);
+ return;
+ }
+
+ RenderResult res = mIdsToResults.get(id);
+ if (res == null) {
+ Log.d(TAG, "No result found during completion of "
+ + url);
+ return;
+ }
+
+ // Set the result.
+ if (result != null) {
+ InputStream input =
+ new ParcelFileDescriptor.AutoCloseInputStream(result);
+ Bitmap orig = BitmapFactory.decodeStream(input, null, null);
+ // XXX: Hard-coded scaled bitmap until I can query the image
+ // dimensions.
+ res.mBitmap = Bitmap.createScaledBitmap(orig, WIDTH, HEIGHT, true);
+ try {
+ input.close();
+ } catch (IOException e) {
+ // oh well...
+ }
+ }
+
+ // If we are currently looking at the bookmark that just finished,
+ // update the widget.
+ if (mCurrentId == id) {
+ updateWidget(res);
+ }
+ }
+}