OSDN Git Service

Data provider shim
authorJohn Reck <jreck@google.com>
Thu, 7 Mar 2013 00:36:45 +0000 (16:36 -0800)
committerJohn Reck <jreck@google.com>
Thu, 7 Mar 2013 00:38:37 +0000 (16:38 -0800)
Bolt the new UI framework on top of the old data model temporarily
to unblock UI work

Change-Id: I2f61f70647faca1f6a95b1f02f719ec4277fa5fb

res/layout/photo_set.xml
res/layout/photo_set_item.xml [new file with mode: 0644]
src/com/android/photos/PhotoSetFragment.java
src/com/android/photos/data/PhotoSetLoader.java
src/com/android/photos/drawables/DrawableFactory.java [new file with mode: 0644]
src/com/android/photos/shims/BitmapJobDrawable.java [new file with mode: 0644]
src/com/android/photos/shims/MediaItemsLoader.java [new file with mode: 0644]
src/com/android/photos/shims/MediaSetLoader.java [moved from src/com/android/photos/data/MediaSetLoader.java with 98% similarity]

index f6ff637..d929cad 100644 (file)
@@ -5,12 +5,18 @@
     android:paddingLeft="8dp"
     android:paddingRight="8dp" >
 
-    <com.android.photos.views.GalleryThumbnailView
+    <GridView
         android:id="@id/android:list"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:layout_weight="1"
-        android:drawSelectorOnTop="true" />
+        android:drawSelectorOnTop="true"
+        android:numColumns="auto_fit"
+        android:stretchMode="columnWidth"
+        android:columnWidth="200dip"
+        android:horizontalSpacing="4dip"
+        android:verticalSpacing="4dip"
+        android:padding="4dip" />
 
     <TextView
         android:id="@id/android:empty"
diff --git a/res/layout/photo_set_item.xml b/res/layout/photo_set_item.xml
new file mode 100644 (file)
index 0000000..b56184e
--- /dev/null
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="200dip"
+    android:id="@+id/thumbnail">
+
+</ImageView>
\ No newline at end of file
index 0e9efa4..1de8de5 100644 (file)
@@ -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
index 8c511a5..21da906 100644 (file)
@@ -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 (file)
index 0000000..ad046c8
--- /dev/null
@@ -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 (file)
index 0000000..6623b91
--- /dev/null
@@ -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 (file)
index 0000000..886b3c3
--- /dev/null
@@ -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);
+    }
+
+}
@@ -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;