Owen Lin | f9a0a43 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2010 The Android Open Source Project |
| 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. |
| 6 | * You may obtain a copy of the License at |
| 7 | * |
| 8 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | * |
| 10 | * Unless required by applicable law or agreed to in writing, software |
| 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | * See the License for the specific language governing permissions and |
| 14 | * limitations under the License. |
| 15 | */ |
| 16 | |
| 17 | package com.android.gallery3d.ui; |
| 18 | |
Owen Lin | f9a0a43 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 19 | import android.content.Context; |
Owen Lin | f9a0a43 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 20 | import android.graphics.Color; |
Chih-Chung Chang | 2ef46ed | 2012-05-08 17:57:33 +0800 | [diff] [blame] | 21 | import android.graphics.Matrix; |
Yuli Huang | 04ac045 | 2012-03-20 16:37:05 +0800 | [diff] [blame] | 22 | import android.graphics.Rect; |
Hung-ying Tyan | 4d47b34 | 2012-08-29 14:15:32 +0800 | [diff] [blame] | 23 | import android.os.Build; |
Owen Lin | f9a0a43 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 24 | import android.os.Message; |
Chih-Chung Chang | 6b891c6 | 2012-06-07 20:09:13 +0800 | [diff] [blame] | 25 | import android.util.FloatMath; |
Owen Lin | f9a0a43 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 26 | import android.view.MotionEvent; |
Chih-Chung Chang | 6b891c6 | 2012-06-07 20:09:13 +0800 | [diff] [blame] | 27 | import android.view.View.MeasureSpec; |
Chih-Chung Chang | cb4fb7c | 2012-03-21 16:19:21 +0800 | [diff] [blame] | 28 | import android.view.animation.AccelerateInterpolator; |
Owen Lin | f9a0a43 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 29 | |
Owen Lin | 2b3ee0e | 2012-03-14 17:27:24 +0800 | [diff] [blame] | 30 | import com.android.gallery3d.R; |
Owen Lin | b21b8e5 | 2012-08-24 12:25:57 +0800 | [diff] [blame] | 31 | import com.android.gallery3d.app.AbstractGalleryActivity; |
Hung-ying Tyan | 4d47b34 | 2012-08-29 14:15:32 +0800 | [diff] [blame] | 32 | import com.android.gallery3d.common.ApiHelper; |
Chih-Chung Chang | cb4fb7c | 2012-03-21 16:19:21 +0800 | [diff] [blame] | 33 | import com.android.gallery3d.common.Utils; |
Owen Lin | 616a70f | 2012-05-07 16:35:53 +0800 | [diff] [blame] | 34 | import com.android.gallery3d.data.MediaItem; |
Chih-Chung Chang | b7ec553 | 2012-04-03 12:21:16 +0800 | [diff] [blame] | 35 | import com.android.gallery3d.data.MediaObject; |
Chih-Chung Chang | 6b891c6 | 2012-06-07 20:09:13 +0800 | [diff] [blame] | 36 | import com.android.gallery3d.data.Path; |
| 37 | import com.android.gallery3d.util.GalleryUtils; |
Chih-Chung Chang | b7ec553 | 2012-04-03 12:21:16 +0800 | [diff] [blame] | 38 | import com.android.gallery3d.util.RangeArray; |
Chih-Chung Chang | b7ec553 | 2012-04-03 12:21:16 +0800 | [diff] [blame] | 39 | |
Owen Lin | f9a0a43 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 40 | public class PhotoView extends GLView { |
| 41 | @SuppressWarnings("unused") |
| 42 | private static final String TAG = "PhotoView"; |
Bobby Georgescu | 915c2c5 | 2012-08-23 13:05:53 -0700 | [diff] [blame] | 43 | private final int mPlaceholderColor; |
Owen Lin | f9a0a43 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 44 | |
| 45 | public static final int INVALID_SIZE = -1; |
Chih-Chung Chang | b7ec553 | 2012-04-03 12:21:16 +0800 | [diff] [blame] | 46 | public static final long INVALID_DATA_VERSION = |
| 47 | MediaObject.INVALID_DATA_VERSION; |
Owen Lin | f9a0a43 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 48 | |
Chih-Chung Chang | c3b2d47 | 2012-04-19 20:14:11 +0800 | [diff] [blame] | 49 | public static class Size { |
| 50 | public int width; |
| 51 | public int height; |
| 52 | } |
| 53 | |
| 54 | public interface Model extends TileImageView.Model { |
Chih-Chung Chang | bd141b5 | 2012-04-26 10:10:49 +0800 | [diff] [blame] | 55 | public int getCurrentIndex(); |
| 56 | public void moveTo(int index); |
Chih-Chung Chang | c3b2d47 | 2012-04-19 20:14:11 +0800 | [diff] [blame] | 57 | |
| 58 | // Returns the size for the specified picture. If the size information is |
| 59 | // not avaiable, width = height = 0. |
| 60 | public void getImageSize(int offset, Size size); |
| 61 | |
Owen Lin | 616a70f | 2012-05-07 16:35:53 +0800 | [diff] [blame] | 62 | // Returns the media item for the specified picture. |
| 63 | public MediaItem getMediaItem(int offset); |
| 64 | |
Chih-Chung Chang | c3b2d47 | 2012-04-19 20:14:11 +0800 | [diff] [blame] | 65 | // Returns the rotation for the specified picture. |
| 66 | public int getImageRotation(int offset); |
Chih-Chung Chang | b7ec553 | 2012-04-03 12:21:16 +0800 | [diff] [blame] | 67 | |
| 68 | // This amends the getScreenNail() method of TileImageView.Model to get |
| 69 | // ScreenNail at previous (negative offset) or next (positive offset) |
| 70 | // positions. Returns null if the specified ScreenNail is unavailable. |
| 71 | public ScreenNail getScreenNail(int offset); |
Chih-Chung Chang | c3b2d47 | 2012-04-19 20:14:11 +0800 | [diff] [blame] | 72 | |
| 73 | // Set this to true if we need the model to provide full images. |
Chih-Chung Chang | b8be1e0 | 2012-04-17 20:35:14 +0800 | [diff] [blame] | 74 | public void setNeedFullImage(boolean enabled); |
Chih-Chung Chang | bd141b5 | 2012-04-26 10:10:49 +0800 | [diff] [blame] | 75 | |
| 76 | // Returns true if the item is the Camera preview. |
| 77 | public boolean isCamera(int offset); |
Chih-Chung Chang | d935511 | 2012-05-06 01:24:22 +0800 | [diff] [blame] | 78 | |
Angus Kong | 43a80fd | 2012-05-17 12:47:26 -0700 | [diff] [blame] | 79 | // Returns true if the item is the Panorama. |
| 80 | public boolean isPanorama(int offset); |
| 81 | |
Wu-cheng Li | dbb6acc | 2012-08-19 17:04:02 +0800 | [diff] [blame] | 82 | // Returns true if the item is a static image that represents camera |
| 83 | // preview. |
| 84 | public boolean isStaticCamera(int offset); |
| 85 | |
Chih-Chung Chang | d935511 | 2012-05-06 01:24:22 +0800 | [diff] [blame] | 86 | // Returns true if the item is a Video. |
| 87 | public boolean isVideo(int offset); |
Chih-Chung Chang | f5ce6ae | 2012-05-11 17:55:02 +0800 | [diff] [blame] | 88 | |
Chih-Chung Chang | 6b891c6 | 2012-06-07 20:09:13 +0800 | [diff] [blame] | 89 | // Returns true if the item can be deleted. |
| 90 | public boolean isDeletable(int offset); |
| 91 | |
Chih-Chung Chang | f5ce6ae | 2012-05-11 17:55:02 +0800 | [diff] [blame] | 92 | public static final int LOADING_INIT = 0; |
| 93 | public static final int LOADING_COMPLETE = 1; |
| 94 | public static final int LOADING_FAIL = 2; |
| 95 | |
| 96 | public int getLoadingState(int offset); |
Chih-Chung Chang | 6b891c6 | 2012-06-07 20:09:13 +0800 | [diff] [blame] | 97 | |
| 98 | // When data change happens, we need to decide which MediaItem to focus |
| 99 | // on. |
| 100 | // |
| 101 | // 1. If focus hint path != null, we try to focus on it if we can find |
| 102 | // it. This is used for undo a deletion, so we can focus on the |
| 103 | // undeleted item. |
| 104 | // |
| 105 | // 2. Otherwise try to focus on the MediaItem that is currently focused, |
| 106 | // if we can find it. |
| 107 | // |
| 108 | // 3. Otherwise try to focus on the previous MediaItem or the next |
| 109 | // MediaItem, depending on the value of focus hint direction. |
| 110 | public static final int FOCUS_HINT_NEXT = 0; |
| 111 | public static final int FOCUS_HINT_PREVIOUS = 1; |
| 112 | public void setFocusHintDirection(int direction); |
| 113 | public void setFocusHintPath(Path path); |
Chih-Chung Chang | b7ec553 | 2012-04-03 12:21:16 +0800 | [diff] [blame] | 114 | } |
| 115 | |
Chih-Chung Chang | bd141b5 | 2012-04-26 10:10:49 +0800 | [diff] [blame] | 116 | public interface Listener { |
Chih-Chung Chang | b7ec553 | 2012-04-03 12:21:16 +0800 | [diff] [blame] | 117 | public void onSingleTapUp(int x, int y); |
Chih-Chung Chang | bd141b5 | 2012-04-26 10:10:49 +0800 | [diff] [blame] | 118 | public void onFullScreenChanged(boolean full); |
Chih-Chung Chang | 61f9471 | 2012-05-02 20:05:16 +0800 | [diff] [blame] | 119 | public void onActionBarAllowed(boolean allowed); |
Chih-Chung Chang | e6251df | 2012-05-22 11:35:46 -0700 | [diff] [blame] | 120 | public void onActionBarWanted(); |
Yuli Huang | bd7c016 | 2012-05-15 22:36:59 +0800 | [diff] [blame] | 121 | public void onCurrentImageUpdated(); |
Chih-Chung Chang | 6b891c6 | 2012-06-07 20:09:13 +0800 | [diff] [blame] | 122 | public void onDeleteImage(Path path, int offset); |
| 123 | public void onUndoDeleteImage(); |
| 124 | public void onCommitDeleteImage(); |
Bobby Georgescu | 7eea4d3 | 2012-09-06 17:14:02 -0700 | [diff] [blame] | 125 | public void onFilmModeChanged(boolean enabled); |
Bobby Georgescu | f4e22eb | 2012-10-02 18:34:08 -0700 | [diff] [blame] | 126 | public void onPictureCenter(boolean isCamera); |
Chih-Chung Chang | b7ec553 | 2012-04-03 12:21:16 +0800 | [diff] [blame] | 127 | } |
| 128 | |
Chih-Chung Chang | e6251df | 2012-05-22 11:35:46 -0700 | [diff] [blame] | 129 | // The rules about orientation locking: |
Chih-Chung Chang | bd141b5 | 2012-04-26 10:10:49 +0800 | [diff] [blame] | 130 | // |
Chih-Chung Chang | e6251df | 2012-05-22 11:35:46 -0700 | [diff] [blame] | 131 | // (1) We need to lock the orientation if we are in page mode camera |
| 132 | // preview, so there is no (unwanted) rotation animation when the user |
| 133 | // rotates the device. |
Chih-Chung Chang | bd141b5 | 2012-04-26 10:10:49 +0800 | [diff] [blame] | 134 | // |
Chih-Chung Chang | e6251df | 2012-05-22 11:35:46 -0700 | [diff] [blame] | 135 | // (2) We need to unlock the orientation if we want to show the action bar |
| 136 | // because the action bar follows the system orientation. |
Chih-Chung Chang | bd141b5 | 2012-04-26 10:10:49 +0800 | [diff] [blame] | 137 | // |
Chih-Chung Chang | e6251df | 2012-05-22 11:35:46 -0700 | [diff] [blame] | 138 | // The rules about action bar: |
Chih-Chung Chang | bd141b5 | 2012-04-26 10:10:49 +0800 | [diff] [blame] | 139 | // |
Chih-Chung Chang | e6251df | 2012-05-22 11:35:46 -0700 | [diff] [blame] | 140 | // (1) If we are in film mode, we don't show action bar. |
| 141 | // |
| 142 | // (2) If we go from camera to gallery with capture animation, we show |
| 143 | // action bar. |
Chih-Chung Chang | b7ec553 | 2012-04-03 12:21:16 +0800 | [diff] [blame] | 144 | private static final int MSG_CANCEL_EXTRA_SCALING = 2; |
| 145 | private static final int MSG_SWITCH_FOCUS = 3; |
Chih-Chung Chang | 2c61738 | 2012-04-20 20:06:19 +0800 | [diff] [blame] | 146 | private static final int MSG_CAPTURE_ANIMATION_DONE = 4; |
Chih-Chung Chang | 6b891c6 | 2012-06-07 20:09:13 +0800 | [diff] [blame] | 147 | private static final int MSG_DELETE_ANIMATION_DONE = 5; |
| 148 | private static final int MSG_DELETE_DONE = 6; |
Chih-Chung Chang | 6118af9 | 2012-06-22 20:56:04 +0800 | [diff] [blame] | 149 | private static final int MSG_UNDO_BAR_TIMEOUT = 7; |
| 150 | private static final int MSG_UNDO_BAR_FULL_CAMERA = 8; |
Owen Lin | f9a0a43 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 151 | |
Owen Lin | f9a0a43 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 152 | private static final float SWIPE_THRESHOLD = 300f; |
| 153 | |
| 154 | private static final float DEFAULT_TEXT_SIZE = 20; |
Chih-Chung Chang | cb4fb7c | 2012-03-21 16:19:21 +0800 | [diff] [blame] | 155 | private static float TRANSITION_SCALE_FACTOR = 0.74f; |
Chih-Chung Chang | d935511 | 2012-05-06 01:24:22 +0800 | [diff] [blame] | 156 | private static final int ICON_RATIO = 6; |
Chih-Chung Chang | 2c61738 | 2012-04-20 20:06:19 +0800 | [diff] [blame] | 157 | |
| 158 | // whether we want to apply card deck effect in page mode. |
Chih-Chung Chang | b7ec553 | 2012-04-03 12:21:16 +0800 | [diff] [blame] | 159 | private static final boolean CARD_EFFECT = true; |
Chih-Chung Chang | cb4fb7c | 2012-03-21 16:19:21 +0800 | [diff] [blame] | 160 | |
Chih-Chung Chang | 6b891c6 | 2012-06-07 20:09:13 +0800 | [diff] [blame] | 161 | // whether we want to apply offset effect in film mode. |
| 162 | private static final boolean OFFSET_EFFECT = true; |
| 163 | |
| 164 | // Used to calculate the scaling factor for the card deck effect. |
Chih-Chung Chang | cb4fb7c | 2012-03-21 16:19:21 +0800 | [diff] [blame] | 165 | private ZInterpolator mScaleInterpolator = new ZInterpolator(0.5f); |
| 166 | |
| 167 | // Used to calculate the alpha factor for the fading animation. |
| 168 | private AccelerateInterpolator mAlphaInterpolator = |
| 169 | new AccelerateInterpolator(0.9f); |
Owen Lin | f9a0a43 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 170 | |
Chih-Chung Chang | b7ec553 | 2012-04-03 12:21:16 +0800 | [diff] [blame] | 171 | // We keep this many previous ScreenNails. (also this many next ScreenNails) |
| 172 | public static final int SCREEN_NAIL_MAX = 3; |
Owen Lin | f9a0a43 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 173 | |
Chih-Chung Chang | 6b891c6 | 2012-06-07 20:09:13 +0800 | [diff] [blame] | 174 | // These are constants for the delete gesture. |
Bobby Georgescu | 3711d3e | 2012-10-06 17:39:23 -0700 | [diff] [blame] | 175 | private static final int SWIPE_ESCAPE_VELOCITY = 2500; // dp/sec |
| 176 | private static final int MAX_DISMISS_VELOCITY = 4000; // dp/sec |
Chih-Chung Chang | 6b891c6 | 2012-06-07 20:09:13 +0800 | [diff] [blame] | 177 | |
Chih-Chung Chang | b7ec553 | 2012-04-03 12:21:16 +0800 | [diff] [blame] | 178 | // The picture entries, the valid index is from -SCREEN_NAIL_MAX to |
| 179 | // SCREEN_NAIL_MAX. |
| 180 | private final RangeArray<Picture> mPictures = |
| 181 | new RangeArray<Picture>(-SCREEN_NAIL_MAX, SCREEN_NAIL_MAX); |
Chih-Chung Chang | 6b891c6 | 2012-06-07 20:09:13 +0800 | [diff] [blame] | 182 | private Size[] mSizes = new Size[2 * SCREEN_NAIL_MAX + 1]; |
Chih-Chung Chang | b7ec553 | 2012-04-03 12:21:16 +0800 | [diff] [blame] | 183 | |
Chih-Chung Chang | 6575794 | 2012-05-06 03:25:16 +0800 | [diff] [blame] | 184 | private final MyGestureListener mGestureListener; |
Chih-Chung Chang | 3a02809 | 2012-03-14 17:39:42 +0800 | [diff] [blame] | 185 | private final GestureRecognizer mGestureRecognizer; |
Owen Lin | f9a0a43 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 186 | private final PositionController mPositionController; |
| 187 | |
Chih-Chung Chang | bd141b5 | 2012-04-26 10:10:49 +0800 | [diff] [blame] | 188 | private Listener mListener; |
Owen Lin | f9a0a43 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 189 | private Model mModel; |
Owen Lin | f9a0a43 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 190 | private StringTexture mNoThumbnailText; |
Chih-Chung Chang | b7ec553 | 2012-04-03 12:21:16 +0800 | [diff] [blame] | 191 | private TileImageView mTileView; |
Chih-Chung Chang | 532d93c | 2011-10-12 17:10:33 +0800 | [diff] [blame] | 192 | private EdgeView mEdgeView; |
Chih-Chung Chang | 6b891c6 | 2012-06-07 20:09:13 +0800 | [diff] [blame] | 193 | private UndoBarView mUndoBar; |
Owen Lin | f9a0a43 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 194 | private Texture mVideoPlayIcon; |
| 195 | |
Owen Lin | f9a0a43 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 196 | private SynchronizedHandler mHandler; |
| 197 | |
Chih-Chung Chang | 534b12f | 2012-03-21 19:01:30 +0800 | [diff] [blame] | 198 | private boolean mCancelExtraScalingPending; |
Chih-Chung Chang | b7ec553 | 2012-04-03 12:21:16 +0800 | [diff] [blame] | 199 | private boolean mFilmMode = false; |
Bobby Georgescu | f4e22eb | 2012-10-02 18:34:08 -0700 | [diff] [blame] | 200 | private boolean mWantPictureCenterCallbacks = false; |
Chih-Chung Chang | bd141b5 | 2012-04-26 10:10:49 +0800 | [diff] [blame] | 201 | private int mDisplayRotation = 0; |
| 202 | private int mCompensation = 0; |
Chih-Chung Chang | 9f44f35 | 2012-05-29 15:28:36 -0700 | [diff] [blame] | 203 | private boolean mFullScreenCamera; |
Chih-Chung Chang | 2ef46ed | 2012-05-08 17:57:33 +0800 | [diff] [blame] | 204 | private Rect mCameraRelativeFrame = new Rect(); |
Chih-Chung Chang | bd141b5 | 2012-04-26 10:10:49 +0800 | [diff] [blame] | 205 | private Rect mCameraRect = new Rect(); |
Owen Lin | f9a0a43 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 206 | |
Chih-Chung Chang | c3b2d47 | 2012-04-19 20:14:11 +0800 | [diff] [blame] | 207 | // [mPrevBound, mNextBound] is the range of index for all pictures in the |
| 208 | // model, if we assume the index of current focused picture is 0. So if |
| 209 | // there are some previous pictures, mPrevBound < 0, and if there are some |
| 210 | // next pictures, mNextBound > 0. |
| 211 | private int mPrevBound; |
| 212 | private int mNextBound; |
| 213 | |
Chih-Chung Chang | 2c61738 | 2012-04-20 20:06:19 +0800 | [diff] [blame] | 214 | // This variable prevents us doing snapback until its values goes to 0. This |
| 215 | // happens if the user gesture is still in progress or we are in a capture |
| 216 | // animation. |
Chih-Chung Chang | 2c61738 | 2012-04-20 20:06:19 +0800 | [diff] [blame] | 217 | private int mHolding; |
| 218 | private static final int HOLD_TOUCH_DOWN = 1; |
Chih-Chung Chang | 18958c5 | 2012-05-01 02:58:59 +0800 | [diff] [blame] | 219 | private static final int HOLD_CAPTURE_ANIMATION = 2; |
Chih-Chung Chang | 6b891c6 | 2012-06-07 20:09:13 +0800 | [diff] [blame] | 220 | private static final int HOLD_DELETE = 4; |
| 221 | |
| 222 | // mTouchBoxIndex is the index of the box that is touched by the down |
| 223 | // gesture in film mode. The value Integer.MAX_VALUE means no box was |
| 224 | // touched. |
| 225 | private int mTouchBoxIndex = Integer.MAX_VALUE; |
| 226 | // Whether the box indicated by mTouchBoxIndex is deletable. Only meaningful |
| 227 | // if mTouchBoxIndex is not Integer.MAX_VALUE. |
| 228 | private boolean mTouchBoxDeletable; |
Chih-Chung Chang | 517e1bd | 2012-06-19 17:34:50 +0800 | [diff] [blame] | 229 | // This is the index of the last deleted item. This is only used as a hint |
| 230 | // to hide the undo button when we are too far away from the deleted |
| 231 | // item. The value Integer.MAX_VALUE means there is no such hint. |
| 232 | private int mUndoIndexHint = Integer.MAX_VALUE; |
Chih-Chung Chang | 2c61738 | 2012-04-20 20:06:19 +0800 | [diff] [blame] | 233 | |
Owen Lin | b21b8e5 | 2012-08-24 12:25:57 +0800 | [diff] [blame] | 234 | public PhotoView(AbstractGalleryActivity activity) { |
Owen Lin | f9a0a43 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 235 | mTileView = new TileImageView(activity); |
| 236 | addComponent(mTileView); |
| 237 | Context context = activity.getAndroidContext(); |
Bobby Georgescu | 915c2c5 | 2012-08-23 13:05:53 -0700 | [diff] [blame] | 238 | mPlaceholderColor = context.getResources().getColor( |
| 239 | R.color.photo_placeholder); |
Chih-Chung Chang | 532d93c | 2011-10-12 17:10:33 +0800 | [diff] [blame] | 240 | mEdgeView = new EdgeView(context); |
| 241 | addComponent(mEdgeView); |
Chih-Chung Chang | 6b891c6 | 2012-06-07 20:09:13 +0800 | [diff] [blame] | 242 | mUndoBar = new UndoBarView(context); |
| 243 | addComponent(mUndoBar); |
| 244 | mUndoBar.setVisibility(GLView.INVISIBLE); |
| 245 | mUndoBar.setOnClickListener(new OnClickListener() { |
| 246 | @Override |
| 247 | public void onClick(GLView v) { |
| 248 | mListener.onUndoDeleteImage(); |
Chih-Chung Chang | 6118af9 | 2012-06-22 20:56:04 +0800 | [diff] [blame] | 249 | hideUndoBar(); |
Chih-Chung Chang | 6b891c6 | 2012-06-07 20:09:13 +0800 | [diff] [blame] | 250 | } |
| 251 | }); |
Owen Lin | f9a0a43 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 252 | mNoThumbnailText = StringTexture.newInstance( |
| 253 | context.getString(R.string.no_thumbnail), |
| 254 | DEFAULT_TEXT_SIZE, Color.WHITE); |
| 255 | |
Chih-Chung Chang | b7ec553 | 2012-04-03 12:21:16 +0800 | [diff] [blame] | 256 | mHandler = new MyHandler(activity.getGLRoot()); |
Owen Lin | f9a0a43 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 257 | |
Chih-Chung Chang | 6575794 | 2012-05-06 03:25:16 +0800 | [diff] [blame] | 258 | mGestureListener = new MyGestureListener(); |
| 259 | mGestureRecognizer = new GestureRecognizer(context, mGestureListener); |
Owen Lin | f9a0a43 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 260 | |
Chih-Chung Chang | b7ec553 | 2012-04-03 12:21:16 +0800 | [diff] [blame] | 261 | mPositionController = new PositionController(context, |
| 262 | new PositionController.Listener() { |
Owen Lin | 28cb416 | 2012-08-29 11:53:10 +0800 | [diff] [blame] | 263 | |
| 264 | @Override |
| 265 | public void invalidate() { |
| 266 | PhotoView.this.invalidate(); |
| 267 | } |
| 268 | |
| 269 | @Override |
| 270 | public boolean isHoldingDown() { |
| 271 | return (mHolding & HOLD_TOUCH_DOWN) != 0; |
| 272 | } |
| 273 | |
| 274 | @Override |
| 275 | public boolean isHoldingDelete() { |
| 276 | return (mHolding & HOLD_DELETE) != 0; |
| 277 | } |
| 278 | |
| 279 | @Override |
| 280 | public void onPull(int offset, int direction) { |
| 281 | mEdgeView.onPull(offset, direction); |
| 282 | } |
| 283 | |
| 284 | @Override |
| 285 | public void onRelease() { |
| 286 | mEdgeView.onRelease(); |
| 287 | } |
| 288 | |
| 289 | @Override |
| 290 | public void onAbsorb(int velocity, int direction) { |
| 291 | mEdgeView.onAbsorb(velocity, direction); |
| 292 | } |
| 293 | }); |
Owen Lin | f9a0a43 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 294 | mVideoPlayIcon = new ResourceTexture(context, R.drawable.ic_control_play); |
Chih-Chung Chang | b7ec553 | 2012-04-03 12:21:16 +0800 | [diff] [blame] | 295 | for (int i = -SCREEN_NAIL_MAX; i <= SCREEN_NAIL_MAX; i++) { |
| 296 | if (i == 0) { |
| 297 | mPictures.put(i, new FullPicture()); |
| 298 | } else { |
| 299 | mPictures.put(i, new ScreenNailPicture(i)); |
| 300 | } |
| 301 | } |
Owen Lin | f9a0a43 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 302 | } |
| 303 | |
Bobby Georgescu | b27df46 | 2012-09-27 23:55:44 -0700 | [diff] [blame] | 304 | public void stopScrolling() { |
| 305 | mPositionController.stopScrolling(); |
| 306 | } |
| 307 | |
Owen Lin | f9a0a43 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 308 | public void setModel(Model model) { |
Owen Lin | f9a0a43 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 309 | mModel = model; |
Chih-Chung Chang | b7ec553 | 2012-04-03 12:21:16 +0800 | [diff] [blame] | 310 | mTileView.setModel(mModel); |
Owen Lin | f9a0a43 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 311 | } |
| 312 | |
Chih-Chung Chang | b7ec553 | 2012-04-03 12:21:16 +0800 | [diff] [blame] | 313 | class MyHandler extends SynchronizedHandler { |
| 314 | public MyHandler(GLRoot root) { |
| 315 | super(root); |
Chih-Chung Chang | cb4fb7c | 2012-03-21 16:19:21 +0800 | [diff] [blame] | 316 | } |
| 317 | |
Chih-Chung Chang | b7ec553 | 2012-04-03 12:21:16 +0800 | [diff] [blame] | 318 | @Override |
| 319 | public void handleMessage(Message message) { |
| 320 | switch (message.what) { |
Chih-Chung Chang | b7ec553 | 2012-04-03 12:21:16 +0800 | [diff] [blame] | 321 | case MSG_CANCEL_EXTRA_SCALING: { |
| 322 | mGestureRecognizer.cancelScale(); |
| 323 | mPositionController.setExtraScalingRange(false); |
| 324 | mCancelExtraScalingPending = false; |
| 325 | break; |
| 326 | } |
| 327 | case MSG_SWITCH_FOCUS: { |
| 328 | switchFocus(); |
| 329 | break; |
| 330 | } |
Chih-Chung Chang | 2c61738 | 2012-04-20 20:06:19 +0800 | [diff] [blame] | 331 | case MSG_CAPTURE_ANIMATION_DONE: { |
Chih-Chung Chang | bd141b5 | 2012-04-26 10:10:49 +0800 | [diff] [blame] | 332 | // message.arg1 is the offset parameter passed to |
| 333 | // switchWithCaptureAnimation(). |
| 334 | captureAnimationDone(message.arg1); |
Chih-Chung Chang | 2c61738 | 2012-04-20 20:06:19 +0800 | [diff] [blame] | 335 | break; |
| 336 | } |
Chih-Chung Chang | 6b891c6 | 2012-06-07 20:09:13 +0800 | [diff] [blame] | 337 | case MSG_DELETE_ANIMATION_DONE: { |
| 338 | // message.obj is the Path of the MediaItem which should be |
| 339 | // deleted. message.arg1 is the offset of the image. |
| 340 | mListener.onDeleteImage((Path) message.obj, message.arg1); |
| 341 | // Normally a box which finishes delete animation will hold |
| 342 | // position until the underlying MediaItem is actually |
| 343 | // deleted, and HOLD_DELETE will be cancelled that time. In |
| 344 | // case the MediaItem didn't actually get deleted in 2 |
| 345 | // seconds, we will cancel HOLD_DELETE and make it bounce |
| 346 | // back. |
| 347 | |
| 348 | // We make sure there is at most one MSG_DELETE_DONE |
| 349 | // in the handler. |
| 350 | mHandler.removeMessages(MSG_DELETE_DONE); |
| 351 | Message m = mHandler.obtainMessage(MSG_DELETE_DONE); |
| 352 | mHandler.sendMessageDelayed(m, 2000); |
Chih-Chung Chang | 6118af9 | 2012-06-22 20:56:04 +0800 | [diff] [blame] | 353 | |
| 354 | int numberOfPictures = mNextBound - mPrevBound + 1; |
| 355 | if (numberOfPictures == 2) { |
| 356 | if (mModel.isCamera(mNextBound) |
| 357 | || mModel.isCamera(mPrevBound)) { |
| 358 | numberOfPictures--; |
| 359 | } |
| 360 | } |
| 361 | showUndoBar(numberOfPictures <= 1); |
Chih-Chung Chang | 6b891c6 | 2012-06-07 20:09:13 +0800 | [diff] [blame] | 362 | break; |
| 363 | } |
| 364 | case MSG_DELETE_DONE: { |
| 365 | if (!mHandler.hasMessages(MSG_DELETE_ANIMATION_DONE)) { |
| 366 | mHolding &= ~HOLD_DELETE; |
| 367 | snapback(); |
| 368 | } |
| 369 | break; |
| 370 | } |
Chih-Chung Chang | 6118af9 | 2012-06-22 20:56:04 +0800 | [diff] [blame] | 371 | case MSG_UNDO_BAR_TIMEOUT: { |
Chih-Chung Chang | 517e1bd | 2012-06-19 17:34:50 +0800 | [diff] [blame] | 372 | checkHideUndoBar(UNDO_BAR_TIMEOUT); |
| 373 | break; |
| 374 | } |
Chih-Chung Chang | 6118af9 | 2012-06-22 20:56:04 +0800 | [diff] [blame] | 375 | case MSG_UNDO_BAR_FULL_CAMERA: { |
| 376 | checkHideUndoBar(UNDO_BAR_FULL_CAMERA); |
| 377 | break; |
| 378 | } |
Chih-Chung Chang | b7ec553 | 2012-04-03 12:21:16 +0800 | [diff] [blame] | 379 | default: throw new AssertionError(message.what); |
Owen Lin | f9a0a43 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 380 | } |
| 381 | } |
Owen Lin | 28cb416 | 2012-08-29 11:53:10 +0800 | [diff] [blame] | 382 | } |
Owen Lin | f9a0a43 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 383 | |
Bobby Georgescu | f4e22eb | 2012-10-02 18:34:08 -0700 | [diff] [blame] | 384 | public void setWantPictureCenterCallbacks(boolean wanted) { |
| 385 | mWantPictureCenterCallbacks = wanted; |
Bobby Georgescu | b27df46 | 2012-09-27 23:55:44 -0700 | [diff] [blame] | 386 | } |
| 387 | |
Chih-Chung Chang | b7ec553 | 2012-04-03 12:21:16 +0800 | [diff] [blame] | 388 | //////////////////////////////////////////////////////////////////////////// |
| 389 | // Data/Image change notifications |
| 390 | //////////////////////////////////////////////////////////////////////////// |
Owen Lin | f9a0a43 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 391 | |
Chih-Chung Chang | 214993d | 2012-05-11 17:20:53 +0800 | [diff] [blame] | 392 | public void notifyDataChange(int[] fromIndex, int prevBound, int nextBound) { |
Chih-Chung Chang | c3b2d47 | 2012-04-19 20:14:11 +0800 | [diff] [blame] | 393 | mPrevBound = prevBound; |
| 394 | mNextBound = nextBound; |
| 395 | |
Chih-Chung Chang | 6b891c6 | 2012-06-07 20:09:13 +0800 | [diff] [blame] | 396 | // Update mTouchBoxIndex |
| 397 | if (mTouchBoxIndex != Integer.MAX_VALUE) { |
| 398 | int k = mTouchBoxIndex; |
| 399 | mTouchBoxIndex = Integer.MAX_VALUE; |
| 400 | for (int i = 0; i < 2 * SCREEN_NAIL_MAX + 1; i++) { |
| 401 | if (fromIndex[i] == k) { |
| 402 | mTouchBoxIndex = i - SCREEN_NAIL_MAX; |
| 403 | break; |
| 404 | } |
| 405 | } |
| 406 | } |
Chih-Chung Chang | b7ec553 | 2012-04-03 12:21:16 +0800 | [diff] [blame] | 407 | |
Chih-Chung Chang | 517e1bd | 2012-06-19 17:34:50 +0800 | [diff] [blame] | 408 | // Hide undo button if we are too far away |
| 409 | if (mUndoIndexHint != Integer.MAX_VALUE) { |
| 410 | if (Math.abs(mUndoIndexHint - mModel.getCurrentIndex()) >= 3) { |
| 411 | hideUndoBar(); |
| 412 | } |
| 413 | } |
| 414 | |
Chih-Chung Chang | b8be1e0 | 2012-04-17 20:35:14 +0800 | [diff] [blame] | 415 | // Update the ScreenNails. |
Chih-Chung Chang | b7ec553 | 2012-04-03 12:21:16 +0800 | [diff] [blame] | 416 | for (int i = -SCREEN_NAIL_MAX; i <= SCREEN_NAIL_MAX; i++) { |
Chih-Chung Chang | 6b891c6 | 2012-06-07 20:09:13 +0800 | [diff] [blame] | 417 | Picture p = mPictures.get(i); |
| 418 | p.reload(); |
| 419 | mSizes[i + SCREEN_NAIL_MAX] = p.getSize(); |
| 420 | } |
| 421 | |
| 422 | boolean wasDeleting = mPositionController.hasDeletingBox(); |
| 423 | |
| 424 | // Move the boxes |
| 425 | mPositionController.moveBox(fromIndex, mPrevBound < 0, mNextBound > 0, |
| 426 | mModel.isCamera(0), mSizes); |
| 427 | |
| 428 | for (int i = -SCREEN_NAIL_MAX; i <= SCREEN_NAIL_MAX; i++) { |
| 429 | setPictureSize(i); |
| 430 | } |
| 431 | |
| 432 | boolean isDeleting = mPositionController.hasDeletingBox(); |
| 433 | |
| 434 | // If the deletion is done, make HOLD_DELETE persist for only the time |
| 435 | // needed for a snapback animation. |
| 436 | if (wasDeleting && !isDeleting) { |
| 437 | mHandler.removeMessages(MSG_DELETE_DONE); |
| 438 | Message m = mHandler.obtainMessage(MSG_DELETE_DONE); |
| 439 | mHandler.sendMessageDelayed( |
| 440 | m, PositionController.SNAPBACK_ANIMATION_TIME); |
Chih-Chung Chang | b7ec553 | 2012-04-03 12:21:16 +0800 | [diff] [blame] | 441 | } |
| 442 | |
| 443 | invalidate(); |
| 444 | } |
| 445 | |
Chih-Chung Chang | 6b891c6 | 2012-06-07 20:09:13 +0800 | [diff] [blame] | 446 | public boolean isDeleting() { |
| 447 | return (mHolding & HOLD_DELETE) != 0 |
| 448 | && mPositionController.hasDeletingBox(); |
| 449 | } |
| 450 | |
Chih-Chung Chang | b7ec553 | 2012-04-03 12:21:16 +0800 | [diff] [blame] | 451 | public void notifyImageChange(int index) { |
Yuli Huang | bd7c016 | 2012-05-15 22:36:59 +0800 | [diff] [blame] | 452 | if (index == 0) { |
| 453 | mListener.onCurrentImageUpdated(); |
| 454 | } |
Chih-Chung Chang | b7ec553 | 2012-04-03 12:21:16 +0800 | [diff] [blame] | 455 | mPictures.get(index).reload(); |
Chih-Chung Chang | 6b891c6 | 2012-06-07 20:09:13 +0800 | [diff] [blame] | 456 | setPictureSize(index); |
Chih-Chung Chang | b7ec553 | 2012-04-03 12:21:16 +0800 | [diff] [blame] | 457 | invalidate(); |
| 458 | } |
| 459 | |
Chih-Chung Chang | 6b891c6 | 2012-06-07 20:09:13 +0800 | [diff] [blame] | 460 | private void setPictureSize(int index) { |
| 461 | Picture p = mPictures.get(index); |
| 462 | mPositionController.setImageSize(index, p.getSize(), |
| 463 | index == 0 && p.isCamera() ? mCameraRect : null); |
| 464 | } |
| 465 | |
Chih-Chung Chang | bd141b5 | 2012-04-26 10:10:49 +0800 | [diff] [blame] | 466 | @Override |
Chih-Chung Chang | bd141b5 | 2012-04-26 10:10:49 +0800 | [diff] [blame] | 467 | protected void onLayout( |
| 468 | boolean changeSize, int left, int top, int right, int bottom) { |
Chih-Chung Chang | 2ef46ed | 2012-05-08 17:57:33 +0800 | [diff] [blame] | 469 | int w = right - left; |
| 470 | int h = bottom - top; |
| 471 | mTileView.layout(0, 0, w, h); |
| 472 | mEdgeView.layout(0, 0, w, h); |
Chih-Chung Chang | 6b891c6 | 2012-06-07 20:09:13 +0800 | [diff] [blame] | 473 | mUndoBar.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED); |
| 474 | mUndoBar.layout(0, h - mUndoBar.getMeasuredHeight(), w, h); |
Chih-Chung Chang | 2ef46ed | 2012-05-08 17:57:33 +0800 | [diff] [blame] | 475 | |
| 476 | GLRoot root = getGLRoot(); |
| 477 | int displayRotation = root.getDisplayRotation(); |
| 478 | int compensation = root.getCompensation(); |
| 479 | if (mDisplayRotation != displayRotation |
| 480 | || mCompensation != compensation) { |
| 481 | mDisplayRotation = displayRotation; |
| 482 | mCompensation = compensation; |
| 483 | |
Chih-Chung Chang | 3b4a8ae | 2012-05-09 21:17:10 +0800 | [diff] [blame] | 484 | // We need to change the size and rotation of the Camera ScreenNail, |
| 485 | // but we don't want it to animate because the size doen't actually |
Chih-Chung Chang | 2ef46ed | 2012-05-08 17:57:33 +0800 | [diff] [blame] | 486 | // change in the eye of the user. |
| 487 | for (int i = -SCREEN_NAIL_MAX; i <= SCREEN_NAIL_MAX; i++) { |
| 488 | Picture p = mPictures.get(i); |
| 489 | if (p.isCamera()) { |
Chih-Chung Chang | 9f44f35 | 2012-05-29 15:28:36 -0700 | [diff] [blame] | 490 | p.forceSize(); |
Chih-Chung Chang | 2ef46ed | 2012-05-08 17:57:33 +0800 | [diff] [blame] | 491 | } |
| 492 | } |
| 493 | } |
| 494 | |
Chih-Chung Chang | 9f44f35 | 2012-05-29 15:28:36 -0700 | [diff] [blame] | 495 | updateCameraRect(); |
| 496 | mPositionController.setConstrainedFrame(mCameraRect); |
Chih-Chung Chang | bd141b5 | 2012-04-26 10:10:49 +0800 | [diff] [blame] | 497 | if (changeSize) { |
| 498 | mPositionController.setViewSize(getWidth(), getHeight()); |
| 499 | } |
| 500 | } |
| 501 | |
Chih-Chung Chang | 9f44f35 | 2012-05-29 15:28:36 -0700 | [diff] [blame] | 502 | // Update the camera rectangle due to layout change or camera relative frame |
| 503 | // change. |
| 504 | private void updateCameraRect() { |
Chih-Chung Chang | 2ef46ed | 2012-05-08 17:57:33 +0800 | [diff] [blame] | 505 | // Get the width and height in framework orientation because the given |
| 506 | // mCameraRelativeFrame is in that coordinates. |
Chih-Chung Chang | bd141b5 | 2012-04-26 10:10:49 +0800 | [diff] [blame] | 507 | int w = getWidth(); |
| 508 | int h = getHeight(); |
Chih-Chung Chang | 2ef46ed | 2012-05-08 17:57:33 +0800 | [diff] [blame] | 509 | if (mCompensation % 180 != 0) { |
Chih-Chung Chang | bd141b5 | 2012-04-26 10:10:49 +0800 | [diff] [blame] | 510 | int tmp = w; |
| 511 | w = h; |
| 512 | h = tmp; |
| 513 | } |
Chih-Chung Chang | 2ef46ed | 2012-05-08 17:57:33 +0800 | [diff] [blame] | 514 | int l = mCameraRelativeFrame.left; |
| 515 | int t = mCameraRelativeFrame.top; |
| 516 | int r = mCameraRelativeFrame.right; |
| 517 | int b = mCameraRelativeFrame.bottom; |
Chih-Chung Chang | bd141b5 | 2012-04-26 10:10:49 +0800 | [diff] [blame] | 518 | |
Chih-Chung Chang | 2ef46ed | 2012-05-08 17:57:33 +0800 | [diff] [blame] | 519 | // Now convert it to the coordinates we are using. |
| 520 | switch (mCompensation) { |
Chih-Chung Chang | bd141b5 | 2012-04-26 10:10:49 +0800 | [diff] [blame] | 521 | case 0: mCameraRect.set(l, t, r, b); break; |
| 522 | case 90: mCameraRect.set(h - b, l, h - t, r); break; |
| 523 | case 180: mCameraRect.set(w - r, h - b, w - l, h - t); break; |
| 524 | case 270: mCameraRect.set(t, w - r, b, w - l); break; |
| 525 | } |
| 526 | |
Chih-Chung Chang | 2ef46ed | 2012-05-08 17:57:33 +0800 | [diff] [blame] | 527 | Log.d(TAG, "compensation = " + mCompensation |
| 528 | + ", CameraRelativeFrame = " + mCameraRelativeFrame |
| 529 | + ", mCameraRect = " + mCameraRect); |
Chih-Chung Chang | bd141b5 | 2012-04-26 10:10:49 +0800 | [diff] [blame] | 530 | } |
| 531 | |
Chih-Chung Chang | 2ef46ed | 2012-05-08 17:57:33 +0800 | [diff] [blame] | 532 | public void setCameraRelativeFrame(Rect frame) { |
| 533 | mCameraRelativeFrame.set(frame); |
Chih-Chung Chang | 9f44f35 | 2012-05-29 15:28:36 -0700 | [diff] [blame] | 534 | updateCameraRect(); |
| 535 | // Originally we do |
| 536 | // mPositionController.setConstrainedFrame(mCameraRect); |
| 537 | // here, but it is moved to a parameter of the setImageSize() call, so |
| 538 | // it can be updated atomically with the CameraScreenNail's size change. |
Chih-Chung Chang | bd141b5 | 2012-04-26 10:10:49 +0800 | [diff] [blame] | 539 | } |
| 540 | |
| 541 | // Returns the rotation we need to do to the camera texture before drawing |
| 542 | // it to the canvas, assuming the camera texture is correct when the device |
| 543 | // is in its natural orientation. |
| 544 | private int getCameraRotation() { |
| 545 | return (mCompensation - mDisplayRotation + 360) % 360; |
| 546 | } |
| 547 | |
Angus Kong | 43a80fd | 2012-05-17 12:47:26 -0700 | [diff] [blame] | 548 | private int getPanoramaRotation() { |
John Reck | 866ed7a | 2012-10-09 22:10:13 -0700 | [diff] [blame] | 549 | // Panorama only support rotations of 0 and 90, so if it is greater |
| 550 | // than that flip the output surface texture to compensate |
| 551 | if (mDisplayRotation > 180) |
| 552 | return (mCompensation + 180) % 360; |
Angus Kong | 43a80fd | 2012-05-17 12:47:26 -0700 | [diff] [blame] | 553 | return mCompensation; |
| 554 | } |
| 555 | |
Chih-Chung Chang | b7ec553 | 2012-04-03 12:21:16 +0800 | [diff] [blame] | 556 | //////////////////////////////////////////////////////////////////////////// |
| 557 | // Pictures |
| 558 | //////////////////////////////////////////////////////////////////////////// |
| 559 | |
| 560 | private interface Picture { |
| 561 | void reload(); |
| 562 | void draw(GLCanvas canvas, Rect r); |
Chih-Chung Chang | b8be1e0 | 2012-04-17 20:35:14 +0800 | [diff] [blame] | 563 | void setScreenNail(ScreenNail s); |
Chih-Chung Chang | 2c61738 | 2012-04-20 20:06:19 +0800 | [diff] [blame] | 564 | boolean isCamera(); // whether the picture is a camera preview |
Chih-Chung Chang | 6b891c6 | 2012-06-07 20:09:13 +0800 | [diff] [blame] | 565 | boolean isDeletable(); // whether the picture can be deleted |
Chih-Chung Chang | 9f44f35 | 2012-05-29 15:28:36 -0700 | [diff] [blame] | 566 | void forceSize(); // called when mCompensation changes |
Chih-Chung Chang | 6b891c6 | 2012-06-07 20:09:13 +0800 | [diff] [blame] | 567 | Size getSize(); |
Ahbong Chang | 7817979 | 2012-07-30 11:34:13 +0800 | [diff] [blame] | 568 | } |
Chih-Chung Chang | b7ec553 | 2012-04-03 12:21:16 +0800 | [diff] [blame] | 569 | |
| 570 | class FullPicture implements Picture { |
| 571 | private int mRotation; |
Chih-Chung Chang | 2c61738 | 2012-04-20 20:06:19 +0800 | [diff] [blame] | 572 | private boolean mIsCamera; |
Angus Kong | 43a80fd | 2012-05-17 12:47:26 -0700 | [diff] [blame] | 573 | private boolean mIsPanorama; |
Wu-cheng Li | dbb6acc | 2012-08-19 17:04:02 +0800 | [diff] [blame] | 574 | private boolean mIsStaticCamera; |
Chih-Chung Chang | d935511 | 2012-05-06 01:24:22 +0800 | [diff] [blame] | 575 | private boolean mIsVideo; |
Chih-Chung Chang | 6b891c6 | 2012-06-07 20:09:13 +0800 | [diff] [blame] | 576 | private boolean mIsDeletable; |
Chih-Chung Chang | f5ce6ae | 2012-05-11 17:55:02 +0800 | [diff] [blame] | 577 | private int mLoadingState = Model.LOADING_INIT; |
Chih-Chung Chang | 6b891c6 | 2012-06-07 20:09:13 +0800 | [diff] [blame] | 578 | private Size mSize = new Size(); |
Chih-Chung Chang | b7ec553 | 2012-04-03 12:21:16 +0800 | [diff] [blame] | 579 | |
| 580 | @Override |
| 581 | public void reload() { |
| 582 | // mImageWidth and mImageHeight will get updated |
Owen Lin | f9a0a43 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 583 | mTileView.notifyModelInvalidated(); |
Chih-Chung Chang | b7ec553 | 2012-04-03 12:21:16 +0800 | [diff] [blame] | 584 | |
Chih-Chung Chang | bd141b5 | 2012-04-26 10:10:49 +0800 | [diff] [blame] | 585 | mIsCamera = mModel.isCamera(0); |
Angus Kong | 43a80fd | 2012-05-17 12:47:26 -0700 | [diff] [blame] | 586 | mIsPanorama = mModel.isPanorama(0); |
Wu-cheng Li | dbb6acc | 2012-08-19 17:04:02 +0800 | [diff] [blame] | 587 | mIsStaticCamera = mModel.isStaticCamera(0); |
Chih-Chung Chang | d935511 | 2012-05-06 01:24:22 +0800 | [diff] [blame] | 588 | mIsVideo = mModel.isVideo(0); |
Chih-Chung Chang | 6b891c6 | 2012-06-07 20:09:13 +0800 | [diff] [blame] | 589 | mIsDeletable = mModel.isDeletable(0); |
Chih-Chung Chang | f5ce6ae | 2012-05-11 17:55:02 +0800 | [diff] [blame] | 590 | mLoadingState = mModel.getLoadingState(0); |
Chih-Chung Chang | c3b2d47 | 2012-04-19 20:14:11 +0800 | [diff] [blame] | 591 | setScreenNail(mModel.getScreenNail(0)); |
Chih-Chung Chang | 6b891c6 | 2012-06-07 20:09:13 +0800 | [diff] [blame] | 592 | updateSize(); |
Chih-Chung Chang | 9f44f35 | 2012-05-29 15:28:36 -0700 | [diff] [blame] | 593 | } |
| 594 | |
Chih-Chung Chang | 6b891c6 | 2012-06-07 20:09:13 +0800 | [diff] [blame] | 595 | @Override |
| 596 | public Size getSize() { |
| 597 | return mSize; |
Chih-Chung Chang | b7ec553 | 2012-04-03 12:21:16 +0800 | [diff] [blame] | 598 | } |
| 599 | |
| 600 | @Override |
Chih-Chung Chang | 9f44f35 | 2012-05-29 15:28:36 -0700 | [diff] [blame] | 601 | public void forceSize() { |
| 602 | updateSize(); |
Chih-Chung Chang | 6b891c6 | 2012-06-07 20:09:13 +0800 | [diff] [blame] | 603 | mPositionController.forceImageSize(0, mSize); |
Chih-Chung Chang | 9f44f35 | 2012-05-29 15:28:36 -0700 | [diff] [blame] | 604 | } |
| 605 | |
| 606 | private void updateSize() { |
Angus Kong | 43a80fd | 2012-05-17 12:47:26 -0700 | [diff] [blame] | 607 | if (mIsPanorama) { |
| 608 | mRotation = getPanoramaRotation(); |
Wu-cheng Li | dbb6acc | 2012-08-19 17:04:02 +0800 | [diff] [blame] | 609 | } else if (mIsCamera && !mIsStaticCamera) { |
Chih-Chung Chang | bd141b5 | 2012-04-26 10:10:49 +0800 | [diff] [blame] | 610 | mRotation = getCameraRotation(); |
| 611 | } else { |
| 612 | mRotation = mModel.getImageRotation(0); |
| 613 | } |
| 614 | |
| 615 | int w = mTileView.mImageWidth; |
| 616 | int h = mTileView.mImageHeight; |
Chih-Chung Chang | 6b891c6 | 2012-06-07 20:09:13 +0800 | [diff] [blame] | 617 | mSize.width = getRotated(mRotation, w, h); |
| 618 | mSize.height = getRotated(mRotation, h, w); |
Chih-Chung Chang | bd141b5 | 2012-04-26 10:10:49 +0800 | [diff] [blame] | 619 | } |
| 620 | |
| 621 | @Override |
Chih-Chung Chang | b7ec553 | 2012-04-03 12:21:16 +0800 | [diff] [blame] | 622 | public void draw(GLCanvas canvas, Rect r) { |
Chih-Chung Chang | f5ce6ae | 2012-05-11 17:55:02 +0800 | [diff] [blame] | 623 | drawTileView(canvas, r); |
| 624 | |
Chih-Chung Chang | bd141b5 | 2012-04-26 10:10:49 +0800 | [diff] [blame] | 625 | // We want to have the following transitions: |
Chih-Chung Chang | 2c61738 | 2012-04-20 20:06:19 +0800 | [diff] [blame] | 626 | // (1) Move camera preview out of its place: switch to film mode |
| 627 | // (2) Move camera preview into its place: switch to page mode |
| 628 | // The extra mWasCenter check makes sure (1) does not apply if in |
| 629 | // page mode, we move _to_ the camera preview from another picture. |
Chih-Chung Chang | bd141b5 | 2012-04-26 10:10:49 +0800 | [diff] [blame] | 630 | |
| 631 | // Holdings except touch-down prevent the transitions. |
Chih-Chung Chang | 18958c5 | 2012-05-01 02:58:59 +0800 | [diff] [blame] | 632 | if ((mHolding & ~HOLD_TOUCH_DOWN) != 0) return; |
Chih-Chung Chang | bd141b5 | 2012-04-26 10:10:49 +0800 | [diff] [blame] | 633 | |
Bobby Georgescu | f4e22eb | 2012-10-02 18:34:08 -0700 | [diff] [blame] | 634 | if (mWantPictureCenterCallbacks && mPositionController.isCenter()) { |
Bobby Georgescu | 639095c | 2012-10-08 13:37:41 -0700 | [diff] [blame] | 635 | mListener.onPictureCenter(mIsCamera); |
Chih-Chung Chang | bd141b5 | 2012-04-26 10:10:49 +0800 | [diff] [blame] | 636 | } |
Chih-Chung Chang | b7ec553 | 2012-04-03 12:21:16 +0800 | [diff] [blame] | 637 | } |
| 638 | |
| 639 | @Override |
Chih-Chung Chang | b8be1e0 | 2012-04-17 20:35:14 +0800 | [diff] [blame] | 640 | public void setScreenNail(ScreenNail s) { |
Chih-Chung Chang | b8be1e0 | 2012-04-17 20:35:14 +0800 | [diff] [blame] | 641 | mTileView.setScreenNail(s); |
Chih-Chung Chang | b7ec553 | 2012-04-03 12:21:16 +0800 | [diff] [blame] | 642 | } |
| 643 | |
| 644 | @Override |
Chih-Chung Chang | 2c61738 | 2012-04-20 20:06:19 +0800 | [diff] [blame] | 645 | public boolean isCamera() { |
| 646 | return mIsCamera; |
Chih-Chung Chang | b7ec553 | 2012-04-03 12:21:16 +0800 | [diff] [blame] | 647 | } |
| 648 | |
Chih-Chung Chang | 6b891c6 | 2012-06-07 20:09:13 +0800 | [diff] [blame] | 649 | @Override |
| 650 | public boolean isDeletable() { |
| 651 | return mIsDeletable; |
| 652 | } |
| 653 | |
Chih-Chung Chang | d935511 | 2012-05-06 01:24:22 +0800 | [diff] [blame] | 654 | private void drawTileView(GLCanvas canvas, Rect r) { |
Chih-Chung Chang | ba12eae | 2012-05-07 02:29:37 +0800 | [diff] [blame] | 655 | float imageScale = mPositionController.getImageScale(); |
Chih-Chung Chang | b7ec553 | 2012-04-03 12:21:16 +0800 | [diff] [blame] | 656 | int viewW = getWidth(); |
| 657 | int viewH = getHeight(); |
Chih-Chung Chang | d935511 | 2012-05-06 01:24:22 +0800 | [diff] [blame] | 658 | float cx = r.exactCenterX(); |
| 659 | float cy = r.exactCenterY(); |
Chih-Chung Chang | ba12eae | 2012-05-07 02:29:37 +0800 | [diff] [blame] | 660 | float scale = 1f; // the scaling factor due to card effect |
Chih-Chung Chang | b7ec553 | 2012-04-03 12:21:16 +0800 | [diff] [blame] | 661 | |
Chih-Chung Chang | d935511 | 2012-05-06 01:24:22 +0800 | [diff] [blame] | 662 | canvas.save(GLCanvas.SAVE_FLAG_MATRIX | GLCanvas.SAVE_FLAG_ALPHA); |
Chih-Chung Chang | ba12eae | 2012-05-07 02:29:37 +0800 | [diff] [blame] | 663 | float filmRatio = mPositionController.getFilmRatio(); |
| 664 | boolean wantsCardEffect = CARD_EFFECT && !mIsCamera |
Yuli Huang | f320b84 | 2012-05-16 01:38:06 +0800 | [diff] [blame] | 665 | && filmRatio != 1f && !mPictures.get(-1).isCamera() |
| 666 | && !mPositionController.inOpeningAnimation(); |
Chih-Chung Chang | 6b891c6 | 2012-06-07 20:09:13 +0800 | [diff] [blame] | 667 | boolean wantsOffsetEffect = OFFSET_EFFECT && mIsDeletable |
| 668 | && filmRatio == 1f && r.centerY() != viewH / 2; |
Chih-Chung Chang | 2c61738 | 2012-04-20 20:06:19 +0800 | [diff] [blame] | 669 | if (wantsCardEffect) { |
Chih-Chung Chang | b7ec553 | 2012-04-03 12:21:16 +0800 | [diff] [blame] | 670 | // Calculate the move-out progress value. |
| 671 | int left = r.left; |
| 672 | int right = r.right; |
| 673 | float progress = calculateMoveOutProgress(left, right, viewW); |
| 674 | progress = Utils.clamp(progress, -1f, 1f); |
| 675 | |
| 676 | // We only want to apply the fading animation if the scrolling |
| 677 | // movement is to the right. |
Chih-Chung Chang | bd141b5 | 2012-04-26 10:10:49 +0800 | [diff] [blame] | 678 | if (progress < 0) { |
Chih-Chung Chang | ba12eae | 2012-05-07 02:29:37 +0800 | [diff] [blame] | 679 | scale = getScrollScale(progress); |
| 680 | float alpha = getScrollAlpha(progress); |
| 681 | scale = interpolate(filmRatio, scale, 1f); |
| 682 | alpha = interpolate(filmRatio, alpha, 1f); |
Chih-Chung Chang | d935511 | 2012-05-06 01:24:22 +0800 | [diff] [blame] | 683 | |
Chih-Chung Chang | ba12eae | 2012-05-07 02:29:37 +0800 | [diff] [blame] | 684 | imageScale *= scale; |
| 685 | canvas.multiplyAlpha(alpha); |
| 686 | |
| 687 | float cxPage; // the cx value in page mode |
Chih-Chung Chang | d935511 | 2012-05-06 01:24:22 +0800 | [diff] [blame] | 688 | if (right - left <= viewW) { |
Chih-Chung Chang | b7ec553 | 2012-04-03 12:21:16 +0800 | [diff] [blame] | 689 | // If the picture is narrower than the view, keep it at |
| 690 | // the center of the view. |
Chih-Chung Chang | ba12eae | 2012-05-07 02:29:37 +0800 | [diff] [blame] | 691 | cxPage = viewW / 2f; |
Chih-Chung Chang | b7ec553 | 2012-04-03 12:21:16 +0800 | [diff] [blame] | 692 | } else { |
| 693 | // If the picture is wider than the view (it's |
| 694 | // zoomed-in), keep the left edge of the object align |
| 695 | // the the left edge of the view. |
Chih-Chung Chang | ba12eae | 2012-05-07 02:29:37 +0800 | [diff] [blame] | 696 | cxPage = (right - left) * scale / 2f; |
Chih-Chung Chang | b7ec553 | 2012-04-03 12:21:16 +0800 | [diff] [blame] | 697 | } |
Chih-Chung Chang | ba12eae | 2012-05-07 02:29:37 +0800 | [diff] [blame] | 698 | cx = interpolate(filmRatio, cxPage, cx); |
Chih-Chung Chang | b7ec553 | 2012-04-03 12:21:16 +0800 | [diff] [blame] | 699 | } |
Chih-Chung Chang | 6b891c6 | 2012-06-07 20:09:13 +0800 | [diff] [blame] | 700 | } else if (wantsOffsetEffect) { |
| 701 | float offset = (float) (r.centerY() - viewH / 2) / viewH; |
| 702 | float alpha = getOffsetAlpha(offset); |
| 703 | canvas.multiplyAlpha(alpha); |
Chih-Chung Chang | b7ec553 | 2012-04-03 12:21:16 +0800 | [diff] [blame] | 704 | } |
| 705 | |
Chih-Chung Chang | d935511 | 2012-05-06 01:24:22 +0800 | [diff] [blame] | 706 | // Draw the tile view. |
Chih-Chung Chang | ba12eae | 2012-05-07 02:29:37 +0800 | [diff] [blame] | 707 | setTileViewPosition(cx, cy, viewW, viewH, imageScale); |
Chih-Chung Chang | 6b891c6 | 2012-06-07 20:09:13 +0800 | [diff] [blame] | 708 | renderChild(canvas, mTileView); |
Chih-Chung Chang | d935511 | 2012-05-06 01:24:22 +0800 | [diff] [blame] | 709 | |
Chih-Chung Chang | f5ce6ae | 2012-05-11 17:55:02 +0800 | [diff] [blame] | 710 | // Draw the play video icon and the message. |
| 711 | canvas.translate((int) (cx + 0.5f), (int) (cy + 0.5f)); |
| 712 | int s = (int) (scale * Math.min(r.width(), r.height()) + 0.5f); |
Mangesh Ghiware | c1c67ea | 2012-09-30 12:58:56 -0700 | [diff] [blame] | 713 | if (mIsVideo) drawVideoPlayIcon(canvas, s); |
Chih-Chung Chang | f5ce6ae | 2012-05-11 17:55:02 +0800 | [diff] [blame] | 714 | if (mLoadingState == Model.LOADING_FAIL) { |
| 715 | drawLoadingFailMessage(canvas); |
Chih-Chung Chang | d935511 | 2012-05-06 01:24:22 +0800 | [diff] [blame] | 716 | } |
| 717 | |
Chih-Chung Chang | f5ce6ae | 2012-05-11 17:55:02 +0800 | [diff] [blame] | 718 | // Draw a debug indicator showing which picture has focus (index == |
| 719 | // 0). |
| 720 | //canvas.fillRect(-10, -10, 20, 20, 0x80FF00FF); |
| 721 | |
Chih-Chung Chang | d935511 | 2012-05-06 01:24:22 +0800 | [diff] [blame] | 722 | canvas.restore(); |
| 723 | } |
| 724 | |
| 725 | // Set the position of the tile view |
| 726 | private void setTileViewPosition(float cx, float cy, |
| 727 | int viewW, int viewH, float scale) { |
| 728 | // Find out the bitmap coordinates of the center of the view |
| 729 | int imageW = mPositionController.getImageWidth(); |
| 730 | int imageH = mPositionController.getImageHeight(); |
| 731 | int centerX = (int) (imageW / 2f + (viewW / 2f - cx) / scale + 0.5f); |
| 732 | int centerY = (int) (imageH / 2f + (viewH / 2f - cy) / scale + 0.5f); |
| 733 | |
Chih-Chung Chang | b7ec553 | 2012-04-03 12:21:16 +0800 | [diff] [blame] | 734 | int inverseX = imageW - centerX; |
| 735 | int inverseY = imageH - centerY; |
Chih-Chung Chang | d935511 | 2012-05-06 01:24:22 +0800 | [diff] [blame] | 736 | int x, y; |
| 737 | switch (mRotation) { |
| 738 | case 0: x = centerX; y = centerY; break; |
| 739 | case 90: x = centerY; y = inverseX; break; |
| 740 | case 180: x = inverseX; y = inverseY; break; |
| 741 | case 270: x = inverseY; y = centerX; break; |
Chih-Chung Chang | b7ec553 | 2012-04-03 12:21:16 +0800 | [diff] [blame] | 742 | default: |
Chih-Chung Chang | d935511 | 2012-05-06 01:24:22 +0800 | [diff] [blame] | 743 | throw new RuntimeException(String.valueOf(mRotation)); |
Chih-Chung Chang | b7ec553 | 2012-04-03 12:21:16 +0800 | [diff] [blame] | 744 | } |
Chih-Chung Chang | d935511 | 2012-05-06 01:24:22 +0800 | [diff] [blame] | 745 | mTileView.setPosition(x, y, scale, mRotation); |
Chih-Chung Chang | b7ec553 | 2012-04-03 12:21:16 +0800 | [diff] [blame] | 746 | } |
Owen Lin | f9a0a43 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 747 | } |
| 748 | |
Chih-Chung Chang | b7ec553 | 2012-04-03 12:21:16 +0800 | [diff] [blame] | 749 | private class ScreenNailPicture implements Picture { |
| 750 | private int mIndex; |
Chih-Chung Chang | b7ec553 | 2012-04-03 12:21:16 +0800 | [diff] [blame] | 751 | private int mRotation; |
| 752 | private ScreenNail mScreenNail; |
Chih-Chung Chang | 2c61738 | 2012-04-20 20:06:19 +0800 | [diff] [blame] | 753 | private boolean mIsCamera; |
Angus Kong | 43a80fd | 2012-05-17 12:47:26 -0700 | [diff] [blame] | 754 | private boolean mIsPanorama; |
Wu-cheng Li | dbb6acc | 2012-08-19 17:04:02 +0800 | [diff] [blame] | 755 | private boolean mIsStaticCamera; |
Chih-Chung Chang | d935511 | 2012-05-06 01:24:22 +0800 | [diff] [blame] | 756 | private boolean mIsVideo; |
Chih-Chung Chang | 6b891c6 | 2012-06-07 20:09:13 +0800 | [diff] [blame] | 757 | private boolean mIsDeletable; |
Chih-Chung Chang | f5ce6ae | 2012-05-11 17:55:02 +0800 | [diff] [blame] | 758 | private int mLoadingState = Model.LOADING_INIT; |
Chih-Chung Chang | 6b891c6 | 2012-06-07 20:09:13 +0800 | [diff] [blame] | 759 | private Size mSize = new Size(); |
Chih-Chung Chang | b7ec553 | 2012-04-03 12:21:16 +0800 | [diff] [blame] | 760 | |
| 761 | public ScreenNailPicture(int index) { |
| 762 | mIndex = index; |
| 763 | } |
| 764 | |
| 765 | @Override |
| 766 | public void reload() { |
Chih-Chung Chang | bd141b5 | 2012-04-26 10:10:49 +0800 | [diff] [blame] | 767 | mIsCamera = mModel.isCamera(mIndex); |
Angus Kong | 43a80fd | 2012-05-17 12:47:26 -0700 | [diff] [blame] | 768 | mIsPanorama = mModel.isPanorama(mIndex); |
Wu-cheng Li | dbb6acc | 2012-08-19 17:04:02 +0800 | [diff] [blame] | 769 | mIsStaticCamera = mModel.isStaticCamera(mIndex); |
Chih-Chung Chang | d935511 | 2012-05-06 01:24:22 +0800 | [diff] [blame] | 770 | mIsVideo = mModel.isVideo(mIndex); |
Chih-Chung Chang | 6b891c6 | 2012-06-07 20:09:13 +0800 | [diff] [blame] | 771 | mIsDeletable = mModel.isDeletable(mIndex); |
Chih-Chung Chang | f5ce6ae | 2012-05-11 17:55:02 +0800 | [diff] [blame] | 772 | mLoadingState = mModel.getLoadingState(mIndex); |
Chih-Chung Chang | c3b2d47 | 2012-04-19 20:14:11 +0800 | [diff] [blame] | 773 | setScreenNail(mModel.getScreenNail(mIndex)); |
Chih-Chung Chang | 6b891c6 | 2012-06-07 20:09:13 +0800 | [diff] [blame] | 774 | updateSize(); |
| 775 | } |
| 776 | |
| 777 | @Override |
| 778 | public Size getSize() { |
| 779 | return mSize; |
Chih-Chung Chang | b7ec553 | 2012-04-03 12:21:16 +0800 | [diff] [blame] | 780 | } |
| 781 | |
| 782 | @Override |
| 783 | public void draw(GLCanvas canvas, Rect r) { |
| 784 | if (mScreenNail == null) { |
Chih-Chung Chang | f5ce6ae | 2012-05-11 17:55:02 +0800 | [diff] [blame] | 785 | // Draw a placeholder rectange if there should be a picture in |
| 786 | // this position (but somehow there isn't). |
Chih-Chung Chang | c3b2d47 | 2012-04-19 20:14:11 +0800 | [diff] [blame] | 787 | if (mIndex >= mPrevBound && mIndex <= mNextBound) { |
Chih-Chung Chang | f5ce6ae | 2012-05-11 17:55:02 +0800 | [diff] [blame] | 788 | drawPlaceHolder(canvas, r); |
Chih-Chung Chang | c3b2d47 | 2012-04-19 20:14:11 +0800 | [diff] [blame] | 789 | } |
Chih-Chung Chang | b7ec553 | 2012-04-03 12:21:16 +0800 | [diff] [blame] | 790 | return; |
| 791 | } |
Chih-Chung Chang | 6b891c6 | 2012-06-07 20:09:13 +0800 | [diff] [blame] | 792 | int w = getWidth(); |
| 793 | int h = getHeight(); |
| 794 | if (r.left >= w || r.right <= 0 || r.top >= h || r.bottom <= 0) { |
Chih-Chung Chang | b7ec553 | 2012-04-03 12:21:16 +0800 | [diff] [blame] | 795 | mScreenNail.noDraw(); |
| 796 | return; |
| 797 | } |
| 798 | |
Chih-Chung Chang | ba12eae | 2012-05-07 02:29:37 +0800 | [diff] [blame] | 799 | float filmRatio = mPositionController.getFilmRatio(); |
| 800 | boolean wantsCardEffect = CARD_EFFECT && mIndex > 0 |
| 801 | && filmRatio != 1f && !mPictures.get(0).isCamera(); |
Chih-Chung Chang | 6b891c6 | 2012-06-07 20:09:13 +0800 | [diff] [blame] | 802 | boolean wantsOffsetEffect = OFFSET_EFFECT && mIsDeletable |
| 803 | && filmRatio == 1f && r.centerY() != h / 2; |
Chih-Chung Chang | ba12eae | 2012-05-07 02:29:37 +0800 | [diff] [blame] | 804 | int cx = wantsCardEffect |
| 805 | ? (int) (interpolate(filmRatio, w / 2, r.centerX()) + 0.5f) |
| 806 | : r.centerX(); |
Chih-Chung Chang | b7ec553 | 2012-04-03 12:21:16 +0800 | [diff] [blame] | 807 | int cy = r.centerY(); |
Chih-Chung Chang | ba12eae | 2012-05-07 02:29:37 +0800 | [diff] [blame] | 808 | canvas.save(GLCanvas.SAVE_FLAG_MATRIX | GLCanvas.SAVE_FLAG_ALPHA); |
Chih-Chung Chang | b7ec553 | 2012-04-03 12:21:16 +0800 | [diff] [blame] | 809 | canvas.translate(cx, cy); |
Chih-Chung Chang | 2c61738 | 2012-04-20 20:06:19 +0800 | [diff] [blame] | 810 | if (wantsCardEffect) { |
Chih-Chung Chang | b7ec553 | 2012-04-03 12:21:16 +0800 | [diff] [blame] | 811 | float progress = (float) (w / 2 - r.centerX()) / w; |
| 812 | progress = Utils.clamp(progress, -1, 1); |
| 813 | float alpha = getScrollAlpha(progress); |
| 814 | float scale = getScrollScale(progress); |
Chih-Chung Chang | ba12eae | 2012-05-07 02:29:37 +0800 | [diff] [blame] | 815 | alpha = interpolate(filmRatio, alpha, 1f); |
| 816 | scale = interpolate(filmRatio, scale, 1f); |
Chih-Chung Chang | b7ec553 | 2012-04-03 12:21:16 +0800 | [diff] [blame] | 817 | canvas.multiplyAlpha(alpha); |
| 818 | canvas.scale(scale, scale, 1); |
Chih-Chung Chang | 6b891c6 | 2012-06-07 20:09:13 +0800 | [diff] [blame] | 819 | } else if (wantsOffsetEffect) { |
| 820 | float offset = (float) (r.centerY() - h / 2) / h; |
| 821 | float alpha = getOffsetAlpha(offset); |
| 822 | canvas.multiplyAlpha(alpha); |
Chih-Chung Chang | b7ec553 | 2012-04-03 12:21:16 +0800 | [diff] [blame] | 823 | } |
| 824 | if (mRotation != 0) { |
| 825 | canvas.rotate(mRotation, 0, 0, 1); |
| 826 | } |
Chih-Chung Chang | ba12eae | 2012-05-07 02:29:37 +0800 | [diff] [blame] | 827 | int drawW = getRotated(mRotation, r.width(), r.height()); |
| 828 | int drawH = getRotated(mRotation, r.height(), r.width()); |
Chih-Chung Chang | b7ec553 | 2012-04-03 12:21:16 +0800 | [diff] [blame] | 829 | mScreenNail.draw(canvas, -drawW / 2, -drawH / 2, drawW, drawH); |
Chih-Chung Chang | f5ce6ae | 2012-05-11 17:55:02 +0800 | [diff] [blame] | 830 | if (isScreenNailAnimating()) { |
| 831 | invalidate(); |
| 832 | } |
| 833 | int s = Math.min(drawW, drawH); |
Mangesh Ghiware | c1c67ea | 2012-09-30 12:58:56 -0700 | [diff] [blame] | 834 | if (mIsVideo) drawVideoPlayIcon(canvas, s); |
Chih-Chung Chang | f5ce6ae | 2012-05-11 17:55:02 +0800 | [diff] [blame] | 835 | if (mLoadingState == Model.LOADING_FAIL) { |
| 836 | drawLoadingFailMessage(canvas); |
| 837 | } |
Chih-Chung Chang | b7ec553 | 2012-04-03 12:21:16 +0800 | [diff] [blame] | 838 | canvas.restore(); |
| 839 | } |
| 840 | |
Chih-Chung Chang | f5ce6ae | 2012-05-11 17:55:02 +0800 | [diff] [blame] | 841 | private boolean isScreenNailAnimating() { |
| 842 | return (mScreenNail instanceof BitmapScreenNail) |
| 843 | && ((BitmapScreenNail) mScreenNail).isAnimating(); |
| 844 | } |
| 845 | |
Chih-Chung Chang | b7ec553 | 2012-04-03 12:21:16 +0800 | [diff] [blame] | 846 | @Override |
Chih-Chung Chang | b8be1e0 | 2012-04-17 20:35:14 +0800 | [diff] [blame] | 847 | public void setScreenNail(ScreenNail s) { |
Chih-Chung Chang | b7ec553 | 2012-04-03 12:21:16 +0800 | [diff] [blame] | 848 | mScreenNail = s; |
Chih-Chung Chang | 9f44f35 | 2012-05-29 15:28:36 -0700 | [diff] [blame] | 849 | } |
| 850 | |
Chih-Chung Chang | bd141b5 | 2012-04-26 10:10:49 +0800 | [diff] [blame] | 851 | @Override |
Chih-Chung Chang | 9f44f35 | 2012-05-29 15:28:36 -0700 | [diff] [blame] | 852 | public void forceSize() { |
| 853 | updateSize(); |
Chih-Chung Chang | 6b891c6 | 2012-06-07 20:09:13 +0800 | [diff] [blame] | 854 | mPositionController.forceImageSize(mIndex, mSize); |
Chih-Chung Chang | 9f44f35 | 2012-05-29 15:28:36 -0700 | [diff] [blame] | 855 | } |
| 856 | |
| 857 | private void updateSize() { |
Angus Kong | 43a80fd | 2012-05-17 12:47:26 -0700 | [diff] [blame] | 858 | if (mIsPanorama) { |
| 859 | mRotation = getPanoramaRotation(); |
Wu-cheng Li | dbb6acc | 2012-08-19 17:04:02 +0800 | [diff] [blame] | 860 | } else if (mIsCamera && !mIsStaticCamera) { |
Chih-Chung Chang | bd141b5 | 2012-04-26 10:10:49 +0800 | [diff] [blame] | 861 | mRotation = getCameraRotation(); |
| 862 | } else { |
| 863 | mRotation = mModel.getImageRotation(mIndex); |
| 864 | } |
Chih-Chung Chang | c3b2d47 | 2012-04-19 20:14:11 +0800 | [diff] [blame] | 865 | |
Chih-Chung Chang | b7ec553 | 2012-04-03 12:21:16 +0800 | [diff] [blame] | 866 | if (mScreenNail != null) { |
Chih-Chung Chang | 6b891c6 | 2012-06-07 20:09:13 +0800 | [diff] [blame] | 867 | mSize.width = mScreenNail.getWidth(); |
| 868 | mSize.height = mScreenNail.getHeight(); |
| 869 | } else { |
Chih-Chung Chang | c3b2d47 | 2012-04-19 20:14:11 +0800 | [diff] [blame] | 870 | // If we don't have ScreenNail available, we can still try to |
| 871 | // get the size information of it. |
| 872 | mModel.getImageSize(mIndex, mSize); |
Chih-Chung Chang | b7ec553 | 2012-04-03 12:21:16 +0800 | [diff] [blame] | 873 | } |
Chih-Chung Chang | c3b2d47 | 2012-04-19 20:14:11 +0800 | [diff] [blame] | 874 | |
Chih-Chung Chang | 6b891c6 | 2012-06-07 20:09:13 +0800 | [diff] [blame] | 875 | int w = mSize.width; |
| 876 | int h = mSize.height; |
| 877 | mSize.width = getRotated(mRotation, w, h); |
| 878 | mSize.height = getRotated(mRotation, h, w); |
Chih-Chung Chang | b7ec553 | 2012-04-03 12:21:16 +0800 | [diff] [blame] | 879 | } |
| 880 | |
| 881 | @Override |
Chih-Chung Chang | 2c61738 | 2012-04-20 20:06:19 +0800 | [diff] [blame] | 882 | public boolean isCamera() { |
| 883 | return mIsCamera; |
Chih-Chung Chang | b7ec553 | 2012-04-03 12:21:16 +0800 | [diff] [blame] | 884 | } |
Chih-Chung Chang | 6b891c6 | 2012-06-07 20:09:13 +0800 | [diff] [blame] | 885 | |
| 886 | @Override |
| 887 | public boolean isDeletable() { |
| 888 | return mIsDeletable; |
| 889 | } |
Chih-Chung Chang | b7ec553 | 2012-04-03 12:21:16 +0800 | [diff] [blame] | 890 | } |
| 891 | |
Chih-Chung Chang | f5ce6ae | 2012-05-11 17:55:02 +0800 | [diff] [blame] | 892 | // Draw a gray placeholder in the specified rectangle. |
| 893 | private void drawPlaceHolder(GLCanvas canvas, Rect r) { |
Bobby Georgescu | 915c2c5 | 2012-08-23 13:05:53 -0700 | [diff] [blame] | 894 | canvas.fillRect(r.left, r.top, r.width(), r.height(), mPlaceholderColor); |
Chih-Chung Chang | f5ce6ae | 2012-05-11 17:55:02 +0800 | [diff] [blame] | 895 | } |
| 896 | |
Chih-Chung Chang | d935511 | 2012-05-06 01:24:22 +0800 | [diff] [blame] | 897 | // Draw the video play icon (in the place where the spinner was) |
| 898 | private void drawVideoPlayIcon(GLCanvas canvas, int side) { |
| 899 | int s = side / ICON_RATIO; |
| 900 | // Draw the video play icon at the center |
| 901 | mVideoPlayIcon.draw(canvas, -s / 2, -s / 2, s, s); |
| 902 | } |
| 903 | |
Chih-Chung Chang | f5ce6ae | 2012-05-11 17:55:02 +0800 | [diff] [blame] | 904 | // Draw the "no thumbnail" message |
| 905 | private void drawLoadingFailMessage(GLCanvas canvas) { |
| 906 | StringTexture m = mNoThumbnailText; |
| 907 | m.draw(canvas, -m.getWidth() / 2, -m.getHeight() / 2); |
| 908 | } |
| 909 | |
Chih-Chung Chang | b7ec553 | 2012-04-03 12:21:16 +0800 | [diff] [blame] | 910 | private static int getRotated(int degree, int original, int theother) { |
| 911 | return (degree % 180 == 0) ? original : theother; |
| 912 | } |
| 913 | |
| 914 | //////////////////////////////////////////////////////////////////////////// |
| 915 | // Gestures Handling |
| 916 | //////////////////////////////////////////////////////////////////////////// |
| 917 | |
Owen Lin | f9a0a43 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 918 | @Override |
| 919 | protected boolean onTouch(MotionEvent event) { |
Chih-Chung Chang | 3a02809 | 2012-03-14 17:39:42 +0800 | [diff] [blame] | 920 | mGestureRecognizer.onTouchEvent(event); |
Owen Lin | f9a0a43 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 921 | return true; |
| 922 | } |
| 923 | |
Chih-Chung Chang | 3a02809 | 2012-03-14 17:39:42 +0800 | [diff] [blame] | 924 | private class MyGestureListener implements GestureRecognizer.Listener { |
| 925 | private boolean mIgnoreUpEvent = false; |
Chih-Chung Chang | 099989b | 2012-04-18 20:08:57 +0800 | [diff] [blame] | 926 | // If we can change mode for this scale gesture. |
| 927 | private boolean mCanChangeMode; |
Chih-Chung Chang | 18958c5 | 2012-05-01 02:58:59 +0800 | [diff] [blame] | 928 | // If we have changed the film mode in this scaling gesture. |
Chih-Chung Chang | b7ec553 | 2012-04-03 12:21:16 +0800 | [diff] [blame] | 929 | private boolean mModeChanged; |
Chih-Chung Chang | 33f8567 | 2012-05-01 15:35:02 +0800 | [diff] [blame] | 930 | // If this scaling gesture should be ignored. |
| 931 | private boolean mIgnoreScalingGesture; |
Chih-Chung Chang | 17ffedd | 2012-05-03 18:22:59 +0800 | [diff] [blame] | 932 | // whether the down action happened while the view is scrolling. |
| 933 | private boolean mDownInScrolling; |
Chih-Chung Chang | 6575794 | 2012-05-06 03:25:16 +0800 | [diff] [blame] | 934 | // If we should ignore all gestures other than onSingleTapUp. |
| 935 | private boolean mIgnoreSwipingGesture; |
Chih-Chung Chang | 6b891c6 | 2012-06-07 20:09:13 +0800 | [diff] [blame] | 936 | // If a scrolling has happened after a down gesture. |
| 937 | private boolean mScrolledAfterDown; |
| 938 | // If the first scrolling move is in X direction. In the film mode, X |
| 939 | // direction scrolling is normal scrolling. but Y direction scrolling is |
| 940 | // a delete gesture. |
| 941 | private boolean mFirstScrollX; |
| 942 | // The accumulated Y delta that has been sent to mPositionController. |
| 943 | private int mDeltaY; |
Chih-Chung Chang | 2ce59cb | 2012-06-18 17:24:58 +0800 | [diff] [blame] | 944 | // The accumulated scaling change from a scaling gesture. |
| 945 | private float mAccScale; |
Bobby Georgescu | 499ac9e | 2012-10-03 13:31:33 -0700 | [diff] [blame] | 946 | // If an onFling happened after the last onDown |
| 947 | private boolean mHadFling; |
Chih-Chung Chang | b7ec553 | 2012-04-03 12:21:16 +0800 | [diff] [blame] | 948 | |
Owen Lin | f9a0a43 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 949 | @Override |
Chih-Chung Chang | 3a02809 | 2012-03-14 17:39:42 +0800 | [diff] [blame] | 950 | public boolean onSingleTapUp(float x, float y) { |
Hung-ying Tyan | 4d47b34 | 2012-08-29 14:15:32 +0800 | [diff] [blame] | 951 | // On crespo running Android 2.3.6 (gingerbread), a pinch out gesture results in the |
| 952 | // following call sequence: onDown(), onUp() and then onSingleTapUp(). The correct |
| 953 | // sequence for a single-tap-up gesture should be: onDown(), onSingleTapUp() and onUp(). |
| 954 | // The call sequence for a pinch out gesture in JB is: onDown(), then onUp() and there's |
| 955 | // no onSingleTapUp(). Base on these observations, the following condition is added to |
| 956 | // filter out the false alarm where onSingleTapUp() is called within a pinch out |
| 957 | // gesture. The framework fix went into ICS. Refer to b/4588114. |
| 958 | if (Build.VERSION.SDK_INT < ApiHelper.VERSION_CODES.ICE_CREAM_SANDWICH) { |
| 959 | if ((mHolding & HOLD_TOUCH_DOWN) == 0) { |
| 960 | return true; |
| 961 | } |
| 962 | } |
| 963 | |
Chih-Chung Chang | ba12eae | 2012-05-07 02:29:37 +0800 | [diff] [blame] | 964 | // We do this in addition to onUp() because we want the snapback of |
| 965 | // setFilmMode to happen. |
| 966 | mHolding &= ~HOLD_TOUCH_DOWN; |
| 967 | |
Chih-Chung Chang | 17ffedd | 2012-05-03 18:22:59 +0800 | [diff] [blame] | 968 | if (mFilmMode && !mDownInScrolling) { |
| 969 | switchToHitPicture((int) (x + 0.5f), (int) (y + 0.5f)); |
Wu-cheng Li | 743f815 | 2012-09-27 14:01:39 +0800 | [diff] [blame] | 970 | |
| 971 | // If this is a lock screen photo, let the listener handle the |
| 972 | // event. Tapping on lock screen photo should take the user |
| 973 | // directly to the lock screen. |
| 974 | MediaItem item = mModel.getMediaItem(0); |
| 975 | int supported = 0; |
| 976 | if (item != null) supported = item.getSupportedOperations(); |
Bobby Georgescu | c7e3c76 | 2012-09-27 16:36:03 -0700 | [diff] [blame] | 977 | if ((supported & MediaItem.SUPPORT_ACTION) == 0) { |
Wu-cheng Li | 743f815 | 2012-09-27 14:01:39 +0800 | [diff] [blame] | 978 | setFilmMode(false); |
| 979 | mIgnoreUpEvent = true; |
| 980 | return true; |
| 981 | } |
Chih-Chung Chang | b7ec553 | 2012-04-03 12:21:16 +0800 | [diff] [blame] | 982 | } |
| 983 | |
Chih-Chung Chang | bd141b5 | 2012-04-26 10:10:49 +0800 | [diff] [blame] | 984 | if (mListener != null) { |
Chih-Chung Chang | 2ef46ed | 2012-05-08 17:57:33 +0800 | [diff] [blame] | 985 | // Do the inverse transform of the touch coordinates. |
| 986 | Matrix m = getGLRoot().getCompensationMatrix(); |
| 987 | Matrix inv = new Matrix(); |
| 988 | m.invert(inv); |
| 989 | float[] pts = new float[] {x, y}; |
| 990 | inv.mapPoints(pts); |
| 991 | mListener.onSingleTapUp((int) (pts[0] + 0.5f), (int) (pts[1] + 0.5f)); |
Chih-Chung Chang | 3a02809 | 2012-03-14 17:39:42 +0800 | [diff] [blame] | 992 | } |
| 993 | return true; |
| 994 | } |
| 995 | |
| 996 | @Override |
| 997 | public boolean onDoubleTap(float x, float y) { |
Chih-Chung Chang | 6575794 | 2012-05-06 03:25:16 +0800 | [diff] [blame] | 998 | if (mIgnoreSwipingGesture) return true; |
Chih-Chung Chang | 61f9471 | 2012-05-02 20:05:16 +0800 | [diff] [blame] | 999 | if (mPictures.get(0).isCamera()) return false; |
Chih-Chung Chang | 3a02809 | 2012-03-14 17:39:42 +0800 | [diff] [blame] | 1000 | PositionController controller = mPositionController; |
Chih-Chung Chang | b7ec553 | 2012-04-03 12:21:16 +0800 | [diff] [blame] | 1001 | float scale = controller.getImageScale(); |
Chih-Chung Chang | 3a02809 | 2012-03-14 17:39:42 +0800 | [diff] [blame] | 1002 | // onDoubleTap happened on the second ACTION_DOWN. |
| 1003 | // We need to ignore the next UP event. |
| 1004 | mIgnoreUpEvent = true; |
| 1005 | if (scale <= 1.0f || controller.isAtMinimalScale()) { |
| 1006 | controller.zoomIn(x, y, Math.max(1.5f, scale * 1.5f)); |
| 1007 | } else { |
| 1008 | controller.resetToFullView(); |
| 1009 | } |
| 1010 | return true; |
| 1011 | } |
| 1012 | |
| 1013 | @Override |
Chih-Chung Chang | 6b891c6 | 2012-06-07 20:09:13 +0800 | [diff] [blame] | 1014 | public boolean onScroll(float dx, float dy, float totalX, float totalY) { |
Chih-Chung Chang | 6575794 | 2012-05-06 03:25:16 +0800 | [diff] [blame] | 1015 | if (mIgnoreSwipingGesture) return true; |
Chih-Chung Chang | 6b891c6 | 2012-06-07 20:09:13 +0800 | [diff] [blame] | 1016 | if (!mScrolledAfterDown) { |
| 1017 | mScrolledAfterDown = true; |
| 1018 | mFirstScrollX = (Math.abs(dx) > Math.abs(dy)); |
| 1019 | } |
| 1020 | |
| 1021 | int dxi = (int) (-dx + 0.5f); |
| 1022 | int dyi = (int) (-dy + 0.5f); |
| 1023 | if (mFilmMode) { |
| 1024 | if (mFirstScrollX) { |
| 1025 | mPositionController.scrollFilmX(dxi); |
| 1026 | } else { |
| 1027 | if (mTouchBoxIndex == Integer.MAX_VALUE) return true; |
| 1028 | int newDeltaY = calculateDeltaY(totalY); |
| 1029 | int d = newDeltaY - mDeltaY; |
| 1030 | if (d != 0) { |
| 1031 | mPositionController.scrollFilmY(mTouchBoxIndex, d); |
| 1032 | mDeltaY = newDeltaY; |
| 1033 | } |
| 1034 | } |
| 1035 | } else { |
| 1036 | mPositionController.scrollPage(dxi, dyi); |
| 1037 | } |
Owen Lin | f9a0a43 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 1038 | return true; |
| 1039 | } |
| 1040 | |
Chih-Chung Chang | 6b891c6 | 2012-06-07 20:09:13 +0800 | [diff] [blame] | 1041 | private int calculateDeltaY(float delta) { |
| 1042 | if (mTouchBoxDeletable) return (int) (delta + 0.5f); |
| 1043 | |
| 1044 | // don't let items that can't be deleted be dragged more than |
| 1045 | // maxScrollDistance, and make it harder and harder to drag. |
| 1046 | int size = getHeight(); |
| 1047 | float maxScrollDistance = 0.15f * size; |
| 1048 | if (Math.abs(delta) >= size) { |
| 1049 | delta = delta > 0 ? maxScrollDistance : -maxScrollDistance; |
| 1050 | } else { |
| 1051 | delta = maxScrollDistance * |
| 1052 | FloatMath.sin((delta / size) * (float) (Math.PI / 2)); |
| 1053 | } |
| 1054 | return (int) (delta + 0.5f); |
| 1055 | } |
| 1056 | |
Owen Lin | f9a0a43 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 1057 | @Override |
Chih-Chung Chang | 3a02809 | 2012-03-14 17:39:42 +0800 | [diff] [blame] | 1058 | public boolean onFling(float velocityX, float velocityY) { |
Chih-Chung Chang | 6575794 | 2012-05-06 03:25:16 +0800 | [diff] [blame] | 1059 | if (mIgnoreSwipingGesture) return true; |
Chih-Chung Chang | e949416 | 2012-09-06 20:02:09 +0800 | [diff] [blame] | 1060 | if (mModeChanged) return true; |
Yuli Huang | 2ce3c3b | 2012-02-23 22:26:12 +0800 | [diff] [blame] | 1061 | if (swipeImages(velocityX, velocityY)) { |
Chih-Chung Chang | b3aab90 | 2011-10-03 21:11:39 +0800 | [diff] [blame] | 1062 | mIgnoreUpEvent = true; |
Chih-Chung Chang | 6b891c6 | 2012-06-07 20:09:13 +0800 | [diff] [blame] | 1063 | } else { |
| 1064 | flingImages(velocityX, velocityY); |
Owen Lin | f9a0a43 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 1065 | } |
Bobby Georgescu | 499ac9e | 2012-10-03 13:31:33 -0700 | [diff] [blame] | 1066 | mHadFling = true; |
Owen Lin | f9a0a43 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 1067 | return true; |
| 1068 | } |
| 1069 | |
Chih-Chung Chang | 6b891c6 | 2012-06-07 20:09:13 +0800 | [diff] [blame] | 1070 | private boolean flingImages(float velocityX, float velocityY) { |
| 1071 | int vx = (int) (velocityX + 0.5f); |
| 1072 | int vy = (int) (velocityY + 0.5f); |
| 1073 | if (!mFilmMode) { |
| 1074 | return mPositionController.flingPage(vx, vy); |
| 1075 | } |
| 1076 | if (Math.abs(velocityX) > Math.abs(velocityY)) { |
| 1077 | return mPositionController.flingFilmX(vx); |
| 1078 | } |
| 1079 | // If we scrolled in Y direction fast enough, treat it as a delete |
| 1080 | // gesture. |
| 1081 | if (!mFilmMode || mTouchBoxIndex == Integer.MAX_VALUE |
| 1082 | || !mTouchBoxDeletable) { |
| 1083 | return false; |
| 1084 | } |
Ahbong Chang | 7817979 | 2012-07-30 11:34:13 +0800 | [diff] [blame] | 1085 | int maxVelocity = GalleryUtils.dpToPixel(MAX_DISMISS_VELOCITY); |
| 1086 | int escapeVelocity = GalleryUtils.dpToPixel(SWIPE_ESCAPE_VELOCITY); |
Chih-Chung Chang | 6b891c6 | 2012-06-07 20:09:13 +0800 | [diff] [blame] | 1087 | int centerY = mPositionController.getPosition(mTouchBoxIndex) |
| 1088 | .centerY(); |
| 1089 | boolean fastEnough = (Math.abs(vy) > escapeVelocity) |
| 1090 | && (Math.abs(vy) > Math.abs(vx)) |
| 1091 | && ((vy > 0) == (centerY > getHeight() / 2)); |
| 1092 | if (fastEnough) { |
| 1093 | vy = Math.min(vy, maxVelocity); |
| 1094 | int duration = mPositionController.flingFilmY(mTouchBoxIndex, vy); |
| 1095 | if (duration >= 0) { |
| 1096 | mPositionController.setPopFromTop(vy < 0); |
| 1097 | deleteAfterAnimation(duration); |
| 1098 | // We reset mTouchBoxIndex, so up() won't check if Y |
| 1099 | // scrolled far enough to be a delete gesture. |
| 1100 | mTouchBoxIndex = Integer.MAX_VALUE; |
| 1101 | return true; |
| 1102 | } |
| 1103 | } |
| 1104 | return false; |
| 1105 | } |
| 1106 | |
| 1107 | private void deleteAfterAnimation(int duration) { |
| 1108 | MediaItem item = mModel.getMediaItem(mTouchBoxIndex); |
| 1109 | if (item == null) return; |
Chih-Chung Chang | 517e1bd | 2012-06-19 17:34:50 +0800 | [diff] [blame] | 1110 | mListener.onCommitDeleteImage(); |
| 1111 | mUndoIndexHint = mModel.getCurrentIndex() + mTouchBoxIndex; |
Chih-Chung Chang | 6b891c6 | 2012-06-07 20:09:13 +0800 | [diff] [blame] | 1112 | mHolding |= HOLD_DELETE; |
| 1113 | Message m = mHandler.obtainMessage(MSG_DELETE_ANIMATION_DONE); |
| 1114 | m.obj = item.getPath(); |
| 1115 | m.arg1 = mTouchBoxIndex; |
| 1116 | mHandler.sendMessageDelayed(m, duration); |
| 1117 | } |
| 1118 | |
Owen Lin | f9a0a43 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 1119 | @Override |
Chih-Chung Chang | 3a02809 | 2012-03-14 17:39:42 +0800 | [diff] [blame] | 1120 | public boolean onScaleBegin(float focusX, float focusY) { |
Chih-Chung Chang | 6575794 | 2012-05-06 03:25:16 +0800 | [diff] [blame] | 1121 | if (mIgnoreSwipingGesture) return true; |
Chih-Chung Chang | 33f8567 | 2012-05-01 15:35:02 +0800 | [diff] [blame] | 1122 | // We ignore the scaling gesture if it is a camera preview. |
| 1123 | mIgnoreScalingGesture = mPictures.get(0).isCamera(); |
| 1124 | if (mIgnoreScalingGesture) { |
| 1125 | return true; |
| 1126 | } |
Chih-Chung Chang | 3a02809 | 2012-03-14 17:39:42 +0800 | [diff] [blame] | 1127 | mPositionController.beginScale(focusX, focusY); |
Chih-Chung Chang | 099989b | 2012-04-18 20:08:57 +0800 | [diff] [blame] | 1128 | // We can change mode if we are in film mode, or we are in page |
| 1129 | // mode and at minimal scale. |
| 1130 | mCanChangeMode = mFilmMode |
| 1131 | || mPositionController.isAtMinimalScale(); |
Chih-Chung Chang | 2ce59cb | 2012-06-18 17:24:58 +0800 | [diff] [blame] | 1132 | mAccScale = 1f; |
Owen Lin | f9a0a43 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 1133 | return true; |
| 1134 | } |
Owen Lin | f9a0a43 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 1135 | |
| 1136 | @Override |
Chih-Chung Chang | 3a02809 | 2012-03-14 17:39:42 +0800 | [diff] [blame] | 1137 | public boolean onScale(float focusX, float focusY, float scale) { |
Chih-Chung Chang | 6575794 | 2012-05-06 03:25:16 +0800 | [diff] [blame] | 1138 | if (mIgnoreSwipingGesture) return true; |
| 1139 | if (mIgnoreScalingGesture) return true; |
Chih-Chung Chang | 18958c5 | 2012-05-01 02:58:59 +0800 | [diff] [blame] | 1140 | if (mModeChanged) return true; |
Chih-Chung Chang | b7ec553 | 2012-04-03 12:21:16 +0800 | [diff] [blame] | 1141 | if (Float.isNaN(scale) || Float.isInfinite(scale)) return false; |
Chih-Chung Chang | 33f8567 | 2012-05-01 15:35:02 +0800 | [diff] [blame] | 1142 | |
Chih-Chung Chang | b7ec553 | 2012-04-03 12:21:16 +0800 | [diff] [blame] | 1143 | int outOfRange = mPositionController.scaleBy(scale, focusX, focusY); |
| 1144 | |
Chih-Chung Chang | 2ce59cb | 2012-06-18 17:24:58 +0800 | [diff] [blame] | 1145 | // We wait for a large enough scale change before changing mode. |
| 1146 | // Otherwise we may mistakenly treat a zoom-in gesture as zoom-out |
| 1147 | // or vice versa. |
| 1148 | mAccScale *= scale; |
| 1149 | boolean largeEnough = (mAccScale < 0.97f || mAccScale > 1.03f); |
| 1150 | |
Chih-Chung Chang | 18958c5 | 2012-05-01 02:58:59 +0800 | [diff] [blame] | 1151 | // If mode changes, we treat this scaling gesture has ended. |
Chih-Chung Chang | 2ce59cb | 2012-06-18 17:24:58 +0800 | [diff] [blame] | 1152 | if (mCanChangeMode && largeEnough) { |
Chih-Chung Chang | b7ec553 | 2012-04-03 12:21:16 +0800 | [diff] [blame] | 1153 | if ((outOfRange < 0 && !mFilmMode) || |
| 1154 | (outOfRange > 0 && mFilmMode)) { |
Chih-Chung Chang | 18958c5 | 2012-05-01 02:58:59 +0800 | [diff] [blame] | 1155 | stopExtraScalingIfNeeded(); |
| 1156 | |
| 1157 | // Removing the touch down flag allows snapback to happen |
Chih-Chung Chang | 33f8567 | 2012-05-01 15:35:02 +0800 | [diff] [blame] | 1158 | // for film mode change. |
Chih-Chung Chang | 18958c5 | 2012-05-01 02:58:59 +0800 | [diff] [blame] | 1159 | mHolding &= ~HOLD_TOUCH_DOWN; |
Chih-Chung Chang | b7ec553 | 2012-04-03 12:21:16 +0800 | [diff] [blame] | 1160 | setFilmMode(!mFilmMode); |
Chih-Chung Chang | 18958c5 | 2012-05-01 02:58:59 +0800 | [diff] [blame] | 1161 | |
| 1162 | // We need to call onScaleEnd() before setting mModeChanged |
| 1163 | // to true. |
| 1164 | onScaleEnd(); |
Chih-Chung Chang | b7ec553 | 2012-04-03 12:21:16 +0800 | [diff] [blame] | 1165 | mModeChanged = true; |
| 1166 | return true; |
Chih-Chung Chang | 534b12f | 2012-03-21 19:01:30 +0800 | [diff] [blame] | 1167 | } |
Chih-Chung Chang | b7ec553 | 2012-04-03 12:21:16 +0800 | [diff] [blame] | 1168 | } |
| 1169 | |
Chih-Chung Chang | 18958c5 | 2012-05-01 02:58:59 +0800 | [diff] [blame] | 1170 | if (outOfRange != 0) { |
Chih-Chung Chang | b7ec553 | 2012-04-03 12:21:16 +0800 | [diff] [blame] | 1171 | startExtraScalingIfNeeded(); |
Chih-Chung Chang | 534b12f | 2012-03-21 19:01:30 +0800 | [diff] [blame] | 1172 | } else { |
Chih-Chung Chang | b7ec553 | 2012-04-03 12:21:16 +0800 | [diff] [blame] | 1173 | stopExtraScalingIfNeeded(); |
Chih-Chung Chang | 534b12f | 2012-03-21 19:01:30 +0800 | [diff] [blame] | 1174 | } |
Owen Lin | f9a0a43 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 1175 | return true; |
| 1176 | } |
| 1177 | |
Chih-Chung Chang | 33f8567 | 2012-05-01 15:35:02 +0800 | [diff] [blame] | 1178 | @Override |
| 1179 | public void onScaleEnd() { |
Chih-Chung Chang | 6575794 | 2012-05-06 03:25:16 +0800 | [diff] [blame] | 1180 | if (mIgnoreSwipingGesture) return; |
| 1181 | if (mIgnoreScalingGesture) return; |
Chih-Chung Chang | 33f8567 | 2012-05-01 15:35:02 +0800 | [diff] [blame] | 1182 | if (mModeChanged) return; |
| 1183 | mPositionController.endScale(); |
| 1184 | } |
| 1185 | |
Chih-Chung Chang | b7ec553 | 2012-04-03 12:21:16 +0800 | [diff] [blame] | 1186 | private void startExtraScalingIfNeeded() { |
| 1187 | if (!mCancelExtraScalingPending) { |
| 1188 | mHandler.sendEmptyMessageDelayed( |
| 1189 | MSG_CANCEL_EXTRA_SCALING, 700); |
| 1190 | mPositionController.setExtraScalingRange(true); |
| 1191 | mCancelExtraScalingPending = true; |
| 1192 | } |
| 1193 | } |
| 1194 | |
| 1195 | private void stopExtraScalingIfNeeded() { |
| 1196 | if (mCancelExtraScalingPending) { |
| 1197 | mHandler.removeMessages(MSG_CANCEL_EXTRA_SCALING); |
| 1198 | mPositionController.setExtraScalingRange(false); |
| 1199 | mCancelExtraScalingPending = false; |
| 1200 | } |
| 1201 | } |
| 1202 | |
Owen Lin | f9a0a43 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 1203 | @Override |
Chih-Chung Chang | 6b891c6 | 2012-06-07 20:09:13 +0800 | [diff] [blame] | 1204 | public void onDown(float x, float y) { |
Chih-Chung Chang | 517e1bd | 2012-06-19 17:34:50 +0800 | [diff] [blame] | 1205 | checkHideUndoBar(UNDO_BAR_TOUCHED); |
| 1206 | |
Chih-Chung Chang | 6b891c6 | 2012-06-07 20:09:13 +0800 | [diff] [blame] | 1207 | mDeltaY = 0; |
Chih-Chung Chang | e949416 | 2012-09-06 20:02:09 +0800 | [diff] [blame] | 1208 | mModeChanged = false; |
Chih-Chung Chang | 6b891c6 | 2012-06-07 20:09:13 +0800 | [diff] [blame] | 1209 | |
Chih-Chung Chang | 6575794 | 2012-05-06 03:25:16 +0800 | [diff] [blame] | 1210 | if (mIgnoreSwipingGesture) return; |
| 1211 | |
Chih-Chung Chang | 2c61738 | 2012-04-20 20:06:19 +0800 | [diff] [blame] | 1212 | mHolding |= HOLD_TOUCH_DOWN; |
Chih-Chung Chang | 17ffedd | 2012-05-03 18:22:59 +0800 | [diff] [blame] | 1213 | |
| 1214 | if (mFilmMode && mPositionController.isScrolling()) { |
| 1215 | mDownInScrolling = true; |
| 1216 | mPositionController.stopScrolling(); |
| 1217 | } else { |
| 1218 | mDownInScrolling = false; |
| 1219 | } |
Bobby Georgescu | 499ac9e | 2012-10-03 13:31:33 -0700 | [diff] [blame] | 1220 | mHadFling = false; |
Chih-Chung Chang | 6b891c6 | 2012-06-07 20:09:13 +0800 | [diff] [blame] | 1221 | mScrolledAfterDown = false; |
| 1222 | if (mFilmMode) { |
| 1223 | int xi = (int) (x + 0.5f); |
| 1224 | int yi = (int) (y + 0.5f); |
| 1225 | mTouchBoxIndex = mPositionController.hitTest(xi, yi); |
| 1226 | if (mTouchBoxIndex < mPrevBound || mTouchBoxIndex > mNextBound) { |
| 1227 | mTouchBoxIndex = Integer.MAX_VALUE; |
| 1228 | } else { |
| 1229 | mTouchBoxDeletable = |
| 1230 | mPictures.get(mTouchBoxIndex).isDeletable(); |
| 1231 | } |
| 1232 | } else { |
| 1233 | mTouchBoxIndex = Integer.MAX_VALUE; |
| 1234 | } |
Chih-Chung Chang | 3a02809 | 2012-03-14 17:39:42 +0800 | [diff] [blame] | 1235 | } |
| 1236 | |
| 1237 | @Override |
| 1238 | public void onUp() { |
Chih-Chung Chang | 6575794 | 2012-05-06 03:25:16 +0800 | [diff] [blame] | 1239 | if (mIgnoreSwipingGesture) return; |
| 1240 | |
Chih-Chung Chang | 18958c5 | 2012-05-01 02:58:59 +0800 | [diff] [blame] | 1241 | mHolding &= ~HOLD_TOUCH_DOWN; |
Chih-Chung Chang | 3a02809 | 2012-03-14 17:39:42 +0800 | [diff] [blame] | 1242 | mEdgeView.onRelease(); |
| 1243 | |
Chih-Chung Chang | 6b891c6 | 2012-06-07 20:09:13 +0800 | [diff] [blame] | 1244 | // If we scrolled in Y direction far enough, treat it as a delete |
| 1245 | // gesture. |
| 1246 | if (mFilmMode && mScrolledAfterDown && !mFirstScrollX |
| 1247 | && mTouchBoxIndex != Integer.MAX_VALUE) { |
| 1248 | Rect r = mPositionController.getPosition(mTouchBoxIndex); |
| 1249 | int h = getHeight(); |
| 1250 | if (Math.abs(r.centerY() - h * 0.5f) > 0.4f * h) { |
| 1251 | int duration = mPositionController |
| 1252 | .flingFilmY(mTouchBoxIndex, 0); |
| 1253 | if (duration >= 0) { |
| 1254 | mPositionController.setPopFromTop(r.centerY() < h * 0.5f); |
| 1255 | deleteAfterAnimation(duration); |
| 1256 | } |
| 1257 | } |
| 1258 | } |
| 1259 | |
Chih-Chung Chang | 3a02809 | 2012-03-14 17:39:42 +0800 | [diff] [blame] | 1260 | if (mIgnoreUpEvent) { |
| 1261 | mIgnoreUpEvent = false; |
| 1262 | return; |
| 1263 | } |
Bobby Georgescu | b1a5139 | 2012-10-04 11:15:43 -0700 | [diff] [blame] | 1264 | |
Bobby Georgescu | dc55d3b | 2012-10-09 15:59:40 -0700 | [diff] [blame] | 1265 | if (!(mFilmMode && !mHadFling && mFirstScrollX |
| 1266 | && snapToNeighborImage())) { |
Bobby Georgescu | b1a5139 | 2012-10-04 11:15:43 -0700 | [diff] [blame] | 1267 | snapback(); |
Bobby Georgescu | 499ac9e | 2012-10-03 13:31:33 -0700 | [diff] [blame] | 1268 | } |
Chih-Chung Chang | 3a02809 | 2012-03-14 17:39:42 +0800 | [diff] [blame] | 1269 | } |
Chih-Chung Chang | 6575794 | 2012-05-06 03:25:16 +0800 | [diff] [blame] | 1270 | |
| 1271 | public void setSwipingEnabled(boolean enabled) { |
| 1272 | mIgnoreSwipingGesture = !enabled; |
| 1273 | } |
| 1274 | } |
| 1275 | |
| 1276 | public void setSwipingEnabled(boolean enabled) { |
| 1277 | mGestureListener.setSwipingEnabled(enabled); |
Owen Lin | f9a0a43 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 1278 | } |
| 1279 | |
Bobby Georgescu | 00ccf35 | 2012-09-19 16:51:05 -0700 | [diff] [blame] | 1280 | private void updateActionBar() { |
| 1281 | boolean isCamera = mPictures.get(0).isCamera(); |
| 1282 | if (isCamera && !mFilmMode) { |
| 1283 | // Move into camera in page mode, lock |
Bobby Georgescu | 00ccf35 | 2012-09-19 16:51:05 -0700 | [diff] [blame] | 1284 | mListener.onActionBarAllowed(false); |
| 1285 | } else { |
| 1286 | mListener.onActionBarAllowed(true); |
| 1287 | if (mFilmMode) mListener.onActionBarWanted(); |
| 1288 | } |
| 1289 | } |
| 1290 | |
Bobby Georgescu | 7eea4d3 | 2012-09-06 17:14:02 -0700 | [diff] [blame] | 1291 | public void setFilmMode(boolean enabled) { |
Chih-Chung Chang | b7ec553 | 2012-04-03 12:21:16 +0800 | [diff] [blame] | 1292 | if (mFilmMode == enabled) return; |
| 1293 | mFilmMode = enabled; |
| 1294 | mPositionController.setFilmMode(mFilmMode); |
Chih-Chung Chang | b8be1e0 | 2012-04-17 20:35:14 +0800 | [diff] [blame] | 1295 | mModel.setNeedFullImage(!enabled); |
Chih-Chung Chang | 6b891c6 | 2012-06-07 20:09:13 +0800 | [diff] [blame] | 1296 | mModel.setFocusHintDirection( |
| 1297 | mFilmMode ? Model.FOCUS_HINT_PREVIOUS : Model.FOCUS_HINT_NEXT); |
Bobby Georgescu | 00ccf35 | 2012-09-19 16:51:05 -0700 | [diff] [blame] | 1298 | updateActionBar(); |
Bobby Georgescu | 7eea4d3 | 2012-09-06 17:14:02 -0700 | [diff] [blame] | 1299 | mListener.onFilmModeChanged(enabled); |
Chih-Chung Chang | bd141b5 | 2012-04-26 10:10:49 +0800 | [diff] [blame] | 1300 | } |
| 1301 | |
| 1302 | public boolean getFilmMode() { |
| 1303 | return mFilmMode; |
Owen Lin | f9a0a43 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 1304 | } |
| 1305 | |
Chih-Chung Chang | b7ec553 | 2012-04-03 12:21:16 +0800 | [diff] [blame] | 1306 | //////////////////////////////////////////////////////////////////////////// |
| 1307 | // Framework events |
| 1308 | //////////////////////////////////////////////////////////////////////////// |
| 1309 | |
Chih-Chung Chang | b7ec553 | 2012-04-03 12:21:16 +0800 | [diff] [blame] | 1310 | public void pause() { |
| 1311 | mPositionController.skipAnimation(); |
| 1312 | mTileView.freeTextures(); |
| 1313 | for (int i = -SCREEN_NAIL_MAX; i <= SCREEN_NAIL_MAX; i++) { |
Chih-Chung Chang | b8be1e0 | 2012-04-17 20:35:14 +0800 | [diff] [blame] | 1314 | mPictures.get(i).setScreenNail(null); |
Chih-Chung Chang | b7ec553 | 2012-04-03 12:21:16 +0800 | [diff] [blame] | 1315 | } |
Chih-Chung Chang | 6118af9 | 2012-06-22 20:56:04 +0800 | [diff] [blame] | 1316 | hideUndoBar(); |
Chih-Chung Chang | b7ec553 | 2012-04-03 12:21:16 +0800 | [diff] [blame] | 1317 | } |
| 1318 | |
| 1319 | public void resume() { |
| 1320 | mTileView.prepareTextures(); |
Bobby Georgescu | 3ed6ae2 | 2012-10-10 23:29:18 -0700 | [diff] [blame] | 1321 | mPositionController.skipToFinalPosition(); |
Chih-Chung Chang | b7ec553 | 2012-04-03 12:21:16 +0800 | [diff] [blame] | 1322 | } |
| 1323 | |
Chih-Chung Chang | 33f8567 | 2012-05-01 15:35:02 +0800 | [diff] [blame] | 1324 | // move to the camera preview and show controls after resume |
| 1325 | public void resetToFirstPicture() { |
| 1326 | mModel.moveTo(0); |
| 1327 | setFilmMode(false); |
| 1328 | } |
| 1329 | |
Chih-Chung Chang | 517e1bd | 2012-06-19 17:34:50 +0800 | [diff] [blame] | 1330 | //////////////////////////////////////////////////////////////////////////// |
| 1331 | // Undo Bar |
| 1332 | //////////////////////////////////////////////////////////////////////////// |
| 1333 | |
| 1334 | private int mUndoBarState; |
| 1335 | private static final int UNDO_BAR_SHOW = 1; |
| 1336 | private static final int UNDO_BAR_TIMEOUT = 2; |
| 1337 | private static final int UNDO_BAR_TOUCHED = 4; |
Chih-Chung Chang | 6118af9 | 2012-06-22 20:56:04 +0800 | [diff] [blame] | 1338 | private static final int UNDO_BAR_FULL_CAMERA = 8; |
| 1339 | private static final int UNDO_BAR_DELETE_LAST = 16; |
Chih-Chung Chang | 517e1bd | 2012-06-19 17:34:50 +0800 | [diff] [blame] | 1340 | |
Chih-Chung Chang | 6118af9 | 2012-06-22 20:56:04 +0800 | [diff] [blame] | 1341 | // "deleteLast" means if the deletion is on the last remaining picture in |
| 1342 | // the album. |
| 1343 | private void showUndoBar(boolean deleteLast) { |
| 1344 | mHandler.removeMessages(MSG_UNDO_BAR_TIMEOUT); |
Chih-Chung Chang | 517e1bd | 2012-06-19 17:34:50 +0800 | [diff] [blame] | 1345 | mUndoBarState = UNDO_BAR_SHOW; |
Chih-Chung Chang | 6118af9 | 2012-06-22 20:56:04 +0800 | [diff] [blame] | 1346 | if(deleteLast) mUndoBarState |= UNDO_BAR_DELETE_LAST; |
Chih-Chung Chang | 517e1bd | 2012-06-19 17:34:50 +0800 | [diff] [blame] | 1347 | mUndoBar.animateVisibility(GLView.VISIBLE); |
Chih-Chung Chang | 6118af9 | 2012-06-22 20:56:04 +0800 | [diff] [blame] | 1348 | mHandler.sendEmptyMessageDelayed(MSG_UNDO_BAR_TIMEOUT, 3000); |
Chih-Chung Chang | 517e1bd | 2012-06-19 17:34:50 +0800 | [diff] [blame] | 1349 | } |
| 1350 | |
Chih-Chung Chang | 6118af9 | 2012-06-22 20:56:04 +0800 | [diff] [blame] | 1351 | private void hideUndoBar() { |
| 1352 | mHandler.removeMessages(MSG_UNDO_BAR_TIMEOUT); |
Chih-Chung Chang | 517e1bd | 2012-06-19 17:34:50 +0800 | [diff] [blame] | 1353 | mListener.onCommitDeleteImage(); |
| 1354 | mUndoBar.animateVisibility(GLView.INVISIBLE); |
| 1355 | mUndoBarState = 0; |
| 1356 | mUndoIndexHint = Integer.MAX_VALUE; |
| 1357 | } |
| 1358 | |
Chih-Chung Chang | 6118af9 | 2012-06-22 20:56:04 +0800 | [diff] [blame] | 1359 | // Check if the one of the conditions for hiding the undo bar has been |
| 1360 | // met. The conditions are: |
| 1361 | // |
| 1362 | // 1. It has been three seconds since last showing, and (a) the user has |
| 1363 | // touched, or (b) the deleted picture is the last remaining picture in the |
| 1364 | // album. |
| 1365 | // |
| 1366 | // 2. The camera is shown in full screen. |
Chih-Chung Chang | 517e1bd | 2012-06-19 17:34:50 +0800 | [diff] [blame] | 1367 | private void checkHideUndoBar(int addition) { |
| 1368 | mUndoBarState |= addition; |
Chih-Chung Chang | 6118af9 | 2012-06-22 20:56:04 +0800 | [diff] [blame] | 1369 | if ((mUndoBarState & UNDO_BAR_SHOW) == 0) return; |
| 1370 | boolean timeout = (mUndoBarState & UNDO_BAR_TIMEOUT) != 0; |
| 1371 | boolean touched = (mUndoBarState & UNDO_BAR_TOUCHED) != 0; |
| 1372 | boolean fullCamera = (mUndoBarState & UNDO_BAR_FULL_CAMERA) != 0; |
| 1373 | boolean deleteLast = (mUndoBarState & UNDO_BAR_DELETE_LAST) != 0; |
| 1374 | if ((timeout && (touched || deleteLast)) || fullCamera) { |
Chih-Chung Chang | 517e1bd | 2012-06-19 17:34:50 +0800 | [diff] [blame] | 1375 | hideUndoBar(); |
| 1376 | } |
Chih-Chung Chang | 6b891c6 | 2012-06-07 20:09:13 +0800 | [diff] [blame] | 1377 | } |
| 1378 | |
Bobby Georgescu | 639095c | 2012-10-08 13:37:41 -0700 | [diff] [blame] | 1379 | public boolean canUndo() { |
| 1380 | return (mUndoBarState & UNDO_BAR_SHOW) != 0; |
Chih-Chung Chang | 6118af9 | 2012-06-22 20:56:04 +0800 | [diff] [blame] | 1381 | } |
| 1382 | |
Chih-Chung Chang | b7ec553 | 2012-04-03 12:21:16 +0800 | [diff] [blame] | 1383 | //////////////////////////////////////////////////////////////////////////// |
| 1384 | // Rendering |
| 1385 | //////////////////////////////////////////////////////////////////////////// |
| 1386 | |
| 1387 | @Override |
| 1388 | protected void render(GLCanvas canvas) { |
Chih-Chung Chang | 9f44f35 | 2012-05-29 15:28:36 -0700 | [diff] [blame] | 1389 | // Check if the camera preview occupies the full screen. |
| 1390 | boolean full = !mFilmMode && mPictures.get(0).isCamera() |
| 1391 | && mPositionController.isCenter() |
| 1392 | && mPositionController.isAtMinimalScale(); |
| 1393 | if (full != mFullScreenCamera) { |
| 1394 | mFullScreenCamera = full; |
| 1395 | mListener.onFullScreenChanged(full); |
Chih-Chung Chang | 6118af9 | 2012-06-22 20:56:04 +0800 | [diff] [blame] | 1396 | if (full) mHandler.sendEmptyMessage(MSG_UNDO_BAR_FULL_CAMERA); |
Chih-Chung Chang | 9f44f35 | 2012-05-29 15:28:36 -0700 | [diff] [blame] | 1397 | } |
| 1398 | |
| 1399 | // Determine how many photos we need to draw in addition to the center |
| 1400 | // one. |
| 1401 | int neighbors; |
| 1402 | if (mFullScreenCamera) { |
| 1403 | neighbors = 0; |
| 1404 | } else { |
| 1405 | // In page mode, we draw only one previous/next photo. But if we are |
| 1406 | // doing capture animation, we want to draw all photos. |
| 1407 | boolean inPageMode = (mPositionController.getFilmRatio() == 0f); |
| 1408 | boolean inCaptureAnimation = |
| 1409 | ((mHolding & HOLD_CAPTURE_ANIMATION) != 0); |
| 1410 | if (inPageMode && !inCaptureAnimation) { |
| 1411 | neighbors = 1; |
| 1412 | } else { |
| 1413 | neighbors = SCREEN_NAIL_MAX; |
| 1414 | } |
| 1415 | } |
Chih-Chung Chang | ba12eae | 2012-05-07 02:29:37 +0800 | [diff] [blame] | 1416 | |
Chih-Chung Chang | c4791b7 | 2012-05-18 19:10:36 -0700 | [diff] [blame] | 1417 | // Draw photos from back to front |
| 1418 | for (int i = neighbors; i >= -neighbors; i--) { |
Chih-Chung Chang | b7ec553 | 2012-04-03 12:21:16 +0800 | [diff] [blame] | 1419 | Rect r = mPositionController.getPosition(i); |
| 1420 | mPictures.get(i).draw(canvas, r); |
Chih-Chung Chang | b7ec553 | 2012-04-03 12:21:16 +0800 | [diff] [blame] | 1421 | } |
| 1422 | |
Chih-Chung Chang | 6b891c6 | 2012-06-07 20:09:13 +0800 | [diff] [blame] | 1423 | renderChild(canvas, mEdgeView); |
| 1424 | renderChild(canvas, mUndoBar); |
| 1425 | |
Chih-Chung Chang | b7ec553 | 2012-04-03 12:21:16 +0800 | [diff] [blame] | 1426 | mPositionController.advanceAnimation(); |
| 1427 | checkFocusSwitching(); |
| 1428 | } |
| 1429 | |
| 1430 | //////////////////////////////////////////////////////////////////////////// |
| 1431 | // Film mode focus switching |
| 1432 | //////////////////////////////////////////////////////////////////////////// |
| 1433 | |
| 1434 | // Runs in GL thread. |
| 1435 | private void checkFocusSwitching() { |
| 1436 | if (!mFilmMode) return; |
| 1437 | if (mHandler.hasMessages(MSG_SWITCH_FOCUS)) return; |
| 1438 | if (switchPosition() != 0) { |
| 1439 | mHandler.sendEmptyMessage(MSG_SWITCH_FOCUS); |
| 1440 | } |
| 1441 | } |
| 1442 | |
| 1443 | // Runs in main thread. |
| 1444 | private void switchFocus() { |
Chih-Chung Chang | 2c61738 | 2012-04-20 20:06:19 +0800 | [diff] [blame] | 1445 | if (mHolding != 0) return; |
Chih-Chung Chang | b7ec553 | 2012-04-03 12:21:16 +0800 | [diff] [blame] | 1446 | switch (switchPosition()) { |
| 1447 | case -1: |
| 1448 | switchToPrevImage(); |
| 1449 | break; |
| 1450 | case 1: |
| 1451 | switchToNextImage(); |
| 1452 | break; |
| 1453 | } |
| 1454 | } |
| 1455 | |
| 1456 | // Returns -1 if we should switch focus to the previous picture, +1 if we |
| 1457 | // should switch to the next, 0 otherwise. |
| 1458 | private int switchPosition() { |
| 1459 | Rect curr = mPositionController.getPosition(0); |
| 1460 | int center = getWidth() / 2; |
| 1461 | |
Chih-Chung Chang | 2c61738 | 2012-04-20 20:06:19 +0800 | [diff] [blame] | 1462 | if (curr.left > center && mPrevBound < 0) { |
Chih-Chung Chang | b7ec553 | 2012-04-03 12:21:16 +0800 | [diff] [blame] | 1463 | Rect prev = mPositionController.getPosition(-1); |
| 1464 | int currDist = curr.left - center; |
| 1465 | int prevDist = center - prev.right; |
| 1466 | if (prevDist < currDist) { |
| 1467 | return -1; |
| 1468 | } |
Chih-Chung Chang | 2c61738 | 2012-04-20 20:06:19 +0800 | [diff] [blame] | 1469 | } else if (curr.right < center && mNextBound > 0) { |
Chih-Chung Chang | b7ec553 | 2012-04-03 12:21:16 +0800 | [diff] [blame] | 1470 | Rect next = mPositionController.getPosition(1); |
| 1471 | int currDist = center - curr.right; |
| 1472 | int nextDist = next.left - center; |
| 1473 | if (nextDist < currDist) { |
| 1474 | return 1; |
| 1475 | } |
| 1476 | } |
| 1477 | |
| 1478 | return 0; |
| 1479 | } |
| 1480 | |
Chih-Chung Chang | 17ffedd | 2012-05-03 18:22:59 +0800 | [diff] [blame] | 1481 | // Switch to the previous or next picture if the hit position is inside |
| 1482 | // one of their boxes. This runs in main thread. |
| 1483 | private void switchToHitPicture(int x, int y) { |
| 1484 | if (mPrevBound < 0) { |
| 1485 | Rect r = mPositionController.getPosition(-1); |
| 1486 | if (r.right >= x) { |
| 1487 | slideToPrevPicture(); |
| 1488 | return; |
| 1489 | } |
| 1490 | } |
| 1491 | |
| 1492 | if (mNextBound > 0) { |
| 1493 | Rect r = mPositionController.getPosition(1); |
| 1494 | if (r.left <= x) { |
| 1495 | slideToNextPicture(); |
| 1496 | return; |
| 1497 | } |
| 1498 | } |
| 1499 | } |
| 1500 | |
Chih-Chung Chang | b7ec553 | 2012-04-03 12:21:16 +0800 | [diff] [blame] | 1501 | //////////////////////////////////////////////////////////////////////////// |
| 1502 | // Page mode focus switching |
| 1503 | // |
| 1504 | // We slide image to the next one or the previous one in two cases: 1: If |
| 1505 | // the user did a fling gesture with enough velocity. 2 If the user has |
| 1506 | // moved the picture a lot. |
| 1507 | //////////////////////////////////////////////////////////////////////////// |
| 1508 | |
| 1509 | private boolean swipeImages(float velocityX, float velocityY) { |
| 1510 | if (mFilmMode) return false; |
| 1511 | |
| 1512 | // Avoid swiping images if we're possibly flinging to view the |
| 1513 | // zoomed in picture vertically. |
| 1514 | PositionController controller = mPositionController; |
| 1515 | boolean isMinimal = controller.isAtMinimalScale(); |
| 1516 | int edges = controller.getImageAtEdges(); |
| 1517 | if (!isMinimal && Math.abs(velocityY) > Math.abs(velocityX)) |
| 1518 | if ((edges & PositionController.IMAGE_AT_TOP_EDGE) == 0 |
| 1519 | || (edges & PositionController.IMAGE_AT_BOTTOM_EDGE) == 0) |
| 1520 | return false; |
| 1521 | |
| 1522 | // If we are at the edge of the current photo and the sweeping velocity |
| 1523 | // exceeds the threshold, slide to the next / previous image. |
| 1524 | if (velocityX < -SWIPE_THRESHOLD && (isMinimal |
| 1525 | || (edges & PositionController.IMAGE_AT_RIGHT_EDGE) != 0)) { |
| 1526 | return slideToNextPicture(); |
| 1527 | } else if (velocityX > SWIPE_THRESHOLD && (isMinimal |
| 1528 | || (edges & PositionController.IMAGE_AT_LEFT_EDGE) != 0)) { |
| 1529 | return slideToPrevPicture(); |
| 1530 | } |
| 1531 | |
| 1532 | return false; |
| 1533 | } |
| 1534 | |
Chih-Chung Chang | 2c61738 | 2012-04-20 20:06:19 +0800 | [diff] [blame] | 1535 | private void snapback() { |
Chih-Chung Chang | 6b891c6 | 2012-06-07 20:09:13 +0800 | [diff] [blame] | 1536 | if ((mHolding & ~HOLD_DELETE) != 0) return; |
Bobby Georgescu | 499ac9e | 2012-10-03 13:31:33 -0700 | [diff] [blame] | 1537 | if (mFilmMode || !snapToNeighborImage()) { |
Chih-Chung Chang | 2c61738 | 2012-04-20 20:06:19 +0800 | [diff] [blame] | 1538 | mPositionController.snapback(); |
| 1539 | } |
| 1540 | } |
| 1541 | |
Chih-Chung Chang | b7ec553 | 2012-04-03 12:21:16 +0800 | [diff] [blame] | 1542 | private boolean snapToNeighborImage() { |
Chih-Chung Chang | b7ec553 | 2012-04-03 12:21:16 +0800 | [diff] [blame] | 1543 | Rect r = mPositionController.getPosition(0); |
| 1544 | int viewW = getWidth(); |
Doris Liu | cda9315 | 2012-09-05 19:51:58 -0700 | [diff] [blame] | 1545 | // Setting the move threshold proportional to the width of the view |
| 1546 | int moveThreshold = viewW / 5 ; |
| 1547 | int threshold = moveThreshold + gapToSide(r.width(), viewW); |
Chih-Chung Chang | b7ec553 | 2012-04-03 12:21:16 +0800 | [diff] [blame] | 1548 | |
| 1549 | // If we have moved the picture a lot, switching. |
| 1550 | if (viewW - r.right > threshold) { |
| 1551 | return slideToNextPicture(); |
| 1552 | } else if (r.left > threshold) { |
| 1553 | return slideToPrevPicture(); |
| 1554 | } |
| 1555 | |
| 1556 | return false; |
| 1557 | } |
| 1558 | |
| 1559 | private boolean slideToNextPicture() { |
Chih-Chung Chang | 2c61738 | 2012-04-20 20:06:19 +0800 | [diff] [blame] | 1560 | if (mNextBound <= 0) return false; |
Chih-Chung Chang | b7ec553 | 2012-04-03 12:21:16 +0800 | [diff] [blame] | 1561 | switchToNextImage(); |
Chih-Chung Chang | 2c61738 | 2012-04-20 20:06:19 +0800 | [diff] [blame] | 1562 | mPositionController.startHorizontalSlide(); |
Chih-Chung Chang | b7ec553 | 2012-04-03 12:21:16 +0800 | [diff] [blame] | 1563 | return true; |
| 1564 | } |
| 1565 | |
| 1566 | private boolean slideToPrevPicture() { |
Chih-Chung Chang | 2c61738 | 2012-04-20 20:06:19 +0800 | [diff] [blame] | 1567 | if (mPrevBound >= 0) return false; |
Chih-Chung Chang | b7ec553 | 2012-04-03 12:21:16 +0800 | [diff] [blame] | 1568 | switchToPrevImage(); |
Chih-Chung Chang | 2c61738 | 2012-04-20 20:06:19 +0800 | [diff] [blame] | 1569 | mPositionController.startHorizontalSlide(); |
Chih-Chung Chang | b7ec553 | 2012-04-03 12:21:16 +0800 | [diff] [blame] | 1570 | return true; |
| 1571 | } |
| 1572 | |
| 1573 | private static int gapToSide(int imageWidth, int viewWidth) { |
| 1574 | return Math.max(0, (viewWidth - imageWidth) / 2); |
| 1575 | } |
| 1576 | |
| 1577 | //////////////////////////////////////////////////////////////////////////// |
| 1578 | // Focus switching |
| 1579 | //////////////////////////////////////////////////////////////////////////// |
| 1580 | |
Bobby Georgescu | 7eea4d3 | 2012-09-06 17:14:02 -0700 | [diff] [blame] | 1581 | public void switchToImage(int index) { |
| 1582 | mModel.moveTo(index); |
| 1583 | } |
| 1584 | |
Owen Lin | f9a0a43 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 1585 | private void switchToNextImage() { |
Chih-Chung Chang | bd141b5 | 2012-04-26 10:10:49 +0800 | [diff] [blame] | 1586 | mModel.moveTo(mModel.getCurrentIndex() + 1); |
Owen Lin | f9a0a43 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 1587 | } |
| 1588 | |
Chih-Chung Chang | b7ec553 | 2012-04-03 12:21:16 +0800 | [diff] [blame] | 1589 | private void switchToPrevImage() { |
Chih-Chung Chang | bd141b5 | 2012-04-26 10:10:49 +0800 | [diff] [blame] | 1590 | mModel.moveTo(mModel.getCurrentIndex() - 1); |
Owen Lin | f9a0a43 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 1591 | } |
| 1592 | |
Chih-Chung Chang | 160e6d7 | 2012-04-25 11:50:08 +0800 | [diff] [blame] | 1593 | private void switchToFirstImage() { |
Chih-Chung Chang | bd141b5 | 2012-04-26 10:10:49 +0800 | [diff] [blame] | 1594 | mModel.moveTo(0); |
Chih-Chung Chang | 160e6d7 | 2012-04-25 11:50:08 +0800 | [diff] [blame] | 1595 | } |
| 1596 | |
Chih-Chung Chang | b7ec553 | 2012-04-03 12:21:16 +0800 | [diff] [blame] | 1597 | //////////////////////////////////////////////////////////////////////////// |
| 1598 | // Opening Animation |
| 1599 | //////////////////////////////////////////////////////////////////////////// |
| 1600 | |
| 1601 | public void setOpenAnimationRect(Rect rect) { |
| 1602 | mPositionController.setOpenAnimationRect(rect); |
Chih-Chung Chang | ec41254 | 2011-09-26 17:34:06 +0800 | [diff] [blame] | 1603 | } |
| 1604 | |
Chih-Chung Chang | b7ec553 | 2012-04-03 12:21:16 +0800 | [diff] [blame] | 1605 | //////////////////////////////////////////////////////////////////////////// |
Chih-Chung Chang | 2c61738 | 2012-04-20 20:06:19 +0800 | [diff] [blame] | 1606 | // Capture Animation |
| 1607 | //////////////////////////////////////////////////////////////////////////// |
| 1608 | |
| 1609 | public boolean switchWithCaptureAnimation(int offset) { |
| 1610 | GLRoot root = getGLRoot(); |
Bobby Georgescu | 14ce29a | 2012-07-25 10:20:19 -0700 | [diff] [blame] | 1611 | if(root == null) return false; |
Chih-Chung Chang | 2c61738 | 2012-04-20 20:06:19 +0800 | [diff] [blame] | 1612 | root.lockRenderThread(); |
| 1613 | try { |
| 1614 | return switchWithCaptureAnimationLocked(offset); |
| 1615 | } finally { |
| 1616 | root.unlockRenderThread(); |
| 1617 | } |
| 1618 | } |
| 1619 | |
| 1620 | private boolean switchWithCaptureAnimationLocked(int offset) { |
Chih-Chung Chang | 2c61738 | 2012-04-20 20:06:19 +0800 | [diff] [blame] | 1621 | if (mHolding != 0) return true; |
| 1622 | if (offset == 1) { |
| 1623 | if (mNextBound <= 0) return false; |
Chih-Chung Chang | 61f9471 | 2012-05-02 20:05:16 +0800 | [diff] [blame] | 1624 | // Temporary disable action bar until the capture animation is done. |
| 1625 | if (!mFilmMode) mListener.onActionBarAllowed(false); |
Chih-Chung Chang | 2c61738 | 2012-04-20 20:06:19 +0800 | [diff] [blame] | 1626 | switchToNextImage(); |
| 1627 | mPositionController.startCaptureAnimationSlide(-1); |
| 1628 | } else if (offset == -1) { |
| 1629 | if (mPrevBound >= 0) return false; |
Chih-Chung Chang | c4791b7 | 2012-05-18 19:10:36 -0700 | [diff] [blame] | 1630 | if (mFilmMode) setFilmMode(false); |
| 1631 | |
| 1632 | // If we are too far away from the first image (so that we don't |
| 1633 | // have all the ScreenNails in-between), we go directly without |
| 1634 | // animation. |
| 1635 | if (mModel.getCurrentIndex() > SCREEN_NAIL_MAX) { |
| 1636 | switchToFirstImage(); |
Chih-Chung Chang | 42e1fed | 2012-05-30 16:29:18 -0700 | [diff] [blame] | 1637 | mPositionController.skipToFinalPosition(); |
Chih-Chung Chang | c4791b7 | 2012-05-18 19:10:36 -0700 | [diff] [blame] | 1638 | return true; |
| 1639 | } |
| 1640 | |
Chih-Chung Chang | 160e6d7 | 2012-04-25 11:50:08 +0800 | [diff] [blame] | 1641 | switchToFirstImage(); |
Chih-Chung Chang | 2c61738 | 2012-04-20 20:06:19 +0800 | [diff] [blame] | 1642 | mPositionController.startCaptureAnimationSlide(1); |
| 1643 | } else { |
| 1644 | return false; |
| 1645 | } |
| 1646 | mHolding |= HOLD_CAPTURE_ANIMATION; |
Chih-Chung Chang | bd141b5 | 2012-04-26 10:10:49 +0800 | [diff] [blame] | 1647 | Message m = mHandler.obtainMessage(MSG_CAPTURE_ANIMATION_DONE, offset, 0); |
Chih-Chung Chang | f5ce6ae | 2012-05-11 17:55:02 +0800 | [diff] [blame] | 1648 | mHandler.sendMessageDelayed(m, PositionController.CAPTURE_ANIMATION_TIME); |
Chih-Chung Chang | 2c61738 | 2012-04-20 20:06:19 +0800 | [diff] [blame] | 1649 | return true; |
| 1650 | } |
| 1651 | |
Chih-Chung Chang | bd141b5 | 2012-04-26 10:10:49 +0800 | [diff] [blame] | 1652 | private void captureAnimationDone(int offset) { |
Chih-Chung Chang | 2c61738 | 2012-04-20 20:06:19 +0800 | [diff] [blame] | 1653 | mHolding &= ~HOLD_CAPTURE_ANIMATION; |
Chih-Chung Chang | e6251df | 2012-05-22 11:35:46 -0700 | [diff] [blame] | 1654 | if (offset == 1 && !mFilmMode) { |
| 1655 | // Now the capture animation is done, enable the action bar. |
| 1656 | mListener.onActionBarAllowed(true); |
| 1657 | mListener.onActionBarWanted(); |
Chih-Chung Chang | bd141b5 | 2012-04-26 10:10:49 +0800 | [diff] [blame] | 1658 | } |
Chih-Chung Chang | 2c61738 | 2012-04-20 20:06:19 +0800 | [diff] [blame] | 1659 | snapback(); |
| 1660 | } |
| 1661 | |
| 1662 | //////////////////////////////////////////////////////////////////////////// |
Chih-Chung Chang | b7ec553 | 2012-04-03 12:21:16 +0800 | [diff] [blame] | 1663 | // Card deck effect calculation |
| 1664 | //////////////////////////////////////////////////////////////////////////// |
Chih-Chung Chang | 15b351a | 2012-03-15 16:38:45 +0800 | [diff] [blame] | 1665 | |
Chih-Chung Chang | cb4fb7c | 2012-03-21 16:19:21 +0800 | [diff] [blame] | 1666 | // Returns the scrolling progress value for an object moving out of a |
| 1667 | // view. The progress value measures how much the object has moving out of |
| 1668 | // the view. The object currently displays in [left, right), and the view is |
| 1669 | // at [0, viewWidth]. |
| 1670 | // |
| 1671 | // The returned value is negative when the object is moving right, and |
| 1672 | // positive when the object is moving left. The value goes to -1 or 1 when |
| 1673 | // the object just moves out of the view completely. The value is 0 if the |
| 1674 | // object currently fills the view. |
| 1675 | private static float calculateMoveOutProgress(int left, int right, |
| 1676 | int viewWidth) { |
| 1677 | // w = object width |
| 1678 | // viewWidth = view width |
| 1679 | int w = right - left; |
| 1680 | |
| 1681 | // If the object width is smaller than the view width, |
| 1682 | // |....view....| |
| 1683 | // |<-->| progress = -1 when left = viewWidth |
Chih-Chung Chang | bd141b5 | 2012-04-26 10:10:49 +0800 | [diff] [blame] | 1684 | // |<-->| progress = 0 when left = viewWidth / 2 - w / 2 |
Chih-Chung Chang | cb4fb7c | 2012-03-21 16:19:21 +0800 | [diff] [blame] | 1685 | // |<-->| progress = 1 when left = -w |
Chih-Chung Chang | cb4fb7c | 2012-03-21 16:19:21 +0800 | [diff] [blame] | 1686 | if (w < viewWidth) { |
Chih-Chung Chang | bd141b5 | 2012-04-26 10:10:49 +0800 | [diff] [blame] | 1687 | int zx = viewWidth / 2 - w / 2; |
| 1688 | if (left > zx) { |
| 1689 | return -(left - zx) / (float) (viewWidth - zx); // progress = (0, -1] |
| 1690 | } else { |
| 1691 | return (left - zx) / (float) (-w - zx); // progress = [0, 1] |
| 1692 | } |
Chih-Chung Chang | cb4fb7c | 2012-03-21 16:19:21 +0800 | [diff] [blame] | 1693 | } |
| 1694 | |
| 1695 | // If the object width is larger than the view width, |
| 1696 | // |..view..| |
| 1697 | // |<--------->| progress = -1 when left = viewWidth |
| 1698 | // |<--------->| progress = 0 between left = 0 |
| 1699 | // |<--------->| and right = viewWidth |
| 1700 | // |<--------->| progress = 1 when right = 0 |
| 1701 | if (left > 0) { |
| 1702 | return -left / (float) viewWidth; |
| 1703 | } |
| 1704 | |
| 1705 | if (right < viewWidth) { |
| 1706 | return (viewWidth - right) / (float) viewWidth; |
| 1707 | } |
| 1708 | |
| 1709 | return 0; |
| 1710 | } |
| 1711 | |
| 1712 | // Maps a scrolling progress value to the alpha factor in the fading |
| 1713 | // animation. |
| 1714 | private float getScrollAlpha(float scrollProgress) { |
| 1715 | return scrollProgress < 0 ? mAlphaInterpolator.getInterpolation( |
| 1716 | 1 - Math.abs(scrollProgress)) : 1.0f; |
| 1717 | } |
| 1718 | |
| 1719 | // Maps a scrolling progress value to the scaling factor in the fading |
| 1720 | // animation. |
| 1721 | private float getScrollScale(float scrollProgress) { |
| 1722 | float interpolatedProgress = mScaleInterpolator.getInterpolation( |
| 1723 | Math.abs(scrollProgress)); |
| 1724 | float scale = (1 - interpolatedProgress) + |
| 1725 | interpolatedProgress * TRANSITION_SCALE_FACTOR; |
| 1726 | return scale; |
| 1727 | } |
| 1728 | |
| 1729 | |
| 1730 | // This interpolator emulates the rate at which the perceived scale of an |
| 1731 | // object changes as its distance from a camera increases. When this |
| 1732 | // interpolator is applied to a scale animation on a view, it evokes the |
| 1733 | // sense that the object is shrinking due to moving away from the camera. |
| 1734 | private static class ZInterpolator { |
| 1735 | private float focalLength; |
| 1736 | |
| 1737 | public ZInterpolator(float foc) { |
| 1738 | focalLength = foc; |
| 1739 | } |
| 1740 | |
| 1741 | public float getInterpolation(float input) { |
| 1742 | return (1.0f - focalLength / (focalLength + input)) / |
| 1743 | (1.0f - focalLength / (focalLength + 1.0f)); |
Owen Lin | f9a0a43 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 1744 | } |
| 1745 | } |
| 1746 | |
Chih-Chung Chang | ba12eae | 2012-05-07 02:29:37 +0800 | [diff] [blame] | 1747 | // Returns an interpolated value for the page/film transition. |
| 1748 | // When ratio = 0, the result is from. |
| 1749 | // When ratio = 1, the result is to. |
| 1750 | private static float interpolate(float ratio, float from, float to) { |
| 1751 | return from + (to - from) * ratio * ratio; |
| 1752 | } |
| 1753 | |
Chih-Chung Chang | 6b891c6 | 2012-06-07 20:09:13 +0800 | [diff] [blame] | 1754 | // Returns the alpha factor in film mode if a picture is not in the center. |
| 1755 | // The 0.03 lower bound is to make the item always visible a bit. |
| 1756 | private float getOffsetAlpha(float offset) { |
| 1757 | offset /= 0.5f; |
| 1758 | float alpha = (offset > 0) ? (1 - offset) : (1 + offset); |
| 1759 | return Utils.clamp(alpha, 0.03f, 1f); |
| 1760 | } |
| 1761 | |
Chih-Chung Chang | b7ec553 | 2012-04-03 12:21:16 +0800 | [diff] [blame] | 1762 | //////////////////////////////////////////////////////////////////////////// |
| 1763 | // Simple public utilities |
| 1764 | //////////////////////////////////////////////////////////////////////////// |
Owen Lin | f9a0a43 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 1765 | |
Chih-Chung Chang | bd141b5 | 2012-04-26 10:10:49 +0800 | [diff] [blame] | 1766 | public void setListener(Listener listener) { |
| 1767 | mListener = listener; |
Owen Lin | f9a0a43 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 1768 | } |
Owen Lin | 616a70f | 2012-05-07 16:35:53 +0800 | [diff] [blame] | 1769 | |
| 1770 | public Rect getPhotoRect(int index) { |
| 1771 | return mPositionController.getPosition(index); |
| 1772 | } |
| 1773 | |
Owen Lin | 616a70f | 2012-05-07 16:35:53 +0800 | [diff] [blame] | 1774 | public PhotoFallbackEffect buildFallbackEffect(GLView root, GLCanvas canvas) { |
| 1775 | Rect location = new Rect(); |
| 1776 | Utils.assertTrue(root.getBoundsOf(this, location)); |
| 1777 | |
| 1778 | Rect fullRect = bounds(); |
| 1779 | PhotoFallbackEffect effect = new PhotoFallbackEffect(); |
| 1780 | for (int i = -SCREEN_NAIL_MAX; i <= SCREEN_NAIL_MAX; ++i) { |
| 1781 | MediaItem item = mModel.getMediaItem(i); |
| 1782 | if (item == null) continue; |
| 1783 | ScreenNail sc = mModel.getScreenNail(i); |
Owen Lin | 49affdc | 2012-05-21 16:52:25 -0700 | [diff] [blame] | 1784 | if (!(sc instanceof BitmapScreenNail) |
| 1785 | || ((BitmapScreenNail) sc).isShowingPlaceholder()) continue; |
| 1786 | |
| 1787 | // Now, sc is BitmapScreenNail and is not showing placeholder |
Owen Lin | 616a70f | 2012-05-07 16:35:53 +0800 | [diff] [blame] | 1788 | Rect rect = new Rect(getPhotoRect(i)); |
| 1789 | if (!Rect.intersects(fullRect, rect)) continue; |
| 1790 | rect.offset(location.left, location.top); |
| 1791 | |
Owen Lin | 38155c4 | 2012-08-08 18:47:33 +0800 | [diff] [blame] | 1792 | int width = sc.getWidth(); |
| 1793 | int height = sc.getHeight(); |
| 1794 | |
| 1795 | int rotation = mModel.getImageRotation(i); |
| 1796 | RawTexture texture; |
| 1797 | if ((rotation % 180) == 0) { |
| 1798 | texture = new RawTexture(width, height, true); |
| 1799 | canvas.beginRenderTarget(texture); |
| 1800 | canvas.translate(width / 2f, height / 2f); |
| 1801 | } else { |
| 1802 | texture = new RawTexture(height, width, true); |
| 1803 | canvas.beginRenderTarget(texture); |
| 1804 | canvas.translate(height / 2f, width / 2f); |
| 1805 | } |
| 1806 | |
| 1807 | canvas.rotate(rotation, 0, 0, 1); |
| 1808 | canvas.translate(-width / 2f, -height / 2f); |
| 1809 | sc.draw(canvas, 0, 0, width, height); |
Owen Lin | 616a70f | 2012-05-07 16:35:53 +0800 | [diff] [blame] | 1810 | canvas.endRenderTarget(); |
| 1811 | effect.addEntry(item.getPath(), rect, texture); |
| 1812 | } |
| 1813 | return effect; |
| 1814 | } |
Owen Lin | f9a0a43 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 1815 | } |