PhotoView.java revision 2b3ee0ea07246b859a5b75d8a6102a7cce7ec838
1f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin/* 2f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * Copyright (C) 2010 The Android Open Source Project 3f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * 4f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * Licensed under the Apache License, Version 2.0 (the "License"); 5f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * you may not use this file except in compliance with the License. 6f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * You may obtain a copy of the License at 7f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * 8f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * http://www.apache.org/licenses/LICENSE-2.0 9f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * 10f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * Unless required by applicable law or agreed to in writing, software 11f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * distributed under the License is distributed on an "AS IS" BASIS, 12f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * See the License for the specific language governing permissions and 14f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * limitations under the License. 15f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin */ 16f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 17f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linpackage com.android.gallery3d.ui; 18f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 19f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport android.content.Context; 20f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport android.graphics.Bitmap; 21f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport android.graphics.Color; 22f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport android.graphics.RectF; 23f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport android.os.Message; 24f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport android.view.GestureDetector; 25f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport android.view.MotionEvent; 26f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport android.view.ScaleGestureDetector; 27f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 282b3ee0ea07246b859a5b75d8a6102a7cce7ec838Owen Linimport com.android.gallery3d.R; 292b3ee0ea07246b859a5b75d8a6102a7cce7ec838Owen Linimport com.android.gallery3d.app.GalleryActivity; 302b3ee0ea07246b859a5b75d8a6102a7cce7ec838Owen Linimport com.android.gallery3d.data.Path; 312b3ee0ea07246b859a5b75d8a6102a7cce7ec838Owen Linimport com.android.gallery3d.ui.PositionRepository.Position; 322b3ee0ea07246b859a5b75d8a6102a7cce7ec838Owen Lin 33f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linpublic class PhotoView extends GLView { 34f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin @SuppressWarnings("unused") 35f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private static final String TAG = "PhotoView"; 36f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 37f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin public static final int INVALID_SIZE = -1; 38f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 39f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private static final int MSG_TRANSITION_COMPLETE = 1; 40f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private static final int MSG_SHOW_LOADING = 2; 41f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 42f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private static final long DELAY_SHOW_LOADING = 250; // 250ms; 43f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 44f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private static final int TRANS_NONE = 0; 45f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private static final int TRANS_SWITCH_NEXT = 3; 46f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private static final int TRANS_SWITCH_PREVIOUS = 4; 47f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 48f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin public static final int TRANS_SLIDE_IN_RIGHT = 1; 49f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin public static final int TRANS_SLIDE_IN_LEFT = 2; 50f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin public static final int TRANS_OPEN_ANIMATION = 5; 51f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 52f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private static final int LOADING_INIT = 0; 53f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private static final int LOADING_TIMEOUT = 1; 54f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private static final int LOADING_COMPLETE = 2; 55f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private static final int LOADING_FAIL = 3; 56f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 57f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private static final int ENTRY_PREVIOUS = 0; 58f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private static final int ENTRY_NEXT = 1; 59f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 60f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private static final int IMAGE_GAP = 96; 61f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private static final int SWITCH_THRESHOLD = 256; 62f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private static final float SWIPE_THRESHOLD = 300f; 63f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 64f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private static final float DEFAULT_TEXT_SIZE = 20; 65f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 66f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin public interface PhotoTapListener { 67f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin public void onSingleTapUp(int x, int y); 68f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 69f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 70f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin // the previous/next image entries 71f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private final ScreenNailEntry mScreenNails[] = new ScreenNailEntry[2]; 72f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 73f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private final ScaleGestureDetector mScaleDetector; 74f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private final GestureDetector mGestureDetector; 75f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private final DownUpDetector mDownUpDetector; 76f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 77f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private PhotoTapListener mPhotoTapListener; 78f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 79f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private final PositionController mPositionController; 80f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 81f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private Model mModel; 82f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private StringTexture mLoadingText; 83f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private StringTexture mNoThumbnailText; 84f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private int mTransitionMode = TRANS_NONE; 85f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private final TileImageView mTileView; 86532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang private EdgeView mEdgeView; 87f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private Texture mVideoPlayIcon; 88f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 89f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private boolean mShowVideoPlayIcon; 90f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private ProgressSpinner mLoadingSpinner; 91f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 92f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private SynchronizedHandler mHandler; 93f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 94f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private int mLoadingState = LOADING_COMPLETE; 95f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 96f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private int mImageRotation; 97f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 98f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private Path mOpenedItemPath; 99f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private GalleryActivity mActivity; 100f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 101f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin public PhotoView(GalleryActivity activity) { 102f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mActivity = activity; 103f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mTileView = new TileImageView(activity); 104f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin addComponent(mTileView); 105f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin Context context = activity.getAndroidContext(); 106532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang mEdgeView = new EdgeView(context); 107532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang addComponent(mEdgeView); 108f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mLoadingSpinner = new ProgressSpinner(context); 109f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mLoadingText = StringTexture.newInstance( 110f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin context.getString(R.string.loading), 111f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin DEFAULT_TEXT_SIZE, Color.WHITE); 112f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mNoThumbnailText = StringTexture.newInstance( 113f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin context.getString(R.string.no_thumbnail), 114f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin DEFAULT_TEXT_SIZE, Color.WHITE); 115f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 116f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mHandler = new SynchronizedHandler(activity.getGLRoot()) { 117f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin @Override 118f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin public void handleMessage(Message message) { 119f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin switch (message.what) { 120f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin case MSG_TRANSITION_COMPLETE: { 121f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin onTransitionComplete(); 122f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin break; 123f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 124f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin case MSG_SHOW_LOADING: { 125f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin if (mLoadingState == LOADING_INIT) { 126f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin // We don't need the opening animation 127f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mOpenedItemPath = null; 128f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 129f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mLoadingSpinner.startAnimation(); 130f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mLoadingState = LOADING_TIMEOUT; 131f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin invalidate(); 132f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 133f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin break; 134f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 135f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin default: throw new AssertionError(message.what); 136f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 137f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 138f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin }; 139f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 140f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mGestureDetector = new GestureDetector(context, 141f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin new MyGestureListener(), null, true /* ignoreMultitouch */); 142f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mScaleDetector = new ScaleGestureDetector(context, new MyScaleListener()); 143f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mDownUpDetector = new DownUpDetector(new MyDownUpListener()); 144f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 145f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin for (int i = 0, n = mScreenNails.length; i < n; ++i) { 146f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mScreenNails[i] = new ScreenNailEntry(); 147f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 148f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 149532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang mPositionController = new PositionController(this, context, mEdgeView); 150f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mVideoPlayIcon = new ResourceTexture(context, R.drawable.ic_control_play); 151f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 152f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 153f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 154f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin public void setModel(Model model) { 155f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin if (mModel == model) return; 156f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mModel = model; 157f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mTileView.setModel(model); 158f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin if (model != null) notifyOnNewImage(); 159f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 160f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 161f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin public void setPhotoTapListener(PhotoTapListener listener) { 162f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mPhotoTapListener = listener; 163f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 164f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 165f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private boolean setTileViewPosition(int centerX, int centerY, float scale) { 166ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang int inverseX = mPositionController.getImageWidth() - centerX; 167ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang int inverseY = mPositionController.getImageHeight() - centerY; 168f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin TileImageView t = mTileView; 169f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin int rotation = mImageRotation; 170f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin switch (rotation) { 171f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin case 0: return t.setPosition(centerX, centerY, scale, 0); 172f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin case 90: return t.setPosition(centerY, inverseX, scale, 90); 173f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin case 180: return t.setPosition(inverseX, inverseY, scale, 180); 174f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin case 270: return t.setPosition(inverseY, centerX, scale, 270); 175f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin default: throw new IllegalArgumentException(String.valueOf(rotation)); 176f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 177f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 178f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 179f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin public void setPosition(int centerX, int centerY, float scale) { 180f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin if (setTileViewPosition(centerX, centerY, scale)) { 181f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin layoutScreenNails(); 182f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 183f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 184f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 185f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private void updateScreenNailEntry(int which, ImageData data) { 186f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin if (mTransitionMode == TRANS_SWITCH_NEXT 187f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin || mTransitionMode == TRANS_SWITCH_PREVIOUS) { 188f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin // ignore screen nail updating during switching 189f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin return; 190f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 191f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin ScreenNailEntry entry = mScreenNails[which]; 192f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin if (data == null) { 193f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin entry.set(false, null, 0); 194f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } else { 195f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin entry.set(true, data.bitmap, data.rotation); 196f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 197f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 198f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 199f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin // -1 previous, 0 current, 1 next 200f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin public void notifyImageInvalidated(int which) { 201f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin switch (which) { 202f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin case -1: { 203f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin updateScreenNailEntry( 204f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin ENTRY_PREVIOUS, mModel.getPreviousImage()); 205f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin layoutScreenNails(); 206f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin invalidate(); 207f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin break; 208f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 209f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin case 1: { 210f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin updateScreenNailEntry(ENTRY_NEXT, mModel.getNextImage()); 211f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin layoutScreenNails(); 212f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin invalidate(); 213f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin break; 214f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 215f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin case 0: { 216f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin // mImageWidth and mImageHeight will get updated 217f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mTileView.notifyModelInvalidated(); 218f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 219f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mImageRotation = mModel.getImageRotation(); 220f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin if (((mImageRotation / 90) & 1) == 0) { 221f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mPositionController.setImageSize( 222f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mTileView.mImageWidth, mTileView.mImageHeight); 223f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } else { 224f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mPositionController.setImageSize( 225f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mTileView.mImageHeight, mTileView.mImageWidth); 226f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 227f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin updateLoadingState(); 228f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin break; 229f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 230f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 231f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 232f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 233f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private void updateLoadingState() { 234f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin // Possible transitions of mLoadingState: 235f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin // INIT --> TIMEOUT, COMPLETE, FAIL 236f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin // TIMEOUT --> COMPLETE, FAIL, INIT 237f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin // COMPLETE --> INIT 238f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin // FAIL --> INIT 239f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin if (mModel.getLevelCount() != 0 || mModel.getBackupImage() != null) { 240f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mHandler.removeMessages(MSG_SHOW_LOADING); 241f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mLoadingState = LOADING_COMPLETE; 242f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } else if (mModel.isFailedToLoad()) { 243f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mHandler.removeMessages(MSG_SHOW_LOADING); 244f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mLoadingState = LOADING_FAIL; 245a7b78e224b1808895ea2c3d42ae385526dea12aaYuli Huang // We don't want the opening animation after loading failure 246a7b78e224b1808895ea2c3d42ae385526dea12aaYuli Huang mOpenedItemPath = null; 247f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } else if (mLoadingState != LOADING_INIT) { 248f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mLoadingState = LOADING_INIT; 249f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mHandler.removeMessages(MSG_SHOW_LOADING); 250f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mHandler.sendEmptyMessageDelayed( 251f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin MSG_SHOW_LOADING, DELAY_SHOW_LOADING); 252f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 253f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 254f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 255f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin public void notifyModelInvalidated() { 256f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin if (mModel == null) { 257f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin updateScreenNailEntry(ENTRY_PREVIOUS, null); 258f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin updateScreenNailEntry(ENTRY_NEXT, null); 259f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } else { 260f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin updateScreenNailEntry(ENTRY_PREVIOUS, mModel.getPreviousImage()); 261f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin updateScreenNailEntry(ENTRY_NEXT, mModel.getNextImage()); 262f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 263f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin layoutScreenNails(); 264f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 265f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin if (mModel == null) { 266f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mTileView.notifyModelInvalidated(); 267f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mImageRotation = 0; 268f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mPositionController.setImageSize(0, 0); 269f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin updateLoadingState(); 270f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } else { 271f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin notifyImageInvalidated(0); 272f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 273f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 274f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 275f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin @Override 276f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin protected boolean onTouch(MotionEvent event) { 277f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mGestureDetector.onTouchEvent(event); 278f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mScaleDetector.onTouchEvent(event); 279f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mDownUpDetector.onTouchEvent(event); 280f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin return true; 281f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 282f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 283f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin @Override 284f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin protected void onLayout( 285f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin boolean changeSize, int left, int top, int right, int bottom) { 286f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mTileView.layout(left, top, right, bottom); 287532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang mEdgeView.layout(left, top, right, bottom); 288f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin if (changeSize) { 289f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mPositionController.setViewSize(getWidth(), getHeight()); 290f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin for (ScreenNailEntry entry : mScreenNails) { 291f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin entry.updateDrawingSize(); 292f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 293f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 294f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 295f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 296f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private static int gapToSide(int imageWidth, int viewWidth) { 297f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin return Math.max(0, (viewWidth - imageWidth) / 2); 298f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 299f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 300f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin /* 301f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * Here is how we layout the screen nails 302f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * 303f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * previous current next 304f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * ___________ ________________ __________ 305f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * | _______ | | __________ | | ______ | 306f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * | | | | | | right->| | | | | | 307f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * | | |<-------->|<--left | | | | | | 308f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * | |_______| | | | |__________| | | |______| | 309f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * |___________| | |________________| |__________| 310f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * | <--> gapToSide() 311f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * | 312f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * IMAGE_GAP + Max(previous.gapToSide(), current.gapToSide) 313f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin */ 314f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private void layoutScreenNails() { 315f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin int width = getWidth(); 316f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin int height = getHeight(); 317f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 318f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin // Use the image width in AC, since we may fake the size if the 319f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin // image is unavailable 320ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang RectF bounds = mPositionController.getImageBounds(); 321f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin int left = Math.round(bounds.left); 322f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin int right = Math.round(bounds.right); 323f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin int gap = gapToSide(right - left, width); 324f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 325f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin // layout the previous image 326f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin ScreenNailEntry entry = mScreenNails[ENTRY_PREVIOUS]; 327f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 328f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin if (entry.isEnabled()) { 329f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin entry.layoutRightEdgeAt(left - ( 330f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin IMAGE_GAP + Math.max(gap, entry.gapToSide()))); 331f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 332f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 333f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin // layout the next image 334f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin entry = mScreenNails[ENTRY_NEXT]; 335f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin if (entry.isEnabled()) { 336f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin entry.layoutLeftEdgeAt(right + ( 337f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin IMAGE_GAP + Math.max(gap, entry.gapToSide()))); 338f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 339f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 340f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 341f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin @Override 342f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin protected void render(GLCanvas canvas) { 343f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin PositionController p = mPositionController; 344f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 345f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin // Draw the current photo 346f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin if (mLoadingState == LOADING_COMPLETE) { 347f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin super.render(canvas); 348f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 349f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 350f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin // Draw the previous and the next photo 351f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin if (mTransitionMode != TRANS_SLIDE_IN_LEFT 352f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin && mTransitionMode != TRANS_SLIDE_IN_RIGHT 353f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin && mTransitionMode != TRANS_OPEN_ANIMATION) { 354f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin ScreenNailEntry prevNail = mScreenNails[ENTRY_PREVIOUS]; 355f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin ScreenNailEntry nextNail = mScreenNails[ENTRY_NEXT]; 356f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 357f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin if (prevNail.mVisible) prevNail.draw(canvas); 358f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin if (nextNail.mVisible) nextNail.draw(canvas); 359f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 360f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 361f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin // Draw the progress spinner and the text below it 362f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin // 363f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin // (x, y) is where we put the center of the spinner. 364f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin // s is the size of the video play icon, and we use s to layout text 365f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin // because we want to keep the text at the same place when the video 366f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin // play icon is shown instead of the spinner. 367f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin int w = getWidth(); 368f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin int h = getHeight(); 369ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang int x = Math.round(mPositionController.getImageBounds().centerX()); 370f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin int y = h / 2; 371f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin int s = Math.min(getWidth(), getHeight()) / 6; 372f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 373f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin if (mLoadingState == LOADING_TIMEOUT) { 374f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin StringTexture m = mLoadingText; 375f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin ProgressSpinner r = mLoadingSpinner; 376f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin r.draw(canvas, x - r.getWidth() / 2, y - r.getHeight() / 2); 377f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin m.draw(canvas, x - m.getWidth() / 2, y + s / 2 + 5); 378f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin invalidate(); // we need to keep the spinner rotating 379f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } else if (mLoadingState == LOADING_FAIL) { 380f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin StringTexture m = mNoThumbnailText; 381f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin m.draw(canvas, x - m.getWidth() / 2, y + s / 2 + 5); 382f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 383f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 384f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin // Draw the video play icon (in the place where the spinner was) 385f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin if (mShowVideoPlayIcon 386f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin && mLoadingState != LOADING_INIT 387f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin && mLoadingState != LOADING_TIMEOUT) { 388f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mVideoPlayIcon.draw(canvas, x - s / 2, y - s / 2, s, s); 389f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 390f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 3916068de20f4f19ca82ca01bef3b6dda796a79f724Yuli Huang mPositionController.advanceAnimation(); 392f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 393f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 394f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private void stopCurrentSwipingIfNeeded() { 395f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin // Enable fast sweeping 396f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin if (mTransitionMode == TRANS_SWITCH_NEXT) { 397f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mTransitionMode = TRANS_NONE; 398f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mPositionController.stopAnimation(); 399f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin switchToNextImage(); 400f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } else if (mTransitionMode == TRANS_SWITCH_PREVIOUS) { 401f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mTransitionMode = TRANS_NONE; 402f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mPositionController.stopAnimation(); 403f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin switchToPreviousImage(); 404f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 405f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 406f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 4072ce3c3bfe08fff5aee58007cc8ba8f4a50861ae2Yuli Huang private boolean swipeImages(float velocityX, float velocityY) { 408f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin if (mTransitionMode != TRANS_NONE 409f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin && mTransitionMode != TRANS_SWITCH_NEXT 410f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin && mTransitionMode != TRANS_SWITCH_PREVIOUS) return false; 411f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 4122ce3c3bfe08fff5aee58007cc8ba8f4a50861ae2Yuli Huang // Avoid swiping images if we're possibly flinging to view the 4132ce3c3bfe08fff5aee58007cc8ba8f4a50861ae2Yuli Huang // zoomed in picture vertically. 414f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin PositionController controller = mPositionController; 415532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang boolean isMinimal = controller.isAtMinimalScale(); 4162ce3c3bfe08fff5aee58007cc8ba8f4a50861ae2Yuli Huang int edges = controller.getImageAtEdges(); 4172ce3c3bfe08fff5aee58007cc8ba8f4a50861ae2Yuli Huang if (!isMinimal && Math.abs(velocityY) > Math.abs(velocityX)) 4182ce3c3bfe08fff5aee58007cc8ba8f4a50861ae2Yuli Huang if ((edges & PositionController.IMAGE_AT_TOP_EDGE) == 0 4192ce3c3bfe08fff5aee58007cc8ba8f4a50861ae2Yuli Huang || (edges & PositionController.IMAGE_AT_BOTTOM_EDGE) == 0) 4202ce3c3bfe08fff5aee58007cc8ba8f4a50861ae2Yuli Huang return false; 421532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang 4222ce3c3bfe08fff5aee58007cc8ba8f4a50861ae2Yuli Huang // If we are at the edge of the current photo and the sweeping velocity 4232ce3c3bfe08fff5aee58007cc8ba8f4a50861ae2Yuli Huang // exceeds the threshold, switch to next / previous image. 4242ce3c3bfe08fff5aee58007cc8ba8f4a50861ae2Yuli Huang int halfWidth = getWidth() / 2; 4252ce3c3bfe08fff5aee58007cc8ba8f4a50861ae2Yuli Huang if (velocityX < -SWIPE_THRESHOLD && (isMinimal 4262ce3c3bfe08fff5aee58007cc8ba8f4a50861ae2Yuli Huang || (edges & PositionController.IMAGE_AT_RIGHT_EDGE) != 0)) { 427532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang stopCurrentSwipingIfNeeded(); 4282ce3c3bfe08fff5aee58007cc8ba8f4a50861ae2Yuli Huang ScreenNailEntry next = mScreenNails[ENTRY_NEXT]; 429532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang if (next.isEnabled()) { 430532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang mTransitionMode = TRANS_SWITCH_NEXT; 4312ce3c3bfe08fff5aee58007cc8ba8f4a50861ae2Yuli Huang controller.startHorizontalSlide(next.mOffsetX - halfWidth); 432532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang return true; 433f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 4342ce3c3bfe08fff5aee58007cc8ba8f4a50861ae2Yuli Huang } else if (velocityX > SWIPE_THRESHOLD && (isMinimal 4352ce3c3bfe08fff5aee58007cc8ba8f4a50861ae2Yuli Huang || (edges & PositionController.IMAGE_AT_LEFT_EDGE) != 0)) { 436532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang stopCurrentSwipingIfNeeded(); 4372ce3c3bfe08fff5aee58007cc8ba8f4a50861ae2Yuli Huang ScreenNailEntry prev = mScreenNails[ENTRY_PREVIOUS]; 438532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang if (prev.isEnabled()) { 439532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang mTransitionMode = TRANS_SWITCH_PREVIOUS; 4402ce3c3bfe08fff5aee58007cc8ba8f4a50861ae2Yuli Huang controller.startHorizontalSlide(prev.mOffsetX - halfWidth); 441532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang return true; 442f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 443f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 444f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 445532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang return false; 446532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang } 447532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang 4482ce3c3bfe08fff5aee58007cc8ba8f4a50861ae2Yuli Huang private boolean snapToNeighborImage() { 449f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin if (mTransitionMode != TRANS_NONE) return false; 450f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 451532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang PositionController controller = mPositionController; 452b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang RectF bounds = controller.getImageBounds(); 453f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin int left = Math.round(bounds.left); 454f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin int right = Math.round(bounds.right); 4552ce3c3bfe08fff5aee58007cc8ba8f4a50861ae2Yuli Huang int width = getWidth(); 456f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin int threshold = SWITCH_THRESHOLD + gapToSide(right - left, width); 457f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 458f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin // If we have moved the picture a lot, switching. 4592ce3c3bfe08fff5aee58007cc8ba8f4a50861ae2Yuli Huang ScreenNailEntry next = mScreenNails[ENTRY_NEXT]; 460f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin if (next.isEnabled() && threshold < width - right) { 461f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mTransitionMode = TRANS_SWITCH_NEXT; 462f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin controller.startHorizontalSlide(next.mOffsetX - width / 2); 463f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin return true; 464f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 4652ce3c3bfe08fff5aee58007cc8ba8f4a50861ae2Yuli Huang ScreenNailEntry prev = mScreenNails[ENTRY_PREVIOUS]; 466f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin if (prev.isEnabled() && threshold < left) { 467f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mTransitionMode = TRANS_SWITCH_PREVIOUS; 468f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin controller.startHorizontalSlide(prev.mOffsetX - width / 2); 469f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin return true; 470f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 471f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 472f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin return false; 473f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 474f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 475f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private boolean mIgnoreUpEvent = false; 476f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 477f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private class MyGestureListener 478f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin extends GestureDetector.SimpleOnGestureListener { 479f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin @Override 480f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin public boolean onScroll( 481f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin MotionEvent e1, MotionEvent e2, float dx, float dy) { 482f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin if (mTransitionMode != TRANS_NONE) return true; 483532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang 484532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang ScreenNailEntry next = mScreenNails[ENTRY_NEXT]; 485532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang ScreenNailEntry prev = mScreenNails[ENTRY_PREVIOUS]; 486532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang 487532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang mPositionController.startScroll(dx, dy, next.isEnabled(), 488532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang prev.isEnabled()); 489f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin return true; 490f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 491f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 492f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin @Override 493f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin public boolean onSingleTapUp(MotionEvent e) { 494f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin if (mPhotoTapListener != null) { 495f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mPhotoTapListener.onSingleTapUp((int) e.getX(), (int) e.getY()); 496f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 497f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin return true; 498f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 499f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 500f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin @Override 501f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, 502f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin float velocityY) { 5032ce3c3bfe08fff5aee58007cc8ba8f4a50861ae2Yuli Huang if (swipeImages(velocityX, velocityY)) { 504b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang mIgnoreUpEvent = true; 505b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang } else if (mTransitionMode != TRANS_NONE) { 506b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang // do nothing 507b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang } else if (mPositionController.fling(velocityX, velocityY)) { 508b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang mIgnoreUpEvent = true; 509f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 510f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin return true; 511f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 512f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 513f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin @Override 514f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin public boolean onDoubleTap(MotionEvent e) { 515f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin if (mTransitionMode != TRANS_NONE) return true; 516f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin PositionController controller = mPositionController; 517ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang float scale = controller.getCurrentScale(); 518f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin // onDoubleTap happened on the second ACTION_DOWN. 519f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin // We need to ignore the next UP event. 520f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mIgnoreUpEvent = true; 521ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang if (scale <= 1.0f || controller.isAtMinimalScale()) { 522f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin controller.zoomIn( 523f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin e.getX(), e.getY(), Math.max(1.5f, scale * 1.5f)); 524f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } else { 525f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin controller.resetToFullView(); 526f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 527f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin return true; 528f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 529f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 530f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 531f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private class MyScaleListener 532f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin extends ScaleGestureDetector.SimpleOnScaleGestureListener { 533f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 534f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin @Override 535f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin public boolean onScale(ScaleGestureDetector detector) { 536f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin float scale = detector.getScaleFactor(); 537f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin if (Float.isNaN(scale) || Float.isInfinite(scale) 538f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin || mTransitionMode != TRANS_NONE) return true; 539f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mPositionController.scaleBy(scale, 540f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin detector.getFocusX(), detector.getFocusY()); 541f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin return true; 542f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 543f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 544f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin @Override 545f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin public boolean onScaleBegin(ScaleGestureDetector detector) { 546f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin if (mTransitionMode != TRANS_NONE) return false; 547f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mPositionController.beginScale( 548f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin detector.getFocusX(), detector.getFocusY()); 549f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin return true; 550f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 551f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 552f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin @Override 553f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin public void onScaleEnd(ScaleGestureDetector detector) { 554f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mPositionController.endScale(); 555532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang snapToNeighborImage(); 556f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 557f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 558f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 5593a54b9ae67eb7b38f19ea0bdeeef26efc0a1afa1Chih-Chung Chang public boolean jumpTo(int index) { 5603a54b9ae67eb7b38f19ea0bdeeef26efc0a1afa1Chih-Chung Chang if (mTransitionMode != TRANS_NONE) return false; 5613a54b9ae67eb7b38f19ea0bdeeef26efc0a1afa1Chih-Chung Chang mModel.jumpTo(index); 5623a54b9ae67eb7b38f19ea0bdeeef26efc0a1afa1Chih-Chung Chang return true; 5633a54b9ae67eb7b38f19ea0bdeeef26efc0a1afa1Chih-Chung Chang } 5643a54b9ae67eb7b38f19ea0bdeeef26efc0a1afa1Chih-Chung Chang 565f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin public void notifyOnNewImage() { 566f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mPositionController.setImageSize(0, 0); 567f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 568f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 569f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin public void startSlideInAnimation(int direction) { 570f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin PositionController a = mPositionController; 571f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin a.stopAnimation(); 572f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin switch (direction) { 573ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang case TRANS_SLIDE_IN_LEFT: 574f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin case TRANS_SLIDE_IN_RIGHT: { 575ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang mTransitionMode = direction; 576ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang a.startSlideInAnimation(direction); 577f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin break; 578f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 579f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin default: throw new IllegalArgumentException(String.valueOf(direction)); 580f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 581f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 582f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 583f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private class MyDownUpListener implements DownUpDetector.DownUpListener { 584f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin public void onDown(MotionEvent e) { 585f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 586f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 587f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin public void onUp(MotionEvent e) { 588532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang mEdgeView.onRelease(); 589532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang 590f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin if (mIgnoreUpEvent) { 591f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mIgnoreUpEvent = false; 592f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin return; 593f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 594532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang if (!snapToNeighborImage() && mTransitionMode == TRANS_NONE) { 595f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mPositionController.up(); 596f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 597f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 598f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 599f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 600f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private void switchToNextImage() { 601f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin // We update the texture here directly to prevent texture uploading. 602f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin ScreenNailEntry prevNail = mScreenNails[ENTRY_PREVIOUS]; 603f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin ScreenNailEntry nextNail = mScreenNails[ENTRY_NEXT]; 604f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mTileView.invalidateTiles(); 605f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin if (prevNail.mTexture != null) prevNail.mTexture.recycle(); 606f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin prevNail.mTexture = mTileView.mBackupImage; 607f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mTileView.mBackupImage = nextNail.mTexture; 608f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin nextNail.mTexture = null; 609f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mModel.next(); 610f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 611f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 612f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private void switchToPreviousImage() { 613f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin // We update the texture here directly to prevent texture uploading. 614f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin ScreenNailEntry prevNail = mScreenNails[ENTRY_PREVIOUS]; 615f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin ScreenNailEntry nextNail = mScreenNails[ENTRY_NEXT]; 616f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mTileView.invalidateTiles(); 617f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin if (nextNail.mTexture != null) nextNail.mTexture.recycle(); 618f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin nextNail.mTexture = mTileView.mBackupImage; 619f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mTileView.mBackupImage = prevNail.mTexture; 620f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin nextNail.mTexture = null; 621f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mModel.previous(); 622f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 623f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 624ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang public void notifyTransitionComplete() { 625ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang mHandler.sendEmptyMessage(MSG_TRANSITION_COMPLETE); 626ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang } 627ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 628f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private void onTransitionComplete() { 629f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin int mode = mTransitionMode; 630f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mTransitionMode = TRANS_NONE; 631f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 632f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin if (mModel == null) return; 633f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin if (mode == TRANS_SWITCH_NEXT) { 634f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin switchToNextImage(); 635f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } else if (mode == TRANS_SWITCH_PREVIOUS) { 636f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin switchToPreviousImage(); 637f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 638f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 639f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 640ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang public boolean isDown() { 641f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin return mDownUpDetector.isDown(); 642f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 643f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 644f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin public static interface Model extends TileImageView.Model { 645f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin public void next(); 646f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin public void previous(); 6473a54b9ae67eb7b38f19ea0bdeeef26efc0a1afa1Chih-Chung Chang public void jumpTo(int index); 648f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin public int getImageRotation(); 649f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 650f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin // Return null if the specified image is unavailable. 651f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin public ImageData getNextImage(); 652f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin public ImageData getPreviousImage(); 653f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 654f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 655f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin public static class ImageData { 656f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin public int rotation; 657f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin public Bitmap bitmap; 658f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 659f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin public ImageData(Bitmap bitmap, int rotation) { 660f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin this.bitmap = bitmap; 661f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin this.rotation = rotation; 662f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 663f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 664f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 665f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private static int getRotated(int degree, int original, int theother) { 666f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin return ((degree / 90) & 1) == 0 ? original : theother; 667f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 668f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 669f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private class ScreenNailEntry { 670f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private boolean mVisible; 671f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private boolean mEnabled; 672f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 673f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private int mRotation; 674f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private int mDrawWidth; 675f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private int mDrawHeight; 676f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private int mOffsetX; 677f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 678f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private BitmapTexture mTexture; 679f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 680f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin public void set(boolean enabled, Bitmap bitmap, int rotation) { 681f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mEnabled = enabled; 682f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mRotation = rotation; 683f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin if (bitmap == null) { 684f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin if (mTexture != null) mTexture.recycle(); 685f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mTexture = null; 686f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } else { 687f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin if (mTexture != null) { 688f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin if (mTexture.getBitmap() != bitmap) { 689f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mTexture.recycle(); 690f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mTexture = new BitmapTexture(bitmap); 691f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 692f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } else { 693f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mTexture = new BitmapTexture(bitmap); 694f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 695f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin updateDrawingSize(); 696f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 697f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 698f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 699f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin public void layoutRightEdgeAt(int x) { 700f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mVisible = x > 0; 701f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mOffsetX = x - getRotated( 702f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mRotation, mDrawWidth, mDrawHeight) / 2; 703f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 704f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 705f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin public void layoutLeftEdgeAt(int x) { 706f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mVisible = x < getWidth(); 707f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mOffsetX = x + getRotated( 708f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mRotation, mDrawWidth, mDrawHeight) / 2; 709f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 710f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 711f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin public int gapToSide() { 712f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin return ((mRotation / 90) & 1) != 0 713f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin ? PhotoView.gapToSide(mDrawHeight, getWidth()) 714f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin : PhotoView.gapToSide(mDrawWidth, getWidth()); 715f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 716f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 717f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin public void updateDrawingSize() { 718f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin if (mTexture == null) return; 719f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 720f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin int width = mTexture.getWidth(); 721f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin int height = mTexture.getHeight(); 722676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang 723676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang // Calculate the initial scale that will used by PositionController 724676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang // (usually fit-to-screen) 725676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang float s = ((mRotation / 90) & 0x01) == 0 726676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang ? mPositionController.getMinimalScale(width, height) 727676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang : mPositionController.getMinimalScale(height, width); 728676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang 729f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mDrawWidth = Math.round(width * s); 730f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mDrawHeight = Math.round(height * s); 731f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 732f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 733f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin public boolean isEnabled() { 734f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin return mEnabled; 735f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 736f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 737f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin public void draw(GLCanvas canvas) { 738f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin int x = mOffsetX; 739f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin int y = getHeight() / 2; 740f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 741f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin if (mTexture != null) { 742f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin if (mRotation != 0) { 743f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin canvas.save(GLCanvas.SAVE_FLAG_MATRIX); 744174cac8f92029fc2829c94f274e70793ae948931Chih-Chung Chang canvas.translate(x, y); 745f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin canvas.rotate(mRotation, 0, 0, 1); //mRotation 746174cac8f92029fc2829c94f274e70793ae948931Chih-Chung Chang canvas.translate(-x, -y); 747f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 748f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mTexture.draw(canvas, x - mDrawWidth / 2, y - mDrawHeight / 2, 749f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mDrawWidth, mDrawHeight); 750f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin if (mRotation != 0) { 751f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin canvas.restore(); 752f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 753f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 754f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 755f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 756f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 757f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin public void pause() { 758f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mPositionController.skipAnimation(); 759f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mTransitionMode = TRANS_NONE; 760f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mTileView.freeTextures(); 761f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin for (ScreenNailEntry entry : mScreenNails) { 762f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin entry.set(false, null, 0); 763f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 764f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 765f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 766f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin public void resume() { 767f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mTileView.prepareTextures(); 768f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 769f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 770f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin public void setOpenedItem(Path itemPath) { 771f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mOpenedItemPath = itemPath; 772f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 773f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 774f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin public void showVideoPlayIcon(boolean show) { 775f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mShowVideoPlayIcon = show; 776f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 777ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 778676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang // Returns the position saved by the previous page. 779676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang public Position retrieveSavedPosition() { 780ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang if (mOpenedItemPath != null) { 781ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang Position position = PositionRepository 78295018d10aa68ecbea7ebce434733341a642510deChih-Chung Chang .getInstance(mActivity).get( 78395018d10aa68ecbea7ebce434733341a642510deChih-Chung Chang System.identityHashCode(mOpenedItemPath)); 784ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang mOpenedItemPath = null; 785ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang return position; 786ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang } 787ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang return null; 788ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang } 789ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 790ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang public void openAnimationStarted() { 791ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang mTransitionMode = TRANS_OPEN_ANIMATION; 792ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang } 793ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 794ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang public boolean isInTransition() { 795ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang return mTransitionMode != TRANS_NONE; 796ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang } 797f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin} 798