| /** |
| * Copyright (C) 2010 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 android.annotation.TargetApi; |
| import android.app.ActionBar.OnMenuVisibilityListener; |
| import android.app.Activity; |
| import android.content.ActivityNotFoundException; |
| import android.content.Context; |
| import android.content.Intent; |
| import android.content.pm.PackageManager; |
| import android.content.pm.ResolveInfo; |
| import android.content.res.Configuration; |
| import android.graphics.Rect; |
| import android.net.Uri; |
| import android.nfc.NfcAdapter; |
| import android.nfc.NfcAdapter.CreateBeamUrisCallback; |
| import android.nfc.NfcEvent; |
| import android.os.AsyncTask; |
| import android.os.Bundle; |
| import android.os.Handler; |
| import android.os.Message; |
| import android.os.SystemClock; |
| import android.text.TextUtils; |
| import android.view.Menu; |
| import android.view.MenuItem; |
| import android.view.View; |
| import android.view.ViewGroup; |
| import android.view.Window; |
| import android.view.WindowManager; |
| import android.widget.RelativeLayout; |
| import android.widget.Toast; |
| import android.widget.Toolbar; |
| |
| import org.codeaurora.gallery.R; |
| |
| import com.android.gallery3d.app.dualcam3d.ThreeDimensionalActivity; |
| import com.android.gallery3d.common.ApiHelper; |
| import com.android.gallery3d.data.ComboAlbum; |
| import com.android.gallery3d.data.DataManager; |
| import com.android.gallery3d.data.FilterDeleteSet; |
| import com.android.gallery3d.data.FilterSource; |
| import com.android.gallery3d.data.MediaDetails; |
| import com.android.gallery3d.data.MediaItem; |
| import com.android.gallery3d.data.MediaObject; |
| import com.android.gallery3d.data.MediaObject.PanoramaSupportCallback; |
| import com.android.gallery3d.data.MediaSet; |
| import com.android.gallery3d.data.Path; |
| import com.android.gallery3d.data.SecureAlbum; |
| import com.android.gallery3d.data.SecureSource; |
| import com.android.gallery3d.data.SnailAlbum; |
| import com.android.gallery3d.data.SnailItem; |
| import com.android.gallery3d.data.SnailSource; |
| import com.android.gallery3d.filtershow.FilterShowActivity; |
| import com.android.gallery3d.filtershow.imageshow.MasterImage; |
| import com.android.gallery3d.filtershow.tools.DualCameraEffect; |
| import com.android.gallery3d.ui.DetailsHelper; |
| import com.android.gallery3d.ui.DetailsHelper.CloseListener; |
| import com.android.gallery3d.ui.DetailsHelper.DetailsSource; |
| import com.android.gallery3d.ui.GLRootView; |
| import com.android.gallery3d.ui.GLView; |
| import com.android.gallery3d.ui.MenuExecutor; |
| import com.android.gallery3d.ui.PhotoView; |
| import com.android.gallery3d.ui.SelectionManager; |
| import com.android.gallery3d.ui.SynchronizedHandler; |
| import com.android.gallery3d.util.GDepth; |
| import com.android.gallery3d.util.GalleryUtils; |
| import com.android.gallery3d.util.UsageStatistics; |
| import com.android.gallery3d.util.ViewGifImage; |
| |
| import java.util.List; |
| import java.util.Locale; |
| |
| //import android.drm.DrmHelper; |
| |
| public abstract class PhotoPage extends ActivityState implements |
| PhotoView.Listener, AppBridge.Server, PhotoPageBottomControls.Delegate, |
| ThreeDButton.Delegate { |
| private static final String TAG = "PhotoPage"; |
| |
| private static final int MSG_HIDE_BARS = 1; |
| private static final int MSG_ON_FULL_SCREEN_CHANGED = 4; |
| private static final int MSG_UPDATE_ACTION_BAR = 5; |
| private static final int MSG_WANT_BARS = 7; |
| private static final int MSG_REFRESH_BOTTOM_CONTROLS = 8; |
| private static final int MSG_ON_CAMERA_CENTER = 9; |
| private static final int MSG_ON_PICTURE_CENTER = 10; |
| private static final int MSG_REFRESH_IMAGE = 11; |
| private static final int MSG_UPDATE_PHOTO_UI = 12; |
| 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 REQUEST_SLIDESHOW = 1; |
| private static final int REQUEST_CROP = 2; |
| private static final int REQUEST_CROP_PICASA = 3; |
| private static final int REQUEST_EDIT = 4; |
| private static final int REQUEST_PLAY_VIDEO = 5; |
| private static final int REQUEST_TRIM = 6; |
| |
| public static final String KEY_MEDIA_SET_PATH = "media-set-path"; |
| public static final String KEY_MEDIA_ITEM_PATH = "media-item-path"; |
| public static final String KEY_INDEX_HINT = "index-hint"; |
| public static final String KEY_CURRENT_PHOTO_HINT = "currtent_photo_path"; |
| public static final String KEY_OPEN_ANIMATION_RECT = "open-animation-rect"; |
| public static final String KEY_APP_BRIDGE = "app-bridge"; |
| public static final String KEY_TREAT_BACK_AS_UP = "treat-back-as-up"; |
| public static final String KEY_START_IN_FILMSTRIP = "start-in-filmstrip"; |
| public static final String KEY_RETURN_INDEX_HINT = "return-index-hint"; |
| public static final String KEY_SHOW_WHEN_LOCKED = "show_when_locked"; |
| public static final String KEY_IN_CAMERA_ROLL = "in_camera_roll"; |
| public static final String KEY_READONLY = "read-only"; |
| public static final String KEY_FROM_VIDEOS_SCREEN = "from-video-screen"; |
| public static final String KEY_FROM_TIMELINE_SCREEN = "from-timeline-screen"; |
| |
| // Bundle key, used for checking whether it is from widget |
| public static final String KEY_IS_FROM_WIDGET = "is_from_widget"; |
| |
| public static final String KEY_ALBUMPAGE_TRANSITION = "albumpage-transition"; |
| public static final int MSG_ALBUMPAGE_NONE = 0; |
| public static final int MSG_ALBUMPAGE_STARTED = 1; |
| public static final int MSG_ALBUMPAGE_RESUMED = 2; |
| public static final int MSG_ALBUMPAGE_PICKED = 4; |
| |
| public static final String ACTION_NEXTGEN_EDIT = "action_nextgen_edit"; |
| public static final String ACTION_SIMPLE_EDIT = "action_simple_edit"; |
| |
| private GalleryApp mApplication; |
| private SelectionManager mSelectionManager; |
| |
| private PhotoView mPhotoView; |
| private PhotoPage.Model mModel; |
| private DetailsHelper mDetailsHelper; |
| private boolean mShowDetails; |
| |
| // mMediaSet could be null if there is no KEY_MEDIA_SET_PATH supplied. |
| // E.g., viewing a photo in gmail attachment |
| private FilterDeleteSet mMediaSet; |
| |
| // The mediaset used by camera launched from secure lock screen. |
| private SecureAlbum mSecureAlbum; |
| |
| private int mCurrentIndex = 0; |
| private Handler mHandler; |
| private boolean mShowBars = true; |
| private volatile boolean mActionBarAllowed = true; |
| private GalleryActionBar mActionBar; |
| private boolean mIsMenuVisible; |
| private boolean mHaveImageEditor; |
| private PhotoPageBottomControls mBottomControls; |
| private MediaItem mCurrentPhoto = null; |
| private MenuExecutor mMenuExecutor; |
| private boolean mIsActive; |
| private boolean mShowSpinner; |
| private String mSetPathString; |
| // This is the original mSetPathString before adding the camera preview item. |
| private boolean mReadOnlyView = false; |
| private String mOriginalSetPathString; |
| private AppBridge mAppBridge; |
| private SnailItem mScreenNailItem; |
| private SnailAlbum mScreenNailSet; |
| private OrientationManager mOrientationManager; |
| private boolean mTreatBackAsUp; |
| // Used for checking whether it is from widget |
| private boolean mIsFromWidget; |
| private boolean mStartInFilmstrip; |
| private boolean mHasCameraScreennailOrPlaceholder = false; |
| private boolean mRecenterCameraOnResume = true; |
| private boolean mIsFromVideoScreen; |
| private boolean mIsFromTimelineScreen; |
| |
| // These are only valid after the panorama callback |
| private boolean mIsPanorama; |
| private boolean mIsPanorama360; |
| |
| private long mCameraSwitchCutoff = 0; |
| private boolean mSkipUpdateCurrentPhoto = false; |
| private static final long CAMERA_SWITCH_CUTOFF_THRESHOLD_MS = 300; |
| |
| private static final long DEFERRED_UPDATE_MS = 250; |
| private boolean mDeferredUpdateWaiting = false; |
| private long mDeferUpdateUntil = Long.MAX_VALUE; |
| |
| // The item that is deleted (but it can still be undeleted before commiting) |
| private Path mDeletePath; |
| private boolean mDeleteIsFocus; // whether the deleted item was in focus |
| |
| private Uri[] mNfcPushUris = new Uri[1]; |
| |
| private final MyMenuVisibilityListener mMenuVisibilityListener = |
| new MyMenuVisibilityListener(); |
| |
| private int mLastSystemUiVis = 0; |
| |
| private Intent mShareIntent; |
| |
| //use for saving the original height and padding of toolbar |
| private int originalHeight = 0; |
| private int originalPadding = 0; |
| |
| private ThreeDButton m3DButton; |
| private boolean bShow3DButton; |
| private ParseGDepthTask mParseGDepthTask; |
| |
| 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, isPanorama360 ? 1 : 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(); |
| public void pause(); |
| public boolean isEmpty(); |
| public void setCurrentPhoto(Path path, int indexHint); |
| } |
| |
| private class MyMenuVisibilityListener implements OnMenuVisibilityListener { |
| @Override |
| public void onMenuVisibilityChanged(boolean isVisible) { |
| mIsMenuVisible = isVisible; |
| refreshHidingMessage(); |
| } |
| } |
| |
| @Override |
| protected int getBackgroundColorId() { |
| return R.color.photo_background; |
| } |
| |
| private final GLView mRootPane = new GLView() { |
| @Override |
| protected void onLayout( |
| boolean changed, int left, int top, int right, int bottom) { |
| mPhotoView.layout(0, 0, right - left, bottom - top); |
| if (mShowDetails) { |
| mDetailsHelper.layout(left, mActionBar.getHeight(), right, bottom); |
| } |
| } |
| }; |
| |
| @Override |
| public void onCreate(Bundle data, Bundle restoreState) { |
| super.onCreate(data, restoreState); |
| mActionBar = mActivity.getGalleryActionBar(); |
| mActionBar.setBackGroundTransparent(); |
| mSelectionManager = new SelectionManager(mActivity, false); |
| mMenuExecutor = new MenuExecutor(mActivity, mSelectionManager); |
| |
| mPhotoView = new PhotoView(mActivity); |
| mPhotoView.setListener(this); |
| mRootPane.addComponent(mPhotoView); |
| mApplication = (GalleryApp) ((Activity) mActivity).getApplication(); |
| mOrientationManager = mActivity.getOrientationManager(); |
| mActivity.getGLRoot().setOrientationSource(mOrientationManager); |
| mIsFromVideoScreen = data.getBoolean(KEY_FROM_VIDEOS_SCREEN, false); |
| mIsFromTimelineScreen = data.getBoolean(KEY_FROM_TIMELINE_SCREEN, false); |
| |
| mHandler = new SynchronizedHandler(mActivity.getGLRoot()) { |
| @Override |
| public void handleMessage(Message message) { |
| switch (message.what) { |
| case MSG_HIDE_BARS: { |
| hideBars(); |
| break; |
| } |
| case MSG_REFRESH_BOTTOM_CONTROLS: { |
| if (mCurrentPhoto == message.obj && mBottomControls != null) { |
| mIsPanorama = message.arg1 == 1; |
| mIsPanorama360 = message.arg2 == 1; |
| mBottomControls.refresh(); |
| if (null != m3DButton) { |
| m3DButton.refresh(); |
| } |
| } |
| break; |
| } |
| case MSG_ON_FULL_SCREEN_CHANGED: { |
| if (mAppBridge != null) { |
| mAppBridge.onFullScreenChanged(message.arg1 == 1); |
| } |
| break; |
| } |
| case MSG_UPDATE_ACTION_BAR: { |
| updateBars(); |
| break; |
| } |
| case MSG_WANT_BARS: { |
| wantBars(); |
| break; |
| } |
| case MSG_UPDATE_DEFERRED: { |
| long nextUpdate = mDeferUpdateUntil - SystemClock.uptimeMillis(); |
| if (nextUpdate <= 0) { |
| mDeferredUpdateWaiting = false; |
| updateUIForCurrentPhoto(); |
| } else { |
| mHandler.sendEmptyMessageDelayed(MSG_UPDATE_DEFERRED, nextUpdate); |
| } |
| break; |
| } |
| case MSG_ON_CAMERA_CENTER: { |
| mSkipUpdateCurrentPhoto = false; |
| boolean stayedOnCamera = false; |
| if (!mPhotoView.getFilmMode()) { |
| stayedOnCamera = true; |
| } else if (SystemClock.uptimeMillis() < mCameraSwitchCutoff && |
| mMediaSet.getMediaItemCount() > 1) { |
| mPhotoView.switchToImage(1); |
| } else { |
| if (mAppBridge != null) mPhotoView.setFilmMode(false); |
| stayedOnCamera = true; |
| } |
| |
| if (stayedOnCamera) { |
| if (mAppBridge == null && mMediaSet.getTotalMediaItemCount() > 1) { |
| launchCamera(); |
| /* We got here by swiping from photo 1 to the |
| placeholder, so make it be the thing that |
| is in focus when the user presses back from |
| the camera app */ |
| mPhotoView.switchToImage(1); |
| } else { |
| updateBars(); |
| updateCurrentPhoto(mModel.getMediaItem(0)); |
| } |
| } |
| break; |
| } |
| case MSG_ON_PICTURE_CENTER: { |
| if (!mPhotoView.getFilmMode() && mCurrentPhoto != null |
| && (mCurrentPhoto.getSupportedOperations() & MediaObject.SUPPORT_ACTION) != 0) { |
| mPhotoView.setFilmMode(true); |
| } |
| break; |
| } |
| case MSG_REFRESH_IMAGE: { |
| final MediaItem photo = mCurrentPhoto; |
| mCurrentPhoto = null; |
| updateCurrentPhoto(photo); |
| break; |
| } |
| case MSG_UPDATE_PHOTO_UI: { |
| updateUIForCurrentPhoto(); |
| 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); |
| if (shareIntent != null) { |
| 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); |
| } |
| } |
| }; |
| |
| mSetPathString = data.getString(KEY_MEDIA_SET_PATH); |
| mReadOnlyView = data.getBoolean(KEY_READONLY); |
| mOriginalSetPathString = mSetPathString; |
| setupNfcBeamPush(); |
| String itemPathString = data.getString(KEY_MEDIA_ITEM_PATH); |
| Path itemPath = itemPathString != null ? |
| Path.fromString(data.getString(KEY_MEDIA_ITEM_PATH)) : |
| null; |
| mTreatBackAsUp = data.getBoolean(KEY_TREAT_BACK_AS_UP, false); |
| mIsFromWidget = data.getBoolean(KEY_IS_FROM_WIDGET, false); |
| mStartInFilmstrip = data.getBoolean(KEY_START_IN_FILMSTRIP, false); |
| boolean inCameraRoll = data.getBoolean(KEY_IN_CAMERA_ROLL, false); |
| if (restoreState == null || mSetPathString == null) { |
| mCurrentIndex = data.getInt(KEY_INDEX_HINT, 0); |
| } else { |
| mCurrentIndex = restoreState.getInt(KEY_INDEX_HINT, 0); |
| //we only save index in onSaveState, set itemPath to null to get the right path later |
| itemPath = null; |
| } |
| if ((mCurrentPhoto == null) && (restoreState != null)) { |
| String curPath = restoreState.getString(KEY_CURRENT_PHOTO_HINT, null); |
| if (curPath != null) |
| mCurrentPhoto = (MediaItem) |
| mActivity.getDataManager().getMediaObject(curPath); |
| } |
| if (mSetPathString != null) { |
| mShowSpinner = true; |
| mAppBridge = (AppBridge) data.getParcelable(KEY_APP_BRIDGE); |
| if (mAppBridge != null) { |
| mShowBars = false; |
| mHasCameraScreennailOrPlaceholder = true; |
| mAppBridge.setServer(this); |
| |
| // Get the ScreenNail from AppBridge and register it. |
| int id = SnailSource.newId(); |
| Path screenNailSetPath = SnailSource.getSetPath(id); |
| Path screenNailItemPath = SnailSource.getItemPath(id); |
| mScreenNailSet = (SnailAlbum) mActivity.getDataManager() |
| .getMediaObject(screenNailSetPath); |
| mScreenNailItem = (SnailItem) mActivity.getDataManager() |
| .getMediaObject(screenNailItemPath); |
| mScreenNailItem.setScreenNail(mAppBridge.attachScreenNail()); |
| |
| if (data.getBoolean(KEY_SHOW_WHEN_LOCKED, false)) { |
| // Set the flag to be on top of the lock screen. |
| mFlags |= FLAG_SHOW_WHEN_LOCKED; |
| } |
| |
| // Don't display "empty album" action item for capture intents. |
| if (!mSetPathString.equals("/local/all/0")) { |
| // Check if the path is a secure album. |
| if (SecureSource.isSecurePath(mSetPathString)) { |
| mSecureAlbum = (SecureAlbum) mActivity.getDataManager() |
| .getMediaSet(mSetPathString); |
| mShowSpinner = false; |
| } |
| mSetPathString = "/filter/empty/{"+mSetPathString+"}"; |
| } |
| |
| // Combine the original MediaSet with the one for ScreenNail |
| // from AppBridge. |
| mSetPathString = "/combo/item/{" + screenNailSetPath + |
| "," + mSetPathString + "}"; |
| |
| // Start from the screen nail. |
| itemPath = screenNailItemPath; |
| } else if (inCameraRoll && GalleryUtils.isCameraAvailable(mActivity)) { |
| mSetPathString = "/combo/item/{" + FilterSource.FILTER_CAMERA_SHORTCUT + |
| "," + mSetPathString + "}"; |
| mCurrentIndex++; |
| mHasCameraScreennailOrPlaceholder = true; |
| } |
| |
| MediaSet originalSet = mActivity.getDataManager() |
| .getMediaSet(mSetPathString); |
| if (mHasCameraScreennailOrPlaceholder && originalSet instanceof ComboAlbum) { |
| // Use the name of the camera album rather than the default |
| // ComboAlbum behavior |
| ((ComboAlbum) originalSet).useNameOfChild(1); |
| } |
| mSelectionManager.setSourceMediaSet(originalSet); |
| mSetPathString = "/filter/delete/{" + mSetPathString + "}"; |
| mMediaSet = (FilterDeleteSet) mActivity.getDataManager() |
| .getMediaSet(mSetPathString); |
| if (mMediaSet != null && mIsFromTimelineScreen) { |
| mMediaSet.setShowAlbumsetTimeTitle(false); |
| } |
| if (mMediaSet == null) { |
| Log.w(TAG, "failed to restore " + mSetPathString); |
| } |
| if (itemPath == null) { |
| int mediaItemCount = mMediaSet.getMediaItemCount(); |
| if (mediaItemCount > 0) { |
| if (mCurrentIndex >= mediaItemCount) mCurrentIndex = 0; |
| itemPath = mMediaSet.getMediaItem(mCurrentIndex, 1) |
| .get(0).getPath(); |
| } else { |
| // Bail out, PhotoPage can't load on an empty album |
| return; |
| } |
| } |
| PhotoDataAdapter pda = new PhotoDataAdapter( |
| mActivity, mPhotoView, mMediaSet, itemPath, mCurrentIndex, |
| mAppBridge == null ? -1 : 0, |
| mAppBridge == null ? false : mAppBridge.isPanorama(), |
| mAppBridge == null ? false : mAppBridge.isStaticCamera()); |
| mModel = pda; |
| mPhotoView.setModel(mModel); |
| |
| // If RTL and from widget, set the flag into PhotoDataAdapter. |
| if (View.LAYOUT_DIRECTION_RTL == TextUtils |
| .getLayoutDirectionFromLocale(Locale.getDefault()) |
| && mIsFromWidget) { |
| pda.setFromWidget(mIsFromWidget); |
| } |
| pda.setDataListener(new PhotoDataAdapter.DataListener() { |
| |
| @Override |
| public void onPhotoChanged(int index, Path item) { |
| int oldIndex = mCurrentIndex; |
| mCurrentIndex = index; |
| |
| if (mHasCameraScreennailOrPlaceholder) { |
| if (mCurrentIndex > 0) { |
| mSkipUpdateCurrentPhoto = false; |
| } |
| |
| if (oldIndex == 0 && mCurrentIndex > 0 |
| && !mPhotoView.getFilmMode()) { |
| mPhotoView.setFilmMode(true); |
| if (mAppBridge != null) { |
| UsageStatistics.onEvent("CameraToFilmstrip", |
| UsageStatistics.TRANSITION_SWIPE, null); |
| } |
| } else if (oldIndex == 2 && mCurrentIndex == 1) { |
| mCameraSwitchCutoff = SystemClock.uptimeMillis() + |
| CAMERA_SWITCH_CUTOFF_THRESHOLD_MS; |
| mPhotoView.stopScrolling(); |
| } else if (oldIndex >= 1 && mCurrentIndex == 0) { |
| mPhotoView.setWantPictureCenterCallbacks(true); |
| mSkipUpdateCurrentPhoto = true; |
| } |
| } |
| if (!mSkipUpdateCurrentPhoto) { |
| if (item != null) { |
| MediaItem photo = mModel.getMediaItem(0); |
| if (photo != null) { |
| mActionBar.setTitle(photo.getName()); |
| updateCurrentPhoto(photo); |
| } |
| } |
| updateBars(); |
| } |
| // Reset the timeout for the bars after a swipe |
| refreshHidingMessage(); |
| } |
| |
| @Override |
| public void onLoadingFinished(boolean loadingFailed) { |
| if (!mModel.isEmpty()) { |
| MediaItem photo = mModel.getMediaItem(0); |
| if (photo != null) { |
| updateCurrentPhoto(photo); |
| } else { |
| mModel.resume(); |
| } |
| } else if (mIsActive) { |
| // We only want to finish the PhotoPage if there is no |
| // deletion that the user can undo. |
| if (mMediaSet.getNumberOfDeletions() == 0) { |
| onBackPressed(); |
| } |
| } |
| } |
| |
| @Override |
| public void onLoadingStarted() { |
| } |
| }); |
| } else { |
| // Get default media set by the URI |
| MediaItem mediaItem = (MediaItem) |
| mActivity.getDataManager().getMediaObject(itemPath); |
| mModel = new SinglePhotoDataAdapter(mActivity, mPhotoView, mediaItem); |
| mPhotoView.setModel(mModel); |
| updateCurrentPhoto(mediaItem); |
| mShowSpinner = false; |
| } |
| |
| mPhotoView.setFilmMode(mStartInFilmstrip && mMediaSet.getMediaItemCount() > 1); |
| RelativeLayout galleryRoot = (RelativeLayout) ((Activity) mActivity) |
| .findViewById(mAppBridge != null ? R.id.content : R.id.gallery_root); |
| if (galleryRoot != null) { |
| if (mSecureAlbum == null) { |
| mBottomControls = new PhotoPageBottomControls(this, mActivity, galleryRoot); |
| m3DButton = new ThreeDButton(this, mActivity, galleryRoot); |
| } |
| } |
| |
| ((GLRootView) mActivity.getGLRoot()).setOnSystemUiVisibilityChangeListener( |
| new View.OnSystemUiVisibilityChangeListener() { |
| @Override |
| public void onSystemUiVisibilityChange(int visibility) { |
| int diff = mLastSystemUiVis ^ visibility; |
| mLastSystemUiVis = visibility; |
| if ((diff & View.SYSTEM_UI_FLAG_FULLSCREEN) != 0 |
| && (visibility & View.SYSTEM_UI_FLAG_FULLSCREEN) == 0) { |
| showBars(); |
| } |
| } |
| }); |
| } |
| |
| @Override |
| protected void onSaveState(Bundle outState) { |
| if (mCurrentPhoto != null) { |
| outState.putInt(KEY_INDEX_HINT,mCurrentIndex); |
| outState.putString(KEY_CURRENT_PHOTO_HINT, mCurrentPhoto.getFilePath()); |
| super.onSaveState(outState); |
| } else { |
| onBackPressed(); |
| } |
| } |
| |
| @Override |
| public void onPictureCenter(boolean isCamera) { |
| isCamera = isCamera || (mHasCameraScreennailOrPlaceholder && mAppBridge == null); |
| mPhotoView.setWantPictureCenterCallbacks(false); |
| mHandler.removeMessages(MSG_ON_CAMERA_CENTER); |
| mHandler.removeMessages(MSG_ON_PICTURE_CENTER); |
| mHandler.sendEmptyMessage(isCamera ? MSG_ON_CAMERA_CENTER : MSG_ON_PICTURE_CENTER); |
| } |
| |
| @Override |
| public boolean canDisplayBottomControls() { |
| return mIsActive && !mPhotoView.canUndo(); |
| } |
| |
| @Override |
| public boolean canDisplayBottomControl(int control) { |
| if (mCurrentPhoto == null) { |
| return false; |
| } |
| switch (control) { |
| case R.id.photopage_bottom_controls: |
| return mShowBars; |
| case R.id.photopage_bottom_control_edit: |
| return mHaveImageEditor |
| && mShowBars |
| && !mReadOnlyView |
| && !mPhotoView.getFilmMode() |
| && (mCurrentPhoto.getSupportedOperations() & MediaItem.SUPPORT_EDIT) != 0 |
| && mCurrentPhoto.getMediaType() == MediaObject.MEDIA_TYPE_IMAGE; |
| case R.id.photopage_bottom_control_share: |
| mShareIntent = new Intent(Intent.ACTION_SEND); |
| return mShowBars; |
| case R.id.photopage_bottom_control_delete: |
| return mShowBars && !mReadOnlyView; |
| default: |
| return false; |
| } |
| } |
| |
| @Override |
| public void onBottomControlClicked(int control) { |
| switch(control) { |
| case R.id.photopage_bottom_control_edit: |
| launchPhotoEditor(); |
| return; |
| case R.id.photopage_bottom_control_share: |
| if (mModel != null && mModel.getMediaItem(0) != null) { |
| Uri uri = mActivity.getDataManager().getContentUri(mModel.getMediaItem(0).getPath()); |
| mActivity.isTopMenuShow = true; |
| mShareIntent.setType(MenuExecutor.getMimeType(mModel |
| .getMediaItem(0).getMediaType())); |
| mShareIntent.putExtra(Intent.EXTRA_STREAM, uri); |
| String shareTitle = mActivity.getResources(). |
| getString(R.string.share_dialogue_title); |
| if (uri.toString().contains("file:")) { |
| Log.d(TAG, "can't share uri started with file://"); |
| return; |
| } |
| mActivity.startActivity(Intent.createChooser(mShareIntent, |
| shareTitle)); |
| } |
| return; |
| |
| case R.id.photopage_bottom_control_delete: |
| String confirmMsg = null; |
| confirmMsg = mActivity.getResources().getQuantityString( |
| R.plurals.delete_selection, 1); |
| if (mModel != null && mModel.getMediaItem(0) != null) { |
| Path path = mModel.getMediaItem(0).getPath(); |
| mSelectionManager.deSelectAll(); |
| mSelectionManager.toggle(path); |
| MenuItem item = null; |
| mMenuExecutor.onMenuClicked(item, confirmMsg, |
| mConfirmDialogListener); |
| } |
| return; |
| default: |
| return; |
| } |
| } |
| |
| @Override |
| public boolean canDisplay3DButton() { |
| return bShow3DButton && mShowBars |
| && (mPhotoView == null ? false : !mPhotoView.getFilmMode()); |
| } |
| |
| @Override |
| public void on3DButtonClicked() { |
| if (mCurrentPhoto != null) { |
| Path p = mCurrentPhoto.getPath(); |
| Uri uri = mActivity.getDataManager().getContentUri(p); |
| Intent intent = new Intent(); |
| intent.setClass(mActivity, ThreeDimensionalActivity.class); |
| intent.setData(uri); |
| mActivity.startActivity(intent); |
| } |
| } |
| |
| @TargetApi(ApiHelper.VERSION_CODES.JELLY_BEAN) |
| private void setupNfcBeamPush() { |
| if (!ApiHelper.HAS_SET_BEAM_PUSH_URIS) return; |
| |
| try { |
| NfcAdapter adapter = NfcAdapter.getDefaultAdapter(mActivity); |
| if (adapter != null) { |
| adapter.setBeamPushUris(null, mActivity); |
| adapter.setBeamPushUrisCallback(new CreateBeamUrisCallback() { |
| @Override |
| public Uri[] createBeamUris(NfcEvent event) { |
| return mNfcPushUris; |
| } |
| }, mActivity); |
| } |
| } catch (NullPointerException e) { |
| e.printStackTrace(); |
| } catch (Exception e) { |
| e.printStackTrace(); |
| } |
| } |
| |
| private void setNfcBeamPushUri(Uri uri) { |
| mNfcPushUris[0] = uri; |
| } |
| |
| private Intent createShareIntent(MediaObject mediaObject) { |
| int type = mediaObject.getMediaType(); |
| return new Intent(Intent.ACTION_SEND) |
| .setType(MenuExecutor.getMimeType(type)) |
| .putExtra(Intent.EXTRA_STREAM, mediaObject.getContentUri()) |
| .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); |
| } |
| |
| private static Intent createSharePanoramaIntent(Uri contentUri) { |
| return new Intent(Intent.ACTION_SEND) |
| .setType(GalleryUtils.MIME_TYPE_PANORAMA360) |
| .putExtra(Intent.EXTRA_STREAM, contentUri) |
| .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); |
| } |
| |
| private void overrideTransitionToEditor() { |
| ((Activity) mActivity).overridePendingTransition(android.R.anim.fade_in, |
| android.R.anim.fade_out); |
| } |
| |
| private void launchTinyPlanet() { |
| // Deep link into tiny planet |
| MediaItem current = mModel.getMediaItem(0); |
| Intent intent = new Intent(FilterShowActivity.TINY_PLANET_ACTION); |
| intent.setClass(mActivity, FilterShowActivity.class); |
| intent.setDataAndType(current.getContentUri(), current.getMimeType()) |
| .setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); |
| intent.putExtra(FilterShowActivity.LAUNCH_FULLSCREEN, |
| mActivity.isFullscreen()); |
| mActivity.startActivityForResult(intent, REQUEST_EDIT); |
| overrideTransitionToEditor(); |
| } |
| |
| private void launchCamera() { |
| mRecenterCameraOnResume = false; |
| GalleryUtils.startCameraActivity(mActivity); |
| } |
| |
| private void launchPhotoEditor() { |
| MediaItem current = mModel.getMediaItem(0); |
| if (current == null || (current.getSupportedOperations() |
| & MediaObject.SUPPORT_EDIT) == 0) { |
| return; |
| } |
| |
| Intent intent = new Intent(ACTION_NEXTGEN_EDIT); |
| |
| intent.setDataAndType(current.getContentUri(), current.getMimeType()) |
| .setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); |
| if (mActivity.getPackageManager() |
| .queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY).size() == 0) { |
| intent.setAction(Intent.ACTION_EDIT); |
| } |
| intent.putExtra(FilterShowActivity.LAUNCH_FULLSCREEN, |
| mActivity.isFullscreen()); |
| |
| List<ResolveInfo> resolveInfoList = mActivity.getPackageManager() |
| .queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY); |
| if (resolveInfoList != null && resolveInfoList.size() > 0) { |
| if (resolveInfoList.size() == 1) { |
| // only one app can resolve intent, don't use createChooser. |
| mActivity.startActivityForResult(intent, REQUEST_EDIT); |
| } else { |
| mActivity.startActivityForResult(Intent.createChooser(intent, null), REQUEST_EDIT); |
| } |
| overrideTransitionToEditor(); |
| } |
| } |
| |
| private void launchSimpleEditor() { |
| MediaItem current = mModel.getMediaItem(0); |
| if (current == null || (current.getSupportedOperations() |
| & MediaObject.SUPPORT_EDIT) == 0) { |
| return; |
| } |
| |
| Intent intent = new Intent(ACTION_SIMPLE_EDIT); |
| |
| intent.setDataAndType(current.getContentUri(), current.getMimeType()) |
| .setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); |
| if (mActivity.getPackageManager() |
| .queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY).size() == 0) { |
| intent.setAction(Intent.ACTION_EDIT); |
| } |
| intent.putExtra(FilterShowActivity.LAUNCH_FULLSCREEN, |
| mActivity.isFullscreen()); |
| ((Activity) mActivity).startActivityForResult(Intent.createChooser(intent, null), |
| REQUEST_EDIT); |
| overrideTransitionToEditor(); |
| } |
| |
| private void requestDeferredUpdate() { |
| mDeferUpdateUntil = SystemClock.uptimeMillis() + DEFERRED_UPDATE_MS; |
| if (!mDeferredUpdateWaiting) { |
| mDeferredUpdateWaiting = true; |
| mHandler.sendEmptyMessageDelayed(MSG_UPDATE_DEFERRED, DEFERRED_UPDATE_MS); |
| } |
| } |
| |
| private void updateUIForCurrentPhoto() { |
| if (mCurrentPhoto == null) return; |
| |
| // If by swiping or deletion the user ends up on an action item |
| // and zoomed in, zoom out so that the context of the action is |
| // more clear |
| if ((mCurrentPhoto.getSupportedOperations() & MediaObject.SUPPORT_ACTION) != 0 |
| && !mPhotoView.getFilmMode()) { |
| mPhotoView.setWantPictureCenterCallbacks(true); |
| } |
| |
| updateMenuOperations(); |
| refreshBottomControlsWhenReady(); |
| parseGDepth(); |
| if (mShowDetails) { |
| mDetailsHelper.reloadDetails(); |
| } |
| if ((mSecureAlbum == null) |
| && (mCurrentPhoto.getSupportedOperations() & MediaItem.SUPPORT_SHARE) != 0) { |
| mCurrentPhoto.getPanoramaSupport(mUpdateShareURICallback); |
| } |
| } |
| |
| private void updateCurrentPhoto(MediaItem photo) { |
| if (mCurrentPhoto == photo) return; |
| mCurrentPhoto = photo; |
| if (mPhotoView == null) { |
| return; |
| } |
| if (mPhotoView.getFilmMode()) { |
| requestDeferredUpdate(); |
| } else { |
| updateUIForCurrentPhoto(); |
| |
| // Manage DRM rights while image selection changed. this |
| // flow will comes for both image and video, but here |
| // we will consume rights for image files only. |
| // Do not consume rights of a GIF image and video here. |
| // ViewGifImage will take care of GIF rights consumption stub. |
| // MediaPlayer will handle the video rights consumption stub. |
| // String mime = mCurrentPhoto.getMimeType(); |
| // if (!TextUtils.isEmpty(mime) && !mime.equals("image/gif") |
| // && !mime.startsWith("video/")) { |
| // DrmHelper.manageDrmLicense(mActivity.getAndroidContext(), |
| // mHandler, mCurrentPhoto.getFilePath(), |
| // mCurrentPhoto.getMimeType()); |
| // } |
| } |
| } |
| |
| private void updateMenuOperations() { |
| Menu menu = mActionBar.getMenu(); |
| |
| // it could be null if onCreateActionBar has not been called yet |
| if (menu == null) return; |
| |
| MenuItem item = menu.findItem(R.id.action_slideshow); |
| if (item != null) { |
| item.setVisible((mSecureAlbum == null) && canDoSlideShow()); |
| } |
| if (mCurrentPhoto == null) return; |
| |
| int supportedOperations = mCurrentPhoto.getSupportedOperations(); |
| if (mReadOnlyView) { |
| supportedOperations &= ~MediaObject.SUPPORT_EDIT; |
| } |
| if (mSecureAlbum != null) { |
| supportedOperations &= MediaObject.SUPPORT_DELETE; |
| } else { |
| mCurrentPhoto.getPanoramaSupport(mUpdatePanoramaMenuItemsCallback); |
| if (!mHaveImageEditor) { |
| supportedOperations &= ~MediaObject.SUPPORT_EDIT; |
| } |
| // If current photo page is single item only, to cut some menu items |
| boolean singleItemOnly = mData.getBoolean("SingleItemOnly", false); |
| if (singleItemOnly) { |
| supportedOperations &= ~MediaObject.SUPPORT_DELETE; |
| supportedOperations &= ~MediaObject.SUPPORT_ROTATE; |
| supportedOperations &= ~MediaObject.SUPPORT_SHARE; |
| supportedOperations &= ~MediaObject.SUPPORT_CROP; |
| supportedOperations &= ~MediaObject.SUPPORT_INFO; |
| supportedOperations &= ~MediaObject.SUPPORT_SETAS; |
| } |
| } |
| MenuExecutor.updateMenuOperation(menu, supportedOperations); |
| } |
| |
| private boolean canDoSlideShow() { |
| if (mMediaSet == null || mCurrentPhoto == null) { |
| return false; |
| } |
| if (mCurrentPhoto.getMediaType() != MediaObject.MEDIA_TYPE_IMAGE) { |
| return false; |
| } |
| return true; |
| } |
| |
| ////////////////////////////////////////////////////////////////////////// |
| // Action Bar show/hide management |
| ////////////////////////////////////////////////////////////////////////// |
| |
| private void showBars() { |
| if (mShowBars) return; |
| mShowBars = true; |
| mOrientationManager.unlockOrientation(); |
| mActionBar.show(); |
| mActivity.getGLRoot().setLightsOutMode(true); |
| refreshHidingMessage(); |
| refreshBottomControlsWhenReady(); |
| } |
| |
| private void hideBars() { |
| if (!mShowBars) return; |
| mShowBars = false; |
| mActionBar.hide(); |
| mActivity.getGLRoot().setLightsOutMode(true); |
| mHandler.removeMessages(MSG_HIDE_BARS); |
| refreshBottomControlsWhenReady(); |
| } |
| |
| private void refreshHidingMessage() { |
| mHandler.removeMessages(MSG_HIDE_BARS); |
| if (mPhotoView == null) { |
| return; |
| } |
| if (!mIsMenuVisible && !mPhotoView.getFilmMode()) { |
| mHandler.sendEmptyMessageDelayed(MSG_HIDE_BARS, HIDE_BARS_TIMEOUT); |
| } |
| } |
| |
| private boolean canShowBars() { |
| // No bars if we are showing camera preview. |
| if (mAppBridge != null && mCurrentIndex == 0 |
| && !mPhotoView.getFilmMode()) return false; |
| |
| // No bars if it's not allowed. |
| if (!mActionBarAllowed) return false; |
| |
| Configuration config = mActivity.getResources().getConfiguration(); |
| if (config.touchscreen == Configuration.TOUCHSCREEN_NOTOUCH) { |
| return false; |
| } |
| |
| return true; |
| } |
| |
| private void wantBars() { |
| if (canShowBars()) showBars(); |
| } |
| |
| private void toggleBars() { |
| if (mShowBars) { |
| hideBars(); |
| } else { |
| if (canShowBars()) showBars(); |
| } |
| } |
| |
| private void updateBars() { |
| if (!canShowBars()) { |
| hideBars(); |
| } |
| } |
| |
| @Override |
| protected void onBackPressed() { |
| showBars(); |
| if (mShowDetails) { |
| hideDetails(); |
| } else if (mAppBridge == null || !switchWithCaptureAnimation(-1)) { |
| // We are leaving this page. Set the result now. |
| setResult(); |
| if (mStartInFilmstrip && !mPhotoView.getFilmMode()) { |
| mPhotoView.setFilmMode(true); |
| } else if (mTreatBackAsUp) { |
| onUpPressed(); |
| } else { |
| if (mMediaSet != null && mIsFromTimelineScreen) { |
| //if back to TimeLinePage, need show timeline title |
| mMediaSet.setShowAlbumsetTimeTitle(true); |
| } |
| super.onBackPressed(); |
| mActionBar.setBackGroundDefault(); |
| int count = mActivity.getStateManager().getStateCount(); |
| if (mIsFromVideoScreen || count == 1 || mIsFromTimelineScreen) { |
| mActivity.getToolbar().setNavigationContentDescription( |
| "drawer"); |
| mActivity.getToolbar().setNavigationIcon(R.drawable.drawer); |
| ((GalleryActivity)mActivity).toggleNavDrawer(true); |
| if (mModel instanceof PhotoDataAdapter) { |
| ((PhotoDataAdapter) mModel).setDataListener(null); |
| } |
| } |
| } |
| } |
| } |
| |
| private void onUpPressed() { |
| if ((mStartInFilmstrip || mAppBridge != null) |
| && !mPhotoView.getFilmMode()) { |
| mPhotoView.setFilmMode(true); |
| return; |
| } |
| |
| if (mActivity.getStateManager().getStateCount() > 1) { |
| setResult(); |
| super.onBackPressed(); |
| return; |
| } |
| |
| if (mOriginalSetPathString == null) return; |
| |
| if (mAppBridge == null) { |
| // We're in view mode so set up the stacks on our own. |
| Bundle data = new Bundle(getData()); |
| data.putString(AlbumPage.KEY_MEDIA_PATH, mOriginalSetPathString); |
| data.putString(AlbumPage.KEY_PARENT_MEDIA_PATH, |
| mActivity.getDataManager().getTopSetPath( |
| DataManager.INCLUDE_ALL)); |
| mActivity.getStateManager().switchState(this, AlbumPage.class, data); |
| } else { |
| GalleryUtils.startGalleryActivity(mActivity); |
| } |
| } |
| |
| private void setResult() { |
| Intent result = null; |
| result = new Intent(); |
| result.putExtra(KEY_RETURN_INDEX_HINT, mCurrentIndex); |
| setStateResult(Activity.RESULT_OK, result); |
| } |
| |
| ////////////////////////////////////////////////////////////////////////// |
| // AppBridge.Server interface |
| ////////////////////////////////////////////////////////////////////////// |
| |
| @Override |
| public void setCameraRelativeFrame(Rect frame) { |
| mPhotoView.setCameraRelativeFrame(frame); |
| } |
| |
| @Override |
| public boolean switchWithCaptureAnimation(int offset) { |
| return mPhotoView.switchWithCaptureAnimation(offset); |
| } |
| |
| @Override |
| public void setSwipingEnabled(boolean enabled) { |
| mPhotoView.setSwipingEnabled(enabled); |
| } |
| |
| @Override |
| public void notifyScreenNailChanged() { |
| mScreenNailItem.setScreenNail(mAppBridge.attachScreenNail()); |
| mScreenNailSet.notifyChange(); |
| } |
| |
| @Override |
| public void addSecureAlbumItem(boolean isVideo, int id) { |
| mSecureAlbum.addMediaItem(isVideo, id); |
| } |
| |
| @Override |
| protected boolean onCreateActionBar(Menu menu) { |
| mActionBar.createActionBarMenu(R.menu.photo, menu); |
| mHaveImageEditor = GalleryUtils.isEditorAvailable(mActivity, "image/*"); |
| updateMenuOperations(); |
| //mActionBar.setTitle(mMediaSet != null ? mMediaSet.getName() : ""); |
| return true; |
| } |
| |
| private MenuExecutor.ProgressListener mConfirmDialogListener = |
| new MenuExecutor.ProgressListener() { |
| @Override |
| public void onProgressUpdate(int index) {} |
| |
| @Override |
| public void onProgressComplete(int result) {} |
| |
| @Override |
| public void onConfirmDialogShown() { |
| mHandler.removeMessages(MSG_HIDE_BARS); |
| } |
| |
| @Override |
| public void onConfirmDialogDismissed(boolean confirmed) { |
| refreshHidingMessage(); |
| } |
| |
| @Override |
| public void onProgressStart() {} |
| }; |
| |
| private void switchToGrid() { |
| if (mActivity.getStateManager().hasStateClass(AlbumPage.class)) { |
| onUpPressed(); |
| } else { |
| if (mOriginalSetPathString == null) return; |
| Bundle data = new Bundle(getData()); |
| data.putString(AlbumPage.KEY_MEDIA_PATH, mOriginalSetPathString); |
| data.putString(AlbumPage.KEY_PARENT_MEDIA_PATH, |
| mActivity.getDataManager().getTopSetPath( |
| DataManager.INCLUDE_ALL)); |
| |
| // We only show cluster menu in the first AlbumPage in stack |
| // TODO: Enable this when running from the camera app |
| boolean inAlbum = mActivity.getStateManager().hasStateClass(AlbumPage.class); |
| data.putBoolean(AlbumPage.KEY_SHOW_CLUSTER_MENU, !inAlbum |
| && mAppBridge == null); |
| |
| data.putBoolean(PhotoPage.KEY_APP_BRIDGE, mAppBridge != null); |
| |
| // Account for live preview being first item |
| mActivity.getTransitionStore().put(KEY_RETURN_INDEX_HINT, |
| mAppBridge != null ? mCurrentIndex - 1 : mCurrentIndex); |
| |
| if (mHasCameraScreennailOrPlaceholder && mAppBridge != null) { |
| mActivity.getStateManager().startState(AlbumPage.class, data); |
| } else { |
| mActivity.getStateManager().switchState(this, AlbumPage.class, data); |
| } |
| } |
| } |
| |
| @Override |
| protected boolean onItemSelected(MenuItem item) { |
| if (mModel == null) return true; |
| refreshHidingMessage(); |
| MediaItem current = mModel.getMediaItem(0); |
| |
| // This is a shield for monkey when it clicks the action bar |
| // menu when transitioning from filmstrip to camera |
| if (current instanceof SnailItem) return true; |
| // TODO: We should check the current photo against the MediaItem |
| // that the menu was initially created for. We need to fix this |
| // after PhotoPage being refactored. |
| if (current == null) { |
| // item is not ready, ignore |
| return true; |
| } |
| int currentIndex = mModel.getCurrentIndex(); |
| |
| // If RTL, the current index need be revised. |
| if (View.LAYOUT_DIRECTION_RTL == TextUtils |
| .getLayoutDirectionFromLocale(Locale.getDefault())) { |
| currentIndex = mMediaSet.getMediaItemCount() - currentIndex - 1; |
| } |
| Path path = current.getPath(); |
| |
| DataManager manager = mActivity.getDataManager(); |
| int action = item.getItemId(); |
| String confirmMsg = null; |
| switch (action) { |
| case android.R.id.home: { |
| onUpPressed(); |
| return true; |
| } |
| case R.id.action_slideshow: { |
| Bundle data = new Bundle(); |
| data.putString(SlideshowPage.KEY_SET_PATH, mMediaSet.getPath().toString()); |
| data.putString(SlideshowPage.KEY_ITEM_PATH, path.toString()); |
| data.putInt(SlideshowPage.KEY_PHOTO_INDEX, currentIndex); |
| data.putBoolean(SlideshowPage.KEY_REPEAT, true); |
| mActivity.getStateManager().startStateForResult( |
| SlideshowPage.class, REQUEST_SLIDESHOW, data); |
| return true; |
| } |
| /*case R.id.action_crop: { |
| Activity activity = mActivity; |
| Intent intent = new Intent(CropActivity.CROP_ACTION); |
| intent.setClass(activity, CropActivity.class); |
| intent.setDataAndType(manager.getContentUri(path), current.getMimeType()) |
| .setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); |
| activity.startActivityForResult(intent, PicasaSource.isPicasaImage(current) |
| ? REQUEST_CROP_PICASA |
| : REQUEST_CROP); |
| return true; |
| }*/ |
| case R.id.action_trim: { |
| Intent intent = new Intent(mActivity, TrimVideo.class); |
| intent.setData(manager.getContentUri(path)); |
| // We need the file path to wrap this into a RandomAccessFile. |
| String str = current.getMimeType(); |
| if("video/mp4".equals(str) || "video/mpeg4".equals(str) |
| || "video/3gpp".equals(str) || "video/3gpp2".equals(str)) { |
| intent.putExtra(KEY_MEDIA_ITEM_PATH, current.getFilePath()); |
| mActivity.startActivityForResult(intent, REQUEST_TRIM); |
| } else { |
| Toast.makeText(mActivity,mActivity.getString(R.string.can_not_trim), |
| Toast.LENGTH_SHORT).show(); |
| } |
| return true; |
| } |
| case R.id.action_mute: { |
| MuteVideo muteVideo = new MuteVideo(current.getFilePath(), |
| manager.getContentUri(path), mActivity); |
| muteVideo.muteInBackground(); |
| return true; |
| } |
| case R.id.action_edit: { |
| launchPhotoEditor(); |
| return true; |
| } |
| /*case R.id.action_simple_edit: { |
| launchSimpleEditor(); |
| return true; |
| }*/ |
| case R.id.action_details: { |
| if (mShowDetails) { |
| hideDetails(); |
| } else { |
| showDetails(); |
| } |
| return true; |
| } |
| case R.id.print: { |
| try { |
| mActivity.printSelectedImage(manager.getContentUri(path)); |
| } catch (SecurityException e) { |
| e.printStackTrace(); |
| mActivity.finish(); |
| } |
| return true; |
| } |
| case R.id.action_delete: |
| confirmMsg = mActivity.getResources().getQuantityString( |
| R.plurals.delete_selection, 1); |
| case R.id.action_setas: |
| //case R.id.action_rotate_ccw: |
| //case R.id.action_rotate_cw: |
| case R.id.action_show_on_map: |
| mSelectionManager.deSelectAll(); |
| mSelectionManager.toggle(path); |
| mMenuExecutor.onMenuClicked(item, confirmMsg, mConfirmDialogListener); |
| return true; |
| // case R.id.action_drm_info: |
| // String filepath = current.getFilePath(); |
| // if (DrmHelper.isDrmFile(filepath)) { |
| // DrmHelper.showDrmInfo(mActivity.getAndroidContext(), filepath); |
| // } |
| // return true; |
| default : |
| return false; |
| } |
| } |
| |
| private void hideDetails() { |
| mShowDetails = false; |
| mDetailsHelper.hide(); |
| } |
| |
| private void showDetails() { |
| mShowDetails = true; |
| if (mDetailsHelper == null) { |
| mDetailsHelper = new DetailsHelper(mActivity, mRootPane, new MyDetailsSource()); |
| mDetailsHelper.setCloseListener(new CloseListener() { |
| @Override |
| public void onClose() { |
| hideDetails(); |
| } |
| }); |
| } |
| mDetailsHelper.show(); |
| } |
| |
| //////////////////////////////////////////////////////////////////////////// |
| // Callbacks from PhotoView |
| //////////////////////////////////////////////////////////////////////////// |
| @Override |
| public void onSingleTapUp(int x, int y) { |
| if (mAppBridge != null) { |
| if (mAppBridge.onSingleTapUp(x, y)) return; |
| } |
| |
| MediaItem item = mModel.getMediaItem(0); |
| if (item == null || item == mScreenNailItem) { |
| // item is not ready or it is camera preview, ignore |
| return; |
| } |
| if (item.getMimeType().equals(MediaItem.MIME_TYPE_GIF)) { |
| viewAnimateGif((Activity) mActivity, item.getContentUri()); |
| return; |
| } |
| |
| int supported = item.getSupportedOperations(); |
| boolean playVideo = ((supported & MediaItem.SUPPORT_PLAY) != 0); |
| boolean unlock = ((supported & MediaItem.SUPPORT_UNLOCK) != 0); |
| boolean goBack = ((supported & MediaItem.SUPPORT_BACK) != 0); |
| boolean launchCamera = ((supported & MediaItem.SUPPORT_CAMERA_SHORTCUT) != 0); |
| |
| if (playVideo) { |
| // determine if the point is at center (1/6) of the photo view. |
| // (The position of the "play" icon is at center (1/6) of the photo) |
| int w = mPhotoView.getWidth(); |
| int h = mPhotoView.getHeight(); |
| playVideo = (Math.abs(x - w / 2) * 12 <= w) |
| && (Math.abs(y - h / 2) * 12 <= h); |
| } |
| |
| if (playVideo) { |
| if (mSecureAlbum == null) { |
| playVideo(mActivity, item.getPlayUri(), item.getName()); |
| } else { |
| mActivity.getStateManager().finishState(this); |
| } |
| } else if (goBack) { |
| onBackPressed(); |
| } else if (unlock) { |
| Intent intent = new Intent(mActivity, GalleryActivity.class); |
| intent.putExtra(GalleryActivity.KEY_DISMISS_KEYGUARD, true); |
| mActivity.startActivity(intent); |
| } else if (launchCamera) { |
| launchCamera(); |
| } else { |
| toggleBars(); |
| } |
| } |
| |
| @Override |
| public void onActionBarAllowed(boolean allowed) { |
| mActionBarAllowed = allowed; |
| mHandler.sendEmptyMessage(MSG_UPDATE_ACTION_BAR); |
| } |
| |
| @Override |
| public void onActionBarWanted() { |
| mHandler.sendEmptyMessage(MSG_WANT_BARS); |
| } |
| |
| @Override |
| public void onFullScreenChanged(boolean full) { |
| Message m = mHandler.obtainMessage( |
| MSG_ON_FULL_SCREEN_CHANGED, full ? 1 : 0, 0); |
| m.sendToTarget(); |
| } |
| |
| // How we do delete/undo: |
| // |
| // When the user choose to delete a media item, we just tell the |
| // FilterDeleteSet to hide that item. If the user choose to undo it, we |
| // again tell FilterDeleteSet not to hide it. If the user choose to commit |
| // the deletion, we then actually delete the media item. |
| @Override |
| public void onDeleteImage(Path path, int offset) { |
| onCommitDeleteImage(); // commit the previous deletion |
| mDeletePath = path; |
| mDeleteIsFocus = (offset == 0); |
| |
| // If RTL, the index need be revised. |
| if (View.LAYOUT_DIRECTION_RTL == TextUtils |
| .getLayoutDirectionFromLocale(Locale.getDefault())) { |
| mMediaSet.addDeletion(path, mMediaSet.getMediaItemCount() - mCurrentIndex - 1); |
| } else { |
| mMediaSet.addDeletion(path, mCurrentIndex + offset); |
| } |
| } |
| |
| @Override |
| public void onUndoDeleteImage() { |
| if (mDeletePath == null) return; |
| // If the deletion was done on the focused item, we want the model to |
| // focus on it when it is undeleted. |
| if (mDeleteIsFocus) mModel.setFocusHintPath(mDeletePath); |
| mMediaSet.removeDeletion(mDeletePath); |
| mDeletePath = null; |
| } |
| |
| @Override |
| public void onCommitDeleteImage() { |
| if (mDeletePath == null) return; |
| mMenuExecutor.startSingleItemAction(R.id.action_delete, mDeletePath); |
| mDeletePath = null; |
| } |
| |
| public void playVideo(Activity activity, Uri uri, String title) { |
| if (GalleryUtils.isTelephonyCallInProgress(mActivity)) { |
| Log.w(TAG, "CS/CSVT Call is in progress, can't play video"); |
| return; |
| } |
| try { |
| Intent intent = new Intent(Intent.ACTION_VIEW) |
| .setDataAndType(uri, "video/*") |
| .putExtra(Intent.EXTRA_TITLE, title) |
| .putExtra(MovieActivity.KEY_TREAT_UP_AS_BACK, true); |
| activity.startActivityForResult(intent, REQUEST_PLAY_VIDEO); |
| } catch (ActivityNotFoundException e) { |
| Toast.makeText(activity, activity.getString(R.string.video_err), |
| Toast.LENGTH_SHORT).show(); |
| } |
| } |
| |
| private void setCurrentPhotoByIntent(Intent intent) { |
| if (intent == null) return; |
| Path path = mApplication.getDataManager() |
| .findPathByUri(intent.getData(), intent.getType()); |
| if (path != null) { |
| Path albumPath = mApplication.getDataManager().getDefaultSetOf(path); |
| if (albumPath == null) { |
| return; |
| } |
| boolean isClusterType = FilterUtils.isClusterPath(mOriginalSetPathString); |
| if (!albumPath.equalsIgnoreCase(mOriginalSetPathString) && !isClusterType) { |
| // If the edited image is stored in a different album, we need |
| // to start a new activity state to show the new image |
| Bundle data = new Bundle(getData()); |
| data.putString(KEY_MEDIA_SET_PATH, albumPath.toString()); |
| data.putString(PhotoPage.KEY_MEDIA_ITEM_PATH, path.toString()); |
| mActivity.getStateManager().startState(SinglePhotoPage.class, data); |
| return; |
| } |
| if (null != mModel) { |
| mModel.setCurrentPhoto(path, mCurrentIndex); |
| parseGDepth(); |
| } |
| } |
| } |
| |
| @Override |
| protected void onStateResult(int requestCode, int resultCode, Intent data) { |
| if (resultCode == Activity.RESULT_CANCELED) { |
| // This is a reset, not a canceled |
| return; |
| } |
| mRecenterCameraOnResume = false; |
| switch (requestCode) { |
| case REQUEST_EDIT: |
| if (data.getData() == null) { |
| onUpPressed(); |
| } else { |
| setCurrentPhotoByIntent(data); |
| } |
| break; |
| /*case REQUEST_CROP: |
| if (resultCode == Activity.RESULT_OK) { |
| setCurrentPhotoByIntent(data); |
| } |
| break;*/ |
| case REQUEST_CROP_PICASA: { |
| if (resultCode == Activity.RESULT_OK) { |
| Context context = mActivity.getAndroidContext(); |
| String message = context.getString(R.string.crop_saved, |
| context.getString(R.string.folder_edited_online_photos)); |
| Toast.makeText(context, message, Toast.LENGTH_SHORT).show(); |
| } |
| break; |
| } |
| case REQUEST_SLIDESHOW: { |
| if (data == null) break; |
| String path = data.getStringExtra(SlideshowPage.KEY_ITEM_PATH); |
| int index = data.getIntExtra(SlideshowPage.KEY_PHOTO_INDEX, 0); |
| |
| // If RTL, the index need be revised. |
| if (View.LAYOUT_DIRECTION_RTL == TextUtils |
| .getLayoutDirectionFromLocale(Locale.getDefault())) { |
| index = mMediaSet.getMediaItemCount() - index - 1; |
| } |
| if (path != null) { |
| mModel.setCurrentPhoto(Path.fromString(path), index); |
| parseGDepth(); |
| } |
| } |
| } |
| } |
| |
| private void parseGDepth() { |
| bShow3DButton = false; |
| if (DualCameraEffect.isSupported()) { |
| if (mParseGDepthTask != null |
| && mParseGDepthTask.getStatus() != AsyncTask.Status.FINISHED) { |
| mParseGDepthTask.cancel(true); |
| } |
| mParseGDepthTask = new ParseGDepthTask(); |
| mParseGDepthTask.execute(mCurrentPhoto.getContentUri()); |
| } |
| } |
| |
| @Override |
| public void onPause() { |
| super.onPause(); |
| mIsActive = false; |
| //restore the orginal heigh and padding of toolbar |
| Toolbar toolbar = mActivity.getToolbar(); |
| if (toolbar != null) { |
| ViewGroup.LayoutParams layoutParams = toolbar.getLayoutParams(); |
| layoutParams.height = originalHeight; |
| toolbar.setLayoutParams(layoutParams); |
| toolbar.setPadding(0, originalPadding, 0, 0); |
| } |
| showFullScreen(false); |
| |
| DetailsHelper.pause(); |
| // Hide the detail dialog on exit |
| if (mShowDetails) hideDetails(); |
| if (mModel != null) { |
| mModel.pause(); |
| } |
| mPhotoView.pause(); |
| mHandler.removeMessages(MSG_HIDE_BARS); |
| mHandler.removeMessages(MSG_REFRESH_BOTTOM_CONTROLS); |
| refreshBottomControlsWhenReady(); |
| mActionBar.removeOnMenuVisibilityListener(mMenuVisibilityListener); |
| // if (mShowSpinner) { |
| // mActionBar.disableAlbumModeMenu(true); |
| // } |
| onCommitDeleteImage(); |
| mMenuExecutor.pause(); |
| if (mMediaSet != null) mMediaSet.clearDeletion(); |
| } |
| |
| private void showFullScreen(boolean show) { |
| Window win = mActivity.getWindow(); |
| WindowManager.LayoutParams winParams = win.getAttributes(); |
| if (show) { |
| //set full screen |
| winParams.flags |= (WindowManager.LayoutParams.FLAG_FULLSCREEN); |
| } else { |
| //cancel full screen |
| winParams.flags &= (~WindowManager.LayoutParams.FLAG_FULLSCREEN); |
| } |
| win.setAttributes(winParams); |
| } |
| |
| |
| @Override |
| public void onFilmModeChanged(boolean enabled) { |
| refreshBottomControlsWhenReady(); |
| /*if (mShowSpinner) { |
| if (enabled) { |
| mActionBar.enableAlbumModeMenu( |
| GalleryActionBar.ALBUM_FILMSTRIP_MODE_SELECTED, this); |
| } else { |
| mActionBar.disableAlbumModeMenu(true); |
| } |
| }*/ |
| if (enabled) { |
| mHandler.removeMessages(MSG_HIDE_BARS); |
| UsageStatistics.onContentViewChanged( |
| UsageStatistics.COMPONENT_GALLERY, "FilmstripPage"); |
| } else { |
| refreshHidingMessage(); |
| if (mAppBridge == null || mCurrentIndex > 0) { |
| UsageStatistics.onContentViewChanged( |
| UsageStatistics.COMPONENT_GALLERY, "SinglePhotoPage"); |
| } else { |
| UsageStatistics.onContentViewChanged( |
| UsageStatistics.COMPONENT_CAMERA, "Unknown"); // TODO |
| } |
| |
| // Manage DRM rights while image selection changed. this |
| // flow will comes for both image and video, but here |
| // we will consume rights for image files only. |
| // Do not consume rights of a GIF image and video here. |
| // ViewGifImage will take care of GIF rights consumption stub. |
| // MediaPlayer will handle the video rights consumption stub. |
| // if ((mMediaSet != null && mMediaSet.getMediaItemCount() > 1) |
| // || !(this instanceof SinglePhotoPage)) { |
| // String mime = mCurrentPhoto.getMimeType(); |
| // if (!TextUtils.isEmpty(mime) && !mime.equals("image/gif") |
| // && !mime.startsWith("video/")) { |
| // DrmHelper.manageDrmLicense(mActivity.getAndroidContext(), |
| // mHandler, mCurrentPhoto.getFilePath(), |
| // mCurrentPhoto.getMimeType()); |
| // } |
| // } |
| } |
| } |
| |
| private void transitionFromAlbumPageIfNeeded() { |
| TransitionStore transitions = mActivity.getTransitionStore(); |
| |
| int albumPageTransition = transitions.get( |
| KEY_ALBUMPAGE_TRANSITION, MSG_ALBUMPAGE_NONE); |
| |
| if (albumPageTransition == MSG_ALBUMPAGE_NONE && mAppBridge != null |
| && mRecenterCameraOnResume) { |
| // Generally, resuming the PhotoPage when in Camera should |
| // reset to the capture mode to allow quick photo taking |
| mCurrentIndex = 0; |
| mPhotoView.resetToFirstPicture(); |
| } else { |
| int resumeIndex = transitions.get(KEY_INDEX_HINT, -1); |
| if (resumeIndex >= 0) { |
| if (mHasCameraScreennailOrPlaceholder) { |
| // Account for preview/placeholder being the first item |
| resumeIndex++; |
| } |
| if (resumeIndex < mMediaSet.getMediaItemCount()) { |
| mCurrentIndex = resumeIndex; |
| mModel.moveTo(mCurrentIndex); |
| } |
| } |
| } |
| |
| if (albumPageTransition == MSG_ALBUMPAGE_RESUMED) { |
| mPhotoView.setFilmMode(mStartInFilmstrip || mAppBridge != null); |
| } else if (albumPageTransition == MSG_ALBUMPAGE_PICKED) { |
| mPhotoView.setFilmMode(false); |
| } |
| } |
| |
| @Override |
| protected void onResume() { |
| super.onResume(); |
| //set full screen to hide the status bar |
| showFullScreen(true); |
| |
| transitionFromAlbumPageIfNeeded(); |
| |
| Toolbar toolbar = mActivity.getToolbar(); |
| //set the new height and padding to toolbar |
| if (toolbar != null) { |
| ViewGroup.LayoutParams layoutParams = toolbar.getLayoutParams(); |
| originalHeight = layoutParams.height; |
| originalPadding = toolbar.getPaddingTop(); |
| layoutParams.height = originalHeight - originalPadding; |
| toolbar.setPadding(0, 0, 0, 0); |
| toolbar.setLayoutParams(layoutParams); |
| } |
| mIsActive = true; |
| setContentPane(mRootPane); |
| |
| //if from TimeLinePage, don't show the timeline title |
| if (mMediaSet != null && mIsFromTimelineScreen) { |
| mMediaSet.setShowAlbumsetTimeTitle(false); |
| } |
| |
| mActionBar.setDisplayOptions( |
| ((mSecureAlbum == null) && (mSetPathString != null)), true); |
| mActionBar.addOnMenuVisibilityListener(mMenuVisibilityListener); |
| refreshBottomControlsWhenReady(); |
| if (((mSecureAlbum == null) && (mSetPathString != null))) { |
| mActivity.getToolbar().setNavigationContentDescription("back"); |
| mActivity.getToolbar().setNavigationIcon(R.drawable.back); |
| } |
| // if (mShowSpinner && mPhotoView.getFilmMode()) { |
| // mActionBar.enableAlbumModeMenu( |
| // GalleryActionBar.ALBUM_FILMSTRIP_MODE_SELECTED, this); |
| // } |
| if (!mShowBars) { |
| mActionBar.hide(); |
| } |
| //hide the status bar |
| mActivity.getGLRoot().setLightsOutMode(true); |
| boolean haveImageEditor = GalleryUtils.isEditorAvailable(mActivity, "image/*"); |
| if (haveImageEditor != mHaveImageEditor) { |
| mHaveImageEditor = haveImageEditor; |
| updateMenuOperations(); |
| } |
| |
| mRecenterCameraOnResume = true; |
| if (mModel == null) { |
| onBackPressed(); |
| return; |
| } |
| mModel.resume(); |
| mPhotoView.resume(); |
| } |
| |
| @Override |
| protected void onDestroy() { |
| if (mAppBridge != null) { |
| mAppBridge.setServer(null); |
| mScreenNailItem.setScreenNail(null); |
| mAppBridge.detachScreenNail(); |
| mAppBridge = null; |
| mScreenNailSet = null; |
| mScreenNailItem = null; |
| } |
| mActivity.getGLRoot().setOrientationSource(null); |
| if (mBottomControls != null) mBottomControls.cleanup(); |
| if (m3DButton != null) m3DButton.cleanup(); |
| mPhotoView.destroy(); |
| mPhotoView = null; |
| // Remove all pending messages. |
| mHandler.removeCallbacksAndMessages(null); |
| super.onDestroy(); |
| } |
| |
| private class MyDetailsSource implements DetailsSource { |
| |
| @Override |
| public MediaDetails getDetails() { |
| return mModel.getMediaItem(0).getDetails(); |
| } |
| |
| @Override |
| public int size() { |
| return mMediaSet != null ? mMediaSet.getMediaItemCount() : 1; |
| } |
| |
| @Override |
| public int setIndex() { |
| return mModel.getCurrentIndex(); |
| } |
| } |
| |
| /*@Override |
| public void onAlbumModeSelected(int mode) { |
| if (mode == GalleryActionBar.ALBUM_GRID_MODE_SELECTED) { |
| 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)); |
| } |
| } |
| } |
| |
| @Override |
| public void onUndoBarVisibilityChanged(boolean visible) { |
| refreshBottomControlsWhenReady(); |
| } |
| |
| private static String getMediaTypeString(MediaItem item) { |
| if (item.getMediaType() == MediaObject.MEDIA_TYPE_VIDEO) { |
| return "Video"; |
| } else if (item.getMediaType() == MediaObject.MEDIA_TYPE_IMAGE) { |
| return "Photo"; |
| } else { |
| return "Unknown:" + item.getMediaType(); |
| } |
| } |
| |
| private class ParseGDepthTask extends AsyncTask<Uri, Void, Boolean> { |
| @Override |
| protected Boolean doInBackground(Uri... params) { |
| GDepth.Parser parser = new GDepth.Parser(); |
| return parser.parse(mActivity, params[0]); |
| } |
| |
| @Override |
| protected void onPostExecute(Boolean result) { |
| bShow3DButton = !isCancelled() && result; |
| m3DButton.refresh(); |
| } |
| } |
| |
| private static void viewAnimateGif(Activity activity, Uri uri) { |
| Intent intent = new Intent(ViewGifImage.VIEW_GIF_ACTION, uri); |
| activity.startActivity(intent); |
| } |
| } |