OSDN Git Service

Move calls to LightCycleHelper off the UI thread.
authorMangesh Ghiware <mghiware@google.com>
Thu, 11 Oct 2012 01:37:24 +0000 (18:37 -0700)
committerMangesh Ghiware <mghiware@google.com>
Thu, 11 Oct 2012 22:21:29 +0000 (15:21 -0700)
Bug: 7320303

Replace multiple methods in LightCycleHelper with a single one that
returns relevant metadata.

Change-Id: Iaa75b3c1ccc8a5b724b253cec654b5b5acb4878e

gallerycommon/src/com/android/gallery3d/util/ThreadPool.java
src/com/android/gallery3d/app/PhotoPage.java
src/com/android/gallery3d/data/DataManager.java
src/com/android/gallery3d/data/LocalImage.java
src/com/android/gallery3d/data/MediaItem.java
src/com/android/gallery3d/data/MediaObject.java
src/com/android/gallery3d/data/PanoramaMetadataJob.java [new file with mode: 0644]
src/com/android/gallery3d/data/UriImage.java
src/com/android/gallery3d/ui/ActionModeHandler.java
src_pd/com/android/gallery3d/util/LightCycleHelper.java

index cada234..115dc66 100644 (file)
@@ -81,8 +81,12 @@ public class ThreadPool {
     private final Executor mExecutor;
 
     public ThreadPool() {
+        this(CORE_POOL_SIZE, MAX_POOL_SIZE);
+    }
+
+    public ThreadPool(int initPoolSize, int maxPoolSize) {
         mExecutor = new ThreadPoolExecutor(
-                CORE_POOL_SIZE, MAX_POOL_SIZE, KEEP_ALIVE_TIME,
+                initPoolSize, maxPoolSize, KEEP_ALIVE_TIME,
                 TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(),
                 new PriorityThreadFactory("thread-pool",
                 android.os.Process.THREAD_PRIORITY_BACKGROUND));
index dd52745..db7f3d9 100644 (file)
@@ -50,6 +50,7 @@ import com.android.gallery3d.data.LocalImage;
 import com.android.gallery3d.data.MediaDetails;
 import com.android.gallery3d.data.MediaItem;
 import com.android.gallery3d.data.MediaObject;
+import com.android.gallery3d.data.MediaObject.SupportedOperationsListener;
 import com.android.gallery3d.data.MediaSet;
 import com.android.gallery3d.data.MtpSource;
 import com.android.gallery3d.data.Path;
@@ -94,6 +95,7 @@ public class PhotoPage extends ActivityState implements
     private static final int MSG_ON_PICTURE_CENTER = 10;
     private static final int MSG_REFRESH_IMAGE = 11;
     private static final int MSG_UPDATE_DEFERRED = 12;
+    private static final int MSG_UPDATE_PROGRESS = 13;
 
     private static final int HIDE_BARS_TIMEOUT = 3500;
     private static final int UNFREEZE_GLROOT_TIMEOUT = 250;
@@ -188,6 +190,14 @@ public class PhotoPage extends ActivityState implements
             new MyMenuVisibilityListener();
     private UpdateProgressListener mProgressListener;
 
+    private SupportedOperationsListener mSupportedOperationsListener =
+        new SupportedOperationsListener() {
+            @Override
+            public void onChange(int operations) {
+                mHandler.sendEmptyMessage(MSG_REFRESH_IMAGE);
+            }
+        };
+
     public static interface Model extends PhotoView.Model {
         public void resume();
         public void pause();
@@ -214,24 +224,24 @@ public class PhotoPage extends ActivityState implements
 
         @Override
         public void onStitchingResult(Uri uri) {
-            sendUpdate(uri);
+            sendUpdate(uri, MSG_REFRESH_IMAGE);
         }
 
         @Override
         public void onStitchingQueued(Uri uri) {
-            sendUpdate(uri);
+            sendUpdate(uri, MSG_UPDATE_PROGRESS);
         }
 
         @Override
         public void onStitchingProgress(Uri uri, final int progress) {
-            sendUpdate(uri);
+            sendUpdate(uri, MSG_UPDATE_PROGRESS);
         }
 
-        private void sendUpdate(Uri uri) {
+        private void sendUpdate(Uri uri, int message) {
             boolean isCurrentPhoto = mCurrentPhoto instanceof LocalImage
                     && mCurrentPhoto.getContentUri().equals(uri);
             if (isCurrentPhoto) {
-                mHandler.sendEmptyMessage(MSG_REFRESH_IMAGE);
+                mHandler.sendEmptyMessage(message);
             }
         }
     };
@@ -356,9 +366,11 @@ public class PhotoPage extends ActivityState implements
                         break;
                     }
                     case MSG_REFRESH_IMAGE: {
-                        MediaItem currentPhoto = mCurrentPhoto;
-                        mCurrentPhoto = null;
-                        updateCurrentPhoto(currentPhoto);
+                        updateUIForCurrentPhoto();
+                        break;
+                    }
+                    case MSG_UPDATE_PROGRESS: {
+                        updateProgressBar();
                         break;
                     }
                     default: throw new AssertionError(message.what);
@@ -682,21 +694,30 @@ public class PhotoPage extends ActivityState implements
                 && (mCurrentPhoto.getSupportedOperations() & MediaItem.SUPPORT_SHARE) != 0) {
             updateShareURI(mCurrentPhoto.getPath());
         }
+        updateProgressBar();
     }
 
     private void updateCurrentPhoto(MediaItem photo) {
         if (mCurrentPhoto == photo) return;
+        if (mCurrentPhoto != null) {
+            mCurrentPhoto.setSupportedOperationsListener(null);
+        }
         mCurrentPhoto = photo;
+        mCurrentPhoto.setSupportedOperationsListener(
+                mSupportedOperationsListener);
         if (mPhotoView.getFilmMode()) {
             requestDeferredUpdate();
         } else {
             updateUIForCurrentPhoto();
         }
+    }
+
+    private void updateProgressBar() {
         if (mProgressBar != null) {
             mProgressBar.hideProgress();
             StitchingProgressManager progressManager = mApplication.getStitchingProgressManager();
             if (progressManager != null && mCurrentPhoto instanceof LocalImage) {
-                Integer progress = progressManager.getProgress(photo.getContentUri());
+                Integer progress = progressManager.getProgress(mCurrentPhoto.getContentUri());
                 if (progress != null) {
                     mProgressBar.setProgress(progress);
                 }
index 95954e5..3d2c0c2 100644 (file)
@@ -250,6 +250,12 @@ public class DataManager {
         return getMediaObject(path).getSupportedOperations();
     }
 
+    // getAll will cause this call to wait if any of the operations
+    // are expensive to compute. Do not call in UI thread.
+    public int getSupportedOperations(Path path, boolean getAll) {
+        return getMediaObject(path).getSupportedOperations(getAll);
+    }
+
     public void delete(Path path) {
         getMediaObject(path).delete();
     }
index 5d6d39d..dba6b68 100644 (file)
@@ -36,8 +36,11 @@ import com.android.gallery3d.app.GalleryApp;
 import com.android.gallery3d.app.StitchingProgressManager;
 import com.android.gallery3d.common.ApiHelper;
 import com.android.gallery3d.common.BitmapUtils;
+import com.android.gallery3d.util.Future;
+import com.android.gallery3d.util.FutureListener;
 import com.android.gallery3d.util.GalleryUtils;
 import com.android.gallery3d.util.LightCycleHelper;
+import com.android.gallery3d.util.LightCycleHelper.PanoramaMetadata;
 import com.android.gallery3d.util.ThreadPool.Job;
 import com.android.gallery3d.util.ThreadPool.JobContext;
 import com.android.gallery3d.util.UpdateHelper;
@@ -101,11 +104,11 @@ public class LocalImage extends LocalMediaItem {
 
     public int rotation;
 
-    private boolean mUsePanoramaViewer;
-    private boolean mUsePanoramaViewerInitialized;
-
-    private boolean mIsPanorama360;
-    private boolean mIsPanorama360Initialized;
+    private Object mLock = new Object();
+    private Future<PanoramaMetadata> mGetPanoMetadataTask;
+    private boolean mPanoramaMetadataInitialized;
+    private PanoramaMetadata mPanoramaMetadata;
+    private SupportedOperationsListener mListener;
 
     public LocalImage(Path path, GalleryApp application, Cursor cursor) {
         super(path, nextVersionNumber());
@@ -255,9 +258,9 @@ public class LocalImage extends LocalMediaItem {
             operation |= SUPPORT_SHOW_ON_MAP;
         }
 
-        if (usePanoramaViewer()) {
+        if (mPanoramaMetadata != null && mPanoramaMetadata.mUsePanoramaViewer) {
             operation |= SUPPORT_PANORAMA;
-            if (isPanorama360()) {
+            if (mPanoramaMetadata.mIsPanorama360) {
                 operation |= SUPPORT_PANORAMA360;
                 // disable destructive rotate and crop for 360 degree panorama
                 operation &= ~(SUPPORT_ROTATE | SUPPORT_CROP);
@@ -267,6 +270,55 @@ public class LocalImage extends LocalMediaItem {
     }
 
     @Override
+    public int getSupportedOperations(boolean getAll) {
+        synchronized (mLock) {
+            if (getAll && !mPanoramaMetadataInitialized) {
+                if (mGetPanoMetadataTask == null) {
+                    mGetPanoMetadataTask = getThreadPool().submit(
+                            new PanoramaMetadataJob(mApplication.getAndroidContext(),
+                                getContentUri()));
+                }
+                mPanoramaMetadata = mGetPanoMetadataTask.get();
+                mPanoramaMetadataInitialized = true;
+            }
+        }
+        return getSupportedOperations();
+    }
+
+    @Override
+    public void setSupportedOperationsListener(SupportedOperationsListener l) {
+        synchronized (mLock) {
+            if (mPanoramaMetadataInitialized) return; // no more updates
+
+            if (l == null) {
+                if (mGetPanoMetadataTask != null) {
+                    mGetPanoMetadataTask.cancel();
+                    mGetPanoMetadataTask = null;
+                }
+            } else {
+                if (mGetPanoMetadataTask == null) {
+                    mGetPanoMetadataTask = getThreadPool().submit(
+                            new PanoramaMetadataJob(mApplication.getAndroidContext(),
+                                getContentUri()),
+                            new FutureListener<PanoramaMetadata>() {
+                                @Override
+                        public void onFutureDone(Future<PanoramaMetadata> future) {
+                            mGetPanoMetadataTask = null;
+                            if (future.isCancelled()) return;
+                            mPanoramaMetadata = future.get();
+                            mPanoramaMetadataInitialized = true;
+                            if (mListener != null) {
+                                mListener.onChange(getSupportedOperations());
+                            }
+                        }
+                        });
+                        }
+            }
+            mListener = l;
+        }
+    }
+
+    @Override
     public void delete() {
         GalleryUtils.assertNotInRenderThread();
         Uri baseUri = Images.Media.EXTERNAL_CONTENT_URI;
@@ -360,27 +412,4 @@ public class LocalImage extends LocalMediaItem {
     public String getFilePath() {
         return filePath;
     }
-
-    @Override
-    public boolean usePanoramaViewer() {
-        if (!mUsePanoramaViewerInitialized) {
-            Context context = mApplication.getAndroidContext();
-            mUsePanoramaViewer = LightCycleHelper.hasLightCycleView(context)
-                    && LightCycleHelper.isPanorama(mApplication.getContentResolver(),
-                            getContentUri());
-            mUsePanoramaViewerInitialized = true;
-        }
-        return mUsePanoramaViewer;
-    }
-
-    @Override
-    public boolean isPanorama360() {
-        // cache flag for faster access
-        if (!mIsPanorama360Initialized) {
-            mIsPanorama360 = LightCycleHelper.isPanorama360(
-                    mApplication.getAndroidContext(), getContentUri());
-            mIsPanorama360Initialized = true;
-        }
-        return mIsPanorama360;
-    }
 }
index da59abe..19084d4 100644 (file)
@@ -103,14 +103,6 @@ public abstract class MediaItem extends MediaObject {
         return "";
     }
 
-    public boolean usePanoramaViewer() {
-        return false;
-    }
-
-    public boolean isPanorama360() {
-        return false;
-    }
-
     // Returns width and height of the media item.
     // Returns 0, 0 if the information is not available.
     public abstract int getWidth();
index a16b966..382a5c7 100644 (file)
@@ -18,6 +18,8 @@ package com.android.gallery3d.data;
 
 import android.net.Uri;
 
+import com.android.gallery3d.util.ThreadPool;
+
 public abstract class MediaObject {
     @SuppressWarnings("unused")
     private static final String TAG = "MediaObject";
@@ -37,14 +39,20 @@ public abstract class MediaObject {
     public static final int SUPPORT_INFO = 1 << 10;
     public static final int SUPPORT_IMPORT = 1 << 11;
     public static final int SUPPORT_TRIM = 1 << 12;
-    public static final int SUPPORT_PANORAMA = 1 << 13;
-    public static final int SUPPORT_PANORAMA360 = 1 << 14;
-    public static final int SUPPORT_UNLOCK = 1 << 15;
-    public static final int SUPPORT_BACK = 1 << 16;
-    public static final int SUPPORT_ACTION = 1 << 17;
-    public static final int SUPPORT_CAMERA_SHORTCUT = 1 << 18;
+    public static final int SUPPORT_UNLOCK = 1 << 13;
+    public static final int SUPPORT_BACK = 1 << 14;
+    public static final int SUPPORT_ACTION = 1 << 15;
+    public static final int SUPPORT_CAMERA_SHORTCUT = 1 << 16;
+    // The panorama specific bits are expensive to compute.
+    // Use SupportedOperationsListener to request them.
+    public static final int SUPPORT_PANORAMA = 1 << 30;
+    public static final int SUPPORT_PANORAMA360 = 1 << 31;
     public static final int SUPPORT_ALL = 0xffffffff;
 
+    public static interface SupportedOperationsListener {
+        public void onChange(int operations);
+    }
+
     // These are the bits returned from getMediaType():
     public static final int MEDIA_TYPE_UNKNOWN = 1;
     public static final int MEDIA_TYPE_IMAGE = 2;
@@ -72,6 +80,11 @@ public abstract class MediaObject {
 
     protected final Path mPath;
 
+    private static ThreadPool sThreadPool = new ThreadPool(1, 1);
+    public static ThreadPool getThreadPool() {
+        return sThreadPool;
+    }
+
     public MediaObject(Path path, long version) {
         path.setObject(this);
         mPath = path;
@@ -86,6 +99,14 @@ public abstract class MediaObject {
         return 0;
     }
 
+    public int getSupportedOperations(boolean getAll) {
+        return 0;
+    }
+
+    public void setSupportedOperationsListener(SupportedOperationsListener l) {
+        // nothing to do
+    }
+
     public void delete() {
         throw new UnsupportedOperationException();
     }
diff --git a/src/com/android/gallery3d/data/PanoramaMetadataJob.java b/src/com/android/gallery3d/data/PanoramaMetadataJob.java
new file mode 100644 (file)
index 0000000..e0a69c4
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2012 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.data;
+
+import android.content.Context;
+import android.net.Uri;
+
+import com.android.gallery3d.util.LightCycleHelper;
+import com.android.gallery3d.util.LightCycleHelper.PanoramaMetadata;
+import com.android.gallery3d.util.ThreadPool.Job;
+import com.android.gallery3d.util.ThreadPool.JobContext;
+
+class PanoramaMetadataJob implements Job<PanoramaMetadata> {
+    Context mContext;
+    Uri mUri;
+
+    public PanoramaMetadataJob(Context context, Uri uri) {
+        mContext = context;
+        mUri = uri;
+    }
+
+    @Override
+    public PanoramaMetadata run(JobContext jc) {
+        return LightCycleHelper.getPanoramaMetadata(mContext, mUri);
+    }
+}
index b2cbc7d..5fab667 100644 (file)
@@ -28,7 +28,10 @@ import android.os.ParcelFileDescriptor;
 import com.android.gallery3d.app.GalleryApp;
 import com.android.gallery3d.common.BitmapUtils;
 import com.android.gallery3d.common.Utils;
+import com.android.gallery3d.util.Future;
+import com.android.gallery3d.util.FutureListener;
 import com.android.gallery3d.util.LightCycleHelper;
+import com.android.gallery3d.util.LightCycleHelper.PanoramaMetadata;
 import com.android.gallery3d.util.ThreadPool.CancelListener;
 import com.android.gallery3d.util.ThreadPool.Job;
 import com.android.gallery3d.util.ThreadPool.JobContext;
@@ -56,10 +59,12 @@ public class UriImage extends MediaItem {
     private int mWidth;
     private int mHeight;
     private int mRotation;
-    private boolean mUsePanoramaViewer;
-    private boolean mUsePanoramaViewerInitialized;
-    private boolean mIsPanorama360;
-    private boolean mIsPanorama360Initialized;
+
+    private Object mLock = new Object();
+    private Future<PanoramaMetadata> mGetPanoMetadataTask;
+    private boolean mPanoramaMetadataInitialized;
+    private PanoramaMetadata mPanoramaMetadata;
+    private SupportedOperationsListener mListener;
 
     private GalleryApp mApplication;
 
@@ -220,9 +225,9 @@ public class UriImage extends MediaItem {
         if (BitmapUtils.isSupportedByRegionDecoder(mContentType)) {
             supported |= SUPPORT_FULL_IMAGE;
         }
-        if (usePanoramaViewer()) {
+        if (mPanoramaMetadata != null && mPanoramaMetadata.mUsePanoramaViewer) {
             supported |= SUPPORT_PANORAMA;
-            if (isPanorama360()) {
+            if (mPanoramaMetadata.mIsPanorama360) {
                 supported |= SUPPORT_PANORAMA360;
                 // disable destructive crop for 360 degree panorama
                 supported &= ~SUPPORT_CROP;
@@ -231,6 +236,55 @@ public class UriImage extends MediaItem {
         return supported;
     }
 
+    @Override
+    public int getSupportedOperations(boolean getAll) {
+        synchronized (mLock) {
+            if (getAll && !mPanoramaMetadataInitialized) {
+                if (mGetPanoMetadataTask == null) {
+                    mGetPanoMetadataTask = getThreadPool().submit(
+                            new PanoramaMetadataJob(mApplication.getAndroidContext(),
+                                getContentUri()));
+                }
+                mPanoramaMetadata = mGetPanoMetadataTask.get();
+                mPanoramaMetadataInitialized = true;
+            }
+        }
+        return getSupportedOperations();
+    }
+
+    @Override
+    public void setSupportedOperationsListener(SupportedOperationsListener l) {
+        synchronized (mLock) {
+            if (mPanoramaMetadataInitialized) return; // no more updates
+
+            if (l != null) {
+                if (mGetPanoMetadataTask != null) {
+                    mGetPanoMetadataTask.cancel();
+                    mGetPanoMetadataTask = null;
+                }
+            } else {
+                if (mGetPanoMetadataTask == null) {
+                    mGetPanoMetadataTask = getThreadPool().submit(
+                            new PanoramaMetadataJob(mApplication.getAndroidContext(),
+                                getContentUri()),
+                            new FutureListener<PanoramaMetadata>() {
+                                @Override
+                        public void onFutureDone(Future<PanoramaMetadata> future) {
+                            mGetPanoMetadataTask = null;
+                            if (future.isCancelled()) return;
+                            mPanoramaMetadata = future.get();
+                            mPanoramaMetadataInitialized = true;
+                            if (mListener != null) {
+                                mListener.onChange(getSupportedOperations());
+                            }
+                        }
+                        });
+                }
+            }
+            mListener = l;
+        }
+    }
+
     private boolean isSharable() {
         // We cannot grant read permission to the receiver since we put
         // the data URI in EXTRA_STREAM instead of the data part of an intent
@@ -297,27 +351,4 @@ public class UriImage extends MediaItem {
     public int getRotation() {
         return mRotation;
     }
-
-    @Override
-    public boolean usePanoramaViewer() {
-        if (!mUsePanoramaViewerInitialized) {
-            Context context = mApplication.getAndroidContext();
-            mUsePanoramaViewer = LightCycleHelper.hasLightCycleView(context)
-                    && LightCycleHelper.isPanorama(mApplication.getContentResolver(),
-                            getContentUri());
-            mUsePanoramaViewerInitialized = true;
-        }
-        return mUsePanoramaViewer;
-    }
-
-    @Override
-    public boolean isPanorama360() {
-        // cache flag for faster access
-        if (!mIsPanorama360Initialized) {
-            mIsPanorama360 = LightCycleHelper.isPanorama360(
-                    mApplication.getAndroidContext(), getContentUri());
-            mIsPanorama360Initialized = true;
-        }
-        return mIsPanorama360;
-    }
 }
index c5aba53..8cebddd 100644 (file)
@@ -227,7 +227,7 @@ public class ActionModeHandler implements Callback, PopupList.OnPopupItemClickLi
         int type = 0;
         for (Path path : unexpandedPaths) {
             if (jc.isCancelled()) return 0;
-            int support = manager.getSupportedOperations(path);
+            int support = manager.getSupportedOperations(path, true);
             type |= manager.getMediaType(path);
             operation &= support;
         }
@@ -297,7 +297,7 @@ public class ActionModeHandler implements Callback, PopupList.OnPopupItemClickLi
         final Intent intent = new Intent();
         for (Path path : expandedPaths) {
             if (jc.isCancelled()) return null;
-            int support = manager.getSupportedOperations(path);
+            int support = manager.getSupportedOperations(path, true);
             type |= manager.getMediaType(path);
 
             if ((support & MediaObject.SUPPORT_SHARE) != 0) {
index cc8ea20..995eac8 100644 (file)
@@ -27,13 +27,20 @@ import com.android.gallery3d.app.GalleryApp;
 import com.android.gallery3d.app.StitchingProgressManager;
 
 public class LightCycleHelper {
+    public static class PanoramaMetadata {
+        // Whether a panorama viewer should be used
+        public final boolean mUsePanoramaViewer;
+        // Whether a panorama is 360 degrees
+        public final boolean mIsPanorama360;
 
-    public static void setupCaptureIntent(Context context, Intent it, String outputDir) {
-        /* Do nothing */
+        public PanoramaMetadata(boolean usePanoramaViewer, boolean isPanorama360) {
+            mUsePanoramaViewer = usePanoramaViewer;
+            mIsPanorama360 = isPanorama360;
+        }
     }
 
-    public static boolean hasLightCycleView(Context context) {
-        return false;
+    public static void setupCaptureIntent(Context context, Intent it, String outputDir) {
+        /* Do nothing */
     }
 
     public static boolean hasLightCycleCapture(Context context) {
@@ -44,12 +51,8 @@ public class LightCycleHelper {
         /* Do nothing */
     }
 
-    public static boolean isPanorama(ContentResolver contentResolver, Uri uri) {
-        return false;
-    }
-
-    public static boolean isPanorama360(Context context, Uri uri) {
-        return false;
+    public static PanoramaMetadata getPanoramaMetadata(Context context, Uri uri) {
+        return null;
     }
 
     public static CameraModule createPanoramaModule() {