summaryrefslogtreecommitdiffstats
path: root/src/com/android/gallery3d/gadget
diff options
context:
space:
mode:
authorOwen Lin <owenlin@google.com>2011-08-18 21:48:19 +0800
committerOwen Lin <owenlin@google.com>2011-08-24 16:49:06 +0800
commit5673202ac99b89112a82210016885a9355f7e991 (patch)
tree5e9d5322dcc4bfa0fe7926ea95f47c950a028ad1 /src/com/android/gallery3d/gadget
parent31c7b13bf1c93f7eabd658e55c545eccd9d347a9 (diff)
downloadandroid_packages_apps_Snap-5673202ac99b89112a82210016885a9355f7e991.tar.gz
android_packages_apps_Snap-5673202ac99b89112a82210016885a9355f7e991.tar.bz2
android_packages_apps_Snap-5673202ac99b89112a82210016885a9355f7e991.zip
Fix shortcup broken issue.
fix: 5154308 Change-Id: I5cd2ef8efb84d4f356b3fe93106bddf10e0823cc
Diffstat (limited to 'src/com/android/gallery3d/gadget')
-rw-r--r--src/com/android/gallery3d/gadget/LocalPhotoSource.java202
-rw-r--r--src/com/android/gallery3d/gadget/MediaSetSource.java113
-rw-r--r--src/com/android/gallery3d/gadget/PhotoAppWidgetProvider.java125
-rw-r--r--src/com/android/gallery3d/gadget/WidgetClickHandler.java59
-rw-r--r--src/com/android/gallery3d/gadget/WidgetConfigure.java167
-rw-r--r--src/com/android/gallery3d/gadget/WidgetDatabaseHelper.java245
-rw-r--r--src/com/android/gallery3d/gadget/WidgetService.java169
-rw-r--r--src/com/android/gallery3d/gadget/WidgetSource.java31
-rw-r--r--src/com/android/gallery3d/gadget/WidgetTypeChooser.java59
-rw-r--r--src/com/android/gallery3d/gadget/WidgetUtils.java80
10 files changed, 1250 insertions, 0 deletions
diff --git a/src/com/android/gallery3d/gadget/LocalPhotoSource.java b/src/com/android/gallery3d/gadget/LocalPhotoSource.java
new file mode 100644
index 000000000..ad77de5b3
--- /dev/null
+++ b/src/com/android/gallery3d/gadget/LocalPhotoSource.java
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2011 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.gallery3d.gadget;
+
+import com.android.gallery3d.app.GalleryApp;
+import com.android.gallery3d.common.Utils;
+import com.android.gallery3d.data.ContentListener;
+import com.android.gallery3d.data.DataManager;
+import com.android.gallery3d.data.MediaItem;
+import com.android.gallery3d.data.Path;
+import com.android.gallery3d.util.GalleryUtils;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.database.ContentObserver;
+import android.database.Cursor;
+import android.graphics.Bitmap;
+import android.net.Uri;
+import android.os.Environment;
+import android.os.Handler;
+import android.provider.MediaStore.Images.Media;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Random;
+
+public class LocalPhotoSource implements WidgetSource {
+
+ private static final String TAG = "LocalPhotoSource";
+
+ private static final int MAX_PHOTO_COUNT = 128;
+
+ /* Static fields used to query for the correct set of images */
+ private static final Uri CONTENT_URI = Media.EXTERNAL_CONTENT_URI;
+ private static final String DATE_TAKEN = Media.DATE_TAKEN;
+ private static final String[] PROJECTION = {Media._ID};
+ private static final String[] COUNT_PROJECTION = {"count(*)"};
+ /* We don't want to include the download directory */
+ private static final String SELECTION =
+ String.format("%s != %s", Media.BUCKET_ID, getDownloadBucketId());
+ private static final String ORDER = String.format("%s DESC", DATE_TAKEN);
+
+ private Context mContext;
+ private ArrayList<Long> mPhotos = new ArrayList<Long>();
+ private ContentListener mContentListener;
+ private ContentObserver mContentObserver;
+ private boolean mContentDirty = true;
+ private DataManager mDataManager;
+ private static final Path LOCAL_IMAGE_ROOT = Path.fromString("/local/image/item");
+
+ public LocalPhotoSource(Context context) {
+ mContext = context;
+ mDataManager = ((GalleryApp) context.getApplicationContext()).getDataManager();
+ mContentObserver = new ContentObserver(new Handler()) {
+ @Override
+ public void onChange(boolean selfChange) {
+ mContentDirty = true;
+ if (mContentListener != null) mContentListener.onContentDirty();
+ }
+ };
+ mContext.getContentResolver()
+ .registerContentObserver(CONTENT_URI, true, mContentObserver);
+ }
+
+ public void close() {
+ mContext.getContentResolver().unregisterContentObserver(mContentObserver);
+ }
+
+ @Override
+ public Uri getContentUri(int index) {
+ if (index < mPhotos.size()) {
+ return CONTENT_URI.buildUpon()
+ .appendPath(String.valueOf(mPhotos.get(index)))
+ .build();
+ }
+ return null;
+ }
+
+ @Override
+ public Bitmap getImage(int index) {
+ if (index >= mPhotos.size()) return null;
+ long id = mPhotos.get(index);
+ MediaItem image = (MediaItem)
+ mDataManager.getMediaObject(LOCAL_IMAGE_ROOT.getChild(id));
+ if (image == null) return null;
+
+ return WidgetUtils.createWidgetBitmap(image);
+ }
+
+ private int[] getExponentialIndice(int total, int count) {
+ Random random = new Random();
+ if (count > total) count = total;
+ HashSet<Integer> selected = new HashSet<Integer>(count);
+ while (selected.size() < count) {
+ int row = (int)(-Math.log(random.nextDouble()) * total / 2);
+ if (row < total) selected.add(row);
+ }
+ int values[] = new int[count];
+ int index = 0;
+ for (int value : selected) {
+ values[index++] = value;
+ }
+ return values;
+ }
+
+ private int getPhotoCount(ContentResolver resolver) {
+ Cursor cursor = resolver.query(
+ CONTENT_URI, COUNT_PROJECTION, SELECTION, null, null);
+ if (cursor == null) return 0;
+ try {
+ Utils.assertTrue(cursor.moveToNext());
+ return cursor.getInt(0);
+ } finally {
+ cursor.close();
+ }
+ }
+
+ private boolean isContentSound(int totalCount) {
+ if (mPhotos.size() < Math.min(totalCount, MAX_PHOTO_COUNT)) return false;
+ if (mPhotos.size() == 0) return true; // totalCount is also 0
+
+ StringBuilder builder = new StringBuilder();
+ for (Long imageId : mPhotos) {
+ if (builder.length() > 0) builder.append(",");
+ builder.append(imageId);
+ }
+ Cursor cursor = mContext.getContentResolver().query(
+ CONTENT_URI, COUNT_PROJECTION,
+ String.format("%s in (%s)", Media._ID, builder.toString()),
+ null, null);
+ if (cursor == null) return false;
+ try {
+ Utils.assertTrue(cursor.moveToNext());
+ return cursor.getInt(0) == mPhotos.size();
+ } finally {
+ cursor.close();
+ }
+ }
+
+ public void reload() {
+ if (!mContentDirty) return;
+ mContentDirty = false;
+
+ ContentResolver resolver = mContext.getContentResolver();
+ int photoCount = getPhotoCount(resolver);
+ if (isContentSound(photoCount)) return;
+
+ int choosedIds[] = getExponentialIndice(photoCount, MAX_PHOTO_COUNT);
+ Arrays.sort(choosedIds);
+
+ mPhotos.clear();
+ Cursor cursor = mContext.getContentResolver().query(
+ CONTENT_URI, PROJECTION, SELECTION, null, ORDER);
+ if (cursor == null) return;
+ try {
+ for (int index : choosedIds) {
+ if (cursor.moveToPosition(index)) {
+ mPhotos.add(cursor.getLong(0));
+ }
+ }
+ } finally {
+ cursor.close();
+ }
+ }
+
+ @Override
+ public int size() {
+ reload();
+ return mPhotos.size();
+ }
+
+ /**
+ * Builds the bucket ID for the public external storage Downloads directory
+ * @return the bucket ID
+ */
+ private static int getDownloadBucketId() {
+ String downloadsPath = Environment
+ .getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)
+ .getAbsolutePath();
+ return GalleryUtils.getBucketId(downloadsPath);
+ }
+
+ @Override
+ public void setContentListener(ContentListener listener) {
+ mContentListener = listener;
+ }
+}
diff --git a/src/com/android/gallery3d/gadget/MediaSetSource.java b/src/com/android/gallery3d/gadget/MediaSetSource.java
new file mode 100644
index 000000000..c1687e0c2
--- /dev/null
+++ b/src/com/android/gallery3d/gadget/MediaSetSource.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2011 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.gallery3d.gadget;
+
+import com.android.gallery3d.common.Utils;
+import com.android.gallery3d.data.ContentListener;
+import com.android.gallery3d.data.MediaItem;
+import com.android.gallery3d.data.MediaObject;
+import com.android.gallery3d.data.MediaSet;
+
+import android.graphics.Bitmap;
+import android.net.Uri;
+import android.os.Binder;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+
+public class MediaSetSource implements WidgetSource, ContentListener {
+ private static final int CACHE_SIZE = 32;
+
+ private static final String TAG = "MediaSetSource";
+
+ private MediaSet mSource;
+ private MediaItem mCache[] = new MediaItem[CACHE_SIZE];
+ private int mCacheStart;
+ private int mCacheEnd;
+ private long mSourceVersion = MediaObject.INVALID_DATA_VERSION;
+
+ private ContentListener mContentListener;
+
+ public MediaSetSource(MediaSet source) {
+ mSource = Utils.checkNotNull(source);
+ mSource.addContentListener(this);
+ }
+
+ @Override
+ public void close() {
+ mSource.removeContentListener(this);
+ }
+
+ private void ensureCacheRange(int index) {
+ if (index >= mCacheStart && index < mCacheEnd) return;
+
+ long token = Binder.clearCallingIdentity();
+ try {
+ mCacheStart = index;
+ ArrayList<MediaItem> items = mSource.getMediaItem(mCacheStart, CACHE_SIZE);
+ mCacheEnd = mCacheStart + items.size();
+ items.toArray(mCache);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
+ public synchronized Uri getContentUri(int index) {
+ ensureCacheRange(index);
+ if (index < mCacheStart || index >= mCacheEnd) return null;
+ return mCache[index - mCacheStart].getContentUri();
+ }
+
+ @Override
+ public synchronized Bitmap getImage(int index) {
+ ensureCacheRange(index);
+ if (index < mCacheStart || index >= mCacheEnd) return null;
+ return WidgetUtils.createWidgetBitmap(mCache[index - mCacheStart]);
+ }
+
+ @Override
+ public void reload() {
+ long version = mSource.reload();
+ if (mSourceVersion != version) {
+ mSourceVersion = version;
+ mCacheStart = 0;
+ mCacheEnd = 0;
+ Arrays.fill(mCache, null);
+ }
+ }
+
+ @Override
+ public void setContentListener(ContentListener listener) {
+ mContentListener = listener;
+ }
+
+ @Override
+ public int size() {
+ long token = Binder.clearCallingIdentity();
+ try {
+ return mSource.getMediaItemCount();
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
+ public void onContentDirty() {
+ if (mContentListener != null) mContentListener.onContentDirty();
+ }
+}
diff --git a/src/com/android/gallery3d/gadget/PhotoAppWidgetProvider.java b/src/com/android/gallery3d/gadget/PhotoAppWidgetProvider.java
new file mode 100644
index 000000000..814ede25f
--- /dev/null
+++ b/src/com/android/gallery3d/gadget/PhotoAppWidgetProvider.java
@@ -0,0 +1,125 @@
+/*
+ * 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.gallery3d.gadget;
+
+import com.android.gallery3d.R;
+import com.android.gallery3d.gadget.WidgetDatabaseHelper.Entry;
+
+import android.app.PendingIntent;
+import android.appwidget.AppWidgetManager;
+import android.appwidget.AppWidgetProvider;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.net.Uri;
+import android.util.Log;
+import android.widget.RemoteViews;
+
+public class PhotoAppWidgetProvider extends AppWidgetProvider {
+
+ private static final String TAG = "WidgetProvider";
+
+ static RemoteViews buildWidget(Context context, int id, Entry entry) {
+
+ switch (entry.type) {
+ case WidgetDatabaseHelper.TYPE_ALBUM:
+ case WidgetDatabaseHelper.TYPE_SHUFFLE:
+ return buildStackWidget(context, id, entry);
+ case WidgetDatabaseHelper.TYPE_SINGLE_PHOTO:
+ return buildFrameWidget(context, id, entry);
+ }
+ throw new RuntimeException("invalid type - " + entry.type);
+ }
+
+ @Override
+ public void onUpdate(Context context,
+ AppWidgetManager appWidgetManager, int[] appWidgetIds) {
+ WidgetDatabaseHelper helper = new WidgetDatabaseHelper(context);
+ try {
+ for (int id : appWidgetIds) {
+ Entry entry = helper.getEntry(id);
+ if (entry != null) {
+ RemoteViews views = buildWidget(context, id, entry);
+ appWidgetManager.updateAppWidget(id, views);
+ } else {
+ Log.e(TAG, "cannot load widget: " + id);
+ }
+ }
+ } finally {
+ helper.close();
+ }
+ super.onUpdate(context, appWidgetManager, appWidgetIds);
+ }
+
+ private static RemoteViews buildStackWidget(Context context, int widgetId, Entry entry) {
+ RemoteViews views = new RemoteViews(
+ context.getPackageName(), R.layout.appwidget_main);
+
+ Intent intent = new Intent(context, WidgetService.class);
+ intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, widgetId);
+ intent.putExtra(WidgetService.EXTRA_WIDGET_TYPE, entry.type);
+ intent.putExtra(WidgetService.EXTRA_ALBUM_PATH, entry.albumPath);
+ intent.setData(Uri.parse("widget://gallery/" + widgetId));
+
+ views.setRemoteAdapter(R.id.appwidget_stack_view, intent);
+ views.setEmptyView(R.id.appwidget_stack_view, R.id.appwidget_empty_view);
+
+ Intent clickIntent = new Intent(context, WidgetClickHandler.class);
+ PendingIntent pendingIntent = PendingIntent.getActivity(
+ context, 0, clickIntent, PendingIntent.FLAG_UPDATE_CURRENT);
+ views.setPendingIntentTemplate(R.id.appwidget_stack_view, pendingIntent);
+
+ return views;
+ }
+
+ static RemoteViews buildFrameWidget(Context context, int appWidgetId, Entry entry) {
+ RemoteViews views = new RemoteViews(
+ context.getPackageName(), R.layout.photo_frame);
+ try {
+ byte[] data = entry.imageData;
+ Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
+ views.setImageViewBitmap(R.id.photo, bitmap);
+ } catch (Throwable t) {
+ Log.w(TAG, "cannot load widget image: " + appWidgetId, t);
+ }
+
+ if (entry.imageUri != null) {
+ try {
+ Uri uri = Uri.parse(entry.imageUri);
+ Intent clickIntent = new Intent(context, WidgetClickHandler.class)
+ .setData(uri);
+ PendingIntent pendingClickIntent = PendingIntent.getActivity(context, 0,
+ clickIntent, PendingIntent.FLAG_CANCEL_CURRENT);
+ views.setOnClickPendingIntent(R.id.photo, pendingClickIntent);
+ } catch (Throwable t) {
+ Log.w(TAG, "cannot load widget uri: " + appWidgetId, t);
+ }
+ }
+ return views;
+ }
+
+ @Override
+ public void onDeleted(Context context, int[] appWidgetIds) {
+ // Clean deleted photos out of our database
+ WidgetDatabaseHelper helper = new WidgetDatabaseHelper(context);
+ for (int appWidgetId : appWidgetIds) {
+ helper.deleteEntry(appWidgetId);
+ }
+ helper.close();
+ }
+} \ No newline at end of file
diff --git a/src/com/android/gallery3d/gadget/WidgetClickHandler.java b/src/com/android/gallery3d/gadget/WidgetClickHandler.java
new file mode 100644
index 000000000..075644d5e
--- /dev/null
+++ b/src/com/android/gallery3d/gadget/WidgetClickHandler.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2009 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.gallery3d.gadget;
+
+import com.android.gallery3d.R;
+import com.android.gallery3d.app.Gallery;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.content.res.AssetFileDescriptor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.util.Log;
+import android.widget.Toast;
+
+public class WidgetClickHandler extends Activity {
+ private static final String TAG = "PhotoAppWidgetClickHandler";
+
+ private boolean isValidDataUri(Uri dataUri) {
+ if (dataUri == null) return false;
+ try {
+ AssetFileDescriptor f = getContentResolver()
+ .openAssetFileDescriptor(dataUri, "r");
+ f.close();
+ return true;
+ } catch (Throwable e) {
+ Log.w(TAG, "cannot open uri: " + dataUri, e);
+ return false;
+ }
+ }
+
+ @Override
+ protected void onCreate(Bundle savedState) {
+ super.onCreate(savedState);
+ Intent intent = getIntent();
+ if (isValidDataUri(intent.getData())) {
+ startActivity(new Intent(Intent.ACTION_VIEW, intent.getData()));
+ } else {
+ Toast.makeText(this,
+ R.string.no_such_item, Toast.LENGTH_LONG).show();
+ startActivity(new Intent(this, Gallery.class));
+ }
+ finish();
+ }
+}
diff --git a/src/com/android/gallery3d/gadget/WidgetConfigure.java b/src/com/android/gallery3d/gadget/WidgetConfigure.java
new file mode 100644
index 000000000..747cc3a6d
--- /dev/null
+++ b/src/com/android/gallery3d/gadget/WidgetConfigure.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2011 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.gallery3d.gadget;
+
+import com.android.gallery3d.R;
+import com.android.gallery3d.app.AlbumPicker;
+import com.android.gallery3d.app.CropImage;
+import com.android.gallery3d.app.DialogPicker;
+
+import android.app.Activity;
+import android.appwidget.AppWidgetManager;
+import android.content.Intent;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.net.Uri;
+import android.os.Bundle;
+import android.widget.RemoteViews;
+
+public class WidgetConfigure extends Activity {
+ @SuppressWarnings("unused")
+ private static final String TAG = "WidgetConfigure";
+
+ public static final String KEY_WIDGET_TYPE = "widget-type";
+
+ private static final int REQUEST_WIDGET_TYPE = 1;
+ private static final int REQUEST_CHOOSE_ALBUM = 2;
+ private static final int REQUEST_CROP_IMAGE = 3;
+ private static final int REQUEST_GET_PHOTO = 4;
+
+ public static final int RESULT_ERROR = RESULT_FIRST_USER;
+
+ // Scale up the widget size since we only specified the minimized
+ // size of the gadget. The real size could be larger.
+ // Note: There is also a limit on the size of data that can be
+ // passed in Binder's transaction.
+ private static float WIDGET_SCALE_FACTOR = 1.5f;
+
+ private int mAppWidgetId = -1;
+ private int mWidgetType = 0;
+ private Uri mPickedItem;
+
+ @Override
+ protected void onCreate(Bundle bundle) {
+ super.onCreate(bundle);
+ mAppWidgetId = getIntent().getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1);
+
+ if (mAppWidgetId == -1) {
+ setResult(Activity.RESULT_CANCELED);
+ finish();
+ return;
+ }
+
+ if (mWidgetType == 0) {
+ Intent intent = new Intent(this, WidgetTypeChooser.class);
+ startActivityForResult(intent, REQUEST_WIDGET_TYPE);
+ }
+ }
+
+ private void updateWidgetAndFinish(WidgetDatabaseHelper.Entry entry) {
+ AppWidgetManager manager = AppWidgetManager.getInstance(this);
+ RemoteViews views = PhotoAppWidgetProvider.buildWidget(this, mAppWidgetId, entry);
+ manager.updateAppWidget(mAppWidgetId, views);
+ setResult(RESULT_OK, new Intent().putExtra(
+ AppWidgetManager.EXTRA_APPWIDGET_ID, mAppWidgetId));
+ finish();
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ if (resultCode != RESULT_OK) {
+ setResult(resultCode, new Intent().putExtra(
+ AppWidgetManager.EXTRA_APPWIDGET_ID, mAppWidgetId));
+ finish();
+ return;
+ }
+
+ if (requestCode == REQUEST_WIDGET_TYPE) {
+ setWidgetType(data);
+ } else if (requestCode == REQUEST_CHOOSE_ALBUM) {
+ setChoosenAlbum(data);
+ } else if (requestCode == REQUEST_GET_PHOTO) {
+ setChoosenPhoto(data);
+ } else if (requestCode == REQUEST_CROP_IMAGE) {
+ setPhotoWidget(data);
+ } else {
+ throw new AssertionError("unknown request: " + requestCode);
+ }
+ }
+
+ private void setPhotoWidget(Intent data) {
+ // Store the cropped photo in our database
+ Bitmap bitmap = (Bitmap) data.getParcelableExtra("data");
+ WidgetDatabaseHelper helper = new WidgetDatabaseHelper(this);
+ try {
+ helper.setPhoto(mAppWidgetId, mPickedItem, bitmap);
+ updateWidgetAndFinish(helper.getEntry(mAppWidgetId));
+ } finally {
+ helper.close();
+ }
+ }
+
+ private void setChoosenPhoto(Intent data) {
+ Resources res = getResources();
+ int widgetWidth = Math.round(WIDGET_SCALE_FACTOR
+ * res.getDimension(R.dimen.appwidget_width));
+ int widgetHeight = Math.round(WIDGET_SCALE_FACTOR
+ * res.getDimension(R.dimen.appwidget_height));
+ mPickedItem = data.getData();
+ Intent request = new Intent(CropImage.ACTION_CROP, mPickedItem)
+ .putExtra(CropImage.KEY_OUTPUT_X, widgetWidth)
+ .putExtra(CropImage.KEY_OUTPUT_Y, widgetHeight)
+ .putExtra(CropImage.KEY_ASPECT_X, widgetWidth)
+ .putExtra(CropImage.KEY_ASPECT_Y, widgetHeight)
+ .putExtra(CropImage.KEY_SCALE_UP_IF_NEEDED, true)
+ .putExtra(CropImage.KEY_SCALE, true)
+ .putExtra(CropImage.KEY_RETURN_DATA, true);
+ startActivityForResult(request, REQUEST_CROP_IMAGE);
+ }
+
+ private void setChoosenAlbum(Intent data) {
+ String albumPath = data.getStringExtra(AlbumPicker.KEY_ALBUM_PATH);
+ WidgetDatabaseHelper helper = new WidgetDatabaseHelper(this);
+ try {
+ helper.setWidget(mAppWidgetId,
+ WidgetDatabaseHelper.TYPE_ALBUM, albumPath);
+ updateWidgetAndFinish(helper.getEntry(mAppWidgetId));
+ } finally {
+ helper.close();
+ }
+ }
+
+ private void setWidgetType(Intent data) {
+ mWidgetType = data.getIntExtra(KEY_WIDGET_TYPE, R.id.widget_type_shuffle);
+ if (mWidgetType == R.id.widget_type_album) {
+ Intent intent = new Intent(this, AlbumPicker.class);
+ startActivityForResult(intent, REQUEST_CHOOSE_ALBUM);
+ } else if (mWidgetType == R.id.widget_type_shuffle) {
+ WidgetDatabaseHelper helper = new WidgetDatabaseHelper(this);
+ try {
+ helper.setWidget(mAppWidgetId, WidgetDatabaseHelper.TYPE_SHUFFLE, null);
+ updateWidgetAndFinish(helper.getEntry(mAppWidgetId));
+ } finally {
+ helper.close();
+ }
+ } else {
+ // Explicitly send the intent to the DialogPhotoPicker
+ Intent request = new Intent(this, DialogPicker.class)
+ .setAction(Intent.ACTION_GET_CONTENT)
+ .setType("image/*");
+ startActivityForResult(request, REQUEST_GET_PHOTO);
+ }
+ }
+}
diff --git a/src/com/android/gallery3d/gadget/WidgetDatabaseHelper.java b/src/com/android/gallery3d/gadget/WidgetDatabaseHelper.java
new file mode 100644
index 000000000..1d0754808
--- /dev/null
+++ b/src/com/android/gallery3d/gadget/WidgetDatabaseHelper.java
@@ -0,0 +1,245 @@
+/*
+ * 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.gallery3d.gadget;
+
+import com.android.gallery3d.common.Utils;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteException;
+import android.database.sqlite.SQLiteOpenHelper;
+import android.graphics.Bitmap;
+import android.net.Uri;
+import android.util.Log;
+
+import java.io.ByteArrayOutputStream;
+import java.util.ArrayList;
+
+public class WidgetDatabaseHelper extends SQLiteOpenHelper {
+ private static final String TAG = "PhotoDatabaseHelper";
+ private static final String DATABASE_NAME = "launcher.db";
+
+ private static final int DATABASE_VERSION = 4;
+
+ private static final String TABLE_WIDGETS = "widgets";
+
+ private static final String FIELD_APPWIDGET_ID = "appWidgetId";
+ private static final String FIELD_IMAGE_URI = "imageUri";
+ private static final String FIELD_PHOTO_BLOB = "photoBlob";
+ private static final String FIELD_WIDGET_TYPE = "widgetType";
+ private static final String FIELD_ALBUM_PATH = "albumPath";
+
+ public static final int TYPE_SINGLE_PHOTO = 0;
+ public static final int TYPE_SHUFFLE = 1;
+ public static final int TYPE_ALBUM = 2;
+
+ private static final String[] PROJECTION = {
+ FIELD_WIDGET_TYPE, FIELD_IMAGE_URI, FIELD_PHOTO_BLOB, FIELD_ALBUM_PATH};
+ private static final int INDEX_WIDGET_TYPE = 0;
+ private static final int INDEX_IMAGE_URI = 1;
+ private static final int INDEX_PHOTO_BLOB = 2;
+ private static final int INDEX_ALBUM_PATH = 3;
+ private static final String WHERE_CLAUSE = FIELD_APPWIDGET_ID + " = ?";
+
+ public static class Entry {
+ public int widgetId;
+ public int type;
+ public String imageUri;
+ public byte imageData[];
+ public String albumPath;
+
+ private Entry() {}
+
+ private Entry(int id, Cursor cursor) {
+ widgetId = id;
+ type = cursor.getInt(INDEX_WIDGET_TYPE);
+ if (type == TYPE_SINGLE_PHOTO) {
+ imageUri = cursor.getString(INDEX_IMAGE_URI);
+ imageData = cursor.getBlob(INDEX_PHOTO_BLOB);
+ } else if (type == TYPE_ALBUM) {
+ albumPath = cursor.getString(INDEX_ALBUM_PATH);
+ }
+ }
+ }
+
+ public WidgetDatabaseHelper(Context context) {
+ super(context, DATABASE_NAME, null, DATABASE_VERSION);
+ }
+
+ @Override
+ public void onCreate(SQLiteDatabase db) {
+ db.execSQL("CREATE TABLE " + TABLE_WIDGETS + " ("
+ + FIELD_APPWIDGET_ID + " INTEGER PRIMARY KEY, "
+ + FIELD_WIDGET_TYPE + " INTEGER DEFAULT 0, "
+ + FIELD_IMAGE_URI + " TEXT, "
+ + FIELD_ALBUM_PATH + " TEXT, "
+ + FIELD_PHOTO_BLOB + " BLOB)");
+ }
+
+ private void saveData(SQLiteDatabase db, int oldVersion, ArrayList<Entry> data) {
+ if (oldVersion <= 2) {
+ Cursor cursor = db.query("photos",
+ new String[] {FIELD_APPWIDGET_ID, FIELD_PHOTO_BLOB},
+ null, null, null, null, null);
+ if (cursor == null) return;
+ try {
+ while (cursor.moveToNext()) {
+ Entry entry = new Entry();
+ entry.type = TYPE_SINGLE_PHOTO;
+ entry.widgetId = cursor.getInt(0);
+ entry.imageData = cursor.getBlob(1);
+ data.add(entry);
+ }
+ } finally {
+ cursor.close();
+ }
+ } else if (oldVersion == 3) {
+ Utils.debug("saveData of version: %s", oldVersion);
+ Cursor cursor = db.query("photos",
+ new String[] {FIELD_APPWIDGET_ID, FIELD_PHOTO_BLOB, FIELD_IMAGE_URI},
+ null, null, null, null, null);
+ if (cursor == null) return;
+ try {
+ while (cursor.moveToNext()) {
+ Entry entry = new Entry();
+ entry.type = TYPE_SINGLE_PHOTO;
+ entry.widgetId = cursor.getInt(0);
+ entry.imageData = cursor.getBlob(1);
+ entry.imageUri = cursor.getString(2);
+
+ Utils.debug("store widget[%s] - %s", entry.widgetId, entry.imageUri);
+ data.add(entry);
+ }
+ } finally {
+ cursor.close();
+ }
+ }
+ }
+
+ private void restoreData(SQLiteDatabase db, ArrayList<Entry> data) {
+ db.beginTransaction();
+ try {
+ for (Entry entry : data) {
+ ContentValues values = new ContentValues();
+ values.put(FIELD_APPWIDGET_ID, entry.widgetId);
+ values.put(FIELD_WIDGET_TYPE, entry.type);
+ values.put(FIELD_IMAGE_URI, entry.imageUri);
+ values.put(FIELD_PHOTO_BLOB, entry.imageData);
+ values.put(FIELD_ALBUM_PATH, entry.albumPath);
+ db.insert(TABLE_WIDGETS, null, values);
+ }
+ db.setTransactionSuccessful();
+ } finally {
+ db.endTransaction();
+ }
+ }
+
+ @Override
+ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+ int version = oldVersion;
+
+ if (version != DATABASE_VERSION) {
+ ArrayList<Entry> data = new ArrayList<Entry>();
+ saveData(db, oldVersion, data);
+
+ Log.w(TAG, "destroying all old data.");
+ // Table "photos" is renamed to "widget" in version 4
+ db.execSQL("DROP TABLE IF EXISTS photos");
+ db.execSQL("DROP TABLE IF EXISTS " + TABLE_WIDGETS);
+ onCreate(db);
+
+ restoreData(db, data);
+ }
+ }
+
+ /**
+ * Store the given bitmap in this database for the given appWidgetId.
+ */
+ public boolean setPhoto(int appWidgetId, Uri imageUri, Bitmap bitmap) {
+ try {
+ // Try go guesstimate how much space the icon will take when
+ // serialized to avoid unnecessary allocations/copies during
+ // the write.
+ int size = bitmap.getWidth() * bitmap.getHeight() * 4;
+ ByteArrayOutputStream out = new ByteArrayOutputStream(size);
+ bitmap.compress(Bitmap.CompressFormat.PNG, 100, out);
+ out.close();
+
+ ContentValues values = new ContentValues();
+ values.put(FIELD_APPWIDGET_ID, appWidgetId);
+ values.put(FIELD_WIDGET_TYPE, TYPE_SINGLE_PHOTO);
+ values.put(FIELD_IMAGE_URI, imageUri.toString());
+ values.put(FIELD_PHOTO_BLOB, out.toByteArray());
+
+ SQLiteDatabase db = getWritableDatabase();
+ db.replaceOrThrow(TABLE_WIDGETS, null, values);
+ return true;
+ } catch (Throwable e) {
+ Log.e(TAG, "set widget photo fail", e);
+ return false;
+ }
+ }
+
+ public boolean setWidget(int id, int type, String albumPath) {
+ try {
+ ContentValues values = new ContentValues();
+ values.put(FIELD_APPWIDGET_ID, id);
+ values.put(FIELD_WIDGET_TYPE, type);
+ values.put(FIELD_ALBUM_PATH, Utils.ensureNotNull(albumPath));
+ getWritableDatabase().replaceOrThrow(TABLE_WIDGETS, null, values);
+ return true;
+ } catch (Throwable e) {
+ Log.e(TAG, "set widget fail", e);
+ return false;
+ }
+ }
+
+ public Entry getEntry(int appWidgetId) {
+ Cursor cursor = null;
+ try {
+ SQLiteDatabase db = getReadableDatabase();
+ cursor = db.query(TABLE_WIDGETS, PROJECTION,
+ WHERE_CLAUSE, new String[] {String.valueOf(appWidgetId)},
+ null, null, null);
+ if (cursor == null || !cursor.moveToNext()) {
+ Log.e(TAG, "query fail: empty cursor: " + cursor);
+ return null;
+ }
+ return new Entry(appWidgetId, cursor);
+ } catch (Throwable e) {
+ Log.e(TAG, "Could not load photo from database", e);
+ return null;
+ } finally {
+ Utils.closeSilently(cursor);
+ }
+ }
+
+ /**
+ * Remove any bitmap associated with the given appWidgetId.
+ */
+ public void deleteEntry(int appWidgetId) {
+ try {
+ SQLiteDatabase db = getWritableDatabase();
+ db.delete(TABLE_WIDGETS, WHERE_CLAUSE,
+ new String[] {String.valueOf(appWidgetId)});
+ } catch (SQLiteException e) {
+ Log.e(TAG, "Could not delete photo from database", e);
+ }
+ }
+} \ No newline at end of file
diff --git a/src/com/android/gallery3d/gadget/WidgetService.java b/src/com/android/gallery3d/gadget/WidgetService.java
new file mode 100644
index 000000000..a61831ca6
--- /dev/null
+++ b/src/com/android/gallery3d/gadget/WidgetService.java
@@ -0,0 +1,169 @@
+/*
+ * 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.gallery3d.gadget;
+
+import com.android.gallery3d.R;
+import com.android.gallery3d.app.GalleryApp;
+import com.android.gallery3d.data.ContentListener;
+import com.android.gallery3d.data.DataManager;
+import com.android.gallery3d.data.MediaSet;
+import com.android.gallery3d.data.Path;
+
+import android.appwidget.AppWidgetManager;
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.net.Uri;
+import android.widget.RemoteViews;
+import android.widget.RemoteViewsService;
+
+public class WidgetService extends RemoteViewsService {
+
+ @SuppressWarnings("unused")
+ private static final String TAG = "GalleryAppWidgetService";
+
+ public static final String EXTRA_WIDGET_TYPE = "widget-type";
+ public static final String EXTRA_ALBUM_PATH = "album-path";
+
+ @Override
+ public RemoteViewsFactory onGetViewFactory(Intent intent) {
+ int id = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID,
+ AppWidgetManager.INVALID_APPWIDGET_ID);
+ int type = intent.getIntExtra(EXTRA_WIDGET_TYPE, 0);
+ String albumPath = intent.getStringExtra(EXTRA_ALBUM_PATH);
+
+ return new PhotoRVFactory((GalleryApp) getApplicationContext(),
+ id, type, albumPath);
+ }
+
+ private static class EmptySource implements WidgetSource {
+
+ @Override
+ public int size() {
+ return 0;
+ }
+
+ @Override
+ public Bitmap getImage(int index) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Uri getContentUri(int index) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void setContentListener(ContentListener listener) {}
+
+ @Override
+ public void reload() {}
+
+ @Override
+ public void close() {}
+ }
+
+ private static class PhotoRVFactory implements
+ RemoteViewsService.RemoteViewsFactory, ContentListener {
+
+ private final int mAppWidgetId;
+ private final int mType;
+ private final String mAlbumPath;
+ private final GalleryApp mApp;
+
+ private WidgetSource mSource;
+
+ public PhotoRVFactory(GalleryApp app, int id, int type, String albumPath) {
+ mApp = app;
+ mAppWidgetId = id;
+ mType = type;
+ mAlbumPath = albumPath;
+ }
+
+ @Override
+ public void onCreate() {
+ if (mType == WidgetDatabaseHelper.TYPE_ALBUM) {
+ Path path = Path.fromString(mAlbumPath);
+ DataManager manager = mApp.getDataManager();
+ MediaSet mediaSet = (MediaSet) manager.getMediaObject(path);
+ mSource = mediaSet == null
+ ? new EmptySource()
+ : new MediaSetSource(mediaSet);
+ } else {
+ mSource = new LocalPhotoSource(mApp.getAndroidContext());
+ }
+ mSource.setContentListener(this);
+ AppWidgetManager.getInstance(mApp.getAndroidContext())
+ .notifyAppWidgetViewDataChanged(
+ mAppWidgetId, R.id.appwidget_stack_view);
+ }
+
+ @Override
+ public void onDestroy() {
+ mSource.close();
+ mSource = null;
+ }
+
+ public int getCount() {
+ return mSource.size();
+ }
+
+ public long getItemId(int position) {
+ return position;
+ }
+
+ public int getViewTypeCount() {
+ return 1;
+ }
+
+ public boolean hasStableIds() {
+ return true;
+ }
+
+ public RemoteViews getLoadingView() {
+ RemoteViews rv = new RemoteViews(
+ mApp.getAndroidContext().getPackageName(),
+ R.layout.appwidget_loading_item);
+ rv.setProgressBar(R.id.appwidget_loading_item, 0, 0, true);
+ return rv;
+ }
+
+ public RemoteViews getViewAt(int position) {
+ Bitmap bitmap = mSource.getImage(position);
+ if (bitmap == null) return getLoadingView();
+ RemoteViews views = new RemoteViews(
+ mApp.getAndroidContext().getPackageName(),
+ R.layout.appwidget_photo_item);
+ views.setImageViewBitmap(R.id.appwidget_photo_item, bitmap);
+ views.setOnClickFillInIntent(R.id.appwidget_photo_item, new Intent()
+ .setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
+ .setData(mSource.getContentUri(position)));
+ return views;
+ }
+
+ @Override
+ public void onDataSetChanged() {
+ mSource.reload();
+ }
+
+ @Override
+ public void onContentDirty() {
+ AppWidgetManager.getInstance(mApp.getAndroidContext())
+ .notifyAppWidgetViewDataChanged(
+ mAppWidgetId, R.id.appwidget_stack_view);
+ }
+ }
+}
diff --git a/src/com/android/gallery3d/gadget/WidgetSource.java b/src/com/android/gallery3d/gadget/WidgetSource.java
new file mode 100644
index 000000000..8b8eb79dc
--- /dev/null
+++ b/src/com/android/gallery3d/gadget/WidgetSource.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2011 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.gallery3d.gadget;
+
+import com.android.gallery3d.data.ContentListener;
+
+import android.graphics.Bitmap;
+import android.net.Uri;
+
+public interface WidgetSource {
+ public int size();
+ public Bitmap getImage(int index);
+ public Uri getContentUri(int index);
+ public void setContentListener(ContentListener listener);
+ public void reload();
+ public void close();
+}
diff --git a/src/com/android/gallery3d/gadget/WidgetTypeChooser.java b/src/com/android/gallery3d/gadget/WidgetTypeChooser.java
new file mode 100644
index 000000000..c4bca60d0
--- /dev/null
+++ b/src/com/android/gallery3d/gadget/WidgetTypeChooser.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2011 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.gallery3d.gadget;
+
+import com.android.gallery3d.R;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.Button;
+import android.widget.RadioGroup;
+import android.widget.RadioGroup.OnCheckedChangeListener;
+
+public class WidgetTypeChooser extends Activity {
+
+ private OnCheckedChangeListener mListener = new OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(RadioGroup group, int checkedId) {
+ Intent data = new Intent()
+ .putExtra(WidgetConfigure.KEY_WIDGET_TYPE, checkedId);
+ setResult(RESULT_OK, data);
+ finish();
+ }
+ };
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setTitle(R.string.widget_type);
+ setContentView(R.layout.choose_widget_type);
+ RadioGroup rg = (RadioGroup) findViewById(R.id.widget_type);
+ rg.setOnCheckedChangeListener(mListener);
+
+ Button cancel = (Button) findViewById(R.id.cancel);
+ cancel.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ setResult(RESULT_CANCELED);
+ finish();
+ }
+ });
+ }
+}
diff --git a/src/com/android/gallery3d/gadget/WidgetUtils.java b/src/com/android/gallery3d/gadget/WidgetUtils.java
new file mode 100644
index 000000000..b194c7d8e
--- /dev/null
+++ b/src/com/android/gallery3d/gadget/WidgetUtils.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2011 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.gallery3d.gadget;
+
+import com.android.gallery3d.R;
+import com.android.gallery3d.data.MediaItem;
+import com.android.gallery3d.util.ThreadPool;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Bitmap.Config;
+import android.util.Log;
+
+public class WidgetUtils {
+
+ private static final String TAG = "WidgetUtils";
+
+ private static int sStackPhotoWidth = 220;
+ private static int sStackPhotoHeight = 170;
+
+ private WidgetUtils() {
+ }
+
+ public static void initialize(Context context) {
+ Resources r = context.getResources();
+ sStackPhotoWidth = r.getDimensionPixelSize(R.dimen.stack_photo_width);
+ sStackPhotoHeight = r.getDimensionPixelSize(R.dimen.stack_photo_height);
+ }
+
+ public static Bitmap createWidgetBitmap(MediaItem image) {
+ Bitmap bitmap = image.requestImage(MediaItem.TYPE_THUMBNAIL)
+ .run(ThreadPool.JOB_CONTEXT_STUB);
+ if (bitmap == null) {
+ Log.w(TAG, "fail to get image of " + image.toString());
+ return null;
+ }
+ return createWidgetBitmap(bitmap, image.getRotation());
+ }
+
+ public static Bitmap createWidgetBitmap(Bitmap bitmap, int rotation) {
+ int w = bitmap.getWidth();
+ int h = bitmap.getHeight();
+
+ float scale;
+ if (((rotation / 90) & 1) == 0) {
+ scale = Math.max((float) sStackPhotoWidth / w,
+ (float) sStackPhotoHeight / h);
+ } else {
+ scale = Math.max((float) sStackPhotoWidth / h,
+ (float) sStackPhotoHeight / w);
+ }
+
+ Bitmap target = Bitmap.createBitmap(
+ sStackPhotoWidth, sStackPhotoHeight, Config.ARGB_8888);
+ Canvas canvas = new Canvas(target);
+ canvas.translate(sStackPhotoWidth / 2, sStackPhotoHeight / 2);
+ canvas.rotate(rotation);
+ canvas.scale(scale, scale);
+ Paint paint = new Paint(Paint.FILTER_BITMAP_FLAG | Paint.DITHER_FLAG);
+ canvas.drawBitmap(bitmap, -w / 2, -h / 2, paint);
+ return target;
+ }
+}