From 83e7d70bccd93c348d70048e1add3cc1c4ac33c6 Mon Sep 17 00:00:00 2001 From: Mangesh Ghiware Date: Wed, 10 Oct 2012 18:37:24 -0700 Subject: Move calls to LightCycleHelper off the UI thread. Bug: 7320303 Replace multiple methods in LightCycleHelper with a single one that returns relevant metadata. Change-Id: Iaa75b3c1ccc8a5b724b253cec654b5b5acb4878e --- .../src/com/android/gallery3d/util/ThreadPool.java | 6 +- src/com/android/gallery3d/app/PhotoPage.java | 39 +++++++--- src/com/android/gallery3d/data/DataManager.java | 6 ++ src/com/android/gallery3d/data/LocalImage.java | 89 ++++++++++++++-------- src/com/android/gallery3d/data/MediaItem.java | 8 -- src/com/android/gallery3d/data/MediaObject.java | 33 ++++++-- .../gallery3d/data/PanoramaMetadataJob.java | 40 ++++++++++ src/com/android/gallery3d/data/UriImage.java | 89 +++++++++++++++------- .../android/gallery3d/ui/ActionModeHandler.java | 4 +- .../android/gallery3d/util/LightCycleHelper.java | 23 +++--- 10 files changed, 242 insertions(+), 95 deletions(-) create mode 100644 src/com/android/gallery3d/data/PanoramaMetadataJob.java diff --git a/gallerycommon/src/com/android/gallery3d/util/ThreadPool.java b/gallerycommon/src/com/android/gallery3d/util/ThreadPool.java index cada234b3..115dc6625 100644 --- a/gallerycommon/src/com/android/gallery3d/util/ThreadPool.java +++ b/gallerycommon/src/com/android/gallery3d/util/ThreadPool.java @@ -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(), new PriorityThreadFactory("thread-pool", android.os.Process.THREAD_PRIORITY_BACKGROUND)); diff --git a/src/com/android/gallery3d/app/PhotoPage.java b/src/com/android/gallery3d/app/PhotoPage.java index dd5274517..db7f3d9f1 100644 --- a/src/com/android/gallery3d/app/PhotoPage.java +++ b/src/com/android/gallery3d/app/PhotoPage.java @@ -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); } diff --git a/src/com/android/gallery3d/data/DataManager.java b/src/com/android/gallery3d/data/DataManager.java index 95954e59a..3d2c0c2f0 100644 --- a/src/com/android/gallery3d/data/DataManager.java +++ b/src/com/android/gallery3d/data/DataManager.java @@ -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(); } diff --git a/src/com/android/gallery3d/data/LocalImage.java b/src/com/android/gallery3d/data/LocalImage.java index 5d6d39dcf..dba6b68eb 100644 --- a/src/com/android/gallery3d/data/LocalImage.java +++ b/src/com/android/gallery3d/data/LocalImage.java @@ -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 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); @@ -266,6 +269,55 @@ public class LocalImage extends LocalMediaItem { return operation; } + @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() { + @Override + public void onFutureDone(Future 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(); @@ -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; - } } diff --git a/src/com/android/gallery3d/data/MediaItem.java b/src/com/android/gallery3d/data/MediaItem.java index da59abeef..19084d41e 100644 --- a/src/com/android/gallery3d/data/MediaItem.java +++ b/src/com/android/gallery3d/data/MediaItem.java @@ -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(); diff --git a/src/com/android/gallery3d/data/MediaObject.java b/src/com/android/gallery3d/data/MediaObject.java index a16b9666d..382a5c792 100644 --- a/src/com/android/gallery3d/data/MediaObject.java +++ b/src/com/android/gallery3d/data/MediaObject.java @@ -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 index 000000000..e0a69c41f --- /dev/null +++ b/src/com/android/gallery3d/data/PanoramaMetadataJob.java @@ -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 { + 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); + } +} diff --git a/src/com/android/gallery3d/data/UriImage.java b/src/com/android/gallery3d/data/UriImage.java index b2cbc7d68..5fab667b8 100644 --- a/src/com/android/gallery3d/data/UriImage.java +++ b/src/com/android/gallery3d/data/UriImage.java @@ -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 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() { + @Override + public void onFutureDone(Future 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; - } } diff --git a/src/com/android/gallery3d/ui/ActionModeHandler.java b/src/com/android/gallery3d/ui/ActionModeHandler.java index c5aba53f9..8cebdddad 100644 --- a/src/com/android/gallery3d/ui/ActionModeHandler.java +++ b/src/com/android/gallery3d/ui/ActionModeHandler.java @@ -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) { diff --git a/src_pd/com/android/gallery3d/util/LightCycleHelper.java b/src_pd/com/android/gallery3d/util/LightCycleHelper.java index cc8ea20b9..995eac8b1 100644 --- a/src_pd/com/android/gallery3d/util/LightCycleHelper.java +++ b/src_pd/com/android/gallery3d/util/LightCycleHelper.java @@ -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() { -- cgit v1.2.3