diff options
Diffstat (limited to 'src/com/android/gallery3d/app/PhotoPage.java')
-rw-r--r-- | src/com/android/gallery3d/app/PhotoPage.java | 581 |
1 files changed, 581 insertions, 0 deletions
diff --git a/src/com/android/gallery3d/app/PhotoPage.java b/src/com/android/gallery3d/app/PhotoPage.java new file mode 100644 index 000000000..f28eb221d --- /dev/null +++ b/src/com/android/gallery3d/app/PhotoPage.java @@ -0,0 +1,581 @@ +/* + * 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 com.android.gallery3d.R; +import com.android.gallery3d.data.DataManager; +import com.android.gallery3d.data.MediaDetails; +import com.android.gallery3d.data.MediaItem; +import com.android.gallery3d.data.MediaObject; +import com.android.gallery3d.data.MediaSet; +import com.android.gallery3d.data.MtpDevice; +import com.android.gallery3d.data.Path; +import com.android.gallery3d.picasasource.PicasaSource; +import com.android.gallery3d.ui.DetailsWindow; +import com.android.gallery3d.ui.DetailsWindow.CloseListener; +import com.android.gallery3d.ui.DetailsWindow.DetailsSource; +import com.android.gallery3d.ui.FilmStripView; +import com.android.gallery3d.ui.GLCanvas; +import com.android.gallery3d.ui.GLView; +import com.android.gallery3d.ui.ImportCompleteListener; +import com.android.gallery3d.ui.MenuExecutor; +import com.android.gallery3d.ui.PhotoView; +import com.android.gallery3d.ui.PositionRepository; +import com.android.gallery3d.ui.PositionRepository.Position; +import com.android.gallery3d.ui.SelectionManager; +import com.android.gallery3d.ui.SynchronizedHandler; +import com.android.gallery3d.ui.UserInteractionListener; +import com.android.gallery3d.util.GalleryUtils; + +import android.app.ActionBar; +import android.app.ActionBar.OnMenuVisibilityListener; +import android.app.Activity; +import android.content.ActivityNotFoundException; +import android.content.Context; +import android.content.Intent; +import android.net.Uri; +import android.os.Bundle; +import android.os.Handler; +import android.os.Message; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.view.View; +import android.view.View.MeasureSpec; +import android.view.WindowManager; +import android.widget.ShareActionProvider; +import android.widget.Toast; + +public class PhotoPage extends ActivityState + implements PhotoView.PhotoTapListener, FilmStripView.Listener, + UserInteractionListener { + private static final String TAG = "PhotoPage"; + + private static final int MSG_HIDE_BARS = 1; + 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; + + 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"; + + private GalleryApp mApplication; + private SelectionManager mSelectionManager; + + private PhotoView mPhotoView; + private PhotoPage.Model mModel; + private FilmStripView mFilmStripView; + private DetailsWindow mDetailsWindow; + 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 MediaSet mMediaSet; + private Menu mMenu; + + private Intent mResultIntent = new Intent(); + private int mCurrentIndex = 0; + private Handler mHandler; + private boolean mShowBars; + private ActionBar mActionBar; + private MyMenuVisibilityListener mMenuVisibilityListener; + private boolean mIsMenuVisible; + private boolean mIsInteracting; + private MediaItem mCurrentPhoto = null; + private MenuExecutor mMenuExecutor; + private boolean mIsActive; + private ShareActionProvider mShareActionProvider; + + public static interface Model extends PhotoView.Model { + public void resume(); + public void pause(); + public boolean isEmpty(); + public MediaItem getCurrentMediaItem(); + public int getCurrentIndex(); + public void setCurrentPhoto(Path path, int indexHint); + } + + private class MyMenuVisibilityListener implements OnMenuVisibilityListener { + public void onMenuVisibilityChanged(boolean isVisible) { + mIsMenuVisible = isVisible; + refreshHidingMessage(); + } + } + + private GLView mRootPane = new GLView() { + + @Override + protected void renderBackground(GLCanvas view) { + view.clearBuffer(); + } + + @Override + protected void onLayout( + boolean changed, int left, int top, int right, int bottom) { + mPhotoView.layout(0, 0, right - left, bottom - top); + PositionRepository.getInstance(mActivity).setOffset(0, 0); + int filmStripHeight = 0; + if (mFilmStripView != null) { + mFilmStripView.measure( + MeasureSpec.makeMeasureSpec(right - left, MeasureSpec.EXACTLY), + MeasureSpec.UNSPECIFIED); + filmStripHeight = mFilmStripView.getMeasuredHeight(); + mFilmStripView.layout(0, bottom - top - filmStripHeight, + right - left, bottom - top); + } + if (mShowDetails) { + mDetailsWindow.measure( + MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED); + int width = mDetailsWindow.getMeasuredWidth(); + int viewTop = GalleryActionBar.getHeight((Activity) mActivity); + mDetailsWindow.layout( + 0, viewTop, width, bottom - top - filmStripHeight); + } + } + }; + + @Override + public void onCreate(Bundle data, Bundle restoreState) { + mActionBar = ((Activity) mActivity).getActionBar(); + mSelectionManager = new SelectionManager(mActivity, false); + mMenuExecutor = new MenuExecutor(mActivity, mSelectionManager); + + mPhotoView = new PhotoView(mActivity); + mPhotoView.setPhotoTapListener(this); + mRootPane.addComponent(mPhotoView); + mApplication = (GalleryApp)((Activity) mActivity).getApplication(); + + String setPathString = data.getString(KEY_MEDIA_SET_PATH); + Path itemPath = Path.fromString(data.getString(KEY_MEDIA_ITEM_PATH)); + + if (setPathString != null) { + mMediaSet = mActivity.getDataManager().getMediaSet(setPathString); + mCurrentIndex = data.getInt(KEY_INDEX_HINT, 0); + mMediaSet = (MediaSet) + mActivity.getDataManager().getMediaObject(setPathString); + if (mMediaSet == null) { + Log.w(TAG, "failed to restore " + setPathString); + } + PhotoDataAdapter pda = new PhotoDataAdapter( + mActivity, mPhotoView, mMediaSet, itemPath, mCurrentIndex); + mModel = pda; + mPhotoView.setModel(mModel); + + Config.PhotoPage config = Config.PhotoPage.get((Context) mActivity); + + mFilmStripView = new FilmStripView(mActivity, mMediaSet, + config.filmstripTopMargin, config.filmstripMidMargin, config.filmstripBottomMargin, + config.filmstripContentSize, config.filmstripThumbSize, config.filmstripBarSize, + config.filmstripGripSize, config.filmstripGripWidth); + mRootPane.addComponent(mFilmStripView); + mFilmStripView.setListener(this); + mFilmStripView.setUserInteractionListener(this); + mFilmStripView.setFocusIndex(mCurrentIndex); + mFilmStripView.setStartIndex(mCurrentIndex); + + mResultIntent.putExtra(KEY_INDEX_HINT, mCurrentIndex); + setStateResult(Activity.RESULT_OK, mResultIntent); + + pda.setDataListener(new PhotoDataAdapter.DataListener() { + + public void onPhotoChanged(int index, Path item) { + mFilmStripView.setFocusIndex(index); + mCurrentIndex = index; + mResultIntent.putExtra(KEY_INDEX_HINT, index); + if (item != null) { + mResultIntent.putExtra(KEY_MEDIA_ITEM_PATH, item.toString()); + MediaItem photo = mModel.getCurrentMediaItem(); + if (photo != null) updateCurrentPhoto(photo); + } else { + mResultIntent.removeExtra(KEY_MEDIA_ITEM_PATH); + } + setStateResult(Activity.RESULT_OK, mResultIntent); + } + + @Override + public void onLoadingFinished() { + GalleryUtils.setSpinnerVisibility((Activity) mActivity, false); + if (!mModel.isEmpty()) { + MediaItem photo = mModel.getCurrentMediaItem(); + if (photo != null) updateCurrentPhoto(photo); + } else if (mIsActive) { + mActivity.getStateManager().finishState(PhotoPage.this); + } + } + + + @Override + public void onLoadingStarted() { + GalleryUtils.setSpinnerVisibility((Activity) mActivity, true); + } + }); + } 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); + } + mHandler = new SynchronizedHandler(mActivity.getGLRoot()) { + @Override + public void handleMessage(Message message) { + switch (message.what) { + case MSG_HIDE_BARS: { + hideBars(); + break; + } + default: throw new AssertionError(message.what); + } + } + }; + + // start the opening animation + mPhotoView.setOpenedItem(itemPath); + } + + private void updateCurrentPhoto(MediaItem photo) { + if (mCurrentPhoto == photo) return; + mCurrentPhoto = photo; + if (mCurrentPhoto == null) return; + updateMenuOperations(); + if (mShowDetails) { + mDetailsWindow.reloadDetails(mModel.getCurrentIndex()); + } + String title = photo.getName(); + if (title != null) mActionBar.setTitle(title); + mPhotoView.showVideoPlayIcon(photo.getMediaType() + == MediaObject.MEDIA_TYPE_VIDEO); + + // If we have an ActionBar then we update the share intent + if (mShareActionProvider != null) { + Path path = photo.getPath(); + DataManager manager = mActivity.getDataManager(); + int type = manager.getMediaType(path); + Intent intent = new Intent(Intent.ACTION_SEND); + intent.setType(MenuExecutor.getMimeType(type)); + intent.putExtra(Intent.EXTRA_STREAM, manager.getContentUri(path)); + mShareActionProvider.setShareIntent(intent); + } + } + + private void updateMenuOperations() { + if (mCurrentPhoto == null || mMenu == null) return; + int supportedOperations = mCurrentPhoto.getSupportedOperations(); + if (!GalleryUtils.isEditorAvailable((Context) mActivity, "image/*")) { + supportedOperations &= ~MediaObject.SUPPORT_EDIT; + } + MenuExecutor.updateMenuOperation(mMenu, supportedOperations); + } + + private void showBars() { + if (mShowBars) return; + mShowBars = true; + mActionBar.show(); + WindowManager.LayoutParams params = ((Activity) mActivity).getWindow().getAttributes(); + params.systemUiVisibility = View.SYSTEM_UI_FLAG_VISIBLE; + ((Activity) mActivity).getWindow().setAttributes(params); + if (mFilmStripView != null) { + mFilmStripView.show(); + } + } + + private void hideBars() { + if (!mShowBars) return; + mShowBars = false; + mActionBar.hide(); + WindowManager.LayoutParams params = ((Activity) mActivity).getWindow().getAttributes(); + params.systemUiVisibility = View. SYSTEM_UI_FLAG_LOW_PROFILE; + ((Activity) mActivity).getWindow().setAttributes(params); + if (mFilmStripView != null) { + mFilmStripView.hide(); + } + } + + private void refreshHidingMessage() { + mHandler.removeMessages(MSG_HIDE_BARS); + if (!mIsMenuVisible && !mIsInteracting) { + mHandler.sendEmptyMessageDelayed(MSG_HIDE_BARS, HIDE_BARS_TIMEOUT); + } + } + + public void onUserInteraction() { + showBars(); + refreshHidingMessage(); + } + + public void onUserInteractionTap() { + if (mShowBars) { + hideBars(); + mHandler.removeMessages(MSG_HIDE_BARS); + } else { + showBars(); + refreshHidingMessage(); + } + } + + public void onUserInteractionBegin() { + showBars(); + mIsInteracting = true; + refreshHidingMessage(); + } + + public void onUserInteractionEnd() { + mIsInteracting = false; + refreshHidingMessage(); + } + + @Override + protected void onBackPressed() { + if (mShowDetails) { + hideDetails(); + } else { + PositionRepository repository = PositionRepository.getInstance(mActivity); + repository.clear(); + if (mCurrentPhoto != null) { + Position position = new Position(); + position.x = mRootPane.getWidth() / 2; + position.y = mRootPane.getHeight() / 2; + position.z = -1000; + repository.putPosition( + Long.valueOf(System.identityHashCode(mCurrentPhoto.getPath())), + position); + } + super.onBackPressed(); + } + } + + @Override + protected boolean onCreateActionBar(Menu menu) { + MenuInflater inflater = ((Activity) mActivity).getMenuInflater(); + inflater.inflate(R.menu.photo, menu); + menu.findItem(R.id.action_slideshow).setVisible( + mMediaSet != null && !(mMediaSet instanceof MtpDevice)); + mShareActionProvider = GalleryActionBar.initializeShareActionProvider(menu); + mMenu = menu; + mShowBars = true; + updateMenuOperations(); + return true; + } + + @Override + protected boolean onItemSelected(MenuItem item) { + MediaItem current = mModel.getCurrentMediaItem(); + + if (current == null) { + // item is not ready, ignore + return true; + } + + int currentIndex = mModel.getCurrentIndex(); + Path path = current.getPath(); + + DataManager manager = mActivity.getDataManager(); + int action = item.getItemId(); + switch (action) { + case R.id.action_slideshow: { + Bundle data = new Bundle(); + data.putString(SlideshowPage.KEY_SET_PATH, + mMediaSet.getPath().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 = (Activity) mActivity; + Intent intent = new Intent(CropImage.CROP_ACTION); + intent.setClass(activity, CropImage.class); + intent.setData(manager.getContentUri(path)); + activity.startActivityForResult(intent, PicasaSource.isPicasaImage(current) + ? REQUEST_CROP_PICASA + : REQUEST_CROP); + return true; + } + case R.id.action_details: { + if (mShowDetails) { + hideDetails(); + } else { + showDetails(currentIndex); + } + return true; + } + case R.id.action_setas: + case R.id.action_confirm_delete: + case R.id.action_rotate_ccw: + case R.id.action_rotate_cw: + case R.id.action_show_on_map: + case R.id.action_edit: + mSelectionManager.deSelectAll(); + mSelectionManager.toggle(path); + mMenuExecutor.onMenuClicked(item, null); + return true; + case R.id.action_import: + mSelectionManager.deSelectAll(); + mSelectionManager.toggle(path); + mMenuExecutor.onMenuClicked(item, + new ImportCompleteListener(mActivity)); + return true; + default : + return false; + } + } + + private void hideDetails() { + mShowDetails = false; + mDetailsWindow.hide(); + } + + private void showDetails(int index) { + mShowDetails = true; + if (mDetailsWindow == null) { + mDetailsWindow = new DetailsWindow(mActivity, new MyDetailsSource()); + mDetailsWindow.setCloseListener(new CloseListener() { + public void onClose() { + hideDetails(); + } + }); + mRootPane.addComponent(mDetailsWindow); + } + mDetailsWindow.reloadDetails(index); + mDetailsWindow.show(); + } + + public void onSingleTapUp(int x, int y) { + MediaItem item = mModel.getCurrentMediaItem(); + if (item == null) { + // item is not ready, ignore + return; + } + + boolean playVideo = + (item.getSupportedOperations() & MediaItem.SUPPORT_PLAY) != 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) { + playVideo((Activity) mActivity, item.getPlayUri(), item.getName()); + } else { + onUserInteractionTap(); + } + } + + public static void playVideo(Activity activity, Uri uri, String title) { + try { + Intent intent = new Intent(Intent.ACTION_VIEW) + .setDataAndType(uri, "video/*"); + intent.putExtra(Intent.EXTRA_TITLE, title); + activity.startActivity(intent); + } catch (ActivityNotFoundException e) { + Toast.makeText(activity, activity.getString(R.string.video_err), + Toast.LENGTH_SHORT).show(); + } + } + + // Called by FileStripView + public void onSlotSelected(int slotIndex) { + ((PhotoDataAdapter) mModel).jumpTo(slotIndex); + } + + @Override + protected void onStateResult(int requestCode, int resultCode, Intent data) { + switch (requestCode) { + case REQUEST_CROP: + if (resultCode == Activity.RESULT_OK) { + if (data == null) break; + Path path = mApplication + .getDataManager().findPathByUri(data.getData()); + if (path != null) { + mModel.setCurrentPhoto(path, mCurrentIndex); + } + } + break; + case REQUEST_CROP_PICASA: { + int message = resultCode == Activity.RESULT_OK + ? R.string.crop_saved + : R.string.crop_not_saved; + Toast.makeText(mActivity.getAndroidContext(), + 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 (path != null) { + mModel.setCurrentPhoto(Path.fromString(path), index); + } + } + } + } + + @Override + public void onPause() { + super.onPause(); + mIsActive = false; + if (mFilmStripView != null) { + mFilmStripView.pause(); + } + if (mDetailsWindow != null) { + mDetailsWindow.pause(); + } + mPhotoView.pause(); + mModel.pause(); + mHandler.removeMessages(MSG_HIDE_BARS); + mActionBar.removeOnMenuVisibilityListener(mMenuVisibilityListener); + } + + @Override + protected void onResume() { + super.onResume(); + mIsActive = true; + setContentPane(mRootPane); + mModel.resume(); + mPhotoView.resume(); + if (mFilmStripView != null) { + mFilmStripView.resume(); + } + if (mMenuVisibilityListener == null) { + mMenuVisibilityListener = new MyMenuVisibilityListener(); + } + mActionBar.addOnMenuVisibilityListener(mMenuVisibilityListener); + onUserInteraction(); + } + + private class MyDetailsSource implements DetailsSource { + public MediaDetails getDetails() { + return mModel.getCurrentMediaItem().getDetails(); + } + public int size() { + return mMediaSet != null ? mMediaSet.getMediaItemCount() : 1; + } + public int findIndex(int indexHint) { + return indexHint; + } + } +} |