diff options
author | Patrick Scott <phanna@android.com> | 2009-12-08 08:51:33 -0500 |
---|---|---|
committer | Patrick Scott <phanna@android.com> | 2010-01-28 14:09:28 -0500 |
commit | 1c15ace5e630e361a48cff8f83fef207436bb80b (patch) | |
tree | eba133f957002d0b54dedc2486acf9ea81471006 /src/com | |
parent | 1bc9c7464052c544ff5719bca614be3228f5985c (diff) | |
download | packages_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')
-rw-r--r-- | src/com/android/browser/widget/BookmarkWidgetProvider.java | 49 | ||||
-rw-r--r-- | src/com/android/browser/widget/BookmarkWidgetService.java | 385 |
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); + } + } +} |