PhotoView.java revision dbb6acc204ba6f095eb0e49e1298e9ceda2c3096
1f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin/*
2f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * Copyright (C) 2010 The Android Open Source Project
3f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin *
4f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * Licensed under the Apache License, Version 2.0 (the "License");
5f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * you may not use this file except in compliance with the License.
6f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * You may obtain a copy of the License at
7f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin *
8f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin *      http://www.apache.org/licenses/LICENSE-2.0
9f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin *
10f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * Unless required by applicable law or agreed to in writing, software
11f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * distributed under the License is distributed on an "AS IS" BASIS,
12f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * See the License for the specific language governing permissions and
14f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * limitations under the License.
15f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin */
16f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
17f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linpackage com.android.gallery3d.ui;
18f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
19f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport android.content.Context;
20f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport android.graphics.Color;
212ef46ed28b28b355d7f3f1432c7b1196b832a859Chih-Chung Changimport android.graphics.Matrix;
22cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Changimport android.graphics.Point;
2304ac045bf8da5082bbb0bdc9ea5f9c9b5b796ad0Yuli Huangimport android.graphics.Rect;
24f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport android.os.Message;
256b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Changimport android.util.FloatMath;
26f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport android.view.MotionEvent;
276b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Changimport android.view.View.MeasureSpec;
28cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Changimport android.view.animation.AccelerateInterpolator;
29f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
302b3ee0ea07246b859a5b75d8a6102a7cce7ec838Owen Linimport com.android.gallery3d.R;
312b3ee0ea07246b859a5b75d8a6102a7cce7ec838Owen Linimport com.android.gallery3d.app.GalleryActivity;
32cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Changimport com.android.gallery3d.common.Utils;
33616a70fdb4473d2fbd7b70772a3a82b908aeae1eOwen Linimport com.android.gallery3d.data.MediaItem;
34b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Changimport com.android.gallery3d.data.MediaObject;
356b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Changimport com.android.gallery3d.data.Path;
366b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Changimport com.android.gallery3d.util.GalleryUtils;
37b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Changimport com.android.gallery3d.util.RangeArray;
38b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang
39f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linpublic class PhotoView extends GLView {
40f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    @SuppressWarnings("unused")
41f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static final String TAG = "PhotoView";
42c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang    private static final int PLACEHOLDER_COLOR = 0xFF222222;
43f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
44f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public static final int INVALID_SIZE = -1;
45b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    public static final long INVALID_DATA_VERSION =
46b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            MediaObject.INVALID_DATA_VERSION;
47f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
48c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang    public static class Size {
49c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang        public int width;
50c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang        public int height;
51c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang    }
52c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang
53c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang    public interface Model extends TileImageView.Model {
54bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang        public int getCurrentIndex();
55bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang        public void moveTo(int index);
56c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang
57c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang        // Returns the size for the specified picture. If the size information is
58c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang        // not avaiable, width = height = 0.
59c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang        public void getImageSize(int offset, Size size);
60c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang
61616a70fdb4473d2fbd7b70772a3a82b908aeae1eOwen Lin        // Returns the media item for the specified picture.
62616a70fdb4473d2fbd7b70772a3a82b908aeae1eOwen Lin        public MediaItem getMediaItem(int offset);
63616a70fdb4473d2fbd7b70772a3a82b908aeae1eOwen Lin
64c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang        // Returns the rotation for the specified picture.
65c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang        public int getImageRotation(int offset);
66f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
67b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        // This amends the getScreenNail() method of TileImageView.Model to get
68b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        // ScreenNail at previous (negative offset) or next (positive offset)
69b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        // positions. Returns null if the specified ScreenNail is unavailable.
70b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        public ScreenNail getScreenNail(int offset);
71c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang
72c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang        // Set this to true if we need the model to provide full images.
73b8be1e0ad76b6abc0da7ead39f7a9811195d001eChih-Chung Chang        public void setNeedFullImage(boolean enabled);
74bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang
75bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang        // Returns true if the item is the Camera preview.
76bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang        public boolean isCamera(int offset);
77d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang
7843a80fd806247cb50e81bacda3ad21d210e9ae88Angus Kong        // Returns true if the item is the Panorama.
7943a80fd806247cb50e81bacda3ad21d210e9ae88Angus Kong        public boolean isPanorama(int offset);
8043a80fd806247cb50e81bacda3ad21d210e9ae88Angus Kong
81dbb6acc204ba6f095eb0e49e1298e9ceda2c3096Wu-cheng Li        // Returns true if the item is a static image that represents camera
82dbb6acc204ba6f095eb0e49e1298e9ceda2c3096Wu-cheng Li        // preview.
83dbb6acc204ba6f095eb0e49e1298e9ceda2c3096Wu-cheng Li        public boolean isStaticCamera(int offset);
84dbb6acc204ba6f095eb0e49e1298e9ceda2c3096Wu-cheng Li
85d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang        // Returns true if the item is a Video.
86d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang        public boolean isVideo(int offset);
87f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang
886b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        // Returns true if the item can be deleted.
896b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        public boolean isDeletable(int offset);
906b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang
91f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang        public static final int LOADING_INIT = 0;
92f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang        public static final int LOADING_COMPLETE = 1;
93f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang        public static final int LOADING_FAIL = 2;
94f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang
95f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang        public int getLoadingState(int offset);
966b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang
976b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        // When data change happens, we need to decide which MediaItem to focus
986b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        // on.
996b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        //
1006b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        // 1. If focus hint path != null, we try to focus on it if we can find
1016b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        // it.  This is used for undo a deletion, so we can focus on the
1026b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        // undeleted item.
1036b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        //
1046b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        // 2. Otherwise try to focus on the MediaItem that is currently focused,
1056b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        // if we can find it.
1066b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        //
1076b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        // 3. Otherwise try to focus on the previous MediaItem or the next
1086b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        // MediaItem, depending on the value of focus hint direction.
1096b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        public static final int FOCUS_HINT_NEXT = 0;
1106b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        public static final int FOCUS_HINT_PREVIOUS = 1;
1116b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        public void setFocusHintDirection(int direction);
1126b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        public void setFocusHintPath(Path path);
113b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    }
114b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang
115bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang    public interface Listener {
116b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        public void onSingleTapUp(int x, int y);
117bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang        public void lockOrientation();
118bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang        public void unlockOrientation();
119bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang        public void onFullScreenChanged(boolean full);
12061f94714c3702115d2f89bb5f8829697be0c3680Chih-Chung Chang        public void onActionBarAllowed(boolean allowed);
121e6251df06f0b3c8f556043f8b725b4b19be7474dChih-Chung Chang        public void onActionBarWanted();
122bd7c016d728853d38f98a3c6077b7bd9b08858b9Yuli Huang        public void onCurrentImageUpdated();
1236b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        public void onDeleteImage(Path path, int offset);
1246b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        public void onUndoDeleteImage();
1256b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        public void onCommitDeleteImage();
126b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    }
127f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
128e6251df06f0b3c8f556043f8b725b4b19be7474dChih-Chung Chang    // The rules about orientation locking:
129bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang    //
130e6251df06f0b3c8f556043f8b725b4b19be7474dChih-Chung Chang    // (1) We need to lock the orientation if we are in page mode camera
131e6251df06f0b3c8f556043f8b725b4b19be7474dChih-Chung Chang    // preview, so there is no (unwanted) rotation animation when the user
132e6251df06f0b3c8f556043f8b725b4b19be7474dChih-Chung Chang    // rotates the device.
133bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang    //
134e6251df06f0b3c8f556043f8b725b4b19be7474dChih-Chung Chang    // (2) We need to unlock the orientation if we want to show the action bar
135e6251df06f0b3c8f556043f8b725b4b19be7474dChih-Chung Chang    // because the action bar follows the system orientation.
136bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang    //
137e6251df06f0b3c8f556043f8b725b4b19be7474dChih-Chung Chang    // The rules about action bar:
138bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang    //
139e6251df06f0b3c8f556043f8b725b4b19be7474dChih-Chung Chang    // (1) If we are in film mode, we don't show action bar.
140e6251df06f0b3c8f556043f8b725b4b19be7474dChih-Chung Chang    //
141e6251df06f0b3c8f556043f8b725b4b19be7474dChih-Chung Chang    // (2) If we go from camera to gallery with capture animation, we show
142e6251df06f0b3c8f556043f8b725b4b19be7474dChih-Chung Chang    // action bar.
143b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    private static final int MSG_CANCEL_EXTRA_SCALING = 2;
144b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    private static final int MSG_SWITCH_FOCUS = 3;
1452c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang    private static final int MSG_CAPTURE_ANIMATION_DONE = 4;
1466b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang    private static final int MSG_DELETE_ANIMATION_DONE = 5;
1476b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang    private static final int MSG_DELETE_DONE = 6;
1486118af9ddad8acc050133154f8f60c842c7f9bfaChih-Chung Chang    private static final int MSG_UNDO_BAR_TIMEOUT = 7;
1496118af9ddad8acc050133154f8f60c842c7f9bfaChih-Chung Chang    private static final int MSG_UNDO_BAR_FULL_CAMERA = 8;
150f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
151b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    private static final int MOVE_THRESHOLD = 256;
152f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static final float SWIPE_THRESHOLD = 300f;
153f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
154f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static final float DEFAULT_TEXT_SIZE = 20;
155cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang    private static float TRANSITION_SCALE_FACTOR = 0.74f;
156d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang    private static final int ICON_RATIO = 6;
1572c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang
1582c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang    // whether we want to apply card deck effect in page mode.
159b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    private static final boolean CARD_EFFECT = true;
160cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang
1616b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang    // whether we want to apply offset effect in film mode.
1626b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang    private static final boolean OFFSET_EFFECT = true;
1636b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang
1646b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang    // Used to calculate the scaling factor for the card deck effect.
165cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang    private ZInterpolator mScaleInterpolator = new ZInterpolator(0.5f);
166cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang
167cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang    // Used to calculate the alpha factor for the fading animation.
168cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang    private AccelerateInterpolator mAlphaInterpolator =
169cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang            new AccelerateInterpolator(0.9f);
170f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
171b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    // We keep this many previous ScreenNails. (also this many next ScreenNails)
172b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    public static final int SCREEN_NAIL_MAX = 3;
173b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang
1746b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang    // These are constants for the delete gesture.
1756b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang    private static final int SWIPE_ESCAPE_VELOCITY = 500; // dp/sec
1766b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang    private static final int MAX_DISMISS_VELOCITY = 2000; // dp/sec
1776b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang
178b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    // The picture entries, the valid index is from -SCREEN_NAIL_MAX to
179b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    // SCREEN_NAIL_MAX.
180b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    private final RangeArray<Picture> mPictures =
181b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            new RangeArray<Picture>(-SCREEN_NAIL_MAX, SCREEN_NAIL_MAX);
1826b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang    private Size[] mSizes = new Size[2 * SCREEN_NAIL_MAX + 1];
183f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
1846575794a9c09f22d5721e212c093e0a2df376d0cChih-Chung Chang    private final MyGestureListener mGestureListener;
1853a02809c97669a157cf45bfd61d45272110d4091Chih-Chung Chang    private final GestureRecognizer mGestureRecognizer;
186f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private final PositionController mPositionController;
187f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
188bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang    private Listener mListener;
189f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private Model mModel;
190f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private StringTexture mLoadingText;
191f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private StringTexture mNoThumbnailText;
192b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    private TileImageView mTileView;
193532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang    private EdgeView mEdgeView;
1946b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang    private UndoBarView mUndoBar;
195f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private Texture mVideoPlayIcon;
196f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
197f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private SynchronizedHandler mHandler;
198f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
199cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang    private Point mImageCenter = new Point();
200534b12fd804610dd67b8109bc08ba76f31afb33eChih-Chung Chang    private boolean mCancelExtraScalingPending;
201b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    private boolean mFilmMode = false;
202bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang    private int mDisplayRotation = 0;
203bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang    private int mCompensation = 0;
2049f44f35c211229bc0c5ca17823f5d7b6bb3583a1Chih-Chung Chang    private boolean mFullScreenCamera;
2052ef46ed28b28b355d7f3f1432c7b1196b832a859Chih-Chung Chang    private Rect mCameraRelativeFrame = new Rect();
206bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang    private Rect mCameraRect = new Rect();
207f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
208c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang    // [mPrevBound, mNextBound] is the range of index for all pictures in the
209c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang    // model, if we assume the index of current focused picture is 0.  So if
210c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang    // there are some previous pictures, mPrevBound < 0, and if there are some
211c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang    // next pictures, mNextBound > 0.
212c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang    private int mPrevBound;
213c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang    private int mNextBound;
214c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang
2152c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang    // This variable prevents us doing snapback until its values goes to 0. This
2162c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang    // happens if the user gesture is still in progress or we are in a capture
2172c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang    // animation.
2182c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang    private int mHolding;
2192c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang    private static final int HOLD_TOUCH_DOWN = 1;
22018958c51f1412d959d52500ceefc46f987d035f3Chih-Chung Chang    private static final int HOLD_CAPTURE_ANIMATION = 2;
2216b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang    private static final int HOLD_DELETE = 4;
2226b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang
2236b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang    // mTouchBoxIndex is the index of the box that is touched by the down
2246b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang    // gesture in film mode. The value Integer.MAX_VALUE means no box was
2256b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang    // touched.
2266b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang    private int mTouchBoxIndex = Integer.MAX_VALUE;
2276b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang    // Whether the box indicated by mTouchBoxIndex is deletable. Only meaningful
2286b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang    // if mTouchBoxIndex is not Integer.MAX_VALUE.
2296b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang    private boolean mTouchBoxDeletable;
230517e1bd25305d4e82d101a8c06be0119dde2eab3Chih-Chung Chang    // This is the index of the last deleted item. This is only used as a hint
231517e1bd25305d4e82d101a8c06be0119dde2eab3Chih-Chung Chang    // to hide the undo button when we are too far away from the deleted
232517e1bd25305d4e82d101a8c06be0119dde2eab3Chih-Chung Chang    // item. The value Integer.MAX_VALUE means there is no such hint.
233517e1bd25305d4e82d101a8c06be0119dde2eab3Chih-Chung Chang    private int mUndoIndexHint = Integer.MAX_VALUE;
2342c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang
235f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public PhotoView(GalleryActivity activity) {
236f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mTileView = new TileImageView(activity);
237f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        addComponent(mTileView);
238f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        Context context = activity.getAndroidContext();
239532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang        mEdgeView = new EdgeView(context);
240532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang        addComponent(mEdgeView);
2416b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        mUndoBar = new UndoBarView(context);
2426b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        addComponent(mUndoBar);
2436b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        mUndoBar.setVisibility(GLView.INVISIBLE);
2446b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        mUndoBar.setOnClickListener(new OnClickListener() {
2456b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                @Override
2466b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                public void onClick(GLView v) {
2476b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                    mListener.onUndoDeleteImage();
2486118af9ddad8acc050133154f8f60c842c7f9bfaChih-Chung Chang                    hideUndoBar();
2496b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                }
2506b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            });
251f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mLoadingText = StringTexture.newInstance(
252f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                context.getString(R.string.loading),
253f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                DEFAULT_TEXT_SIZE, Color.WHITE);
254f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mNoThumbnailText = StringTexture.newInstance(
255f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                context.getString(R.string.no_thumbnail),
256f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                DEFAULT_TEXT_SIZE, Color.WHITE);
257f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
258b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        mHandler = new MyHandler(activity.getGLRoot());
259f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
2606575794a9c09f22d5721e212c093e0a2df376d0cChih-Chung Chang        mGestureListener = new MyGestureListener();
2616575794a9c09f22d5721e212c093e0a2df376d0cChih-Chung Chang        mGestureRecognizer = new GestureRecognizer(context, mGestureListener);
262f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
263b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        mPositionController = new PositionController(context,
264b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                new PositionController.Listener() {
265b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                    public void invalidate() {
266b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                        PhotoView.this.invalidate();
267b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                    }
2686b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                    public boolean isHoldingDown() {
2696b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                        return (mHolding & HOLD_TOUCH_DOWN) != 0;
2706b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                    }
2716b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                    public boolean isHoldingDelete() {
2726b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                        return (mHolding & HOLD_DELETE) != 0;
273b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                    }
274b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                    public void onPull(int offset, int direction) {
275b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                        mEdgeView.onPull(offset, direction);
276b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                    }
277b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                    public void onRelease() {
278b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                        mEdgeView.onRelease();
279b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                    }
280b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                    public void onAbsorb(int velocity, int direction) {
281b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                        mEdgeView.onAbsorb(velocity, direction);
282b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                    }
283b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                });
284f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mVideoPlayIcon = new ResourceTexture(context, R.drawable.ic_control_play);
285b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        for (int i = -SCREEN_NAIL_MAX; i <= SCREEN_NAIL_MAX; i++) {
286b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            if (i == 0) {
287b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                mPictures.put(i, new FullPicture());
288cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang            } else {
289b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                mPictures.put(i, new ScreenNailPicture(i));
290cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang            }
291f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
292f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
293f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
294b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    public void setModel(Model model) {
295b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        mModel = model;
296b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        mTileView.setModel(mModel);
297f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
298f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
299b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    class MyHandler extends SynchronizedHandler {
300b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        public MyHandler(GLRoot root) {
301b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            super(root);
302f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
303f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
304b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        @Override
305b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        public void handleMessage(Message message) {
306b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            switch (message.what) {
307b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                case MSG_CANCEL_EXTRA_SCALING: {
308b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                    mGestureRecognizer.cancelScale();
309b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                    mPositionController.setExtraScalingRange(false);
310b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                    mCancelExtraScalingPending = false;
311b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                    break;
312b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                }
313b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                case MSG_SWITCH_FOCUS: {
314b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                    switchFocus();
315b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                    break;
316b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                }
3172c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang                case MSG_CAPTURE_ANIMATION_DONE: {
318bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang                    // message.arg1 is the offset parameter passed to
319bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang                    // switchWithCaptureAnimation().
320bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang                    captureAnimationDone(message.arg1);
3212c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang                    break;
3222c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang                }
3236b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                case MSG_DELETE_ANIMATION_DONE: {
3246b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                    // message.obj is the Path of the MediaItem which should be
3256b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                    // deleted. message.arg1 is the offset of the image.
3266b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                    mListener.onDeleteImage((Path) message.obj, message.arg1);
3276b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                    // Normally a box which finishes delete animation will hold
3286b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                    // position until the underlying MediaItem is actually
3296b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                    // deleted, and HOLD_DELETE will be cancelled that time. In
3306b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                    // case the MediaItem didn't actually get deleted in 2
3316b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                    // seconds, we will cancel HOLD_DELETE and make it bounce
3326b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                    // back.
3336b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang
3346b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                    // We make sure there is at most one MSG_DELETE_DONE
3356b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                    // in the handler.
3366b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                    mHandler.removeMessages(MSG_DELETE_DONE);
3376b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                    Message m = mHandler.obtainMessage(MSG_DELETE_DONE);
3386b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                    mHandler.sendMessageDelayed(m, 2000);
3396118af9ddad8acc050133154f8f60c842c7f9bfaChih-Chung Chang
3406118af9ddad8acc050133154f8f60c842c7f9bfaChih-Chung Chang                    int numberOfPictures = mNextBound - mPrevBound + 1;
3416118af9ddad8acc050133154f8f60c842c7f9bfaChih-Chung Chang                    if (numberOfPictures == 2) {
3426118af9ddad8acc050133154f8f60c842c7f9bfaChih-Chung Chang                        if (mModel.isCamera(mNextBound)
3436118af9ddad8acc050133154f8f60c842c7f9bfaChih-Chung Chang                                || mModel.isCamera(mPrevBound)) {
3446118af9ddad8acc050133154f8f60c842c7f9bfaChih-Chung Chang                            numberOfPictures--;
3456118af9ddad8acc050133154f8f60c842c7f9bfaChih-Chung Chang                        }
3466118af9ddad8acc050133154f8f60c842c7f9bfaChih-Chung Chang                    }
3476118af9ddad8acc050133154f8f60c842c7f9bfaChih-Chung Chang                    showUndoBar(numberOfPictures <= 1);
3486b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                    break;
3496b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                }
3506b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                case MSG_DELETE_DONE: {
3516b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                    if (!mHandler.hasMessages(MSG_DELETE_ANIMATION_DONE)) {
3526b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                        mHolding &= ~HOLD_DELETE;
3536b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                        snapback();
3546b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                    }
3556b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                    break;
3566b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                }
3576118af9ddad8acc050133154f8f60c842c7f9bfaChih-Chung Chang                case MSG_UNDO_BAR_TIMEOUT: {
358517e1bd25305d4e82d101a8c06be0119dde2eab3Chih-Chung Chang                    checkHideUndoBar(UNDO_BAR_TIMEOUT);
359517e1bd25305d4e82d101a8c06be0119dde2eab3Chih-Chung Chang                    break;
360517e1bd25305d4e82d101a8c06be0119dde2eab3Chih-Chung Chang                }
3616118af9ddad8acc050133154f8f60c842c7f9bfaChih-Chung Chang                case MSG_UNDO_BAR_FULL_CAMERA: {
3626118af9ddad8acc050133154f8f60c842c7f9bfaChih-Chung Chang                    checkHideUndoBar(UNDO_BAR_FULL_CAMERA);
3636118af9ddad8acc050133154f8f60c842c7f9bfaChih-Chung Chang                    break;
3646118af9ddad8acc050133154f8f60c842c7f9bfaChih-Chung Chang                }
365b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                default: throw new AssertionError(message.what);
366f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            }
367f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
368b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    };
369f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
370b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    ////////////////////////////////////////////////////////////////////////////
371b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    //  Data/Image change notifications
372b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    ////////////////////////////////////////////////////////////////////////////
373b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang
374214993dc4abf87c386123af50e3c34184ba11cb6Chih-Chung Chang    public void notifyDataChange(int[] fromIndex, int prevBound, int nextBound) {
375c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang        mPrevBound = prevBound;
376c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang        mNextBound = nextBound;
377c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang
3786b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        // Update mTouchBoxIndex
3796b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        if (mTouchBoxIndex != Integer.MAX_VALUE) {
3806b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            int k = mTouchBoxIndex;
3816b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            mTouchBoxIndex = Integer.MAX_VALUE;
3826b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            for (int i = 0; i < 2 * SCREEN_NAIL_MAX + 1; i++) {
3836b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                if (fromIndex[i] == k) {
3846b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                    mTouchBoxIndex = i - SCREEN_NAIL_MAX;
3856b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                    break;
3866b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                }
3876b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            }
3886b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        }
3896b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang
390517e1bd25305d4e82d101a8c06be0119dde2eab3Chih-Chung Chang        // Hide undo button if we are too far away
391517e1bd25305d4e82d101a8c06be0119dde2eab3Chih-Chung Chang        if (mUndoIndexHint != Integer.MAX_VALUE) {
392517e1bd25305d4e82d101a8c06be0119dde2eab3Chih-Chung Chang            if (Math.abs(mUndoIndexHint - mModel.getCurrentIndex()) >= 3) {
393517e1bd25305d4e82d101a8c06be0119dde2eab3Chih-Chung Chang                hideUndoBar();
394517e1bd25305d4e82d101a8c06be0119dde2eab3Chih-Chung Chang            }
395517e1bd25305d4e82d101a8c06be0119dde2eab3Chih-Chung Chang        }
396517e1bd25305d4e82d101a8c06be0119dde2eab3Chih-Chung Chang
3976b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        // Update the ScreenNails.
3986b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        for (int i = -SCREEN_NAIL_MAX; i <= SCREEN_NAIL_MAX; i++) {
3996b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            Picture p =  mPictures.get(i);
4006b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            p.reload();
4016b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            mSizes[i + SCREEN_NAIL_MAX] = p.getSize();
4026b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        }
4036b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang
4046b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        boolean wasDeleting = mPositionController.hasDeletingBox();
4056b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang
406b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        // Move the boxes
407214993dc4abf87c386123af50e3c34184ba11cb6Chih-Chung Chang        mPositionController.moveBox(fromIndex, mPrevBound < 0, mNextBound > 0,
4086b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                mModel.isCamera(0), mSizes);
409b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang
410b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        for (int i = -SCREEN_NAIL_MAX; i <= SCREEN_NAIL_MAX; i++) {
4116b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            setPictureSize(i);
4126b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        }
4136b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang
4146b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        boolean isDeleting = mPositionController.hasDeletingBox();
4156b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang
4166b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        // If the deletion is done, make HOLD_DELETE persist for only the time
4176b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        // needed for a snapback animation.
4186b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        if (wasDeleting && !isDeleting) {
4196b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            mHandler.removeMessages(MSG_DELETE_DONE);
4206b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            Message m = mHandler.obtainMessage(MSG_DELETE_DONE);
4216b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            mHandler.sendMessageDelayed(
4226b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                    m, PositionController.SNAPBACK_ANIMATION_TIME);
423b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        }
424b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang
425b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        invalidate();
426f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
427f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
4286b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang    public boolean isDeleting() {
4296b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        return (mHolding & HOLD_DELETE) != 0
4306b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                && mPositionController.hasDeletingBox();
4316b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang    }
4326b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang
433b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    public void notifyImageChange(int index) {
434bd7c016d728853d38f98a3c6077b7bd9b08858b9Yuli Huang        if (index == 0) {
435bd7c016d728853d38f98a3c6077b7bd9b08858b9Yuli Huang            mListener.onCurrentImageUpdated();
436bd7c016d728853d38f98a3c6077b7bd9b08858b9Yuli Huang        }
437b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        mPictures.get(index).reload();
4386b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        setPictureSize(index);
439b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        invalidate();
440f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
441f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
4426b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang    private void setPictureSize(int index) {
4436b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        Picture p = mPictures.get(index);
4446b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        mPositionController.setImageSize(index, p.getSize(),
4456b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                index == 0 && p.isCamera() ? mCameraRect : null);
4466b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang    }
4476b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang
448bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang    @Override
4492ef46ed28b28b355d7f3f1432c7b1196b832a859Chih-Chung Chang    protected void onLayout(
4502ef46ed28b28b355d7f3f1432c7b1196b832a859Chih-Chung Chang            boolean changeSize, int left, int top, int right, int bottom) {
4512ef46ed28b28b355d7f3f1432c7b1196b832a859Chih-Chung Chang        int w = right - left;
4522ef46ed28b28b355d7f3f1432c7b1196b832a859Chih-Chung Chang        int h = bottom - top;
4532ef46ed28b28b355d7f3f1432c7b1196b832a859Chih-Chung Chang        mTileView.layout(0, 0, w, h);
4542ef46ed28b28b355d7f3f1432c7b1196b832a859Chih-Chung Chang        mEdgeView.layout(0, 0, w, h);
4556b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        mUndoBar.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
4566b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        mUndoBar.layout(0, h - mUndoBar.getMeasuredHeight(), w, h);
4572ef46ed28b28b355d7f3f1432c7b1196b832a859Chih-Chung Chang
4582ef46ed28b28b355d7f3f1432c7b1196b832a859Chih-Chung Chang        GLRoot root = getGLRoot();
4592ef46ed28b28b355d7f3f1432c7b1196b832a859Chih-Chung Chang        int displayRotation = root.getDisplayRotation();
4602ef46ed28b28b355d7f3f1432c7b1196b832a859Chih-Chung Chang        int compensation = root.getCompensation();
4612ef46ed28b28b355d7f3f1432c7b1196b832a859Chih-Chung Chang        if (mDisplayRotation != displayRotation
4622ef46ed28b28b355d7f3f1432c7b1196b832a859Chih-Chung Chang                || mCompensation != compensation) {
4632ef46ed28b28b355d7f3f1432c7b1196b832a859Chih-Chung Chang            mDisplayRotation = displayRotation;
4642ef46ed28b28b355d7f3f1432c7b1196b832a859Chih-Chung Chang            mCompensation = compensation;
4652ef46ed28b28b355d7f3f1432c7b1196b832a859Chih-Chung Chang
4663b4a8aeb0353fa18a2b5267b3952a80a6c6d4d13Chih-Chung Chang            // We need to change the size and rotation of the Camera ScreenNail,
4673b4a8aeb0353fa18a2b5267b3952a80a6c6d4d13Chih-Chung Chang            // but we don't want it to animate because the size doen't actually
4682ef46ed28b28b355d7f3f1432c7b1196b832a859Chih-Chung Chang            // change in the eye of the user.
4692ef46ed28b28b355d7f3f1432c7b1196b832a859Chih-Chung Chang            for (int i = -SCREEN_NAIL_MAX; i <= SCREEN_NAIL_MAX; i++) {
4702ef46ed28b28b355d7f3f1432c7b1196b832a859Chih-Chung Chang                Picture p = mPictures.get(i);
4712ef46ed28b28b355d7f3f1432c7b1196b832a859Chih-Chung Chang                if (p.isCamera()) {
4729f44f35c211229bc0c5ca17823f5d7b6bb3583a1Chih-Chung Chang                    p.forceSize();
4732ef46ed28b28b355d7f3f1432c7b1196b832a859Chih-Chung Chang                }
474bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang            }
475bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang        }
476bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang
4779f44f35c211229bc0c5ca17823f5d7b6bb3583a1Chih-Chung Chang        updateCameraRect();
4789f44f35c211229bc0c5ca17823f5d7b6bb3583a1Chih-Chung Chang        mPositionController.setConstrainedFrame(mCameraRect);
479bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang        if (changeSize) {
480bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang            mPositionController.setViewSize(getWidth(), getHeight());
481bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang        }
482bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang    }
483bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang
4849f44f35c211229bc0c5ca17823f5d7b6bb3583a1Chih-Chung Chang    // Update the camera rectangle due to layout change or camera relative frame
4859f44f35c211229bc0c5ca17823f5d7b6bb3583a1Chih-Chung Chang    // change.
4869f44f35c211229bc0c5ca17823f5d7b6bb3583a1Chih-Chung Chang    private void updateCameraRect() {
4872ef46ed28b28b355d7f3f1432c7b1196b832a859Chih-Chung Chang        // Get the width and height in framework orientation because the given
4882ef46ed28b28b355d7f3f1432c7b1196b832a859Chih-Chung Chang        // mCameraRelativeFrame is in that coordinates.
489bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang        int w = getWidth();
490bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang        int h = getHeight();
4912ef46ed28b28b355d7f3f1432c7b1196b832a859Chih-Chung Chang        if (mCompensation % 180 != 0) {
492bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang            int tmp = w;
493bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang            w = h;
494bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang            h = tmp;
495bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang        }
4962ef46ed28b28b355d7f3f1432c7b1196b832a859Chih-Chung Chang        int l = mCameraRelativeFrame.left;
4972ef46ed28b28b355d7f3f1432c7b1196b832a859Chih-Chung Chang        int t = mCameraRelativeFrame.top;
4982ef46ed28b28b355d7f3f1432c7b1196b832a859Chih-Chung Chang        int r = mCameraRelativeFrame.right;
4992ef46ed28b28b355d7f3f1432c7b1196b832a859Chih-Chung Chang        int b = mCameraRelativeFrame.bottom;
500bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang
5012ef46ed28b28b355d7f3f1432c7b1196b832a859Chih-Chung Chang        // Now convert it to the coordinates we are using.
5022ef46ed28b28b355d7f3f1432c7b1196b832a859Chih-Chung Chang        switch (mCompensation) {
503bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang            case 0: mCameraRect.set(l, t, r, b); break;
504bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang            case 90: mCameraRect.set(h - b, l, h - t, r); break;
505bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang            case 180: mCameraRect.set(w - r, h - b, w - l, h - t); break;
506bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang            case 270: mCameraRect.set(t, w - r, b, w - l); break;
507bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang        }
508bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang
5092ef46ed28b28b355d7f3f1432c7b1196b832a859Chih-Chung Chang        Log.d(TAG, "compensation = " + mCompensation
5102ef46ed28b28b355d7f3f1432c7b1196b832a859Chih-Chung Chang                + ", CameraRelativeFrame = " + mCameraRelativeFrame
5112ef46ed28b28b355d7f3f1432c7b1196b832a859Chih-Chung Chang                + ", mCameraRect = " + mCameraRect);
512bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang    }
513bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang
5142ef46ed28b28b355d7f3f1432c7b1196b832a859Chih-Chung Chang    public void setCameraRelativeFrame(Rect frame) {
5152ef46ed28b28b355d7f3f1432c7b1196b832a859Chih-Chung Chang        mCameraRelativeFrame.set(frame);
5169f44f35c211229bc0c5ca17823f5d7b6bb3583a1Chih-Chung Chang        updateCameraRect();
5179f44f35c211229bc0c5ca17823f5d7b6bb3583a1Chih-Chung Chang        // Originally we do
5189f44f35c211229bc0c5ca17823f5d7b6bb3583a1Chih-Chung Chang        //     mPositionController.setConstrainedFrame(mCameraRect);
5199f44f35c211229bc0c5ca17823f5d7b6bb3583a1Chih-Chung Chang        // here, but it is moved to a parameter of the setImageSize() call, so
5209f44f35c211229bc0c5ca17823f5d7b6bb3583a1Chih-Chung Chang        // it can be updated atomically with the CameraScreenNail's size change.
521bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang    }
522bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang
523bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang    // Returns the rotation we need to do to the camera texture before drawing
524bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang    // it to the canvas, assuming the camera texture is correct when the device
525bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang    // is in its natural orientation.
526bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang    private int getCameraRotation() {
527bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang        return (mCompensation - mDisplayRotation + 360) % 360;
528bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang    }
529bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang
53043a80fd806247cb50e81bacda3ad21d210e9ae88Angus Kong    private int getPanoramaRotation() {
53143a80fd806247cb50e81bacda3ad21d210e9ae88Angus Kong        return mCompensation;
53243a80fd806247cb50e81bacda3ad21d210e9ae88Angus Kong    }
53343a80fd806247cb50e81bacda3ad21d210e9ae88Angus Kong
534b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    ////////////////////////////////////////////////////////////////////////////
535b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    //  Pictures
536b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    ////////////////////////////////////////////////////////////////////////////
537b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang
538b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    private interface Picture {
539b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        void reload();
540b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        void draw(GLCanvas canvas, Rect r);
541b8be1e0ad76b6abc0da7ead39f7a9811195d001eChih-Chung Chang        void setScreenNail(ScreenNail s);
5422c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang        boolean isCamera();  // whether the picture is a camera preview
5436b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        boolean isDeletable();  // whether the picture can be deleted
5449f44f35c211229bc0c5ca17823f5d7b6bb3583a1Chih-Chung Chang        void forceSize();  // called when mCompensation changes
5456b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        Size getSize();
5467817979db0c52ffeacb951625b1e821eba303285Ahbong Chang    }
547f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
548b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    class FullPicture implements Picture {
549b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        private int mRotation;
5502c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang        private boolean mIsCamera;
55143a80fd806247cb50e81bacda3ad21d210e9ae88Angus Kong        private boolean mIsPanorama;
552dbb6acc204ba6f095eb0e49e1298e9ceda2c3096Wu-cheng Li        private boolean mIsStaticCamera;
553d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang        private boolean mIsVideo;
5546b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        private boolean mIsDeletable;
555f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang        private int mLoadingState = Model.LOADING_INIT;
5566b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        private Size mSize = new Size();
557bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang        private boolean mWasCameraCenter;
558b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        public void FullPicture(TileImageView tileView) {
559b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            mTileView = tileView;
560f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
561f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
562b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        @Override
563b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        public void reload() {
564b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            // mImageWidth and mImageHeight will get updated
565b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            mTileView.notifyModelInvalidated();
566b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang
567bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang            mIsCamera = mModel.isCamera(0);
56843a80fd806247cb50e81bacda3ad21d210e9ae88Angus Kong            mIsPanorama = mModel.isPanorama(0);
569dbb6acc204ba6f095eb0e49e1298e9ceda2c3096Wu-cheng Li            mIsStaticCamera = mModel.isStaticCamera(0);
570d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang            mIsVideo = mModel.isVideo(0);
5716b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            mIsDeletable = mModel.isDeletable(0);
572f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang            mLoadingState = mModel.getLoadingState(0);
573bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang            setScreenNail(mModel.getScreenNail(0));
5746b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            updateSize();
5759f44f35c211229bc0c5ca17823f5d7b6bb3583a1Chih-Chung Chang        }
5769f44f35c211229bc0c5ca17823f5d7b6bb3583a1Chih-Chung Chang
5776b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        @Override
5786b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        public Size getSize() {
5796b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            return mSize;
580bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang        }
581bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang
582bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang        @Override
5839f44f35c211229bc0c5ca17823f5d7b6bb3583a1Chih-Chung Chang        public void forceSize() {
5849f44f35c211229bc0c5ca17823f5d7b6bb3583a1Chih-Chung Chang            updateSize();
5856b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            mPositionController.forceImageSize(0, mSize);
5869f44f35c211229bc0c5ca17823f5d7b6bb3583a1Chih-Chung Chang        }
5879f44f35c211229bc0c5ca17823f5d7b6bb3583a1Chih-Chung Chang
5889f44f35c211229bc0c5ca17823f5d7b6bb3583a1Chih-Chung Chang        private void updateSize() {
58943a80fd806247cb50e81bacda3ad21d210e9ae88Angus Kong            if (mIsPanorama) {
59043a80fd806247cb50e81bacda3ad21d210e9ae88Angus Kong                mRotation = getPanoramaRotation();
591dbb6acc204ba6f095eb0e49e1298e9ceda2c3096Wu-cheng Li            } else if (mIsCamera && !mIsStaticCamera) {
592bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang                mRotation = getCameraRotation();
593bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang            } else {
594bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang                mRotation = mModel.getImageRotation(0);
595bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang            }
596bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang
597c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang            int w = mTileView.mImageWidth;
598c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang            int h = mTileView.mImageHeight;
5996b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            mSize.width = getRotated(mRotation, w, h);
6006b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            mSize.height = getRotated(mRotation, h, w);
601f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
602f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
603b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        @Override
604b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        public void draw(GLCanvas canvas, Rect r) {
605f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang            drawTileView(canvas, r);
606cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang
607bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang            // We want to have the following transitions:
6082c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang            // (1) Move camera preview out of its place: switch to film mode
6092c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang            // (2) Move camera preview into its place: switch to page mode
6102c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang            // The extra mWasCenter check makes sure (1) does not apply if in
6112c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang            // page mode, we move _to_ the camera preview from another picture.
612bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang
613bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang            // Holdings except touch-down prevent the transitions.
61418958c51f1412d959d52500ceefc46f987d035f3Chih-Chung Chang            if ((mHolding & ~HOLD_TOUCH_DOWN) != 0) return;
615bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang
6169f44f35c211229bc0c5ca17823f5d7b6bb3583a1Chih-Chung Chang            boolean isCenter = mPositionController.isCenter();
6176118af9ddad8acc050133154f8f60c842c7f9bfaChih-Chung Chang            boolean isCameraCenter = mIsCamera && isCenter && !canUndoLastPicture();
618bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang
619bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang            if (mWasCameraCenter && mIsCamera && !isCenter && !mFilmMode) {
62018958c51f1412d959d52500ceefc46f987d035f3Chih-Chung Chang                // Temporary disabled to de-emphasize filmstrip.
62118958c51f1412d959d52500ceefc46f987d035f3Chih-Chung Chang                // setFilmMode(true);
62218958c51f1412d959d52500ceefc46f987d035f3Chih-Chung Chang            } else if (!mWasCameraCenter && isCameraCenter && mFilmMode) {
623bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang                setFilmMode(false);
624aeb3062e84ed28f0e25a73f511f287ae0a3e4294Chih-Chung Chang            }
625aeb3062e84ed28f0e25a73f511f287ae0a3e4294Chih-Chung Chang
6269f44f35c211229bc0c5ca17823f5d7b6bb3583a1Chih-Chung Chang            if (isCameraCenter && !mFilmMode) {
627e6251df06f0b3c8f556043f8b725b4b19be7474dChih-Chung Chang                // Move into camera in page mode, lock
628e6251df06f0b3c8f556043f8b725b4b19be7474dChih-Chung Chang                mListener.lockOrientation();
629bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang            }
630bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang
631bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang            mWasCameraCenter = isCameraCenter;
632cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang        }
633cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang
634b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        @Override
635b8be1e0ad76b6abc0da7ead39f7a9811195d001eChih-Chung Chang        public void setScreenNail(ScreenNail s) {
636b8be1e0ad76b6abc0da7ead39f7a9811195d001eChih-Chung Chang            mTileView.setScreenNail(s);
637cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang        }
638f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
639b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        @Override
6402c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang        public boolean isCamera() {
6412c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang            return mIsCamera;
642f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
643f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
6446b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        @Override
6456b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        public boolean isDeletable() {
6466b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            return mIsDeletable;
6476b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        }
6486b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang
649d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang        private void drawTileView(GLCanvas canvas, Rect r) {
650ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang            float imageScale = mPositionController.getImageScale();
651b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            int viewW = getWidth();
652b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            int viewH = getHeight();
653d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang            float cx = r.exactCenterX();
654d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang            float cy = r.exactCenterY();
655ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang            float scale = 1f;  // the scaling factor due to card effect
656b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang
657d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang            canvas.save(GLCanvas.SAVE_FLAG_MATRIX | GLCanvas.SAVE_FLAG_ALPHA);
658ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang            float filmRatio = mPositionController.getFilmRatio();
659ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang            boolean wantsCardEffect = CARD_EFFECT && !mIsCamera
660f320b8429dd5c68c117b8e9b1a36a435f4f4ff13Yuli Huang                    && filmRatio != 1f && !mPictures.get(-1).isCamera()
661f320b8429dd5c68c117b8e9b1a36a435f4f4ff13Yuli Huang                    && !mPositionController.inOpeningAnimation();
6626b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            boolean wantsOffsetEffect = OFFSET_EFFECT && mIsDeletable
6636b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                    && filmRatio == 1f && r.centerY() != viewH / 2;
6642c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang            if (wantsCardEffect) {
665b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                // Calculate the move-out progress value.
666b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                int left = r.left;
667b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                int right = r.right;
668b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                float progress = calculateMoveOutProgress(left, right, viewW);
669b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                progress = Utils.clamp(progress, -1f, 1f);
670b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang
671b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                // We only want to apply the fading animation if the scrolling
672b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                // movement is to the right.
673bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang                if (progress < 0) {
674ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang                    scale = getScrollScale(progress);
675ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang                    float alpha = getScrollAlpha(progress);
676ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang                    scale = interpolate(filmRatio, scale, 1f);
677ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang                    alpha = interpolate(filmRatio, alpha, 1f);
678d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang
679ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang                    imageScale *= scale;
680ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang                    canvas.multiplyAlpha(alpha);
681ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang
682ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang                    float cxPage;  // the cx value in page mode
683d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang                    if (right - left <= viewW) {
684b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                        // If the picture is narrower than the view, keep it at
685b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                        // the center of the view.
686ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang                        cxPage = viewW / 2f;
687b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                    } else {
688b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                        // If the picture is wider than the view (it's
689b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                        // zoomed-in), keep the left edge of the object align
690b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                        // the the left edge of the view.
691ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang                        cxPage = (right - left) * scale / 2f;
692b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                    }
693ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang                    cx = interpolate(filmRatio, cxPage, cx);
694b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                }
6956b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            } else if (wantsOffsetEffect) {
6966b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                float offset = (float) (r.centerY() - viewH / 2) / viewH;
6976b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                float alpha = getOffsetAlpha(offset);
6986b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                canvas.multiplyAlpha(alpha);
699b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            }
700f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
701d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang            // Draw the tile view.
702ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang            setTileViewPosition(cx, cy, viewW, viewH, imageScale);
7036b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            renderChild(canvas, mTileView);
704d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang
705f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang            // Draw the play video icon and the message.
706f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang            canvas.translate((int) (cx + 0.5f), (int) (cy + 0.5f));
707f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang            int s = (int) (scale * Math.min(r.width(), r.height()) + 0.5f);
708f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang            if (mIsVideo) drawVideoPlayIcon(canvas, s);
709f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang            if (mLoadingState == Model.LOADING_FAIL) {
710f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang                drawLoadingFailMessage(canvas);
711d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang            }
712d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang
713f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang            // Draw a debug indicator showing which picture has focus (index ==
714f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang            // 0).
715f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang            //canvas.fillRect(-10, -10, 20, 20, 0x80FF00FF);
716f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang
717d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang            canvas.restore();
718d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang        }
719d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang
720d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang        // Set the position of the tile view
721d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang        private void setTileViewPosition(float cx, float cy,
722d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang                int viewW, int viewH, float scale) {
723d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang            // Find out the bitmap coordinates of the center of the view
724d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang            int imageW = mPositionController.getImageWidth();
725d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang            int imageH = mPositionController.getImageHeight();
726d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang            int centerX = (int) (imageW / 2f + (viewW / 2f - cx) / scale + 0.5f);
727d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang            int centerY = (int) (imageH / 2f + (viewH / 2f - cy) / scale + 0.5f);
728d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang
729b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            int inverseX = imageW - centerX;
730b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            int inverseY = imageH - centerY;
731d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang            int x, y;
732d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang            switch (mRotation) {
733d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang                case 0: x = centerX; y = centerY; break;
734d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang                case 90: x = centerY; y = inverseX; break;
735d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang                case 180: x = inverseX; y = inverseY; break;
736d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang                case 270: x = inverseY; y = centerX; break;
737b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                default:
738d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang                    throw new RuntimeException(String.valueOf(mRotation));
739b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            }
740d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang            mTileView.setPosition(x, y, scale, mRotation);
741f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
742f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
743f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
744b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    private class ScreenNailPicture implements Picture {
745b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        private int mIndex;
746b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        private int mRotation;
747b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        private ScreenNail mScreenNail;
7482c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang        private boolean mIsCamera;
74943a80fd806247cb50e81bacda3ad21d210e9ae88Angus Kong        private boolean mIsPanorama;
750dbb6acc204ba6f095eb0e49e1298e9ceda2c3096Wu-cheng Li        private boolean mIsStaticCamera;
751d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang        private boolean mIsVideo;
7526b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        private boolean mIsDeletable;
753f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang        private int mLoadingState = Model.LOADING_INIT;
7546b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        private Size mSize = new Size();
755f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
756b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        public ScreenNailPicture(int index) {
757b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            mIndex = index;
758b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        }
759532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang
760b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        @Override
761b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        public void reload() {
762bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang            mIsCamera = mModel.isCamera(mIndex);
76343a80fd806247cb50e81bacda3ad21d210e9ae88Angus Kong            mIsPanorama = mModel.isPanorama(mIndex);
764dbb6acc204ba6f095eb0e49e1298e9ceda2c3096Wu-cheng Li            mIsStaticCamera = mModel.isStaticCamera(mIndex);
765d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang            mIsVideo = mModel.isVideo(mIndex);
7666b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            mIsDeletable = mModel.isDeletable(mIndex);
767f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang            mLoadingState = mModel.getLoadingState(mIndex);
768c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang            setScreenNail(mModel.getScreenNail(mIndex));
7696b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            updateSize();
7706b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        }
7716b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang
7726b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        @Override
7736b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        public Size getSize() {
7746b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            return mSize;
775b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        }
776b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang
777b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        @Override
778b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        public void draw(GLCanvas canvas, Rect r) {
779b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            if (mScreenNail == null) {
780f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang                // Draw a placeholder rectange if there should be a picture in
781f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang                // this position (but somehow there isn't).
782c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang                if (mIndex >= mPrevBound && mIndex <= mNextBound) {
783f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang                    drawPlaceHolder(canvas, r);
784c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang                }
785b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                return;
786f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            }
7876b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            int w = getWidth();
7886b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            int h = getHeight();
7896b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            if (r.left >= w || r.right <= 0 || r.top >= h || r.bottom <= 0) {
790b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                mScreenNail.noDraw();
791b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                return;
792f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            }
793f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
794ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang            float filmRatio = mPositionController.getFilmRatio();
795ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang            boolean wantsCardEffect = CARD_EFFECT && mIndex > 0
796ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang                    && filmRatio != 1f && !mPictures.get(0).isCamera();
7976b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            boolean wantsOffsetEffect = OFFSET_EFFECT && mIsDeletable
7986b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                    && filmRatio == 1f && r.centerY() != h / 2;
799ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang            int cx = wantsCardEffect
800ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang                    ? (int) (interpolate(filmRatio, w / 2, r.centerX()) + 0.5f)
801ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang                    : r.centerX();
802b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            int cy = r.centerY();
803ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang            canvas.save(GLCanvas.SAVE_FLAG_MATRIX | GLCanvas.SAVE_FLAG_ALPHA);
804b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            canvas.translate(cx, cy);
8052c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang            if (wantsCardEffect) {
806b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                float progress = (float) (w / 2 - r.centerX()) / w;
807b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                progress = Utils.clamp(progress, -1, 1);
808b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                float alpha = getScrollAlpha(progress);
809b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                float scale = getScrollScale(progress);
810ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang                alpha = interpolate(filmRatio, alpha, 1f);
811ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang                scale = interpolate(filmRatio, scale, 1f);
812b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                canvas.multiplyAlpha(alpha);
813b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                canvas.scale(scale, scale, 1);
8146b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            } else if (wantsOffsetEffect) {
8156b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                float offset = (float) (r.centerY() - h / 2) / h;
8166b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                float alpha = getOffsetAlpha(offset);
8176b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                canvas.multiplyAlpha(alpha);
818b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            }
819b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            if (mRotation != 0) {
820b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                canvas.rotate(mRotation, 0, 0, 1);
821b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            }
822ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang            int drawW = getRotated(mRotation, r.width(), r.height());
823ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang            int drawH = getRotated(mRotation, r.height(), r.width());
824b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            mScreenNail.draw(canvas, -drawW / 2, -drawH / 2, drawW, drawH);
825f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang            if (isScreenNailAnimating()) {
826f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang                invalidate();
827f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang            }
828f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang            int s = Math.min(drawW, drawH);
829f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang            if (mIsVideo) drawVideoPlayIcon(canvas, s);
830f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang            if (mLoadingState == Model.LOADING_FAIL) {
831f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang                drawLoadingFailMessage(canvas);
832f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang            }
833b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            canvas.restore();
834b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        }
835f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
836f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang        private boolean isScreenNailAnimating() {
837f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang            return (mScreenNail instanceof BitmapScreenNail)
838f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang                    && ((BitmapScreenNail) mScreenNail).isAnimating();
839f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang        }
840f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang
841b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        @Override
842b8be1e0ad76b6abc0da7ead39f7a9811195d001eChih-Chung Chang        public void setScreenNail(ScreenNail s) {
843b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            mScreenNail = s;
8449f44f35c211229bc0c5ca17823f5d7b6bb3583a1Chih-Chung Chang        }
8459f44f35c211229bc0c5ca17823f5d7b6bb3583a1Chih-Chung Chang
846bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang        @Override
8479f44f35c211229bc0c5ca17823f5d7b6bb3583a1Chih-Chung Chang        public void forceSize() {
8489f44f35c211229bc0c5ca17823f5d7b6bb3583a1Chih-Chung Chang            updateSize();
8496b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            mPositionController.forceImageSize(mIndex, mSize);
8509f44f35c211229bc0c5ca17823f5d7b6bb3583a1Chih-Chung Chang        }
8519f44f35c211229bc0c5ca17823f5d7b6bb3583a1Chih-Chung Chang
8529f44f35c211229bc0c5ca17823f5d7b6bb3583a1Chih-Chung Chang        private void updateSize() {
85343a80fd806247cb50e81bacda3ad21d210e9ae88Angus Kong            if (mIsPanorama) {
85443a80fd806247cb50e81bacda3ad21d210e9ae88Angus Kong                mRotation = getPanoramaRotation();
855dbb6acc204ba6f095eb0e49e1298e9ceda2c3096Wu-cheng Li            } else if (mIsCamera && !mIsStaticCamera) {
856bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang                mRotation = getCameraRotation();
857bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang            } else {
858bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang                mRotation = mModel.getImageRotation(mIndex);
859bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang            }
860c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang
861b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            if (mScreenNail != null) {
8626b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                mSize.width = mScreenNail.getWidth();
8636b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                mSize.height = mScreenNail.getHeight();
8646b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            } else {
865c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang                // If we don't have ScreenNail available, we can still try to
866c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang                // get the size information of it.
867c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang                mModel.getImageSize(mIndex, mSize);
868b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            }
869c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang
8706b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            int w = mSize.width;
8716b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            int h = mSize.height;
8726b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            mSize.width = getRotated(mRotation, w, h);
8736b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            mSize.height = getRotated(mRotation, h, w);
874f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
875b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang
876b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        @Override
8772c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang        public boolean isCamera() {
8782c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang            return mIsCamera;
879b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        }
8806b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang
8816b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        @Override
8826b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        public boolean isDeletable() {
8836b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            return mIsDeletable;
8846b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        }
885b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    }
886b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang
887f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang    // Draw a gray placeholder in the specified rectangle.
888f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang    private void drawPlaceHolder(GLCanvas canvas, Rect r) {
889f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang        canvas.fillRect(r.left, r.top, r.width(), r.height(), PLACEHOLDER_COLOR);
890f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang    }
891f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang
892d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang    // Draw the video play icon (in the place where the spinner was)
893d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang    private void drawVideoPlayIcon(GLCanvas canvas, int side) {
894d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang        int s = side / ICON_RATIO;
895d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang        // Draw the video play icon at the center
896d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang        mVideoPlayIcon.draw(canvas, -s / 2, -s / 2, s, s);
897d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang    }
898d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang
899f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang    // Draw the "no thumbnail" message
900f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang    private void drawLoadingFailMessage(GLCanvas canvas) {
901f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang        StringTexture m = mNoThumbnailText;
902f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang        m.draw(canvas, -m.getWidth() / 2, -m.getHeight() / 2);
903f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang    }
904f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang
905b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    private static int getRotated(int degree, int original, int theother) {
906b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        return (degree % 180 == 0) ? original : theother;
907b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    }
908b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang
909b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    ////////////////////////////////////////////////////////////////////////////
910b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    //  Gestures Handling
911b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    ////////////////////////////////////////////////////////////////////////////
912b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang
913b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    @Override
914b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    protected boolean onTouch(MotionEvent event) {
915b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        mGestureRecognizer.onTouchEvent(event);
916b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        return true;
917f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
918f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
9193a02809c97669a157cf45bfd61d45272110d4091Chih-Chung Chang    private class MyGestureListener implements GestureRecognizer.Listener {
9203a02809c97669a157cf45bfd61d45272110d4091Chih-Chung Chang        private boolean mIgnoreUpEvent = false;
921099989b310d84fe13eff0cdf2902bb3fb0bcbd14Chih-Chung Chang        // If we can change mode for this scale gesture.
922099989b310d84fe13eff0cdf2902bb3fb0bcbd14Chih-Chung Chang        private boolean mCanChangeMode;
92318958c51f1412d959d52500ceefc46f987d035f3Chih-Chung Chang        // If we have changed the film mode in this scaling gesture.
924b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        private boolean mModeChanged;
92533f8567dd5003e4bb342683f3768ab7552648b02Chih-Chung Chang        // If this scaling gesture should be ignored.
92633f8567dd5003e4bb342683f3768ab7552648b02Chih-Chung Chang        private boolean mIgnoreScalingGesture;
9272ce59cbd4368eaf0f30cfea28891bd47155182cbChih-Chung Chang        // If we have seen a scaling gesture.
9282ce59cbd4368eaf0f30cfea28891bd47155182cbChih-Chung Chang        private boolean mSeenScaling;
92917ffedda6e3ed57b38afbb775594cf30d83fc652Chih-Chung Chang        // whether the down action happened while the view is scrolling.
93017ffedda6e3ed57b38afbb775594cf30d83fc652Chih-Chung Chang        private boolean mDownInScrolling;
9316575794a9c09f22d5721e212c093e0a2df376d0cChih-Chung Chang        // If we should ignore all gestures other than onSingleTapUp.
9326575794a9c09f22d5721e212c093e0a2df376d0cChih-Chung Chang        private boolean mIgnoreSwipingGesture;
9336b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        // If a scrolling has happened after a down gesture.
9346b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        private boolean mScrolledAfterDown;
9356b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        // If the first scrolling move is in X direction. In the film mode, X
9366b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        // direction scrolling is normal scrolling. but Y direction scrolling is
9376b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        // a delete gesture.
9386b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        private boolean mFirstScrollX;
9396b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        // The accumulated Y delta that has been sent to mPositionController.
9406b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        private int mDeltaY;
9412ce59cbd4368eaf0f30cfea28891bd47155182cbChih-Chung Chang        // The accumulated scaling change from a scaling gesture.
9422ce59cbd4368eaf0f30cfea28891bd47155182cbChih-Chung Chang        private float mAccScale;
943b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang
9443a02809c97669a157cf45bfd61d45272110d4091Chih-Chung Chang        @Override
9453a02809c97669a157cf45bfd61d45272110d4091Chih-Chung Chang        public boolean onSingleTapUp(float x, float y) {
946ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang            // We do this in addition to onUp() because we want the snapback of
947ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang            // setFilmMode to happen.
948ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang            mHolding &= ~HOLD_TOUCH_DOWN;
949ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang
95017ffedda6e3ed57b38afbb775594cf30d83fc652Chih-Chung Chang            if (mFilmMode && !mDownInScrolling) {
95117ffedda6e3ed57b38afbb775594cf30d83fc652Chih-Chung Chang                switchToHitPicture((int) (x + 0.5f), (int) (y + 0.5f));
952b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                setFilmMode(false);
95317ffedda6e3ed57b38afbb775594cf30d83fc652Chih-Chung Chang                mIgnoreUpEvent = true;
954b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                return true;
955b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            }
956b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang
957bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang            if (mListener != null) {
9582ef46ed28b28b355d7f3f1432c7b1196b832a859Chih-Chung Chang                // Do the inverse transform of the touch coordinates.
9592ef46ed28b28b355d7f3f1432c7b1196b832a859Chih-Chung Chang                Matrix m = getGLRoot().getCompensationMatrix();
9602ef46ed28b28b355d7f3f1432c7b1196b832a859Chih-Chung Chang                Matrix inv = new Matrix();
9612ef46ed28b28b355d7f3f1432c7b1196b832a859Chih-Chung Chang                m.invert(inv);
9622ef46ed28b28b355d7f3f1432c7b1196b832a859Chih-Chung Chang                float[] pts = new float[] {x, y};
9632ef46ed28b28b355d7f3f1432c7b1196b832a859Chih-Chung Chang                inv.mapPoints(pts);
9642ef46ed28b28b355d7f3f1432c7b1196b832a859Chih-Chung Chang                mListener.onSingleTapUp((int) (pts[0] + 0.5f), (int) (pts[1] + 0.5f));
9653a02809c97669a157cf45bfd61d45272110d4091Chih-Chung Chang            }
9663a02809c97669a157cf45bfd61d45272110d4091Chih-Chung Chang            return true;
9673a02809c97669a157cf45bfd61d45272110d4091Chih-Chung Chang        }
9683a02809c97669a157cf45bfd61d45272110d4091Chih-Chung Chang
9693a02809c97669a157cf45bfd61d45272110d4091Chih-Chung Chang        @Override
9703a02809c97669a157cf45bfd61d45272110d4091Chih-Chung Chang        public boolean onDoubleTap(float x, float y) {
9716575794a9c09f22d5721e212c093e0a2df376d0cChih-Chung Chang            if (mIgnoreSwipingGesture) return true;
97261f94714c3702115d2f89bb5f8829697be0c3680Chih-Chung Chang            if (mPictures.get(0).isCamera()) return false;
9733a02809c97669a157cf45bfd61d45272110d4091Chih-Chung Chang            PositionController controller = mPositionController;
974b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            float scale = controller.getImageScale();
9753a02809c97669a157cf45bfd61d45272110d4091Chih-Chung Chang            // onDoubleTap happened on the second ACTION_DOWN.
9763a02809c97669a157cf45bfd61d45272110d4091Chih-Chung Chang            // We need to ignore the next UP event.
9773a02809c97669a157cf45bfd61d45272110d4091Chih-Chung Chang            mIgnoreUpEvent = true;
9783a02809c97669a157cf45bfd61d45272110d4091Chih-Chung Chang            if (scale <= 1.0f || controller.isAtMinimalScale()) {
9793a02809c97669a157cf45bfd61d45272110d4091Chih-Chung Chang                controller.zoomIn(x, y, Math.max(1.5f, scale * 1.5f));
9803a02809c97669a157cf45bfd61d45272110d4091Chih-Chung Chang            } else {
9813a02809c97669a157cf45bfd61d45272110d4091Chih-Chung Chang                controller.resetToFullView();
9823a02809c97669a157cf45bfd61d45272110d4091Chih-Chung Chang            }
9833a02809c97669a157cf45bfd61d45272110d4091Chih-Chung Chang            return true;
9843a02809c97669a157cf45bfd61d45272110d4091Chih-Chung Chang        }
985f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
986f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        @Override
9876b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        public boolean onScroll(float dx, float dy, float totalX, float totalY) {
9886575794a9c09f22d5721e212c093e0a2df376d0cChih-Chung Chang            if (mIgnoreSwipingGesture) return true;
9896b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            if (!mScrolledAfterDown) {
9906b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                mScrolledAfterDown = true;
9916b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                mFirstScrollX = (Math.abs(dx) > Math.abs(dy));
9926b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            }
9936b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang
9946b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            int dxi = (int) (-dx + 0.5f);
9956b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            int dyi = (int) (-dy + 0.5f);
9966b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            if (mFilmMode) {
9976b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                if (mFirstScrollX) {
9986b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                    mPositionController.scrollFilmX(dxi);
9996b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                } else {
10006b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                    if (mTouchBoxIndex == Integer.MAX_VALUE) return true;
10016b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                    int newDeltaY = calculateDeltaY(totalY);
10026b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                    int d = newDeltaY - mDeltaY;
10036b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                    if (d != 0) {
10046b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                        mPositionController.scrollFilmY(mTouchBoxIndex, d);
10056b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                        mDeltaY = newDeltaY;
10066b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                    }
10076b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                }
10086b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            } else {
10096b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                mPositionController.scrollPage(dxi, dyi);
10106b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            }
1011f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            return true;
1012f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
1013f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
10146b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        private int calculateDeltaY(float delta) {
10156b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            if (mTouchBoxDeletable) return (int) (delta + 0.5f);
10166b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang
10176b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            // don't let items that can't be deleted be dragged more than
10186b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            // maxScrollDistance, and make it harder and harder to drag.
10196b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            int size = getHeight();
10206b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            float maxScrollDistance = 0.15f * size;
10216b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            if (Math.abs(delta) >= size) {
10226b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                delta = delta > 0 ? maxScrollDistance : -maxScrollDistance;
10236b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            } else {
10246b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                delta = maxScrollDistance *
10256b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                        FloatMath.sin((delta / size) * (float) (Math.PI / 2));
10266b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            }
10276b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            return (int) (delta + 0.5f);
10286b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        }
10296b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang
1030f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        @Override
10313a02809c97669a157cf45bfd61d45272110d4091Chih-Chung Chang        public boolean onFling(float velocityX, float velocityY) {
10326575794a9c09f22d5721e212c093e0a2df376d0cChih-Chung Chang            if (mIgnoreSwipingGesture) return true;
10332ce59cbd4368eaf0f30cfea28891bd47155182cbChih-Chung Chang            if (mSeenScaling) return true;
10342ce3c3bfe08fff5aee58007cc8ba8f4a50861ae2Yuli Huang            if (swipeImages(velocityX, velocityY)) {
1035b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang                mIgnoreUpEvent = true;
10366b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            } else {
10376b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                flingImages(velocityX, velocityY);
1038f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            }
1039f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            return true;
1040f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
1041f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
10426b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        private boolean flingImages(float velocityX, float velocityY) {
10436b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            int vx = (int) (velocityX + 0.5f);
10446b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            int vy = (int) (velocityY + 0.5f);
10456b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            if (!mFilmMode) {
10466b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                return mPositionController.flingPage(vx, vy);
10476b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            }
10486b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            if (Math.abs(velocityX) > Math.abs(velocityY)) {
10496b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                return mPositionController.flingFilmX(vx);
10506b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            }
10516b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            // If we scrolled in Y direction fast enough, treat it as a delete
10526b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            // gesture.
10536b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            if (!mFilmMode || mTouchBoxIndex == Integer.MAX_VALUE
10546b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                    || !mTouchBoxDeletable) {
10556b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                return false;
10566b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            }
10577817979db0c52ffeacb951625b1e821eba303285Ahbong Chang            int maxVelocity = GalleryUtils.dpToPixel(MAX_DISMISS_VELOCITY);
10587817979db0c52ffeacb951625b1e821eba303285Ahbong Chang            int escapeVelocity = GalleryUtils.dpToPixel(SWIPE_ESCAPE_VELOCITY);
10596b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            int centerY = mPositionController.getPosition(mTouchBoxIndex)
10606b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                    .centerY();
10616b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            boolean fastEnough = (Math.abs(vy) > escapeVelocity)
10626b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                    && (Math.abs(vy) > Math.abs(vx))
10636b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                    && ((vy > 0) == (centerY > getHeight() / 2));
10646b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            if (fastEnough) {
10656b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                vy = Math.min(vy, maxVelocity);
10666b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                int duration = mPositionController.flingFilmY(mTouchBoxIndex, vy);
10676b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                if (duration >= 0) {
10686b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                    mPositionController.setPopFromTop(vy < 0);
10696b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                    deleteAfterAnimation(duration);
10706b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                    // We reset mTouchBoxIndex, so up() won't check if Y
10716b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                    // scrolled far enough to be a delete gesture.
10726b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                    mTouchBoxIndex = Integer.MAX_VALUE;
10736b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                    return true;
10746b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                }
10756b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            }
10766b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            return false;
10776b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        }
10786b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang
10796b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        private void deleteAfterAnimation(int duration) {
10806b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            MediaItem item = mModel.getMediaItem(mTouchBoxIndex);
10816b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            if (item == null) return;
1082517e1bd25305d4e82d101a8c06be0119dde2eab3Chih-Chung Chang            mListener.onCommitDeleteImage();
1083517e1bd25305d4e82d101a8c06be0119dde2eab3Chih-Chung Chang            mUndoIndexHint = mModel.getCurrentIndex() + mTouchBoxIndex;
10846b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            mHolding |= HOLD_DELETE;
10856b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            Message m = mHandler.obtainMessage(MSG_DELETE_ANIMATION_DONE);
10866b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            m.obj = item.getPath();
10876b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            m.arg1 = mTouchBoxIndex;
10886b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            mHandler.sendMessageDelayed(m, duration);
10896b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        }
10906b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang
1091f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        @Override
10923a02809c97669a157cf45bfd61d45272110d4091Chih-Chung Chang        public boolean onScaleBegin(float focusX, float focusY) {
10936575794a9c09f22d5721e212c093e0a2df376d0cChih-Chung Chang            if (mIgnoreSwipingGesture) return true;
109433f8567dd5003e4bb342683f3768ab7552648b02Chih-Chung Chang            // We ignore the scaling gesture if it is a camera preview.
109533f8567dd5003e4bb342683f3768ab7552648b02Chih-Chung Chang            mIgnoreScalingGesture = mPictures.get(0).isCamera();
109633f8567dd5003e4bb342683f3768ab7552648b02Chih-Chung Chang            if (mIgnoreScalingGesture) {
109733f8567dd5003e4bb342683f3768ab7552648b02Chih-Chung Chang                return true;
109833f8567dd5003e4bb342683f3768ab7552648b02Chih-Chung Chang            }
10993a02809c97669a157cf45bfd61d45272110d4091Chih-Chung Chang            mPositionController.beginScale(focusX, focusY);
1100099989b310d84fe13eff0cdf2902bb3fb0bcbd14Chih-Chung Chang            // We can change mode if we are in film mode, or we are in page
1101099989b310d84fe13eff0cdf2902bb3fb0bcbd14Chih-Chung Chang            // mode and at minimal scale.
1102099989b310d84fe13eff0cdf2902bb3fb0bcbd14Chih-Chung Chang            mCanChangeMode = mFilmMode
1103099989b310d84fe13eff0cdf2902bb3fb0bcbd14Chih-Chung Chang                    || mPositionController.isAtMinimalScale();
1104b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            mModeChanged = false;
11052ce59cbd4368eaf0f30cfea28891bd47155182cbChih-Chung Chang            mSeenScaling = true;
11062ce59cbd4368eaf0f30cfea28891bd47155182cbChih-Chung Chang            mAccScale = 1f;
1107f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            return true;
1108f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
1109f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
1110f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        @Override
11113a02809c97669a157cf45bfd61d45272110d4091Chih-Chung Chang        public boolean onScale(float focusX, float focusY, float scale) {
11126575794a9c09f22d5721e212c093e0a2df376d0cChih-Chung Chang            if (mIgnoreSwipingGesture) return true;
11136575794a9c09f22d5721e212c093e0a2df376d0cChih-Chung Chang            if (mIgnoreScalingGesture) return true;
111418958c51f1412d959d52500ceefc46f987d035f3Chih-Chung Chang            if (mModeChanged) return true;
1115b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            if (Float.isNaN(scale) || Float.isInfinite(scale)) return false;
111633f8567dd5003e4bb342683f3768ab7552648b02Chih-Chung Chang
1117b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            int outOfRange = mPositionController.scaleBy(scale, focusX, focusY);
1118b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang
11192ce59cbd4368eaf0f30cfea28891bd47155182cbChih-Chung Chang            // We wait for a large enough scale change before changing mode.
11202ce59cbd4368eaf0f30cfea28891bd47155182cbChih-Chung Chang            // Otherwise we may mistakenly treat a zoom-in gesture as zoom-out
11212ce59cbd4368eaf0f30cfea28891bd47155182cbChih-Chung Chang            // or vice versa.
11222ce59cbd4368eaf0f30cfea28891bd47155182cbChih-Chung Chang            mAccScale *= scale;
11232ce59cbd4368eaf0f30cfea28891bd47155182cbChih-Chung Chang            boolean largeEnough = (mAccScale < 0.97f || mAccScale > 1.03f);
11242ce59cbd4368eaf0f30cfea28891bd47155182cbChih-Chung Chang
112518958c51f1412d959d52500ceefc46f987d035f3Chih-Chung Chang            // If mode changes, we treat this scaling gesture has ended.
11262ce59cbd4368eaf0f30cfea28891bd47155182cbChih-Chung Chang            if (mCanChangeMode && largeEnough) {
1127b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                if ((outOfRange < 0 && !mFilmMode) ||
1128b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                        (outOfRange > 0 && mFilmMode)) {
112918958c51f1412d959d52500ceefc46f987d035f3Chih-Chung Chang                    stopExtraScalingIfNeeded();
113018958c51f1412d959d52500ceefc46f987d035f3Chih-Chung Chang
113118958c51f1412d959d52500ceefc46f987d035f3Chih-Chung Chang                    // Removing the touch down flag allows snapback to happen
113233f8567dd5003e4bb342683f3768ab7552648b02Chih-Chung Chang                    // for film mode change.
113318958c51f1412d959d52500ceefc46f987d035f3Chih-Chung Chang                    mHolding &= ~HOLD_TOUCH_DOWN;
1134b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                    setFilmMode(!mFilmMode);
113518958c51f1412d959d52500ceefc46f987d035f3Chih-Chung Chang
113618958c51f1412d959d52500ceefc46f987d035f3Chih-Chung Chang                    // We need to call onScaleEnd() before setting mModeChanged
113718958c51f1412d959d52500ceefc46f987d035f3Chih-Chung Chang                    // to true.
113818958c51f1412d959d52500ceefc46f987d035f3Chih-Chung Chang                    onScaleEnd();
1139b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                    mModeChanged = true;
1140b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                    return true;
1141534b12fd804610dd67b8109bc08ba76f31afb33eChih-Chung Chang                }
1142b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang           }
1143b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang
114418958c51f1412d959d52500ceefc46f987d035f3Chih-Chung Chang            if (outOfRange != 0) {
1145b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                startExtraScalingIfNeeded();
1146534b12fd804610dd67b8109bc08ba76f31afb33eChih-Chung Chang            } else {
1147b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                stopExtraScalingIfNeeded();
1148534b12fd804610dd67b8109bc08ba76f31afb33eChih-Chung Chang            }
1149f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            return true;
1150f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
1151f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
115233f8567dd5003e4bb342683f3768ab7552648b02Chih-Chung Chang        @Override
115333f8567dd5003e4bb342683f3768ab7552648b02Chih-Chung Chang        public void onScaleEnd() {
11546575794a9c09f22d5721e212c093e0a2df376d0cChih-Chung Chang            if (mIgnoreSwipingGesture) return;
11556575794a9c09f22d5721e212c093e0a2df376d0cChih-Chung Chang            if (mIgnoreScalingGesture) return;
115633f8567dd5003e4bb342683f3768ab7552648b02Chih-Chung Chang            if (mModeChanged) return;
115733f8567dd5003e4bb342683f3768ab7552648b02Chih-Chung Chang            mPositionController.endScale();
115833f8567dd5003e4bb342683f3768ab7552648b02Chih-Chung Chang        }
115933f8567dd5003e4bb342683f3768ab7552648b02Chih-Chung Chang
1160b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        private void startExtraScalingIfNeeded() {
1161b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            if (!mCancelExtraScalingPending) {
1162b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                mHandler.sendEmptyMessageDelayed(
1163b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                        MSG_CANCEL_EXTRA_SCALING, 700);
1164b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                mPositionController.setExtraScalingRange(true);
1165b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                mCancelExtraScalingPending = true;
1166b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            }
1167b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        }
1168b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang
1169b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        private void stopExtraScalingIfNeeded() {
1170b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            if (mCancelExtraScalingPending) {
1171b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                mHandler.removeMessages(MSG_CANCEL_EXTRA_SCALING);
1172b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                mPositionController.setExtraScalingRange(false);
1173b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                mCancelExtraScalingPending = false;
1174b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            }
1175b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        }
1176b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang
1177f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        @Override
11786b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        public void onDown(float x, float y) {
1179517e1bd25305d4e82d101a8c06be0119dde2eab3Chih-Chung Chang            checkHideUndoBar(UNDO_BAR_TOUCHED);
1180517e1bd25305d4e82d101a8c06be0119dde2eab3Chih-Chung Chang
11816b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            mDeltaY = 0;
11822ce59cbd4368eaf0f30cfea28891bd47155182cbChih-Chung Chang            mSeenScaling = false;
11836b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang
11846575794a9c09f22d5721e212c093e0a2df376d0cChih-Chung Chang            if (mIgnoreSwipingGesture) return;
11856575794a9c09f22d5721e212c093e0a2df376d0cChih-Chung Chang
11862c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang            mHolding |= HOLD_TOUCH_DOWN;
118717ffedda6e3ed57b38afbb775594cf30d83fc652Chih-Chung Chang
118817ffedda6e3ed57b38afbb775594cf30d83fc652Chih-Chung Chang            if (mFilmMode && mPositionController.isScrolling()) {
118917ffedda6e3ed57b38afbb775594cf30d83fc652Chih-Chung Chang                mDownInScrolling = true;
119017ffedda6e3ed57b38afbb775594cf30d83fc652Chih-Chung Chang                mPositionController.stopScrolling();
119117ffedda6e3ed57b38afbb775594cf30d83fc652Chih-Chung Chang            } else {
119217ffedda6e3ed57b38afbb775594cf30d83fc652Chih-Chung Chang                mDownInScrolling = false;
119317ffedda6e3ed57b38afbb775594cf30d83fc652Chih-Chung Chang            }
11946b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang
11956b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            mScrolledAfterDown = false;
11966b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            if (mFilmMode) {
11976b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                int xi = (int) (x + 0.5f);
11986b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                int yi = (int) (y + 0.5f);
11996b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                mTouchBoxIndex = mPositionController.hitTest(xi, yi);
12006b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                if (mTouchBoxIndex < mPrevBound || mTouchBoxIndex > mNextBound) {
12016b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                    mTouchBoxIndex = Integer.MAX_VALUE;
12026b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                } else {
12036b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                    mTouchBoxDeletable =
12046b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                            mPictures.get(mTouchBoxIndex).isDeletable();
12056b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                }
12066b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            } else {
12076b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                mTouchBoxIndex = Integer.MAX_VALUE;
12086b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            }
12093a02809c97669a157cf45bfd61d45272110d4091Chih-Chung Chang        }
12103a02809c97669a157cf45bfd61d45272110d4091Chih-Chung Chang
12113a02809c97669a157cf45bfd61d45272110d4091Chih-Chung Chang        @Override
12123a02809c97669a157cf45bfd61d45272110d4091Chih-Chung Chang        public void onUp() {
12136575794a9c09f22d5721e212c093e0a2df376d0cChih-Chung Chang            if (mIgnoreSwipingGesture) return;
12146575794a9c09f22d5721e212c093e0a2df376d0cChih-Chung Chang
121518958c51f1412d959d52500ceefc46f987d035f3Chih-Chung Chang            mHolding &= ~HOLD_TOUCH_DOWN;
12163a02809c97669a157cf45bfd61d45272110d4091Chih-Chung Chang            mEdgeView.onRelease();
12173a02809c97669a157cf45bfd61d45272110d4091Chih-Chung Chang
12186b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            // If we scrolled in Y direction far enough, treat it as a delete
12196b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            // gesture.
12206b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            if (mFilmMode && mScrolledAfterDown && !mFirstScrollX
12216b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                    && mTouchBoxIndex != Integer.MAX_VALUE) {
12226b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                Rect r = mPositionController.getPosition(mTouchBoxIndex);
12236b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                int h = getHeight();
12246b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                if (Math.abs(r.centerY() - h * 0.5f) > 0.4f * h) {
12256b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                    int duration = mPositionController
12266b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                            .flingFilmY(mTouchBoxIndex, 0);
12276b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                    if (duration >= 0) {
12286b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                        mPositionController.setPopFromTop(r.centerY() < h * 0.5f);
12296b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                        deleteAfterAnimation(duration);
12306b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                    }
12316b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                }
12326b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            }
12336b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang
12343a02809c97669a157cf45bfd61d45272110d4091Chih-Chung Chang            if (mIgnoreUpEvent) {
12353a02809c97669a157cf45bfd61d45272110d4091Chih-Chung Chang                mIgnoreUpEvent = false;
12363a02809c97669a157cf45bfd61d45272110d4091Chih-Chung Chang                return;
12373a02809c97669a157cf45bfd61d45272110d4091Chih-Chung Chang            }
1238b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang
12392c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang            snapback();
1240f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
12416575794a9c09f22d5721e212c093e0a2df376d0cChih-Chung Chang
12426575794a9c09f22d5721e212c093e0a2df376d0cChih-Chung Chang        public void setSwipingEnabled(boolean enabled) {
12436575794a9c09f22d5721e212c093e0a2df376d0cChih-Chung Chang            mIgnoreSwipingGesture = !enabled;
12446575794a9c09f22d5721e212c093e0a2df376d0cChih-Chung Chang        }
12456575794a9c09f22d5721e212c093e0a2df376d0cChih-Chung Chang    }
12466575794a9c09f22d5721e212c093e0a2df376d0cChih-Chung Chang
12476575794a9c09f22d5721e212c093e0a2df376d0cChih-Chung Chang    public void setSwipingEnabled(boolean enabled) {
12486575794a9c09f22d5721e212c093e0a2df376d0cChih-Chung Chang        mGestureListener.setSwipingEnabled(enabled);
1249f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
1250f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
1251b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    private void setFilmMode(boolean enabled) {
1252b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        if (mFilmMode == enabled) return;
1253b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        mFilmMode = enabled;
1254b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        mPositionController.setFilmMode(mFilmMode);
1255b8be1e0ad76b6abc0da7ead39f7a9811195d001eChih-Chung Chang        mModel.setNeedFullImage(!enabled);
12566b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        mModel.setFocusHintDirection(
12576b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                mFilmMode ? Model.FOCUS_HINT_PREVIOUS : Model.FOCUS_HINT_NEXT);
125861f94714c3702115d2f89bb5f8829697be0c3680Chih-Chung Chang        mListener.onActionBarAllowed(!enabled);
1259bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang
1260e6251df06f0b3c8f556043f8b725b4b19be7474dChih-Chung Chang        // Move into camera in page mode, lock
1261e6251df06f0b3c8f556043f8b725b4b19be7474dChih-Chung Chang        if (!enabled && mPictures.get(0).isCamera()) {
1262e6251df06f0b3c8f556043f8b725b4b19be7474dChih-Chung Chang            mListener.lockOrientation();
1263bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang        }
1264bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang    }
1265bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang
1266bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang    public boolean getFilmMode() {
1267bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang        return mFilmMode;
1268f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
1269f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
1270b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    ////////////////////////////////////////////////////////////////////////////
1271b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    //  Framework events
1272b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    ////////////////////////////////////////////////////////////////////////////
1273b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang
1274b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    public void pause() {
1275b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        mPositionController.skipAnimation();
1276b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        mTileView.freeTextures();
1277b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        for (int i = -SCREEN_NAIL_MAX; i <= SCREEN_NAIL_MAX; i++) {
1278b8be1e0ad76b6abc0da7ead39f7a9811195d001eChih-Chung Chang            mPictures.get(i).setScreenNail(null);
1279b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        }
12806118af9ddad8acc050133154f8f60c842c7f9bfaChih-Chung Chang        hideUndoBar();
1281f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
1282f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
1283b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    public void resume() {
1284b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        mTileView.prepareTextures();
1285f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
1286f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
128733f8567dd5003e4bb342683f3768ab7552648b02Chih-Chung Chang    // move to the camera preview and show controls after resume
128833f8567dd5003e4bb342683f3768ab7552648b02Chih-Chung Chang    public void resetToFirstPicture() {
128933f8567dd5003e4bb342683f3768ab7552648b02Chih-Chung Chang        mModel.moveTo(0);
129033f8567dd5003e4bb342683f3768ab7552648b02Chih-Chung Chang        setFilmMode(false);
129133f8567dd5003e4bb342683f3768ab7552648b02Chih-Chung Chang    }
129233f8567dd5003e4bb342683f3768ab7552648b02Chih-Chung Chang
1293517e1bd25305d4e82d101a8c06be0119dde2eab3Chih-Chung Chang    ////////////////////////////////////////////////////////////////////////////
1294517e1bd25305d4e82d101a8c06be0119dde2eab3Chih-Chung Chang    //  Undo Bar
1295517e1bd25305d4e82d101a8c06be0119dde2eab3Chih-Chung Chang    ////////////////////////////////////////////////////////////////////////////
1296517e1bd25305d4e82d101a8c06be0119dde2eab3Chih-Chung Chang
1297517e1bd25305d4e82d101a8c06be0119dde2eab3Chih-Chung Chang    private int mUndoBarState;
1298517e1bd25305d4e82d101a8c06be0119dde2eab3Chih-Chung Chang    private static final int UNDO_BAR_SHOW = 1;
1299517e1bd25305d4e82d101a8c06be0119dde2eab3Chih-Chung Chang    private static final int UNDO_BAR_TIMEOUT = 2;
1300517e1bd25305d4e82d101a8c06be0119dde2eab3Chih-Chung Chang    private static final int UNDO_BAR_TOUCHED = 4;
13016118af9ddad8acc050133154f8f60c842c7f9bfaChih-Chung Chang    private static final int UNDO_BAR_FULL_CAMERA = 8;
13026118af9ddad8acc050133154f8f60c842c7f9bfaChih-Chung Chang    private static final int UNDO_BAR_DELETE_LAST = 16;
1303517e1bd25305d4e82d101a8c06be0119dde2eab3Chih-Chung Chang
13046118af9ddad8acc050133154f8f60c842c7f9bfaChih-Chung Chang    // "deleteLast" means if the deletion is on the last remaining picture in
13056118af9ddad8acc050133154f8f60c842c7f9bfaChih-Chung Chang    // the album.
13066118af9ddad8acc050133154f8f60c842c7f9bfaChih-Chung Chang    private void showUndoBar(boolean deleteLast) {
13076118af9ddad8acc050133154f8f60c842c7f9bfaChih-Chung Chang        mHandler.removeMessages(MSG_UNDO_BAR_TIMEOUT);
1308517e1bd25305d4e82d101a8c06be0119dde2eab3Chih-Chung Chang        mUndoBarState = UNDO_BAR_SHOW;
13096118af9ddad8acc050133154f8f60c842c7f9bfaChih-Chung Chang        if(deleteLast) mUndoBarState |= UNDO_BAR_DELETE_LAST;
1310517e1bd25305d4e82d101a8c06be0119dde2eab3Chih-Chung Chang        mUndoBar.animateVisibility(GLView.VISIBLE);
13116118af9ddad8acc050133154f8f60c842c7f9bfaChih-Chung Chang        mHandler.sendEmptyMessageDelayed(MSG_UNDO_BAR_TIMEOUT, 3000);
1312517e1bd25305d4e82d101a8c06be0119dde2eab3Chih-Chung Chang    }
1313517e1bd25305d4e82d101a8c06be0119dde2eab3Chih-Chung Chang
13146118af9ddad8acc050133154f8f60c842c7f9bfaChih-Chung Chang    private void hideUndoBar() {
13156118af9ddad8acc050133154f8f60c842c7f9bfaChih-Chung Chang        mHandler.removeMessages(MSG_UNDO_BAR_TIMEOUT);
1316517e1bd25305d4e82d101a8c06be0119dde2eab3Chih-Chung Chang        mListener.onCommitDeleteImage();
1317517e1bd25305d4e82d101a8c06be0119dde2eab3Chih-Chung Chang        mUndoBar.animateVisibility(GLView.INVISIBLE);
1318517e1bd25305d4e82d101a8c06be0119dde2eab3Chih-Chung Chang        mUndoBarState = 0;
1319517e1bd25305d4e82d101a8c06be0119dde2eab3Chih-Chung Chang        mUndoIndexHint = Integer.MAX_VALUE;
1320517e1bd25305d4e82d101a8c06be0119dde2eab3Chih-Chung Chang    }
1321517e1bd25305d4e82d101a8c06be0119dde2eab3Chih-Chung Chang
13226118af9ddad8acc050133154f8f60c842c7f9bfaChih-Chung Chang    // Check if the one of the conditions for hiding the undo bar has been
13236118af9ddad8acc050133154f8f60c842c7f9bfaChih-Chung Chang    // met. The conditions are:
13246118af9ddad8acc050133154f8f60c842c7f9bfaChih-Chung Chang    //
13256118af9ddad8acc050133154f8f60c842c7f9bfaChih-Chung Chang    // 1. It has been three seconds since last showing, and (a) the user has
13266118af9ddad8acc050133154f8f60c842c7f9bfaChih-Chung Chang    // touched, or (b) the deleted picture is the last remaining picture in the
13276118af9ddad8acc050133154f8f60c842c7f9bfaChih-Chung Chang    // album.
13286118af9ddad8acc050133154f8f60c842c7f9bfaChih-Chung Chang    //
13296118af9ddad8acc050133154f8f60c842c7f9bfaChih-Chung Chang    // 2. The camera is shown in full screen.
1330517e1bd25305d4e82d101a8c06be0119dde2eab3Chih-Chung Chang    private void checkHideUndoBar(int addition) {
1331517e1bd25305d4e82d101a8c06be0119dde2eab3Chih-Chung Chang        mUndoBarState |= addition;
13326118af9ddad8acc050133154f8f60c842c7f9bfaChih-Chung Chang        if ((mUndoBarState & UNDO_BAR_SHOW) == 0) return;
13336118af9ddad8acc050133154f8f60c842c7f9bfaChih-Chung Chang        boolean timeout = (mUndoBarState & UNDO_BAR_TIMEOUT) != 0;
13346118af9ddad8acc050133154f8f60c842c7f9bfaChih-Chung Chang        boolean touched = (mUndoBarState & UNDO_BAR_TOUCHED) != 0;
13356118af9ddad8acc050133154f8f60c842c7f9bfaChih-Chung Chang        boolean fullCamera = (mUndoBarState & UNDO_BAR_FULL_CAMERA) != 0;
13366118af9ddad8acc050133154f8f60c842c7f9bfaChih-Chung Chang        boolean deleteLast = (mUndoBarState & UNDO_BAR_DELETE_LAST) != 0;
13376118af9ddad8acc050133154f8f60c842c7f9bfaChih-Chung Chang        if ((timeout && (touched || deleteLast)) || fullCamera) {
1338517e1bd25305d4e82d101a8c06be0119dde2eab3Chih-Chung Chang            hideUndoBar();
1339517e1bd25305d4e82d101a8c06be0119dde2eab3Chih-Chung Chang        }
13406b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang    }
13416b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang
13426118af9ddad8acc050133154f8f60c842c7f9bfaChih-Chung Chang    // Returns true if the user can still undo the deletion of the last
13436118af9ddad8acc050133154f8f60c842c7f9bfaChih-Chung Chang    // remaining picture in the album. We need to check this and delay making
13446118af9ddad8acc050133154f8f60c842c7f9bfaChih-Chung Chang    // the camera preview full screen, otherwise the user won't have a chance to
13456118af9ddad8acc050133154f8f60c842c7f9bfaChih-Chung Chang    // undo it.
13466118af9ddad8acc050133154f8f60c842c7f9bfaChih-Chung Chang    private boolean canUndoLastPicture() {
13476118af9ddad8acc050133154f8f60c842c7f9bfaChih-Chung Chang        if ((mUndoBarState & UNDO_BAR_SHOW) == 0) return false;
13486118af9ddad8acc050133154f8f60c842c7f9bfaChih-Chung Chang        return (mUndoBarState & UNDO_BAR_DELETE_LAST) != 0;
13496118af9ddad8acc050133154f8f60c842c7f9bfaChih-Chung Chang    }
13506118af9ddad8acc050133154f8f60c842c7f9bfaChih-Chung Chang
1351b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    ////////////////////////////////////////////////////////////////////////////
1352b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    //  Rendering
1353b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    ////////////////////////////////////////////////////////////////////////////
1354b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang
1355b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    @Override
1356b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    protected void render(GLCanvas canvas) {
13579f44f35c211229bc0c5ca17823f5d7b6bb3583a1Chih-Chung Chang        // Check if the camera preview occupies the full screen.
13589f44f35c211229bc0c5ca17823f5d7b6bb3583a1Chih-Chung Chang        boolean full = !mFilmMode && mPictures.get(0).isCamera()
13599f44f35c211229bc0c5ca17823f5d7b6bb3583a1Chih-Chung Chang                && mPositionController.isCenter()
13609f44f35c211229bc0c5ca17823f5d7b6bb3583a1Chih-Chung Chang                && mPositionController.isAtMinimalScale();
13619f44f35c211229bc0c5ca17823f5d7b6bb3583a1Chih-Chung Chang        if (full != mFullScreenCamera) {
13629f44f35c211229bc0c5ca17823f5d7b6bb3583a1Chih-Chung Chang            mFullScreenCamera = full;
13639f44f35c211229bc0c5ca17823f5d7b6bb3583a1Chih-Chung Chang            mListener.onFullScreenChanged(full);
13646118af9ddad8acc050133154f8f60c842c7f9bfaChih-Chung Chang            if (full) mHandler.sendEmptyMessage(MSG_UNDO_BAR_FULL_CAMERA);
13659f44f35c211229bc0c5ca17823f5d7b6bb3583a1Chih-Chung Chang        }
13669f44f35c211229bc0c5ca17823f5d7b6bb3583a1Chih-Chung Chang
13679f44f35c211229bc0c5ca17823f5d7b6bb3583a1Chih-Chung Chang        // Determine how many photos we need to draw in addition to the center
13689f44f35c211229bc0c5ca17823f5d7b6bb3583a1Chih-Chung Chang        // one.
13699f44f35c211229bc0c5ca17823f5d7b6bb3583a1Chih-Chung Chang        int neighbors;
13709f44f35c211229bc0c5ca17823f5d7b6bb3583a1Chih-Chung Chang        if (mFullScreenCamera) {
13719f44f35c211229bc0c5ca17823f5d7b6bb3583a1Chih-Chung Chang            neighbors = 0;
13729f44f35c211229bc0c5ca17823f5d7b6bb3583a1Chih-Chung Chang        } else {
13739f44f35c211229bc0c5ca17823f5d7b6bb3583a1Chih-Chung Chang            // In page mode, we draw only one previous/next photo. But if we are
13749f44f35c211229bc0c5ca17823f5d7b6bb3583a1Chih-Chung Chang            // doing capture animation, we want to draw all photos.
13759f44f35c211229bc0c5ca17823f5d7b6bb3583a1Chih-Chung Chang            boolean inPageMode = (mPositionController.getFilmRatio() == 0f);
13769f44f35c211229bc0c5ca17823f5d7b6bb3583a1Chih-Chung Chang            boolean inCaptureAnimation =
13779f44f35c211229bc0c5ca17823f5d7b6bb3583a1Chih-Chung Chang                    ((mHolding & HOLD_CAPTURE_ANIMATION) != 0);
13789f44f35c211229bc0c5ca17823f5d7b6bb3583a1Chih-Chung Chang            if (inPageMode && !inCaptureAnimation) {
13799f44f35c211229bc0c5ca17823f5d7b6bb3583a1Chih-Chung Chang                neighbors = 1;
13809f44f35c211229bc0c5ca17823f5d7b6bb3583a1Chih-Chung Chang            } else {
13819f44f35c211229bc0c5ca17823f5d7b6bb3583a1Chih-Chung Chang                neighbors = SCREEN_NAIL_MAX;
13829f44f35c211229bc0c5ca17823f5d7b6bb3583a1Chih-Chung Chang            }
13839f44f35c211229bc0c5ca17823f5d7b6bb3583a1Chih-Chung Chang        }
1384c4791b7721a8417be5be33a67c8ade6e82b03a2cChih-Chung Chang
1385c4791b7721a8417be5be33a67c8ade6e82b03a2cChih-Chung Chang        // Draw photos from back to front
1386c4791b7721a8417be5be33a67c8ade6e82b03a2cChih-Chung Chang        for (int i = neighbors; i >= -neighbors; i--) {
1387b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            Rect r = mPositionController.getPosition(i);
1388b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            mPictures.get(i).draw(canvas, r);
1389b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        }
1390b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang
13916b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        renderChild(canvas, mEdgeView);
13926b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        renderChild(canvas, mUndoBar);
13936b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang
1394b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        mPositionController.advanceAnimation();
1395b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        checkFocusSwitching();
1396ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang    }
1397ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang
1398b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    ////////////////////////////////////////////////////////////////////////////
1399b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    //  Film mode focus switching
1400b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    ////////////////////////////////////////////////////////////////////////////
1401f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
1402b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    // Runs in GL thread.
1403b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    private void checkFocusSwitching() {
1404b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        if (!mFilmMode) return;
1405b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        if (mHandler.hasMessages(MSG_SWITCH_FOCUS)) return;
1406b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        if (switchPosition() != 0) {
1407b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            mHandler.sendEmptyMessage(MSG_SWITCH_FOCUS);
1408f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
1409f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
1410f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
1411b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    // Runs in main thread.
1412b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    private void switchFocus() {
14132c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang        if (mHolding != 0) return;
1414b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        switch (switchPosition()) {
1415b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            case -1:
1416b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                switchToPrevImage();
1417b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                break;
1418b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            case 1:
1419b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                switchToNextImage();
1420b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                break;
1421b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        }
1422f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
1423f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
1424b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    // Returns -1 if we should switch focus to the previous picture, +1 if we
1425b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    // should switch to the next, 0 otherwise.
1426b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    private int switchPosition() {
1427b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        Rect curr = mPositionController.getPosition(0);
1428b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        int center = getWidth() / 2;
1429f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
14302c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang        if (curr.left > center && mPrevBound < 0) {
1431b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            Rect prev = mPositionController.getPosition(-1);
1432b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            int currDist = curr.left - center;
1433b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            int prevDist = center - prev.right;
1434b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            if (prevDist < currDist) {
1435b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                return -1;
1436b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            }
14372c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang        } else if (curr.right < center && mNextBound > 0) {
1438b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            Rect next = mPositionController.getPosition(1);
1439b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            int currDist = center - curr.right;
1440b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            int nextDist = next.left - center;
1441b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            if (nextDist < currDist) {
1442b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                return 1;
1443b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            }
1444b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        }
1445f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
1446b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        return 0;
1447f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
1448f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
144917ffedda6e3ed57b38afbb775594cf30d83fc652Chih-Chung Chang    // Switch to the previous or next picture if the hit position is inside
145017ffedda6e3ed57b38afbb775594cf30d83fc652Chih-Chung Chang    // one of their boxes. This runs in main thread.
145117ffedda6e3ed57b38afbb775594cf30d83fc652Chih-Chung Chang    private void switchToHitPicture(int x, int y) {
145217ffedda6e3ed57b38afbb775594cf30d83fc652Chih-Chung Chang        if (mPrevBound < 0) {
145317ffedda6e3ed57b38afbb775594cf30d83fc652Chih-Chung Chang            Rect r = mPositionController.getPosition(-1);
145417ffedda6e3ed57b38afbb775594cf30d83fc652Chih-Chung Chang            if (r.right >= x) {
145517ffedda6e3ed57b38afbb775594cf30d83fc652Chih-Chung Chang                slideToPrevPicture();
145617ffedda6e3ed57b38afbb775594cf30d83fc652Chih-Chung Chang                return;
145717ffedda6e3ed57b38afbb775594cf30d83fc652Chih-Chung Chang            }
145817ffedda6e3ed57b38afbb775594cf30d83fc652Chih-Chung Chang        }
145917ffedda6e3ed57b38afbb775594cf30d83fc652Chih-Chung Chang
146017ffedda6e3ed57b38afbb775594cf30d83fc652Chih-Chung Chang        if (mNextBound > 0) {
146117ffedda6e3ed57b38afbb775594cf30d83fc652Chih-Chung Chang            Rect r = mPositionController.getPosition(1);
146217ffedda6e3ed57b38afbb775594cf30d83fc652Chih-Chung Chang            if (r.left <= x) {
146317ffedda6e3ed57b38afbb775594cf30d83fc652Chih-Chung Chang                slideToNextPicture();
146417ffedda6e3ed57b38afbb775594cf30d83fc652Chih-Chung Chang                return;
146517ffedda6e3ed57b38afbb775594cf30d83fc652Chih-Chung Chang            }
146617ffedda6e3ed57b38afbb775594cf30d83fc652Chih-Chung Chang        }
146717ffedda6e3ed57b38afbb775594cf30d83fc652Chih-Chung Chang    }
146817ffedda6e3ed57b38afbb775594cf30d83fc652Chih-Chung Chang
1469b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    ////////////////////////////////////////////////////////////////////////////
1470b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    //  Page mode focus switching
1471b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    //
1472b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    //  We slide image to the next one or the previous one in two cases: 1: If
1473b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    //  the user did a fling gesture with enough velocity.  2 If the user has
1474b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    //  moved the picture a lot.
1475b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    ////////////////////////////////////////////////////////////////////////////
1476f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
1477b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    private boolean swipeImages(float velocityX, float velocityY) {
1478b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        if (mFilmMode) return false;
1479f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
1480b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        // Avoid swiping images if we're possibly flinging to view the
1481b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        // zoomed in picture vertically.
1482b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        PositionController controller = mPositionController;
1483b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        boolean isMinimal = controller.isAtMinimalScale();
1484b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        int edges = controller.getImageAtEdges();
1485b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        if (!isMinimal && Math.abs(velocityY) > Math.abs(velocityX))
1486b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            if ((edges & PositionController.IMAGE_AT_TOP_EDGE) == 0
1487b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                    || (edges & PositionController.IMAGE_AT_BOTTOM_EDGE) == 0)
1488b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                return false;
1489f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
1490b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        // If we are at the edge of the current photo and the sweeping velocity
1491b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        // exceeds the threshold, slide to the next / previous image.
1492b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        if (velocityX < -SWIPE_THRESHOLD && (isMinimal
1493b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                || (edges & PositionController.IMAGE_AT_RIGHT_EDGE) != 0)) {
1494b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            return slideToNextPicture();
1495b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        } else if (velocityX > SWIPE_THRESHOLD && (isMinimal
1496b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                || (edges & PositionController.IMAGE_AT_LEFT_EDGE) != 0)) {
1497b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            return slideToPrevPicture();
1498f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
1499f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
1500b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        return false;
1501b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    }
1502b29a27f475a2c449abdda8d4e03d30914feed8c6Chih-Chung Chang
15032c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang    private void snapback() {
15046b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        if ((mHolding & ~HOLD_DELETE) != 0) return;
15052c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang        if (!snapToNeighborImage()) {
15062c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang            mPositionController.snapback();
15072c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang        }
15082c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang    }
15092c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang
1510b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    private boolean snapToNeighborImage() {
1511b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        if (mFilmMode) return false;
1512f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
1513b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        Rect r = mPositionController.getPosition(0);
1514b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        int viewW = getWidth();
1515b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        int threshold = MOVE_THRESHOLD + gapToSide(r.width(), viewW);
1516f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
1517b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        // If we have moved the picture a lot, switching.
1518b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        if (viewW - r.right > threshold) {
1519b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            return slideToNextPicture();
1520b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        } else if (r.left > threshold) {
1521b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            return slideToPrevPicture();
1522f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
1523f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
1524b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        return false;
1525b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    }
1526f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
1527b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    private boolean slideToNextPicture() {
15282c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang        if (mNextBound <= 0) return false;
1529b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        switchToNextImage();
15302c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang        mPositionController.startHorizontalSlide();
1531b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        return true;
1532b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    }
1533676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang
1534b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    private boolean slideToPrevPicture() {
15352c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang        if (mPrevBound >= 0) return false;
1536b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        switchToPrevImage();
15372c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang        mPositionController.startHorizontalSlide();
1538b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        return true;
1539b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    }
1540676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang
1541b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    private static int gapToSide(int imageWidth, int viewWidth) {
1542b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        return Math.max(0, (viewWidth - imageWidth) / 2);
1543b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    }
1544f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
1545b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    ////////////////////////////////////////////////////////////////////////////
1546b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    //  Focus switching
1547b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    ////////////////////////////////////////////////////////////////////////////
1548f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
1549b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    private void switchToNextImage() {
1550bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang        mModel.moveTo(mModel.getCurrentIndex() + 1);
1551b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    }
155215b351a22d02e89d882fc9fe32b3f4c512080e0aChih-Chung Chang
1553b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    private void switchToPrevImage() {
1554bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang        mModel.moveTo(mModel.getCurrentIndex() - 1);
1555b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    }
1556cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang
1557160e6d776daab93610b3d12413ad9ff2dd867d8bChih-Chung Chang    private void switchToFirstImage() {
1558bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang        mModel.moveTo(0);
1559160e6d776daab93610b3d12413ad9ff2dd867d8bChih-Chung Chang    }
1560160e6d776daab93610b3d12413ad9ff2dd867d8bChih-Chung Chang
1561b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    ////////////////////////////////////////////////////////////////////////////
1562b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    //  Opening Animation
1563b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    ////////////////////////////////////////////////////////////////////////////
1564b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang
1565b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    public void setOpenAnimationRect(Rect rect) {
1566b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        mPositionController.setOpenAnimationRect(rect);
1567cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang    }
156815b351a22d02e89d882fc9fe32b3f4c512080e0aChih-Chung Chang
1569b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    ////////////////////////////////////////////////////////////////////////////
15702c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang    //  Capture Animation
15712c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang    ////////////////////////////////////////////////////////////////////////////
15722c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang
15732c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang    public boolean switchWithCaptureAnimation(int offset) {
15742c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang        GLRoot root = getGLRoot();
157514ce29ab5608daaa3d0074cdfc9fc85988537ff2Bobby Georgescu        if(root == null) return false;
15762c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang        root.lockRenderThread();
15772c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang        try {
15782c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang            return switchWithCaptureAnimationLocked(offset);
15792c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang        } finally {
15802c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang            root.unlockRenderThread();
15812c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang        }
15822c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang    }
15832c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang
15842c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang    private boolean switchWithCaptureAnimationLocked(int offset) {
15852c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang        if (mHolding != 0) return true;
15862c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang        if (offset == 1) {
15872c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang            if (mNextBound <= 0) return false;
158861f94714c3702115d2f89bb5f8829697be0c3680Chih-Chung Chang            // Temporary disable action bar until the capture animation is done.
158961f94714c3702115d2f89bb5f8829697be0c3680Chih-Chung Chang            if (!mFilmMode) mListener.onActionBarAllowed(false);
15902c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang            switchToNextImage();
15912c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang            mPositionController.startCaptureAnimationSlide(-1);
15922c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang        } else if (offset == -1) {
15932c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang            if (mPrevBound >= 0) return false;
1594c4791b7721a8417be5be33a67c8ade6e82b03a2cChih-Chung Chang            if (mFilmMode) setFilmMode(false);
1595c4791b7721a8417be5be33a67c8ade6e82b03a2cChih-Chung Chang
1596c4791b7721a8417be5be33a67c8ade6e82b03a2cChih-Chung Chang            // If we are too far away from the first image (so that we don't
1597c4791b7721a8417be5be33a67c8ade6e82b03a2cChih-Chung Chang            // have all the ScreenNails in-between), we go directly without
1598c4791b7721a8417be5be33a67c8ade6e82b03a2cChih-Chung Chang            // animation.
1599c4791b7721a8417be5be33a67c8ade6e82b03a2cChih-Chung Chang            if (mModel.getCurrentIndex() > SCREEN_NAIL_MAX) {
1600c4791b7721a8417be5be33a67c8ade6e82b03a2cChih-Chung Chang                switchToFirstImage();
160142e1fed4edd9ed1e326aab2f7969242c56812952Chih-Chung Chang                mPositionController.skipToFinalPosition();
1602c4791b7721a8417be5be33a67c8ade6e82b03a2cChih-Chung Chang                return true;
1603c4791b7721a8417be5be33a67c8ade6e82b03a2cChih-Chung Chang            }
1604c4791b7721a8417be5be33a67c8ade6e82b03a2cChih-Chung Chang
1605160e6d776daab93610b3d12413ad9ff2dd867d8bChih-Chung Chang            switchToFirstImage();
16062c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang            mPositionController.startCaptureAnimationSlide(1);
16072c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang        } else {
16082c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang            return false;
16092c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang        }
16102c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang        mHolding |= HOLD_CAPTURE_ANIMATION;
1611bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang        Message m = mHandler.obtainMessage(MSG_CAPTURE_ANIMATION_DONE, offset, 0);
1612f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang        mHandler.sendMessageDelayed(m, PositionController.CAPTURE_ANIMATION_TIME);
16132c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang        return true;
16142c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang    }
16152c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang
1616bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang    private void captureAnimationDone(int offset) {
16172c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang        mHolding &= ~HOLD_CAPTURE_ANIMATION;
1618e6251df06f0b3c8f556043f8b725b4b19be7474dChih-Chung Chang        if (offset == 1 && !mFilmMode) {
1619e6251df06f0b3c8f556043f8b725b4b19be7474dChih-Chung Chang            // Now the capture animation is done, enable the action bar.
1620e6251df06f0b3c8f556043f8b725b4b19be7474dChih-Chung Chang            mListener.onActionBarAllowed(true);
1621e6251df06f0b3c8f556043f8b725b4b19be7474dChih-Chung Chang            mListener.onActionBarWanted();
1622bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang        }
16232c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang        snapback();
16242c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang    }
16252c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang
16262c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang    ////////////////////////////////////////////////////////////////////////////
1627b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    //  Card deck effect calculation
1628b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    ////////////////////////////////////////////////////////////////////////////
1629b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang
1630cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang    // Returns the scrolling progress value for an object moving out of a
1631cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang    // view. The progress value measures how much the object has moving out of
1632cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang    // the view. The object currently displays in [left, right), and the view is
1633cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang    // at [0, viewWidth].
1634cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang    //
1635cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang    // The returned value is negative when the object is moving right, and
1636cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang    // positive when the object is moving left. The value goes to -1 or 1 when
1637cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang    // the object just moves out of the view completely. The value is 0 if the
1638cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang    // object currently fills the view.
1639cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang    private static float calculateMoveOutProgress(int left, int right,
1640cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang            int viewWidth) {
1641cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang        // w = object width
1642cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang        // viewWidth = view width
1643cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang        int w = right - left;
1644cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang
1645cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang        // If the object width is smaller than the view width,
1646cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang        //      |....view....|
1647cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang        //                   |<-->|      progress = -1 when left = viewWidth
1648bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang        //          |<-->|               progress = 0 when left = viewWidth / 2 - w / 2
1649cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang        // |<-->|                        progress = 1 when left = -w
1650cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang        if (w < viewWidth) {
1651bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang            int zx = viewWidth / 2 - w / 2;
1652bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang            if (left > zx) {
1653bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang                return -(left - zx) / (float) (viewWidth - zx);  // progress = (0, -1]
1654bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang            } else {
1655bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang                return (left - zx) / (float) (-w - zx);  // progress = [0, 1]
1656bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang            }
1657cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang        }
1658cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang
1659cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang        // If the object width is larger than the view width,
1660cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang        //             |..view..|
1661cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang        //                      |<--------->| progress = -1 when left = viewWidth
1662cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang        //             |<--------->|          progress = 0 between left = 0
1663cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang        //          |<--------->|                          and right = viewWidth
1664cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang        // |<--------->|                      progress = 1 when right = 0
1665cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang        if (left > 0) {
1666cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang            return -left / (float) viewWidth;
1667cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang        }
1668cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang
1669cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang        if (right < viewWidth) {
1670cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang            return (viewWidth - right) / (float) viewWidth;
1671cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang        }
1672cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang
1673cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang        return 0;
1674cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang    }
1675cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang
1676cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang    // Maps a scrolling progress value to the alpha factor in the fading
1677cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang    // animation.
1678cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang    private float getScrollAlpha(float scrollProgress) {
1679cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang        return scrollProgress < 0 ? mAlphaInterpolator.getInterpolation(
1680cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang                     1 - Math.abs(scrollProgress)) : 1.0f;
1681cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang    }
1682cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang
1683cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang    // Maps a scrolling progress value to the scaling factor in the fading
1684cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang    // animation.
1685cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang    private float getScrollScale(float scrollProgress) {
1686cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang        float interpolatedProgress = mScaleInterpolator.getInterpolation(
1687cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang                Math.abs(scrollProgress));
1688cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang        float scale = (1 - interpolatedProgress) +
1689cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang                interpolatedProgress * TRANSITION_SCALE_FACTOR;
1690cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang        return scale;
1691cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang    }
1692cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang
1693cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang
1694cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang    // This interpolator emulates the rate at which the perceived scale of an
1695cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang    // object changes as its distance from a camera increases. When this
1696cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang    // interpolator is applied to a scale animation on a view, it evokes the
1697cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang    // sense that the object is shrinking due to moving away from the camera.
1698cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang    private static class ZInterpolator {
1699cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang        private float focalLength;
1700cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang
1701cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang        public ZInterpolator(float foc) {
1702cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang            focalLength = foc;
1703cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang        }
1704cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang
1705cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang        public float getInterpolation(float input) {
1706cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang            return (1.0f - focalLength / (focalLength + input)) /
1707cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang                (1.0f - focalLength / (focalLength + 1.0f));
1708f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
1709f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
1710f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
1711ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang    // Returns an interpolated value for the page/film transition.
1712ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang    // When ratio = 0, the result is from.
1713ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang    // When ratio = 1, the result is to.
1714ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang    private static float interpolate(float ratio, float from, float to) {
1715ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang        return from + (to - from) * ratio * ratio;
1716ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang    }
1717ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang
17186b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang    // Returns the alpha factor in film mode if a picture is not in the center.
17196b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang    // The 0.03 lower bound is to make the item always visible a bit.
17206b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang    private float getOffsetAlpha(float offset) {
17216b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        offset /= 0.5f;
17226b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        float alpha = (offset > 0) ? (1 - offset) : (1 + offset);
17236b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        return Utils.clamp(alpha, 0.03f, 1f);
17246b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang    }
17256b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang
1726b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    ////////////////////////////////////////////////////////////////////////////
1727b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    //  Simple public utilities
1728b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    ////////////////////////////////////////////////////////////////////////////
1729f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
1730bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang    public void setListener(Listener listener) {
1731bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang        mListener = listener;
1732f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
1733616a70fdb4473d2fbd7b70772a3a82b908aeae1eOwen Lin
1734616a70fdb4473d2fbd7b70772a3a82b908aeae1eOwen Lin    public Rect getPhotoRect(int index) {
1735616a70fdb4473d2fbd7b70772a3a82b908aeae1eOwen Lin        return mPositionController.getPosition(index);
1736616a70fdb4473d2fbd7b70772a3a82b908aeae1eOwen Lin    }
1737616a70fdb4473d2fbd7b70772a3a82b908aeae1eOwen Lin
1738616a70fdb4473d2fbd7b70772a3a82b908aeae1eOwen Lin    public PhotoFallbackEffect buildFallbackEffect(GLView root, GLCanvas canvas) {
1739616a70fdb4473d2fbd7b70772a3a82b908aeae1eOwen Lin        Rect location = new Rect();
1740616a70fdb4473d2fbd7b70772a3a82b908aeae1eOwen Lin        Utils.assertTrue(root.getBoundsOf(this, location));
1741616a70fdb4473d2fbd7b70772a3a82b908aeae1eOwen Lin
1742616a70fdb4473d2fbd7b70772a3a82b908aeae1eOwen Lin        Rect fullRect = bounds();
1743616a70fdb4473d2fbd7b70772a3a82b908aeae1eOwen Lin        PhotoFallbackEffect effect = new PhotoFallbackEffect();
1744616a70fdb4473d2fbd7b70772a3a82b908aeae1eOwen Lin        for (int i = -SCREEN_NAIL_MAX; i <= SCREEN_NAIL_MAX; ++i) {
1745616a70fdb4473d2fbd7b70772a3a82b908aeae1eOwen Lin            MediaItem item = mModel.getMediaItem(i);
1746616a70fdb4473d2fbd7b70772a3a82b908aeae1eOwen Lin            if (item == null) continue;
1747616a70fdb4473d2fbd7b70772a3a82b908aeae1eOwen Lin            ScreenNail sc = mModel.getScreenNail(i);
174849affdc4e274098a34e4eb2dbe4a89a750f1ba7fOwen Lin            if (!(sc instanceof BitmapScreenNail)
174949affdc4e274098a34e4eb2dbe4a89a750f1ba7fOwen Lin                    || ((BitmapScreenNail) sc).isShowingPlaceholder()) continue;
175049affdc4e274098a34e4eb2dbe4a89a750f1ba7fOwen Lin
175149affdc4e274098a34e4eb2dbe4a89a750f1ba7fOwen Lin            // Now, sc is BitmapScreenNail and is not showing placeholder
1752616a70fdb4473d2fbd7b70772a3a82b908aeae1eOwen Lin            Rect rect = new Rect(getPhotoRect(i));
1753616a70fdb4473d2fbd7b70772a3a82b908aeae1eOwen Lin            if (!Rect.intersects(fullRect, rect)) continue;
1754616a70fdb4473d2fbd7b70772a3a82b908aeae1eOwen Lin            rect.offset(location.left, location.top);
1755616a70fdb4473d2fbd7b70772a3a82b908aeae1eOwen Lin
175638155c4f4dd6f0173337eb9f3fea54803192e229Owen Lin            int width = sc.getWidth();
175738155c4f4dd6f0173337eb9f3fea54803192e229Owen Lin            int height = sc.getHeight();
175838155c4f4dd6f0173337eb9f3fea54803192e229Owen Lin
175938155c4f4dd6f0173337eb9f3fea54803192e229Owen Lin            int rotation = mModel.getImageRotation(i);
176038155c4f4dd6f0173337eb9f3fea54803192e229Owen Lin            RawTexture texture;
176138155c4f4dd6f0173337eb9f3fea54803192e229Owen Lin            if ((rotation % 180) == 0) {
176238155c4f4dd6f0173337eb9f3fea54803192e229Owen Lin                texture = new RawTexture(width, height, true);
176338155c4f4dd6f0173337eb9f3fea54803192e229Owen Lin                canvas.beginRenderTarget(texture);
176438155c4f4dd6f0173337eb9f3fea54803192e229Owen Lin                canvas.translate(width / 2f, height / 2f);
176538155c4f4dd6f0173337eb9f3fea54803192e229Owen Lin            } else {
176638155c4f4dd6f0173337eb9f3fea54803192e229Owen Lin                texture = new RawTexture(height, width, true);
176738155c4f4dd6f0173337eb9f3fea54803192e229Owen Lin                canvas.beginRenderTarget(texture);
176838155c4f4dd6f0173337eb9f3fea54803192e229Owen Lin                canvas.translate(height / 2f, width / 2f);
176938155c4f4dd6f0173337eb9f3fea54803192e229Owen Lin            }
177038155c4f4dd6f0173337eb9f3fea54803192e229Owen Lin
177138155c4f4dd6f0173337eb9f3fea54803192e229Owen Lin            canvas.rotate(rotation, 0, 0, 1);
177238155c4f4dd6f0173337eb9f3fea54803192e229Owen Lin            canvas.translate(-width / 2f, -height / 2f);
177338155c4f4dd6f0173337eb9f3fea54803192e229Owen Lin            sc.draw(canvas, 0, 0, width, height);
1774616a70fdb4473d2fbd7b70772a3a82b908aeae1eOwen Lin            canvas.endRenderTarget();
1775616a70fdb4473d2fbd7b70772a3a82b908aeae1eOwen Lin            effect.addEntry(item.getPath(), rect, texture);
1776616a70fdb4473d2fbd7b70772a3a82b908aeae1eOwen Lin        }
1777616a70fdb4473d2fbd7b70772a3a82b908aeae1eOwen Lin        return effect;
1778616a70fdb4473d2fbd7b70772a3a82b908aeae1eOwen Lin    }
1779f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin}
1780