summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/com/android/gallery3d/app/GalleryAppImpl.java1
-rw-r--r--src/com/android/gallery3d/app/PanoramaMetadataSupport.java89
-rw-r--r--src/com/android/gallery3d/app/PhotoPage.java198
-rw-r--r--src/com/android/gallery3d/app/PhotoPageBottomControls.java10
-rw-r--r--src/com/android/gallery3d/data/DataManager.java33
-rw-r--r--src/com/android/gallery3d/data/LocalImage.java66
-rw-r--r--src/com/android/gallery3d/data/MediaObject.java23
-rw-r--r--src/com/android/gallery3d/data/PanoramaMetadataJob.java2
-rw-r--r--src/com/android/gallery3d/data/UriImage.java66
-rw-r--r--src/com/android/gallery3d/ui/ActionModeHandler.java104
-rw-r--r--src/com/android/gallery3d/ui/MenuExecutor.java14
-rw-r--r--src_pd/com/android/gallery3d/util/LightCycleHelper.java4
12 files changed, 361 insertions, 249 deletions
diff --git a/src/com/android/gallery3d/app/GalleryAppImpl.java b/src/com/android/gallery3d/app/GalleryAppImpl.java
index c4507b3..443bc47 100644
--- a/src/com/android/gallery3d/app/GalleryAppImpl.java
+++ b/src/com/android/gallery3d/app/GalleryAppImpl.java
@@ -63,6 +63,7 @@ public class GalleryAppImpl extends Application implements GalleryApp {
state, PackageManager.DONT_KILL_APP);
mStitchingProgressManager = LightCycleHelper.createStitchingManagerInstance(this);
+ mStitchingProgressManager.addChangeListener(getDataManager());
}
@Override
diff --git a/src/com/android/gallery3d/app/PanoramaMetadataSupport.java b/src/com/android/gallery3d/app/PanoramaMetadataSupport.java
new file mode 100644
index 0000000..d404225
--- /dev/null
+++ b/src/com/android/gallery3d/app/PanoramaMetadataSupport.java
@@ -0,0 +1,89 @@
+/*
+ * 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.app;
+
+import com.android.gallery3d.data.MediaObject;
+import com.android.gallery3d.data.MediaObject.PanoramaSupportCallback;
+import com.android.gallery3d.data.PanoramaMetadataJob;
+import com.android.gallery3d.util.Future;
+import com.android.gallery3d.util.FutureListener;
+import com.android.gallery3d.util.LightCycleHelper.PanoramaMetadata;
+
+import java.util.ArrayList;
+
+/**
+ * This class breaks out the off-thread panorama support checks so that the
+ * complexity can be shared between UriImage and LocalImage, which need to
+ * support panoramas.
+ */
+public class PanoramaMetadataSupport implements FutureListener<PanoramaMetadata> {
+ private Object mLock = new Object();
+ private Future<PanoramaMetadata> mGetPanoMetadataTask;
+ private PanoramaMetadata mPanoramaMetadata;
+ private ArrayList<PanoramaSupportCallback> mCallbacksWaiting;
+ private MediaObject mMediaObject;
+
+ public PanoramaMetadataSupport(MediaObject mediaObject) {
+ mMediaObject = mediaObject;
+ }
+
+ public void getPanoramaSupport(GalleryApp app, PanoramaSupportCallback callback) {
+ synchronized (mLock) {
+ if (mPanoramaMetadata != null) {
+ callback.panoramaInfoAvailable(mMediaObject, mPanoramaMetadata.mUsePanoramaViewer,
+ mPanoramaMetadata.mIsPanorama360);
+ } else {
+ if (mCallbacksWaiting == null) {
+ mCallbacksWaiting = new ArrayList<PanoramaSupportCallback>();
+ mGetPanoMetadataTask = app.getThreadPool().submit(
+ new PanoramaMetadataJob(app.getAndroidContext(),
+ mMediaObject.getContentUri()), this);
+
+ }
+ mCallbacksWaiting.add(callback);
+ }
+ }
+ }
+
+ public void clearCachedValues() {
+ synchronized (mLock) {
+ if (mPanoramaMetadata != null) {
+ mPanoramaMetadata = null;
+ } else if (mGetPanoMetadataTask != null) {
+ mGetPanoMetadataTask.cancel();
+ for (PanoramaSupportCallback cb : mCallbacksWaiting) {
+ cb.panoramaInfoAvailable(mMediaObject, false, false);
+ }
+ mGetPanoMetadataTask = null;
+ mCallbacksWaiting = null;
+ }
+ }
+ }
+
+ @Override
+ public void onFutureDone(Future<PanoramaMetadata> future) {
+ synchronized (mLock) {
+ mPanoramaMetadata = future.get();
+ for (PanoramaSupportCallback cb : mCallbacksWaiting) {
+ cb.panoramaInfoAvailable(mMediaObject,
+ mPanoramaMetadata.mUsePanoramaViewer,
+ mPanoramaMetadata.mIsPanorama360);
+ }
+ mGetPanoMetadataTask = null;
+ mCallbacksWaiting = null;
+ }
+ }
+}
diff --git a/src/com/android/gallery3d/app/PhotoPage.java b/src/com/android/gallery3d/app/PhotoPage.java
index a548e70..f900c40 100644
--- a/src/com/android/gallery3d/app/PhotoPage.java
+++ b/src/com/android/gallery3d/app/PhotoPage.java
@@ -48,7 +48,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.MediaObject.PanoramaSupportCallback;
import com.android.gallery3d.data.MediaSet;
import com.android.gallery3d.data.MtpSource;
import com.android.gallery3d.data.Path;
@@ -91,6 +91,8 @@ public class PhotoPage extends ActivityState implements
private static final int MSG_UPDATE_PHOTO_UI = 12;
private static final int MSG_UPDATE_PROGRESS = 13;
private static final int MSG_UPDATE_DEFERRED = 14;
+ private static final int MSG_UPDATE_SHARE_URI = 15;
+ private static final int MSG_UPDATE_PANORAMA_UI = 16;
private static final int HIDE_BARS_TIMEOUT = 3500;
private static final int UNFREEZE_GLROOT_TIMEOUT = 250;
@@ -166,7 +168,7 @@ public class PhotoPage extends ActivityState implements
private boolean mSkipUpdateCurrentPhoto = false;
private static final long CAMERA_SWITCH_CUTOFF_THRESHOLD_MS = 300;
- private static final long DEFERRED_UPDATE_MS = 150;
+ private static final long DEFERRED_UPDATE_MS = 250;
private boolean mDeferredUpdateWaiting = false;
private long mDeferUpdateUntil = Long.MAX_VALUE;
@@ -182,20 +184,37 @@ public class PhotoPage extends ActivityState implements
new MyMenuVisibilityListener();
private UpdateProgressListener mProgressListener;
- private SupportedOperationsListener mSupportedOperationsListener =
- new SupportedOperationsListener() {
- @Override
- public void onChange(MediaObject item, int operations) {
- if (item == mCurrentPhoto) {
- if (mPhotoView.getFilmMode()
- && SystemClock.uptimeMillis() < mDeferUpdateUntil) {
- requestDeferredUpdate();
- } else {
- mHandler.sendEmptyMessage(MSG_UPDATE_PHOTO_UI);
- }
- }
+ private final PanoramaSupportCallback mUpdatePanoramaMenuItemsCallback = new PanoramaSupportCallback() {
+ @Override
+ public void panoramaInfoAvailable(MediaObject mediaObject, boolean isPanorama,
+ boolean isPanorama360) {
+ if (mediaObject == mCurrentPhoto) {
+ mHandler.obtainMessage(MSG_UPDATE_PANORAMA_UI, isPanorama360 ? 1 : 0, 0,
+ mediaObject).sendToTarget();
}
- };
+ }
+ };
+
+ private final PanoramaSupportCallback mRefreshBottomControlsCallback = new PanoramaSupportCallback() {
+ @Override
+ public void panoramaInfoAvailable(MediaObject mediaObject, boolean isPanorama,
+ boolean isPanorama360) {
+ if (mediaObject == mCurrentPhoto) {
+ mHandler.obtainMessage(MSG_REFRESH_BOTTOM_CONTROLS, isPanorama ? 1 : 0, 0, mediaObject).sendToTarget();
+ }
+ }
+ };
+
+ private final PanoramaSupportCallback mUpdateShareURICallback = new PanoramaSupportCallback() {
+ @Override
+ public void panoramaInfoAvailable(MediaObject mediaObject, boolean isPanorama,
+ boolean isPanorama360) {
+ if (mediaObject == mCurrentPhoto) {
+ mHandler.obtainMessage(MSG_UPDATE_SHARE_URI, isPanorama360 ? 1 : 0, 0, mediaObject)
+ .sendToTarget();
+ }
+ }
+ };
public static interface Model extends PhotoView.Model {
public void resume();
@@ -230,8 +249,9 @@ public class PhotoPage extends ActivityState implements
}
private void sendUpdate(Uri uri, int message) {
- boolean isCurrentPhoto = mCurrentPhoto instanceof LocalImage
- && mCurrentPhoto.getContentUri().equals(uri);
+ MediaObject currentPhoto = mCurrentPhoto;
+ boolean isCurrentPhoto = currentPhoto instanceof LocalImage
+ && currentPhoto.getContentUri().equals(uri);
if (isCurrentPhoto) {
mHandler.sendEmptyMessage(message);
}
@@ -278,7 +298,9 @@ public class PhotoPage extends ActivityState implements
break;
}
case MSG_REFRESH_BOTTOM_CONTROLS: {
- if (mBottomControls != null) mBottomControls.refresh();
+ if (mCurrentPhoto == message.obj && mBottomControls != null) {
+ mBottomControls.refresh(message.arg1 != 0);
+ }
break;
}
case MSG_ON_FULL_SCREEN_CHANGED: {
@@ -347,6 +369,28 @@ public class PhotoPage extends ActivityState implements
updateProgressBar();
break;
}
+ case MSG_UPDATE_SHARE_URI: {
+ if (mCurrentPhoto == message.obj) {
+ boolean isPanorama360 = message.arg1 != 0;
+ Uri contentUri = mCurrentPhoto.getContentUri();
+ Intent panoramaIntent = null;
+ if (isPanorama360) {
+ panoramaIntent = createSharePanoramaIntent(contentUri);
+ }
+ Intent shareIntent = createShareIntent(mCurrentPhoto);
+
+ mActionBar.setShareIntents(panoramaIntent, shareIntent);
+ setNfcBeamPushUri(contentUri);
+ }
+ break;
+ }
+ case MSG_UPDATE_PANORAMA_UI: {
+ if (mCurrentPhoto == message.obj) {
+ boolean isPanorama360 = message.arg1 != 0;
+ updatePanoramaUI(isPanorama360);
+ }
+ break;
+ }
default: throw new AssertionError(message.what);
}
}
@@ -540,18 +584,17 @@ public class PhotoPage extends ActivityState implements
}
@Override
- public boolean canDisplayBottomControl(int control) {
- if (mCurrentPhoto == null) return false;
+ public boolean canDisplayBottomControl(int control, boolean isPanorama) {
+ if (mCurrentPhoto == null) {
+ return false;
+ }
switch(control) {
case R.id.photopage_bottom_control_edit:
return mHaveImageEditor && mShowBars
- && (mCurrentPhoto.getSupportedOperations()
- & MediaItem.SUPPORT_EDIT) != 0
- && mCurrentPhoto.getMediaType()
- == MediaObject.MEDIA_TYPE_IMAGE;
+ && (mCurrentPhoto.getSupportedOperations() & MediaItem.SUPPORT_EDIT) != 0
+ && mCurrentPhoto.getMediaType() == MediaObject.MEDIA_TYPE_IMAGE;
case R.id.photopage_bottom_control_panorama:
- return (mCurrentPhoto.getSupportedOperations()
- & MediaItem.SUPPORT_PANORAMA) != 0;
+ return isPanorama;
default:
return false;
}
@@ -593,24 +636,18 @@ public class PhotoPage extends ActivityState implements
mNfcPushUris[0] = uri;
}
- private Intent createShareIntent(Path path) {
- DataManager manager = mActivity.getDataManager();
- int type = manager.getMediaType(path);
+ private static Intent createShareIntent(MediaObject mediaObject) {
+ int type = mediaObject.getMediaType();
return new Intent(Intent.ACTION_SEND)
.setType(MenuExecutor.getMimeType(type))
- .putExtra(Intent.EXTRA_STREAM, manager.getContentUri(path))
+ .putExtra(Intent.EXTRA_STREAM, mediaObject.getContentUri())
.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
}
- private Intent createSharePanoramaIntent(Path path) {
- DataManager manager = mActivity.getDataManager();
- int supported = manager.getSupportedOperations(path);
- if ((supported & MediaObject.SUPPORT_PANORAMA360) == 0) {
- return null;
- }
+ private static Intent createSharePanoramaIntent(Uri contentUri) {
return new Intent(Intent.ACTION_SEND)
.setType(GalleryUtils.MIME_TYPE_PANORAMA360)
- .putExtra(Intent.EXTRA_STREAM, manager.getContentUri(path))
+ .putExtra(Intent.EXTRA_STREAM, contentUri)
.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
}
@@ -634,15 +671,6 @@ public class PhotoPage extends ActivityState implements
REQUEST_EDIT);
}
- private void updateShareURI(Path path) {
- DataManager manager = mActivity.getDataManager();
- mActionBar.setShareIntents(
- createSharePanoramaIntent(path),
- createShareIntent(path));
- Uri uri = manager.getContentUri(path);
- setNfcBeamPushUri(uri);
- }
-
private void requestDeferredUpdate() {
mDeferUpdateUntil = SystemClock.uptimeMillis() + DEFERRED_UPDATE_MS;
if (!mDeferredUpdateWaiting) {
@@ -663,25 +691,20 @@ public class PhotoPage extends ActivityState implements
}
updateMenuOperations();
- if (mBottomControls != null) mBottomControls.refresh();
+ refreshBottomControlsWhenReady();
if (mShowDetails) {
mDetailsHelper.reloadDetails();
}
if ((mSecureAlbum == null)
&& (mCurrentPhoto.getSupportedOperations() & MediaItem.SUPPORT_SHARE) != 0) {
- updateShareURI(mCurrentPhoto.getPath());
+ mCurrentPhoto.getPanoramaSupport(mUpdateShareURICallback);
}
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 {
@@ -721,22 +744,7 @@ public class PhotoPage extends ActivityState implements
supportedOperations &= ~MediaObject.SUPPORT_EDIT;
}
MenuExecutor.updateMenuOperation(menu, supportedOperations);
- if ((supportedOperations & MediaObject.SUPPORT_PANORAMA360) != 0) {
- mActivity.invalidateOptionsMenu();
- item = menu.findItem(R.id.action_share);
- if (item != null) {
- item.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
- item.setTitle(
- mActivity.getResources().getString(R.string.share_as_photo));
- }
- } else if ((supportedOperations & MediaObject.SUPPORT_SHARE) != 0) {
- item = menu.findItem(R.id.action_share);
- if (item != null) {
- item.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
- item.setTitle(
- mActivity.getResources().getString(R.string.share));
- }
- }
+ mCurrentPhoto.getPanoramaSupport(mUpdatePanoramaMenuItemsCallback);
}
private boolean canDoSlideShow() {
@@ -763,7 +771,7 @@ public class PhotoPage extends ActivityState implements
mActionBar.show();
mActivity.getGLRoot().setLightsOutMode(false);
refreshHidingMessage();
- if (mBottomControls != null) mBottomControls.refresh();
+ refreshBottomControlsWhenReady();
}
private void hideBars() {
@@ -772,7 +780,7 @@ public class PhotoPage extends ActivityState implements
mActionBar.hide();
mActivity.getGLRoot().setLightsOutMode(true);
mHandler.removeMessages(MSG_HIDE_BARS);
- if (mBottomControls != null) mBottomControls.refresh();
+ refreshBottomControlsWhenReady();
}
private void refreshHidingMessage() {
@@ -1283,9 +1291,7 @@ public class PhotoPage extends ActivityState implements
mPhotoView.pause();
mHandler.removeMessages(MSG_HIDE_BARS);
mHandler.removeMessages(MSG_REFRESH_BOTTOM_CONTROLS);
- if (mBottomControls != null) {
- mBottomControls.refresh();
- }
+ refreshBottomControlsWhenReady();
mActionBar.removeOnMenuVisibilityListener(mMenuVisibilityListener);
if (mShowSpinner) {
mActionBar.disableAlbumModeMenu(true);
@@ -1302,7 +1308,7 @@ public class PhotoPage extends ActivityState implements
@Override
public void onFilmModeChanged(boolean enabled) {
- mHandler.sendEmptyMessage(MSG_REFRESH_BOTTOM_CONTROLS);
+ refreshBottomControlsWhenReady();
if (enabled) {
mHandler.removeMessages(MSG_HIDE_BARS);
} else {
@@ -1362,9 +1368,7 @@ public class PhotoPage extends ActivityState implements
mActionBar.setDisplayOptions(
((mSecureAlbum == null) && (mSetPathString != null)), false);
mActionBar.addOnMenuVisibilityListener(mMenuVisibilityListener);
- if (mBottomControls != null) {
- mBottomControls.refresh();
- }
+ refreshBottomControlsWhenReady();
if (mShowSpinner) {
mActionBar.enableAlbumModeMenu(
GalleryActionBar.ALBUM_FILMSTRIP_MODE_SELECTED, this);
@@ -1427,4 +1431,42 @@ public class PhotoPage extends ActivityState implements
switchToGrid();
}
}
+
+ @Override
+ public void refreshBottomControlsWhenReady() {
+ if (mBottomControls == null) {
+ return;
+ }
+ MediaObject currentPhoto = mCurrentPhoto;
+ if (currentPhoto == null) {
+ mHandler.obtainMessage(MSG_REFRESH_BOTTOM_CONTROLS, 0, 0, currentPhoto).sendToTarget();
+ } else {
+ currentPhoto.getPanoramaSupport(mRefreshBottomControlsCallback);
+ }
+ }
+
+ private void updatePanoramaUI(boolean isPanorama360) {
+ Menu menu = mActionBar.getMenu();
+
+ // it could be null if onCreateActionBar has not been called yet
+ if (menu == null) {
+ return;
+ }
+
+ MenuExecutor.updateMenuForPanorama(menu, isPanorama360, isPanorama360);
+
+ if (isPanorama360) {
+ MenuItem item = menu.findItem(R.id.action_share);
+ if (item != null) {
+ item.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
+ item.setTitle(mActivity.getResources().getString(R.string.share_as_photo));
+ }
+ } else if ((mCurrentPhoto.getSupportedOperations() & MediaObject.SUPPORT_SHARE) != 0) {
+ MenuItem item = menu.findItem(R.id.action_share);
+ if (item != null) {
+ item.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
+ item.setTitle(mActivity.getResources().getString(R.string.share));
+ }
+ }
+ }
}
diff --git a/src/com/android/gallery3d/app/PhotoPageBottomControls.java b/src/com/android/gallery3d/app/PhotoPageBottomControls.java
index 40588e1..e58e454 100644
--- a/src/com/android/gallery3d/app/PhotoPageBottomControls.java
+++ b/src/com/android/gallery3d/app/PhotoPageBottomControls.java
@@ -23,7 +23,6 @@ import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
-import android.view.animation.ScaleAnimation;
import android.widget.RelativeLayout;
import com.android.gallery3d.R;
@@ -34,8 +33,9 @@ import java.util.Map;
public class PhotoPageBottomControls implements OnClickListener {
public interface Delegate {
public boolean canDisplayBottomControls();
- public boolean canDisplayBottomControl(int control);
+ public boolean canDisplayBottomControl(int control, boolean isPanorama);
public void onBottomControlClicked(int control);
+ public void refreshBottomControlsWhenReady();
}
private Delegate mDelegate;
@@ -76,7 +76,7 @@ public class PhotoPageBottomControls implements OnClickListener {
mContainerAnimIn.setDuration(CONTAINER_ANIM_DURATION_MS);
mContainerAnimOut.setDuration(CONTAINER_ANIM_DURATION_MS);
- refresh();
+ mDelegate.refreshBottomControlsWhenReady();
}
private void hide() {
@@ -93,7 +93,7 @@ public class PhotoPageBottomControls implements OnClickListener {
mContainer.setVisibility(View.VISIBLE);
}
- public void refresh() {
+ public void refresh(boolean isPanorama) {
boolean visible = mDelegate.canDisplayBottomControls();
boolean containerVisibilityChanged = (visible != mContainerVisible);
if (containerVisibilityChanged) {
@@ -109,7 +109,7 @@ public class PhotoPageBottomControls implements OnClickListener {
}
for (View control : mControlsVisible.keySet()) {
Boolean prevVisibility = mControlsVisible.get(control);
- boolean curVisibility = mDelegate.canDisplayBottomControl(control.getId());
+ boolean curVisibility = mDelegate.canDisplayBottomControl(control.getId(), isPanorama);
if (prevVisibility.booleanValue() != curVisibility) {
if (!containerVisibilityChanged) {
control.clearAnimation();
diff --git a/src/com/android/gallery3d/data/DataManager.java b/src/com/android/gallery3d/data/DataManager.java
index 408a24b..4ec7b6d 100644
--- a/src/com/android/gallery3d/data/DataManager.java
+++ b/src/com/android/gallery3d/data/DataManager.java
@@ -16,15 +16,15 @@
package com.android.gallery3d.data;
-import android.content.Intent;
import android.database.ContentObserver;
import android.net.Uri;
import android.os.Handler;
-import android.support.v4.content.LocalBroadcastManager;
import com.android.gallery3d.app.GalleryApp;
+import com.android.gallery3d.app.StitchingChangeListener;
import com.android.gallery3d.common.ApiHelper;
import com.android.gallery3d.common.Utils;
+import com.android.gallery3d.data.MediaObject.PanoramaSupportCallback;
import com.android.gallery3d.data.MediaSet.ItemConsumer;
import com.android.gallery3d.data.MediaSource.PathId;
import com.android.gallery3d.picasasource.PicasaSource;
@@ -49,7 +49,7 @@ import java.util.WeakHashMap;
// path. And it's used to identify a specific media set even if the process is
// killed and re-created, so child keys should be stable identifiers.
-public class DataManager {
+public class DataManager implements StitchingChangeListener {
public static final int INCLUDE_IMAGE = 1;
public static final int INCLUDE_VIDEO = 2;
public static final int INCLUDE_ALL = INCLUDE_IMAGE | INCLUDE_VIDEO;
@@ -247,10 +247,8 @@ 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 getPanoramaSupport(Path path, PanoramaSupportCallback callback) {
+ getMediaObject(path).getPanoramaSupport(callback);
}
public void delete(Path path) {
@@ -351,4 +349,25 @@ public class DataManager {
}
}
}
+
+ @Override
+ public void onStitchingQueued(Uri uri) {
+ // Do nothing.
+ }
+
+ @Override
+ public void onStitchingResult(Uri uri) {
+ Path path = findPathByUri(uri, null);
+ if (path != null) {
+ MediaObject mediaObject = getMediaObject(path);
+ if (mediaObject != null) {
+ mediaObject.clearCachedPanoramaSupport();
+ }
+ }
+ }
+
+ @Override
+ public void onStitchingProgress(Uri uri, int progress) {
+ // Do nothing.
+ }
}
diff --git a/src/com/android/gallery3d/data/LocalImage.java b/src/com/android/gallery3d/data/LocalImage.java
index 61961d8..b2be124 100644
--- a/src/com/android/gallery3d/data/LocalImage.java
+++ b/src/com/android/gallery3d/data/LocalImage.java
@@ -19,7 +19,6 @@ package com.android.gallery3d.data;
import android.annotation.TargetApi;
import android.content.ContentResolver;
import android.content.ContentValues;
-import android.content.Context;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
@@ -33,14 +32,11 @@ import android.provider.MediaStore.MediaColumns;
import android.util.Log;
import com.android.gallery3d.app.GalleryApp;
+import com.android.gallery3d.app.PanoramaMetadataSupport;
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;
@@ -104,11 +100,7 @@ public class LocalImage extends LocalMediaItem {
public int rotation;
- private Object mLock = new Object();
- private Future<PanoramaMetadata> mGetPanoMetadataTask;
- private boolean mPanoramaMetadataInitialized;
- private PanoramaMetadata mPanoramaMetadata;
- private SupportedOperationsListener mListener;
+ private PanoramaMetadataSupport mPanoramaMetadata = new PanoramaMetadataSupport(this);
public LocalImage(Path path, GalleryApp application, Cursor cursor) {
super(path, nextVersionNumber());
@@ -257,63 +249,17 @@ public class LocalImage extends LocalMediaItem {
if (GalleryUtils.isValidLocation(latitude, longitude)) {
operation |= SUPPORT_SHOW_ON_MAP;
}
-
- if (mPanoramaMetadata != null && mPanoramaMetadata.mUsePanoramaViewer) {
- operation |= SUPPORT_PANORAMA;
- if (mPanoramaMetadata.mIsPanorama360) {
- operation |= SUPPORT_PANORAMA360;
- // disable destructive rotate and crop for 360 degree panorama
- operation &= ~(SUPPORT_ROTATE | SUPPORT_CROP);
- }
- }
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();
+ public void getPanoramaSupport(PanoramaSupportCallback callback) {
+ mPanoramaMetadata.getPanoramaSupport(mApplication, callback);
}
@Override
- public void setSupportedOperationsListener(SupportedOperationsListener l) {
- synchronized (mLock) {
- 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(LocalImage.this, getSupportedOperations());
- }
- }
- });
- }
- }
- mListener = l;
- }
+ public void clearCachedPanoramaSupport() {
+ mPanoramaMetadata.clearCachedValues();
}
@Override
diff --git a/src/com/android/gallery3d/data/MediaObject.java b/src/com/android/gallery3d/data/MediaObject.java
index 14cd524..a41b275 100644
--- a/src/com/android/gallery3d/data/MediaObject.java
+++ b/src/com/android/gallery3d/data/MediaObject.java
@@ -18,8 +18,6 @@ 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";
@@ -43,16 +41,8 @@ public abstract class MediaObject {
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(MediaObject item, 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;
@@ -80,9 +70,9 @@ public abstract class MediaObject {
protected final Path mPath;
- private static ThreadPool sThreadPool = new ThreadPool(1, 1);
- public static ThreadPool getThreadPool() {
- return sThreadPool;
+ public interface PanoramaSupportCallback {
+ void panoramaInfoAvailable(MediaObject mediaObject, boolean isPanorama,
+ boolean isPanorama360);
}
public MediaObject(Path path, long version) {
@@ -99,12 +89,11 @@ public abstract class MediaObject {
return 0;
}
- public int getSupportedOperations(boolean getAll) {
- return getSupportedOperations();
+ public void getPanoramaSupport(PanoramaSupportCallback callback) {
+ callback.panoramaInfoAvailable(this, false, false);
}
- public void setSupportedOperationsListener(SupportedOperationsListener l) {
- // nothing to do
+ public void clearCachedPanoramaSupport() {
}
public void delete() {
diff --git a/src/com/android/gallery3d/data/PanoramaMetadataJob.java b/src/com/android/gallery3d/data/PanoramaMetadataJob.java
index e0a69c4..ab99d6a 100644
--- a/src/com/android/gallery3d/data/PanoramaMetadataJob.java
+++ b/src/com/android/gallery3d/data/PanoramaMetadataJob.java
@@ -24,7 +24,7 @@ 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> {
+public class PanoramaMetadataJob implements Job<PanoramaMetadata> {
Context mContext;
Uri mUri;
diff --git a/src/com/android/gallery3d/data/UriImage.java b/src/com/android/gallery3d/data/UriImage.java
index aaa36a9..e8875b5 100644
--- a/src/com/android/gallery3d/data/UriImage.java
+++ b/src/com/android/gallery3d/data/UriImage.java
@@ -17,7 +17,6 @@
package com.android.gallery3d.data;
import android.content.ContentResolver;
-import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.BitmapFactory.Options;
@@ -26,12 +25,9 @@ import android.net.Uri;
import android.os.ParcelFileDescriptor;
import com.android.gallery3d.app.GalleryApp;
+import com.android.gallery3d.app.PanoramaMetadataSupport;
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;
@@ -59,12 +55,7 @@ public class UriImage extends MediaItem {
private int mWidth;
private int mHeight;
private int mRotation;
-
- private Object mLock = new Object();
- private Future<PanoramaMetadata> mGetPanoMetadataTask;
- private boolean mPanoramaMetadataInitialized;
- private PanoramaMetadata mPanoramaMetadata;
- private SupportedOperationsListener mListener;
+ private PanoramaMetadataSupport mPanoramaMetadata = new PanoramaMetadataSupport(this);
private GalleryApp mApplication;
@@ -225,62 +216,17 @@ public class UriImage extends MediaItem {
if (BitmapUtils.isSupportedByRegionDecoder(mContentType)) {
supported |= SUPPORT_FULL_IMAGE;
}
- if (mPanoramaMetadata != null && mPanoramaMetadata.mUsePanoramaViewer) {
- supported |= SUPPORT_PANORAMA;
- if (mPanoramaMetadata.mIsPanorama360) {
- supported |= SUPPORT_PANORAMA360;
- // disable destructive crop for 360 degree panorama
- supported &= ~SUPPORT_CROP;
- }
- }
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();
+ public void getPanoramaSupport(PanoramaSupportCallback callback) {
+ mPanoramaMetadata.getPanoramaSupport(mApplication, callback);
}
@Override
- public void setSupportedOperationsListener(SupportedOperationsListener l) {
- synchronized (mLock) {
- 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(UriImage.this, getSupportedOperations());
- }
- }
- });
- }
- }
- mListener = l;
- }
+ public void clearCachedPanoramaSupport() {
+ mPanoramaMetadata.clearCachedValues();
}
private boolean isSharable() {
diff --git a/src/com/android/gallery3d/ui/ActionModeHandler.java b/src/com/android/gallery3d/ui/ActionModeHandler.java
index 3384b88..bb195c9 100644
--- a/src/com/android/gallery3d/ui/ActionModeHandler.java
+++ b/src/com/android/gallery3d/ui/ActionModeHandler.java
@@ -38,6 +38,7 @@ import com.android.gallery3d.common.ApiHelper;
import com.android.gallery3d.common.Utils;
import com.android.gallery3d.data.DataManager;
import com.android.gallery3d.data.MediaObject;
+import com.android.gallery3d.data.MediaObject.PanoramaSupportCallback;
import com.android.gallery3d.data.Path;
import com.android.gallery3d.ui.MenuExecutor.ProgressListener;
import com.android.gallery3d.util.Future;
@@ -54,8 +55,7 @@ public class ActionModeHandler implements Callback, PopupList.OnPopupItemClickLi
private static final int SUPPORT_MULTIPLE_MASK = MediaObject.SUPPORT_DELETE
| MediaObject.SUPPORT_ROTATE | MediaObject.SUPPORT_SHARE
- | MediaObject.SUPPORT_CACHE | MediaObject.SUPPORT_IMPORT
- | MediaObject.SUPPORT_PANORAMA | MediaObject.SUPPORT_PANORAMA360;
+ | MediaObject.SUPPORT_CACHE | MediaObject.SUPPORT_IMPORT;
public interface ActionModeListener {
public boolean onActionItemClicked(MenuItem item);
@@ -76,6 +76,49 @@ public class ActionModeHandler implements Callback, PopupList.OnPopupItemClickLi
private final Handler mMainHandler;
private ActionMode mActionMode;
+ private static class GetAllPanoramaSupports implements PanoramaSupportCallback {
+ private int mNumInfoRequired;
+ private JobContext mJobContext;
+ public boolean mAllPanoramas = true;
+ public boolean mAllPanorama360 = true;
+ public boolean mHasPanorama360 = false;
+ private Object mLock = new Object();
+
+ public GetAllPanoramaSupports(ArrayList<MediaObject> mediaObjects, JobContext jc) {
+ mJobContext = jc;
+ mNumInfoRequired = mediaObjects.size();
+ for (MediaObject mediaObject : mediaObjects) {
+ mediaObject.getPanoramaSupport(this);
+ }
+ }
+
+ @Override
+ public void panoramaInfoAvailable(MediaObject mediaObject, boolean isPanorama,
+ boolean isPanorama360) {
+ synchronized (mLock) {
+ mNumInfoRequired--;
+ mAllPanoramas = isPanorama && mAllPanoramas;
+ mAllPanorama360 = isPanorama360 && mAllPanorama360;
+ mHasPanorama360 = mHasPanorama360 || isPanorama360;
+ if (mNumInfoRequired == 0 || mJobContext.isCancelled()) {
+ mLock.notifyAll();
+ }
+ }
+ }
+
+ public void waitForPanoramaSupport() {
+ synchronized (mLock) {
+ while (mNumInfoRequired != 0 && !mJobContext.isCancelled()) {
+ try {
+ mLock.wait();
+ } catch (InterruptedException e) {
+ // May be a cancelled job context
+ }
+ }
+ }
+ }
+ }
+
public ActionModeHandler(
AbstractGalleryActivity activity, SelectionManager selectionManager) {
mActivity = Utils.checkNotNull(activity);
@@ -211,28 +254,38 @@ public class ActionModeHandler implements Callback, PopupList.OnPopupItemClickLi
mSelectionManager.leaveSelectionMode();
}
- // Menu options are determined by selection set itself.
- // We cannot expand it because MenuExecuter executes it based on
- // the selection set instead of the expanded result.
- // e.g. LocalImage can be rotated but collections of them (LocalAlbum) can't.
- private int computeMenuOptions(JobContext jc) {
+ private ArrayList<MediaObject> getSelectedMediaObjects(JobContext jc) {
ArrayList<Path> unexpandedPaths = mSelectionManager.getSelected(false);
if (unexpandedPaths.isEmpty()) {
// This happens when starting selection mode from overflow menu
// (instead of long press a media object)
- return 0;
+ return null;
}
- int operation = MediaObject.SUPPORT_ALL;
+ ArrayList<MediaObject> selected = new ArrayList<MediaObject>();
DataManager manager = mActivity.getDataManager();
- int type = 0;
for (Path path : unexpandedPaths) {
- if (jc.isCancelled()) return 0;
- int support = manager.getSupportedOperations(path, true);
- type |= manager.getMediaType(path);
+ if (jc.isCancelled()) {
+ return null;
+ }
+ selected.add(manager.getMediaObject(path));
+ }
+
+ return selected;
+ }
+ // Menu options are determined by selection set itself.
+ // We cannot expand it because MenuExecuter executes it based on
+ // the selection set instead of the expanded result.
+ // e.g. LocalImage can be rotated but collections of them (LocalAlbum) can't.
+ private int computeMenuOptions(ArrayList<MediaObject> selected) {
+ int operation = MediaObject.SUPPORT_ALL;
+ int type = 0;
+ for (MediaObject mediaObject: selected) {
+ int support = mediaObject.getSupportedOperations();
+ type |= mediaObject.getMediaType();
operation &= support;
}
- switch (unexpandedPaths.size()) {
+ switch (selected.size()) {
case 1:
final String mimeType = MenuExecutor.getMimeType(type);
if (!GalleryUtils.isEditorAvailable(mActivity, mimeType)) {
@@ -298,7 +351,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, true);
+ int support = manager.getSupportedOperations(path);
type |= manager.getMediaType(path);
if ((support & MediaObject.SUPPORT_SHARE) != 0) {
@@ -346,21 +399,36 @@ public class ActionModeHandler implements Callback, PopupList.OnPopupItemClickLi
@Override
public Void run(final JobContext jc) {
// Pass1: Deal with unexpanded media object list for menu operation.
- final int operation = computeMenuOptions(jc);
+ ArrayList<MediaObject> selected = getSelectedMediaObjects(jc);
+ if (selected == null) {
+ return null;
+ }
+ final int operation = computeMenuOptions(selected);
+ if (jc.isCancelled()) {
+ return null;
+ }
+ final GetAllPanoramaSupports supportCallback = new GetAllPanoramaSupports(selected,
+ jc);
// Pass2: Deal with expanded media object list for sharing operation.
final Intent share_panorama_intent = computePanoramaSharingIntent(jc);
final Intent share_intent = computeSharingIntent(jc);
+
+ supportCallback.waitForPanoramaSupport();
+ if (jc.isCancelled()) {
+ return null;
+ }
mMainHandler.post(new Runnable() {
@Override
public void run() {
mMenuTask = null;
if (jc.isCancelled()) return;
MenuExecutor.updateMenuOperation(mMenu, operation);
+ MenuExecutor.updateMenuForPanorama(mMenu, supportCallback.mAllPanorama360,
+ supportCallback.mHasPanorama360);
if (mSharePanoramaMenuItem != null) {
mSharePanoramaMenuItem.setEnabled(true);
- if ((operation & MediaObject.SUPPORT_PANORAMA360) != 0) {
- mActivity.invalidateOptionsMenu();
+ if (supportCallback.mAllPanorama360) {
mShareMenuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
mShareMenuItem.setTitle(
mActivity.getResources().getString(R.string.share_as_photo));
diff --git a/src/com/android/gallery3d/ui/MenuExecutor.java b/src/com/android/gallery3d/ui/MenuExecutor.java
index cacf6fc..1d110be 100644
--- a/src/com/android/gallery3d/ui/MenuExecutor.java
+++ b/src/com/android/gallery3d/ui/MenuExecutor.java
@@ -160,7 +160,6 @@ public class MenuExecutor {
boolean supportRotate = (supported & MediaObject.SUPPORT_ROTATE) != 0;
boolean supportCrop = (supported & MediaObject.SUPPORT_CROP) != 0;
boolean supportTrim = (supported & MediaObject.SUPPORT_TRIM) != 0;
- boolean supportSharePanorama = (supported & MediaObject.SUPPORT_PANORAMA360) != 0;
boolean supportShare = (supported & MediaObject.SUPPORT_SHARE) != 0;
boolean supportSetAs = (supported & MediaObject.SUPPORT_SETAS) != 0;
boolean supportShowOnMap = (supported & MediaObject.SUPPORT_SHOW_ON_MAP) != 0;
@@ -174,7 +173,8 @@ public class MenuExecutor {
setMenuItemVisible(menu, R.id.action_rotate_cw, supportRotate);
setMenuItemVisible(menu, R.id.action_crop, supportCrop);
setMenuItemVisible(menu, R.id.action_trim, supportTrim);
- setMenuItemVisible(menu, R.id.action_share_panorama, supportSharePanorama);
+ // Hide panorama until call to updateMenuForPanorama corrects it
+ setMenuItemVisible(menu, R.id.action_share_panorama, false);
setMenuItemVisible(menu, R.id.action_share, supportShare);
setMenuItemVisible(menu, R.id.action_setas, supportSetAs);
setMenuItemVisible(menu, R.id.action_show_on_map, supportShowOnMap);
@@ -183,6 +183,16 @@ public class MenuExecutor {
setMenuItemVisible(menu, R.id.action_import, supportImport);
}
+ public static void updateMenuForPanorama(Menu menu, boolean shareAsPanorama360,
+ boolean disablePanorama360Options) {
+ setMenuItemVisible(menu, R.id.action_share_panorama, shareAsPanorama360);
+ if (disablePanorama360Options) {
+ setMenuItemVisible(menu, R.id.action_rotate_ccw, false);
+ setMenuItemVisible(menu, R.id.action_rotate_cw, false);
+ setMenuItemVisible(menu, R.id.action_crop, false);
+ }
+ }
+
private static void setMenuItemVisible(Menu menu, int itemId, boolean visible) {
MenuItem item = menu.findItem(itemId);
if (item != null) item.setVisible(visible);
diff --git a/src_pd/com/android/gallery3d/util/LightCycleHelper.java b/src_pd/com/android/gallery3d/util/LightCycleHelper.java
index 6ebd9ec..68c0195 100644
--- a/src_pd/com/android/gallery3d/util/LightCycleHelper.java
+++ b/src_pd/com/android/gallery3d/util/LightCycleHelper.java
@@ -61,6 +61,8 @@ public class LightCycleHelper {
}
}
+ private static final PanoramaMetadata NOT_PANORAMA = new PanoramaMetadata(false, false);
+
public static void setupCaptureIntent(Context context, Intent it, String outputDir) {
/* Do nothing */
}
@@ -70,7 +72,7 @@ public class LightCycleHelper {
}
public static PanoramaMetadata getPanoramaMetadata(Context context, Uri uri) {
- return null;
+ return NOT_PANORAMA;
}
public static CameraModule createPanoramaModule() {