diff options
author | John Reck <jreck@google.com> | 2013-03-06 16:36:45 -0800 |
---|---|---|
committer | John Reck <jreck@google.com> | 2013-03-06 16:38:37 -0800 |
commit | dc0bb6232a45a646799db8a79e9d678c494fc956 (patch) | |
tree | bc018452463c82a5b3b9054ee5478ae4954018be | |
parent | 1bf3f3238727cc73a156e3eb61a2d2343ac3bdc0 (diff) | |
download | android_packages_apps_Snap-dc0bb6232a45a646799db8a79e9d678c494fc956.tar.gz android_packages_apps_Snap-dc0bb6232a45a646799db8a79e9d678c494fc956.tar.bz2 android_packages_apps_Snap-dc0bb6232a45a646799db8a79e9d678c494fc956.zip |
Data provider shim
Bolt the new UI framework on top of the old data model temporarily
to unblock UI work
Change-Id: I2f61f70647faca1f6a95b1f02f719ec4277fa5fb
-rw-r--r-- | src/com/android/photos/PhotoSetFragment.java | 66 | ||||
-rw-r--r-- | src/com/android/photos/data/PhotoSetLoader.java | 22 | ||||
-rw-r--r-- | src/com/android/photos/drawables/DrawableFactory.java | 24 | ||||
-rw-r--r-- | src/com/android/photos/shims/BitmapJobDrawable.java | 154 | ||||
-rw-r--r-- | src/com/android/photos/shims/MediaItemsLoader.java | 148 | ||||
-rw-r--r-- | src/com/android/photos/shims/MediaSetLoader.java (renamed from src/com/android/photos/data/MediaSetLoader.java) | 2 |
6 files changed, 378 insertions, 38 deletions
diff --git a/src/com/android/photos/PhotoSetFragment.java b/src/com/android/photos/PhotoSetFragment.java index 0e9efa4b1..1de8de5a7 100644 --- a/src/com/android/photos/PhotoSetFragment.java +++ b/src/com/android/photos/PhotoSetFragment.java @@ -19,22 +19,22 @@ package com.android.photos; import android.app.Fragment; import android.app.LoaderManager.LoaderCallbacks; import android.content.Context; -import android.content.Intent; import android.content.Loader; import android.database.Cursor; -import android.net.Uri; +import android.graphics.drawable.Drawable; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; -import android.view.View.OnClickListener; import android.view.ViewGroup; +import android.view.ViewGroup.LayoutParams; import android.widget.CursorAdapter; +import android.widget.GridView; import android.widget.ImageView; import com.android.gallery3d.R; import com.android.photos.data.PhotoSetLoader; -import com.android.photos.drawables.DataUriThumbnailDrawable; -import com.android.photos.views.GalleryThumbnailView; +import com.android.photos.drawables.DrawableFactory; +import com.android.photos.shims.MediaItemsLoader; import com.android.photos.views.GalleryThumbnailView.GalleryThumbnailAdapter; @@ -42,7 +42,7 @@ public class PhotoSetFragment extends Fragment implements LoaderCallbacks<Cursor private static final int LOADER_PHOTOSET = 1; - private GalleryThumbnailView mPhotoSetView; + private GridView mPhotoSetView; private View mEmptyView; private ThumbnailAdapter mAdapter; @@ -50,7 +50,9 @@ public class PhotoSetFragment extends Fragment implements LoaderCallbacks<Cursor public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View root = inflater.inflate(R.layout.photo_set, container, false); - mPhotoSetView = (GalleryThumbnailView) root.findViewById(android.R.id.list); + mPhotoSetView = (GridView) root.findViewById(android.R.id.list); + // TODO: Remove once UI stabilizes + mPhotoSetView.setColumnWidth(MediaItemsLoader.getThumbnailSize()); mEmptyView = root.findViewById(android.R.id.empty); mEmptyView.setVisibility(View.GONE); mAdapter = new ThumbnailAdapter(getActivity()); @@ -68,7 +70,10 @@ public class PhotoSetFragment extends Fragment implements LoaderCallbacks<Cursor @Override public Loader<Cursor> onCreateLoader(int id, Bundle args) { - return new PhotoSetLoader(getActivity()); + // TODO: Switch to PhotoSetLoader + MediaItemsLoader loader = new MediaItemsLoader(getActivity()); + mAdapter.setDrawableFactory(loader); + return loader; } @Override @@ -82,46 +87,37 @@ public class PhotoSetFragment extends Fragment implements LoaderCallbacks<Cursor public void onLoaderReset(Loader<Cursor> loader) { } - private static class ShowFullScreen implements OnClickListener { - - @Override - public void onClick(View view) { - String path = (String) view.getTag(); - Intent intent = new Intent(view.getContext(), FullscreenViewer.class); - intent.setAction(Intent.ACTION_VIEW); - intent.setData(Uri.parse(path)); - view.getContext().startActivity(intent); - } - - } - private static class ThumbnailAdapter extends CursorAdapter implements GalleryThumbnailAdapter { - private static ShowFullScreen sShowFullscreenClickListener = new ShowFullScreen(); + private LayoutInflater mInflater; + private DrawableFactory<Cursor> mDrawableFactory; public ThumbnailAdapter(Context context) { super(context, null, false); + mInflater = LayoutInflater.from(context); + } + + public void setDrawableFactory(DrawableFactory<Cursor> factory) { + mDrawableFactory = factory; } @Override public void bindView(View view, Context context, Cursor cursor) { ImageView iv = (ImageView) view; - DataUriThumbnailDrawable drawable = (DataUriThumbnailDrawable) iv.getDrawable(); - int width = cursor.getInt(PhotoSetLoader.INDEX_WIDTH); - int height = cursor.getInt(PhotoSetLoader.INDEX_HEIGHT); - String path = cursor.getString(PhotoSetLoader.INDEX_DATA); - drawable.setImage(path, width, height); - iv.setTag(path); + Drawable recycle = iv.getDrawable(); + Drawable drawable = mDrawableFactory.drawableForItem(cursor, recycle); + if (recycle != drawable) { + iv.setImageDrawable(drawable); + } } @Override public View newView(Context context, Cursor cursor, ViewGroup parent) { - ImageView iv = new ImageView(context); - DataUriThumbnailDrawable drawable = new DataUriThumbnailDrawable(); - iv.setImageDrawable(drawable); - int padding = (int) Math.ceil(2 * context.getResources().getDisplayMetrics().density); - iv.setPadding(padding, padding, padding, padding); - iv.setOnClickListener(sShowFullscreenClickListener); - return iv; + View view = mInflater.inflate(R.layout.photo_set_item, parent, false); + LayoutParams params = view.getLayoutParams(); + int columnWidth = ((GridView) parent).getColumnWidth(); + params.height = columnWidth; + view.setLayoutParams(params); + return view; } @Override diff --git a/src/com/android/photos/data/PhotoSetLoader.java b/src/com/android/photos/data/PhotoSetLoader.java index 8c511a525..21da90694 100644 --- a/src/com/android/photos/data/PhotoSetLoader.java +++ b/src/com/android/photos/data/PhotoSetLoader.java @@ -19,15 +19,20 @@ package com.android.photos.data; import android.content.Context; import android.content.CursorLoader; import android.database.ContentObserver; +import android.database.Cursor; +import android.graphics.drawable.Drawable; import android.net.Uri; import android.provider.MediaStore; import android.provider.MediaStore.Files; import android.provider.MediaStore.Files.FileColumns; -public class PhotoSetLoader extends CursorLoader { +import com.android.photos.drawables.DataUriThumbnailDrawable; +import com.android.photos.drawables.DrawableFactory; + +public class PhotoSetLoader extends CursorLoader implements DrawableFactory<Cursor> { private static final Uri CONTENT_URI = Files.getContentUri("external"); - private static final String[] PROJECTION = new String[] { + public static final String[] PROJECTION = new String[] { FileColumns._ID, FileColumns.DATA, FileColumns.WIDTH, @@ -67,4 +72,17 @@ public class PhotoSetLoader extends CursorLoader { super.onReset(); getContext().getContentResolver().unregisterContentObserver(mGlobalObserver); } + + @Override + public Drawable drawableForItem(Cursor item, Drawable recycle) { + DataUriThumbnailDrawable drawable = null; + if (recycle == null || !(recycle instanceof DataUriThumbnailDrawable)) { + drawable = new DataUriThumbnailDrawable(); + } else { + drawable = (DataUriThumbnailDrawable) recycle; + } + drawable.setImage(item.getString(INDEX_DATA), + item.getInt(INDEX_WIDTH), item.getInt(INDEX_HEIGHT)); + return drawable; + } } diff --git a/src/com/android/photos/drawables/DrawableFactory.java b/src/com/android/photos/drawables/DrawableFactory.java new file mode 100644 index 000000000..ad046c820 --- /dev/null +++ b/src/com/android/photos/drawables/DrawableFactory.java @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2013 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.photos.drawables; + +import android.graphics.drawable.Drawable; + + +public interface DrawableFactory<T> { + Drawable drawableForItem(T item, Drawable recycle); +} diff --git a/src/com/android/photos/shims/BitmapJobDrawable.java b/src/com/android/photos/shims/BitmapJobDrawable.java new file mode 100644 index 000000000..6623b914f --- /dev/null +++ b/src/com/android/photos/shims/BitmapJobDrawable.java @@ -0,0 +1,154 @@ +package com.android.photos.shims; + +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.ColorFilter; +import android.graphics.Matrix; +import android.graphics.Paint; +import android.graphics.PixelFormat; +import android.graphics.Rect; +import android.graphics.drawable.Drawable; + +import com.android.gallery3d.data.MediaItem; +import com.android.gallery3d.ui.BitmapLoader; +import com.android.gallery3d.util.Future; +import com.android.gallery3d.util.FutureListener; +import com.android.gallery3d.util.ThreadPool; +import com.android.photos.drawables.AutoThumbnailDrawable; + + +public class BitmapJobDrawable extends Drawable implements Runnable { + + private ThumbnailLoader mLoader; + private MediaItem mItem; + private Bitmap mBitmap; + private Paint mPaint = new Paint(); + private Matrix mDrawMatrix = new Matrix(); + + public BitmapJobDrawable() { + } + + public void setMediaItem(MediaItem item) { + if (mLoader != null) { + mLoader.cancelLoad(); + } + mItem = item; + mBitmap = null; + // TODO: Figure out why ThumbnailLoader doesn't like to be re-used + mLoader = new ThumbnailLoader(this); + mLoader.startLoad(); + invalidateSelf(); + } + + @Override + public void run() { + Bitmap bitmap = mLoader.getBitmap(); + if (bitmap != null) { + mBitmap = bitmap; + updateDrawMatrix(); + } + } + + @Override + protected void onBoundsChange(Rect bounds) { + super.onBoundsChange(bounds); + updateDrawMatrix(); + } + + @Override + public void draw(Canvas canvas) { + Rect bounds = getBounds(); + if (mBitmap != null) { + canvas.save(); + canvas.clipRect(bounds); + canvas.concat(mDrawMatrix); + canvas.drawBitmap(mBitmap, 0, 0, mPaint); + canvas.restore(); + } else { + mPaint.setColor(0xFFCCCCCC); + canvas.drawRect(bounds, mPaint); + } + } + + private void updateDrawMatrix() { + Rect bounds = getBounds(); + if (mBitmap == null || bounds.isEmpty()) { + mDrawMatrix.reset(); + return; + } + + float scale; + float dx = 0, dy = 0; + + int dwidth = mBitmap.getWidth(); + int dheight = mBitmap.getHeight(); + int vwidth = bounds.width(); + int vheight = bounds.height(); + + // Calculates a matrix similar to ScaleType.CENTER_CROP + if (dwidth * vheight > vwidth * dheight) { + scale = (float) vheight / (float) dheight; + dx = (vwidth - dwidth * scale) * 0.5f; + } else { + scale = (float) vwidth / (float) dwidth; + dy = (vheight - dheight * scale) * 0.5f; + } + + mDrawMatrix.setScale(scale, scale); + mDrawMatrix.postTranslate((int) (dx + 0.5f), (int) (dy + 0.5f)); + invalidateSelf(); + } + + @Override + public int getIntrinsicWidth() { + return MediaItem.getTargetSize(MediaItem.TYPE_MICROTHUMBNAIL); + } + + @Override + public int getIntrinsicHeight() { + return MediaItem.getTargetSize(MediaItem.TYPE_MICROTHUMBNAIL); + } + + @Override + public int getOpacity() { + Bitmap bm = mBitmap; + return (bm == null || bm.hasAlpha() || mPaint.getAlpha() < 255) ? + PixelFormat.TRANSLUCENT : PixelFormat.OPAQUE; + } + + @Override + public void setAlpha(int alpha) { + int oldAlpha = mPaint.getAlpha(); + if (alpha != oldAlpha) { + mPaint.setAlpha(alpha); + invalidateSelf(); + } + } + + @Override + public void setColorFilter(ColorFilter cf) { + mPaint.setColorFilter(cf); + invalidateSelf(); + } + + private static class ThumbnailLoader extends BitmapLoader { + private static final ThreadPool sThreadPool = new ThreadPool(0, 2); + private BitmapJobDrawable mParent; + + public ThumbnailLoader(BitmapJobDrawable parent) { + mParent = parent; + } + + @Override + protected Future<Bitmap> submitBitmapTask(FutureListener<Bitmap> l) { + return sThreadPool.submit( + mParent.mItem.requestImage(MediaItem.TYPE_MICROTHUMBNAIL), this); + } + + @Override + protected void onLoadComplete(Bitmap bitmap) { + mParent.scheduleSelf(mParent, 0); + } + } + +} diff --git a/src/com/android/photos/shims/MediaItemsLoader.java b/src/com/android/photos/shims/MediaItemsLoader.java new file mode 100644 index 000000000..886b3c3a1 --- /dev/null +++ b/src/com/android/photos/shims/MediaItemsLoader.java @@ -0,0 +1,148 @@ +/* + * Copyright (C) 2013 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.photos.shims; + +import android.content.AsyncTaskLoader; +import android.content.Context; +import android.database.Cursor; +import android.database.MatrixCursor; +import android.graphics.drawable.Drawable; +import android.provider.MediaStore.Files.FileColumns; + +import com.android.gallery3d.data.ContentListener; +import com.android.gallery3d.data.DataManager; +import com.android.gallery3d.data.MediaItem; +import com.android.gallery3d.data.MediaSet; +import com.android.gallery3d.data.MediaSet.ItemConsumer; +import com.android.gallery3d.data.MediaSet.SyncListener; +import com.android.gallery3d.util.Future; +import com.android.photos.data.PhotoSetLoader; +import com.android.photos.drawables.DrawableFactory; + +import java.util.ArrayList; + +/** + * Returns all MediaItems in a MediaSet, wrapping them in a cursor to appear + * like a PhotoSetLoader + */ +public class MediaItemsLoader extends AsyncTaskLoader<Cursor> implements DrawableFactory<Cursor> { + + private static final SyncListener sNullListener = new SyncListener() { + @Override + public void onSyncDone(MediaSet mediaSet, int resultCode) { + } + }; + + private MediaSet mMediaSet; + private Future<Integer> mSyncTask = null; + private ContentListener mObserver = new ContentListener() { + @Override + public void onContentDirty() { + onContentChanged(); + } + }; + private ArrayList<MediaItem> mMediaItems = new ArrayList<MediaItem>(); + + public MediaItemsLoader(Context context) { + super(context); + DataManager dm = DataManager.from(context); + String path = dm.getTopSetPath(DataManager.INCLUDE_ALL); + mMediaSet = dm.getMediaSet(path); + } + + public MediaItemsLoader(Context context, String parentPath) { + super(context); + mMediaSet = DataManager.from(getContext()).getMediaSet(parentPath); + } + + @Override + protected void onStartLoading() { + super.onStartLoading(); + mMediaSet.addContentListener(mObserver); + mSyncTask = mMediaSet.requestSync(sNullListener); + forceLoad(); + } + + @Override + protected boolean onCancelLoad() { + if (mSyncTask != null) { + mSyncTask.cancel(); + mSyncTask = null; + } + return super.onCancelLoad(); + } + + @Override + protected void onStopLoading() { + super.onStopLoading(); + cancelLoad(); + mMediaSet.removeContentListener(mObserver); + } + + @Override + protected void onReset() { + super.onReset(); + onStopLoading(); + } + + @Override + public Cursor loadInBackground() { + mMediaSet.loadIfDirty(); + final MatrixCursor cursor = new MatrixCursor(PhotoSetLoader.PROJECTION); + final Object[] row = new Object[PhotoSetLoader.PROJECTION.length]; + mMediaSet.enumerateTotalMediaItems(new ItemConsumer() { + @Override + public void consume(int index, MediaItem item) { + row[PhotoSetLoader.INDEX_ID] = index; + row[PhotoSetLoader.INDEX_DATA] = item.getContentUri().toString(); + row[PhotoSetLoader.INDEX_DATE_ADDED] = item.getDateInMs(); + row[PhotoSetLoader.INDEX_HEIGHT] = item.getHeight(); + row[PhotoSetLoader.INDEX_WIDTH] = item.getWidth(); + row[PhotoSetLoader.INDEX_WIDTH] = item.getWidth(); + int rawMediaType = item.getMediaType(); + int mappedMediaType = FileColumns.MEDIA_TYPE_NONE; + if (rawMediaType == MediaItem.MEDIA_TYPE_IMAGE) { + mappedMediaType = FileColumns.MEDIA_TYPE_IMAGE; + } else if (rawMediaType == MediaItem.MEDIA_TYPE_VIDEO) { + mappedMediaType = FileColumns.MEDIA_TYPE_VIDEO; + } + row[PhotoSetLoader.INDEX_MEDIA_TYPE] = mappedMediaType; + cursor.addRow(row); + mMediaItems.add(item); + } + }); + return cursor; + } + + @Override + public Drawable drawableForItem(Cursor item, Drawable recycle) { + BitmapJobDrawable drawable = null; + if (recycle == null || !(recycle instanceof BitmapJobDrawable)) { + drawable = new BitmapJobDrawable(); + } else { + drawable = (BitmapJobDrawable) recycle; + } + int index = item.getInt(PhotoSetLoader.INDEX_ID); + drawable.setMediaItem(mMediaItems.get(index)); + return drawable; + } + + public static int getThumbnailSize() { + return MediaItem.getTargetSize(MediaItem.TYPE_MICROTHUMBNAIL); + } + +} diff --git a/src/com/android/photos/data/MediaSetLoader.java b/src/com/android/photos/shims/MediaSetLoader.java index 4afb7d922..353fd4e1a 100644 --- a/src/com/android/photos/data/MediaSetLoader.java +++ b/src/com/android/photos/shims/MediaSetLoader.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.photos.data; +package com.android.photos.shims; import android.content.AsyncTaskLoader; import android.content.Context; |