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;
20b1a28418848ce0adeb8a845a78e246b01913d176John Reckimport android.content.res.Configuration;
21f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport android.graphics.Color;
222ef46ed28b28b355d7f3f1432c7b1196b832a859Chih-Chung Changimport android.graphics.Matrix;
2304ac045bf8da5082bbb0bdc9ea5f9c9b5b796ad0Yuli Huangimport android.graphics.Rect;
244d47b3472cccf84e8c71d8fb389a79bc83b321eeHung-ying Tyanimport android.os.Build;
25f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport android.os.Message;
266b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Changimport android.util.FloatMath;
27f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport android.view.MotionEvent;
286b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Changimport android.view.View.MeasureSpec;
29cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Changimport android.view.animation.AccelerateInterpolator;
30f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
312b3ee0ea07246b859a5b75d8a6102a7cce7ec838Owen Linimport com.android.gallery3d.R;
32b21b8e58a604f6c701245d84b141b5b87663192bOwen Linimport com.android.gallery3d.app.AbstractGalleryActivity;
334d47b3472cccf84e8c71d8fb389a79bc83b321eeHung-ying Tyanimport com.android.gallery3d.common.ApiHelper;
34cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Changimport com.android.gallery3d.common.Utils;
35616a70fdb4473d2fbd7b70772a3a82b908aeae1eOwen Linimport com.android.gallery3d.data.MediaItem;
36b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Changimport com.android.gallery3d.data.MediaObject;
376b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Changimport com.android.gallery3d.data.Path;
38a4eae1abb4f2547dfbda84301ee764ce35464881John Reckimport com.android.gallery3d.glrenderer.GLCanvas;
39a4eae1abb4f2547dfbda84301ee764ce35464881John Reckimport com.android.gallery3d.glrenderer.RawTexture;
40a4eae1abb4f2547dfbda84301ee764ce35464881John Reckimport com.android.gallery3d.glrenderer.ResourceTexture;
41a4eae1abb4f2547dfbda84301ee764ce35464881John Reckimport com.android.gallery3d.glrenderer.StringTexture;
42a4eae1abb4f2547dfbda84301ee764ce35464881John Reckimport com.android.gallery3d.glrenderer.Texture;
436b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Changimport com.android.gallery3d.util.GalleryUtils;
44b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Changimport com.android.gallery3d.util.RangeArray;
45f37648b877bf6029d7afead31e965b473114c89cBobby Georgescuimport com.android.gallery3d.util.UsageStatistics;
46b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang
47f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linpublic class PhotoView extends GLView {
48f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    @SuppressWarnings("unused")
49f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static final String TAG = "PhotoView";
50915c2c5b2c367df71599370613af0924bd7c4887Bobby Georgescu    private final int mPlaceholderColor;
51f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
52f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public static final int INVALID_SIZE = -1;
53b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    public static final long INVALID_DATA_VERSION =
54b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            MediaObject.INVALID_DATA_VERSION;
55f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
56c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang    public static class Size {
57c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang        public int width;
58c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang        public int height;
59c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang    }
60c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang
616dc3866ac10cc6bf38e73f098fe1988d1a6eecd4Your Name    public interface Model extends TileImageView.TileSource {
62bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang        public int getCurrentIndex();
63bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang        public void moveTo(int index);
64c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang
65c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang        // Returns the size for the specified picture. If the size information is
66c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang        // not avaiable, width = height = 0.
67c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang        public void getImageSize(int offset, Size size);
68c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang
69616a70fdb4473d2fbd7b70772a3a82b908aeae1eOwen Lin        // Returns the media item for the specified picture.
70616a70fdb4473d2fbd7b70772a3a82b908aeae1eOwen Lin        public MediaItem getMediaItem(int offset);
71616a70fdb4473d2fbd7b70772a3a82b908aeae1eOwen Lin
72c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang        // Returns the rotation for the specified picture.
73c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang        public int getImageRotation(int offset);
74f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
75b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        // This amends the getScreenNail() method of TileImageView.Model to get
76b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        // ScreenNail at previous (negative offset) or next (positive offset)
77b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        // positions. Returns null if the specified ScreenNail is unavailable.
78b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        public ScreenNail getScreenNail(int offset);
79c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang
80c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang        // Set this to true if we need the model to provide full images.
81b8be1e0ad76b6abc0da7ead39f7a9811195d001eChih-Chung Chang        public void setNeedFullImage(boolean enabled);
82bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang
83bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang        // Returns true if the item is the Camera preview.
84bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang        public boolean isCamera(int offset);
85d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang
8643a80fd806247cb50e81bacda3ad21d210e9ae88Angus Kong        // Returns true if the item is the Panorama.
8743a80fd806247cb50e81bacda3ad21d210e9ae88Angus Kong        public boolean isPanorama(int offset);
8843a80fd806247cb50e81bacda3ad21d210e9ae88Angus Kong
89dbb6acc204ba6f095eb0e49e1298e9ceda2c3096Wu-cheng Li        // Returns true if the item is a static image that represents camera
90dbb6acc204ba6f095eb0e49e1298e9ceda2c3096Wu-cheng Li        // preview.
91dbb6acc204ba6f095eb0e49e1298e9ceda2c3096Wu-cheng Li        public boolean isStaticCamera(int offset);
92dbb6acc204ba6f095eb0e49e1298e9ceda2c3096Wu-cheng Li
93d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang        // Returns true if the item is a Video.
94d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang        public boolean isVideo(int offset);
95f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang
966b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        // Returns true if the item can be deleted.
976b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        public boolean isDeletable(int offset);
986b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang
99f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang        public static final int LOADING_INIT = 0;
100f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang        public static final int LOADING_COMPLETE = 1;
101f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang        public static final int LOADING_FAIL = 2;
102f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang
103f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang        public int getLoadingState(int offset);
1046b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang
1056b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        // When data change happens, we need to decide which MediaItem to focus
1066b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        // on.
1076b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        //
1086b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        // 1. If focus hint path != null, we try to focus on it if we can find
1096b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        // it.  This is used for undo a deletion, so we can focus on the
1106b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        // undeleted item.
1116b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        //
1126b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        // 2. Otherwise try to focus on the MediaItem that is currently focused,
1136b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        // if we can find it.
1146b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        //
1156b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        // 3. Otherwise try to focus on the previous MediaItem or the next
1166b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        // MediaItem, depending on the value of focus hint direction.
1176b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        public static final int FOCUS_HINT_NEXT = 0;
1186b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        public static final int FOCUS_HINT_PREVIOUS = 1;
1196b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        public void setFocusHintDirection(int direction);
1206b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        public void setFocusHintPath(Path path);
121b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    }
122b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang
123bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang    public interface Listener {
124b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        public void onSingleTapUp(int x, int y);
125bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang        public void onFullScreenChanged(boolean full);
12661f94714c3702115d2f89bb5f8829697be0c3680Chih-Chung Chang        public void onActionBarAllowed(boolean allowed);
127e6251df06f0b3c8f556043f8b725b4b19be7474dChih-Chung Chang        public void onActionBarWanted();
128bd7c016d728853d38f98a3c6077b7bd9b08858b9Yuli Huang        public void onCurrentImageUpdated();
1296b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        public void onDeleteImage(Path path, int offset);
1306b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        public void onUndoDeleteImage();
1316b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        public void onCommitDeleteImage();
1327eea4d3ac59aa88d327fc0d58f5e4052f43c54c9Bobby Georgescu        public void onFilmModeChanged(boolean enabled);
133f4e22eb44bfae0c2a3b6570f19480c8c3535d808Bobby Georgescu        public void onPictureCenter(boolean isCamera);
134949584340c9f5f41a6133ba06058af280490f0c8Bobby Georgescu        public void onUndoBarVisibilityChanged(boolean visible);
135b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    }
136f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
137e6251df06f0b3c8f556043f8b725b4b19be7474dChih-Chung Chang    // The rules about orientation locking:
138bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang    //
139e6251df06f0b3c8f556043f8b725b4b19be7474dChih-Chung Chang    // (1) We need to lock the orientation if we are in page mode camera
140e6251df06f0b3c8f556043f8b725b4b19be7474dChih-Chung Chang    // preview, so there is no (unwanted) rotation animation when the user
141e6251df06f0b3c8f556043f8b725b4b19be7474dChih-Chung Chang    // rotates the device.
142bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang    //
143e6251df06f0b3c8f556043f8b725b4b19be7474dChih-Chung Chang    // (2) We need to unlock the orientation if we want to show the action bar
144e6251df06f0b3c8f556043f8b725b4b19be7474dChih-Chung Chang    // because the action bar follows the system orientation.
145bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang    //
146e6251df06f0b3c8f556043f8b725b4b19be7474dChih-Chung Chang    // The rules about action bar:
147bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang    //
148e6251df06f0b3c8f556043f8b725b4b19be7474dChih-Chung Chang    // (1) If we are in film mode, we don't show action bar.
149e6251df06f0b3c8f556043f8b725b4b19be7474dChih-Chung Chang    //
150e6251df06f0b3c8f556043f8b725b4b19be7474dChih-Chung Chang    // (2) If we go from camera to gallery with capture animation, we show
151e6251df06f0b3c8f556043f8b725b4b19be7474dChih-Chung Chang    // action bar.
152b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    private static final int MSG_CANCEL_EXTRA_SCALING = 2;
153b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    private static final int MSG_SWITCH_FOCUS = 3;
1542c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang    private static final int MSG_CAPTURE_ANIMATION_DONE = 4;
1556b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang    private static final int MSG_DELETE_ANIMATION_DONE = 5;
1566b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang    private static final int MSG_DELETE_DONE = 6;
1576118af9ddad8acc050133154f8f60c842c7f9bfaChih-Chung Chang    private static final int MSG_UNDO_BAR_TIMEOUT = 7;
1586118af9ddad8acc050133154f8f60c842c7f9bfaChih-Chung Chang    private static final int MSG_UNDO_BAR_FULL_CAMERA = 8;
159f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
160f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static final float SWIPE_THRESHOLD = 300f;
161f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
162f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static final float DEFAULT_TEXT_SIZE = 20;
163cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang    private static float TRANSITION_SCALE_FACTOR = 0.74f;
164d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang    private static final int ICON_RATIO = 6;
1652c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang
1662c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang    // whether we want to apply card deck effect in page mode.
167b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    private static final boolean CARD_EFFECT = true;
168cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang
1696b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang    // whether we want to apply offset effect in film mode.
1706b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang    private static final boolean OFFSET_EFFECT = true;
1716b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang
1726b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang    // Used to calculate the scaling factor for the card deck effect.
173cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang    private ZInterpolator mScaleInterpolator = new ZInterpolator(0.5f);
174cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang
175cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang    // Used to calculate the alpha factor for the fading animation.
176cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang    private AccelerateInterpolator mAlphaInterpolator =
177cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang            new AccelerateInterpolator(0.9f);
178f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
179b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    // We keep this many previous ScreenNails. (also this many next ScreenNails)
180b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    public static final int SCREEN_NAIL_MAX = 3;
181b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang
1826b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang    // These are constants for the delete gesture.
183137c6d060f9710f1a460326f27459e89b49820c2Bobby Georgescu    private static final int SWIPE_ESCAPE_VELOCITY = 500; // dp/sec
184137c6d060f9710f1a460326f27459e89b49820c2Bobby Georgescu    private static final int MAX_DISMISS_VELOCITY = 2500; // dp/sec
185137c6d060f9710f1a460326f27459e89b49820c2Bobby Georgescu    private static final int SWIPE_ESCAPE_DISTANCE = 150; // dp
1866b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang
187b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    // The picture entries, the valid index is from -SCREEN_NAIL_MAX to
188b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    // SCREEN_NAIL_MAX.
189b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    private final RangeArray<Picture> mPictures =
190b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            new RangeArray<Picture>(-SCREEN_NAIL_MAX, SCREEN_NAIL_MAX);
1916b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang    private Size[] mSizes = new Size[2 * SCREEN_NAIL_MAX + 1];
192f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
1936575794a9c09f22d5721e212c093e0a2df376d0cChih-Chung Chang    private final MyGestureListener mGestureListener;
1943a02809c97669a157cf45bfd61d45272110d4091Chih-Chung Chang    private final GestureRecognizer mGestureRecognizer;
195f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private final PositionController mPositionController;
196f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
197bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang    private Listener mListener;
198f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private Model mModel;
199f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private StringTexture mNoThumbnailText;
200b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    private TileImageView mTileView;
201532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang    private EdgeView mEdgeView;
2026b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang    private UndoBarView mUndoBar;
203f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private Texture mVideoPlayIcon;
204f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
205f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private SynchronizedHandler mHandler;
206f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
207534b12fd804610dd67b8109bc08ba76f31afb33eChih-Chung Chang    private boolean mCancelExtraScalingPending;
208b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    private boolean mFilmMode = false;
209f4e22eb44bfae0c2a3b6570f19480c8c3535d808Bobby Georgescu    private boolean mWantPictureCenterCallbacks = false;
210bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang    private int mDisplayRotation = 0;
211bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang    private int mCompensation = 0;
2129f44f35c211229bc0c5ca17823f5d7b6bb3583a1Chih-Chung Chang    private boolean mFullScreenCamera;
2132ef46ed28b28b355d7f3f1432c7b1196b832a859Chih-Chung Chang    private Rect mCameraRelativeFrame = new Rect();
214bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang    private Rect mCameraRect = new Rect();
2156bf226e45ec542eae4f766a2417bfdda625af4f0Michael Kolb    private boolean mFirst = true;
216f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
217c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang    // [mPrevBound, mNextBound] is the range of index for all pictures in the
218c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang    // model, if we assume the index of current focused picture is 0.  So if
219c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang    // there are some previous pictures, mPrevBound < 0, and if there are some
220c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang    // next pictures, mNextBound > 0.
221c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang    private int mPrevBound;
222c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang    private int mNextBound;
223c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang
2242c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang    // This variable prevents us doing snapback until its values goes to 0. This
2252c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang    // happens if the user gesture is still in progress or we are in a capture
2262c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang    // animation.
2272c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang    private int mHolding;
2282c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang    private static final int HOLD_TOUCH_DOWN = 1;
22918958c51f1412d959d52500ceefc46f987d035f3Chih-Chung Chang    private static final int HOLD_CAPTURE_ANIMATION = 2;
2306b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang    private static final int HOLD_DELETE = 4;
2316b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang
2326b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang    // mTouchBoxIndex is the index of the box that is touched by the down
2336b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang    // gesture in film mode. The value Integer.MAX_VALUE means no box was
2346b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang    // touched.
2356b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang    private int mTouchBoxIndex = Integer.MAX_VALUE;
2366b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang    // Whether the box indicated by mTouchBoxIndex is deletable. Only meaningful
2376b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang    // if mTouchBoxIndex is not Integer.MAX_VALUE.
2386b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang    private boolean mTouchBoxDeletable;
239517e1bd25305d4e82d101a8c06be0119dde2eab3Chih-Chung Chang    // This is the index of the last deleted item. This is only used as a hint
240517e1bd25305d4e82d101a8c06be0119dde2eab3Chih-Chung Chang    // to hide the undo button when we are too far away from the deleted
241517e1bd25305d4e82d101a8c06be0119dde2eab3Chih-Chung Chang    // item. The value Integer.MAX_VALUE means there is no such hint.
242517e1bd25305d4e82d101a8c06be0119dde2eab3Chih-Chung Chang    private int mUndoIndexHint = Integer.MAX_VALUE;
2432c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang
244e6c566e7e25b7f961f501c878e98d3ca640e63d8John Reck    private Context mContext;
245e6c566e7e25b7f961f501c878e98d3ca640e63d8John Reck
246b21b8e58a604f6c701245d84b141b5b87663192bOwen Lin    public PhotoView(AbstractGalleryActivity activity) {
247f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mTileView = new TileImageView(activity);
248f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        addComponent(mTileView);
249e6c566e7e25b7f961f501c878e98d3ca640e63d8John Reck        mContext = activity.getAndroidContext();
250e6c566e7e25b7f961f501c878e98d3ca640e63d8John Reck        mPlaceholderColor = mContext.getResources().getColor(
251915c2c5b2c367df71599370613af0924bd7c4887Bobby Georgescu                R.color.photo_placeholder);
252e6c566e7e25b7f961f501c878e98d3ca640e63d8John Reck        mEdgeView = new EdgeView(mContext);
253532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang        addComponent(mEdgeView);
254e6c566e7e25b7f961f501c878e98d3ca640e63d8John Reck        mUndoBar = new UndoBarView(mContext);
2556b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        addComponent(mUndoBar);
2566b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        mUndoBar.setVisibility(GLView.INVISIBLE);
2576b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        mUndoBar.setOnClickListener(new OnClickListener() {
2586b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                @Override
2596b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                public void onClick(GLView v) {
2606b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                    mListener.onUndoDeleteImage();
2616118af9ddad8acc050133154f8f60c842c7f9bfaChih-Chung Chang                    hideUndoBar();
2626b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                }
2636b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            });
264f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mNoThumbnailText = StringTexture.newInstance(
265e6c566e7e25b7f961f501c878e98d3ca640e63d8John Reck                mContext.getString(R.string.no_thumbnail),
266f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                DEFAULT_TEXT_SIZE, Color.WHITE);
267f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
268b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        mHandler = new MyHandler(activity.getGLRoot());
269f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
2706575794a9c09f22d5721e212c093e0a2df376d0cChih-Chung Chang        mGestureListener = new MyGestureListener();
271e6c566e7e25b7f961f501c878e98d3ca640e63d8John Reck        mGestureRecognizer = new GestureRecognizer(mContext, mGestureListener);
272f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
273e6c566e7e25b7f961f501c878e98d3ca640e63d8John Reck        mPositionController = new PositionController(mContext,
274b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                new PositionController.Listener() {
27528cb4161da5fc3756933ca67d509b8af1c6275f1Owen Lin
27628cb4161da5fc3756933ca67d509b8af1c6275f1Owen Lin            @Override
27728cb4161da5fc3756933ca67d509b8af1c6275f1Owen Lin            public void invalidate() {
27828cb4161da5fc3756933ca67d509b8af1c6275f1Owen Lin                PhotoView.this.invalidate();
27928cb4161da5fc3756933ca67d509b8af1c6275f1Owen Lin            }
28028cb4161da5fc3756933ca67d509b8af1c6275f1Owen Lin
28128cb4161da5fc3756933ca67d509b8af1c6275f1Owen Lin            @Override
28228cb4161da5fc3756933ca67d509b8af1c6275f1Owen Lin            public boolean isHoldingDown() {
28328cb4161da5fc3756933ca67d509b8af1c6275f1Owen Lin                return (mHolding & HOLD_TOUCH_DOWN) != 0;
28428cb4161da5fc3756933ca67d509b8af1c6275f1Owen Lin            }
28528cb4161da5fc3756933ca67d509b8af1c6275f1Owen Lin
28628cb4161da5fc3756933ca67d509b8af1c6275f1Owen Lin            @Override
28728cb4161da5fc3756933ca67d509b8af1c6275f1Owen Lin            public boolean isHoldingDelete() {
28828cb4161da5fc3756933ca67d509b8af1c6275f1Owen Lin                return (mHolding & HOLD_DELETE) != 0;
28928cb4161da5fc3756933ca67d509b8af1c6275f1Owen Lin            }
29028cb4161da5fc3756933ca67d509b8af1c6275f1Owen Lin
29128cb4161da5fc3756933ca67d509b8af1c6275f1Owen Lin            @Override
29228cb4161da5fc3756933ca67d509b8af1c6275f1Owen Lin            public void onPull(int offset, int direction) {
29328cb4161da5fc3756933ca67d509b8af1c6275f1Owen Lin                mEdgeView.onPull(offset, direction);
29428cb4161da5fc3756933ca67d509b8af1c6275f1Owen Lin            }
29528cb4161da5fc3756933ca67d509b8af1c6275f1Owen Lin
29628cb4161da5fc3756933ca67d509b8af1c6275f1Owen Lin            @Override
29728cb4161da5fc3756933ca67d509b8af1c6275f1Owen Lin            public void onRelease() {
29828cb4161da5fc3756933ca67d509b8af1c6275f1Owen Lin                mEdgeView.onRelease();
29928cb4161da5fc3756933ca67d509b8af1c6275f1Owen Lin            }
30028cb4161da5fc3756933ca67d509b8af1c6275f1Owen Lin
30128cb4161da5fc3756933ca67d509b8af1c6275f1Owen Lin            @Override
30228cb4161da5fc3756933ca67d509b8af1c6275f1Owen Lin            public void onAbsorb(int velocity, int direction) {
30328cb4161da5fc3756933ca67d509b8af1c6275f1Owen Lin                mEdgeView.onAbsorb(velocity, direction);
30428cb4161da5fc3756933ca67d509b8af1c6275f1Owen Lin            }
30528cb4161da5fc3756933ca67d509b8af1c6275f1Owen Lin        });
306e6c566e7e25b7f961f501c878e98d3ca640e63d8John Reck        mVideoPlayIcon = new ResourceTexture(mContext, R.drawable.ic_control_play);
307b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        for (int i = -SCREEN_NAIL_MAX; i <= SCREEN_NAIL_MAX; i++) {
308b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            if (i == 0) {
309b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                mPictures.put(i, new FullPicture());
310cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang            } else {
311b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                mPictures.put(i, new ScreenNailPicture(i));
312cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang            }
313f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
314f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
315f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
316b27df4650459068b409924493bbadaf25bb7e191Bobby Georgescu    public void stopScrolling() {
317b27df4650459068b409924493bbadaf25bb7e191Bobby Georgescu        mPositionController.stopScrolling();
318b27df4650459068b409924493bbadaf25bb7e191Bobby Georgescu    }
319b27df4650459068b409924493bbadaf25bb7e191Bobby Georgescu
320b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    public void setModel(Model model) {
321b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        mModel = model;
322b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        mTileView.setModel(mModel);
323f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
324f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
325b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    class MyHandler extends SynchronizedHandler {
326b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        public MyHandler(GLRoot root) {
327b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            super(root);
328f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
329f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
330b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        @Override
331b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        public void handleMessage(Message message) {
332b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            switch (message.what) {
333b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                case MSG_CANCEL_EXTRA_SCALING: {
334b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                    mGestureRecognizer.cancelScale();
335b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                    mPositionController.setExtraScalingRange(false);
336b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                    mCancelExtraScalingPending = false;
337b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                    break;
338b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                }
339b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                case MSG_SWITCH_FOCUS: {
340b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                    switchFocus();
341b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                    break;
342b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                }
3432c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang                case MSG_CAPTURE_ANIMATION_DONE: {
344bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang                    // message.arg1 is the offset parameter passed to
345bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang                    // switchWithCaptureAnimation().
346bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang                    captureAnimationDone(message.arg1);
3472c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang                    break;
3482c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang                }
3496b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                case MSG_DELETE_ANIMATION_DONE: {
3506b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                    // message.obj is the Path of the MediaItem which should be
3516b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                    // deleted. message.arg1 is the offset of the image.
3526b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                    mListener.onDeleteImage((Path) message.obj, message.arg1);
3536b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                    // Normally a box which finishes delete animation will hold
3546b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                    // position until the underlying MediaItem is actually
3556b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                    // deleted, and HOLD_DELETE will be cancelled that time. In
3566b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                    // case the MediaItem didn't actually get deleted in 2
3576b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                    // seconds, we will cancel HOLD_DELETE and make it bounce
3586b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                    // back.
3596b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang
3606b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                    // We make sure there is at most one MSG_DELETE_DONE
3616b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                    // in the handler.
3626b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                    mHandler.removeMessages(MSG_DELETE_DONE);
3636b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                    Message m = mHandler.obtainMessage(MSG_DELETE_DONE);
3646b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                    mHandler.sendMessageDelayed(m, 2000);
3656118af9ddad8acc050133154f8f60c842c7f9bfaChih-Chung Chang
3666118af9ddad8acc050133154f8f60c842c7f9bfaChih-Chung Chang                    int numberOfPictures = mNextBound - mPrevBound + 1;
3676118af9ddad8acc050133154f8f60c842c7f9bfaChih-Chung Chang                    if (numberOfPictures == 2) {
3686118af9ddad8acc050133154f8f60c842c7f9bfaChih-Chung Chang                        if (mModel.isCamera(mNextBound)
3696118af9ddad8acc050133154f8f60c842c7f9bfaChih-Chung Chang                                || mModel.isCamera(mPrevBound)) {
3706118af9ddad8acc050133154f8f60c842c7f9bfaChih-Chung Chang                            numberOfPictures--;
3716118af9ddad8acc050133154f8f60c842c7f9bfaChih-Chung Chang                        }
3726118af9ddad8acc050133154f8f60c842c7f9bfaChih-Chung Chang                    }
3736118af9ddad8acc050133154f8f60c842c7f9bfaChih-Chung Chang                    showUndoBar(numberOfPictures <= 1);
3746b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                    break;
3756b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                }
3766b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                case MSG_DELETE_DONE: {
3776b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                    if (!mHandler.hasMessages(MSG_DELETE_ANIMATION_DONE)) {
3786b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                        mHolding &= ~HOLD_DELETE;
3796b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                        snapback();
3806b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                    }
3816b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                    break;
3826b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                }
3836118af9ddad8acc050133154f8f60c842c7f9bfaChih-Chung Chang                case MSG_UNDO_BAR_TIMEOUT: {
384517e1bd25305d4e82d101a8c06be0119dde2eab3Chih-Chung Chang                    checkHideUndoBar(UNDO_BAR_TIMEOUT);
385517e1bd25305d4e82d101a8c06be0119dde2eab3Chih-Chung Chang                    break;
386517e1bd25305d4e82d101a8c06be0119dde2eab3Chih-Chung Chang                }
3876118af9ddad8acc050133154f8f60c842c7f9bfaChih-Chung Chang                case MSG_UNDO_BAR_FULL_CAMERA: {
3886118af9ddad8acc050133154f8f60c842c7f9bfaChih-Chung Chang                    checkHideUndoBar(UNDO_BAR_FULL_CAMERA);
3896118af9ddad8acc050133154f8f60c842c7f9bfaChih-Chung Chang                    break;
3906118af9ddad8acc050133154f8f60c842c7f9bfaChih-Chung Chang                }
391b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                default: throw new AssertionError(message.what);
392f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            }
393f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
39428cb4161da5fc3756933ca67d509b8af1c6275f1Owen Lin    }
395f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
396f4e22eb44bfae0c2a3b6570f19480c8c3535d808Bobby Georgescu    public void setWantPictureCenterCallbacks(boolean wanted) {
397f4e22eb44bfae0c2a3b6570f19480c8c3535d808Bobby Georgescu        mWantPictureCenterCallbacks = wanted;
398b27df4650459068b409924493bbadaf25bb7e191Bobby Georgescu    }
399b27df4650459068b409924493bbadaf25bb7e191Bobby Georgescu
400b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    ////////////////////////////////////////////////////////////////////////////
401b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    //  Data/Image change notifications
402b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    ////////////////////////////////////////////////////////////////////////////
403b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang
404214993dc4abf87c386123af50e3c34184ba11cb6Chih-Chung Chang    public void notifyDataChange(int[] fromIndex, int prevBound, int nextBound) {
405c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang        mPrevBound = prevBound;
406c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang        mNextBound = nextBound;
407c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang
4086b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        // Update mTouchBoxIndex
4096b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        if (mTouchBoxIndex != Integer.MAX_VALUE) {
4106b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            int k = mTouchBoxIndex;
4116b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            mTouchBoxIndex = Integer.MAX_VALUE;
4126b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            for (int i = 0; i < 2 * SCREEN_NAIL_MAX + 1; i++) {
4136b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                if (fromIndex[i] == k) {
4146b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                    mTouchBoxIndex = i - SCREEN_NAIL_MAX;
4156b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                    break;
4166b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                }
4176b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            }
4186b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        }
4196b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang
420517e1bd25305d4e82d101a8c06be0119dde2eab3Chih-Chung Chang        // Hide undo button if we are too far away
421517e1bd25305d4e82d101a8c06be0119dde2eab3Chih-Chung Chang        if (mUndoIndexHint != Integer.MAX_VALUE) {
422517e1bd25305d4e82d101a8c06be0119dde2eab3Chih-Chung Chang            if (Math.abs(mUndoIndexHint - mModel.getCurrentIndex()) >= 3) {
423517e1bd25305d4e82d101a8c06be0119dde2eab3Chih-Chung Chang                hideUndoBar();
424517e1bd25305d4e82d101a8c06be0119dde2eab3Chih-Chung Chang            }
425517e1bd25305d4e82d101a8c06be0119dde2eab3Chih-Chung Chang        }
426517e1bd25305d4e82d101a8c06be0119dde2eab3Chih-Chung Chang
4276b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        // Update the ScreenNails.
4286b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        for (int i = -SCREEN_NAIL_MAX; i <= SCREEN_NAIL_MAX; i++) {
4296b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            Picture p =  mPictures.get(i);
4306b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            p.reload();
4316b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            mSizes[i + SCREEN_NAIL_MAX] = p.getSize();
4326b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        }
4336b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang
4346b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        boolean wasDeleting = mPositionController.hasDeletingBox();
4356b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang
436b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        // Move the boxes
437214993dc4abf87c386123af50e3c34184ba11cb6Chih-Chung Chang        mPositionController.moveBox(fromIndex, mPrevBound < 0, mNextBound > 0,
4386b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                mModel.isCamera(0), mSizes);
439b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang
440b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        for (int i = -SCREEN_NAIL_MAX; i <= SCREEN_NAIL_MAX; i++) {
4416b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            setPictureSize(i);
4426b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        }
4436b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang
4446b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        boolean isDeleting = mPositionController.hasDeletingBox();
4456b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang
4466b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        // If the deletion is done, make HOLD_DELETE persist for only the time
4476b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        // needed for a snapback animation.
4486b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        if (wasDeleting && !isDeleting) {
4496b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            mHandler.removeMessages(MSG_DELETE_DONE);
4506b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            Message m = mHandler.obtainMessage(MSG_DELETE_DONE);
4516b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            mHandler.sendMessageDelayed(
4526b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                    m, PositionController.SNAPBACK_ANIMATION_TIME);
453b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        }
454b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang
455b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        invalidate();
456f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
457f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
4586b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang    public boolean isDeleting() {
4596b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        return (mHolding & HOLD_DELETE) != 0
4606b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                && mPositionController.hasDeletingBox();
4616b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang    }
4626b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang
463b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    public void notifyImageChange(int index) {
464bd7c016d728853d38f98a3c6077b7bd9b08858b9Yuli Huang        if (index == 0) {
465bd7c016d728853d38f98a3c6077b7bd9b08858b9Yuli Huang            mListener.onCurrentImageUpdated();
466bd7c016d728853d38f98a3c6077b7bd9b08858b9Yuli Huang        }
467b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        mPictures.get(index).reload();
4686b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        setPictureSize(index);
469b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        invalidate();
470f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
471f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
4726b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang    private void setPictureSize(int index) {
4736b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        Picture p = mPictures.get(index);
4746b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        mPositionController.setImageSize(index, p.getSize(),
4756b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                index == 0 && p.isCamera() ? mCameraRect : null);
4766b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang    }
4776b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang
478bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang    @Override
4792ef46ed28b28b355d7f3f1432c7b1196b832a859Chih-Chung Chang    protected void onLayout(
4802ef46ed28b28b355d7f3f1432c7b1196b832a859Chih-Chung Chang            boolean changeSize, int left, int top, int right, int bottom) {
4812ef46ed28b28b355d7f3f1432c7b1196b832a859Chih-Chung Chang        int w = right - left;
4822ef46ed28b28b355d7f3f1432c7b1196b832a859Chih-Chung Chang        int h = bottom - top;
4832ef46ed28b28b355d7f3f1432c7b1196b832a859Chih-Chung Chang        mTileView.layout(0, 0, w, h);
4842ef46ed28b28b355d7f3f1432c7b1196b832a859Chih-Chung Chang        mEdgeView.layout(0, 0, w, h);
4856b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        mUndoBar.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
4866b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        mUndoBar.layout(0, h - mUndoBar.getMeasuredHeight(), w, h);
4872ef46ed28b28b355d7f3f1432c7b1196b832a859Chih-Chung Chang
4882ef46ed28b28b355d7f3f1432c7b1196b832a859Chih-Chung Chang        GLRoot root = getGLRoot();
4892ef46ed28b28b355d7f3f1432c7b1196b832a859Chih-Chung Chang        int displayRotation = root.getDisplayRotation();
4902ef46ed28b28b355d7f3f1432c7b1196b832a859Chih-Chung Chang        int compensation = root.getCompensation();
4912ef46ed28b28b355d7f3f1432c7b1196b832a859Chih-Chung Chang        if (mDisplayRotation != displayRotation
4922ef46ed28b28b355d7f3f1432c7b1196b832a859Chih-Chung Chang                || mCompensation != compensation) {
4932ef46ed28b28b355d7f3f1432c7b1196b832a859Chih-Chung Chang            mDisplayRotation = displayRotation;
4942ef46ed28b28b355d7f3f1432c7b1196b832a859Chih-Chung Chang            mCompensation = compensation;
4952ef46ed28b28b355d7f3f1432c7b1196b832a859Chih-Chung Chang
4963b4a8aeb0353fa18a2b5267b3952a80a6c6d4d13Chih-Chung Chang            // We need to change the size and rotation of the Camera ScreenNail,
4973b4a8aeb0353fa18a2b5267b3952a80a6c6d4d13Chih-Chung Chang            // but we don't want it to animate because the size doen't actually
4982ef46ed28b28b355d7f3f1432c7b1196b832a859Chih-Chung Chang            // change in the eye of the user.
4992ef46ed28b28b355d7f3f1432c7b1196b832a859Chih-Chung Chang            for (int i = -SCREEN_NAIL_MAX; i <= SCREEN_NAIL_MAX; i++) {
5002ef46ed28b28b355d7f3f1432c7b1196b832a859Chih-Chung Chang                Picture p = mPictures.get(i);
5012ef46ed28b28b355d7f3f1432c7b1196b832a859Chih-Chung Chang                if (p.isCamera()) {
5029f44f35c211229bc0c5ca17823f5d7b6bb3583a1Chih-Chung Chang                    p.forceSize();
5032ef46ed28b28b355d7f3f1432c7b1196b832a859Chih-Chung Chang                }
504bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang            }
505bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang        }
506bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang
5079f44f35c211229bc0c5ca17823f5d7b6bb3583a1Chih-Chung Chang        updateCameraRect();
5089f44f35c211229bc0c5ca17823f5d7b6bb3583a1Chih-Chung Chang        mPositionController.setConstrainedFrame(mCameraRect);
509bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang        if (changeSize) {
510bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang            mPositionController.setViewSize(getWidth(), getHeight());
511bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang        }
512bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang    }
513bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang
5149f44f35c211229bc0c5ca17823f5d7b6bb3583a1Chih-Chung Chang    // Update the camera rectangle due to layout change or camera relative frame
5159f44f35c211229bc0c5ca17823f5d7b6bb3583a1Chih-Chung Chang    // change.
5169f44f35c211229bc0c5ca17823f5d7b6bb3583a1Chih-Chung Chang    private void updateCameraRect() {
5172ef46ed28b28b355d7f3f1432c7b1196b832a859Chih-Chung Chang        // Get the width and height in framework orientation because the given
5182ef46ed28b28b355d7f3f1432c7b1196b832a859Chih-Chung Chang        // mCameraRelativeFrame is in that coordinates.
519bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang        int w = getWidth();
520bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang        int h = getHeight();
5212ef46ed28b28b355d7f3f1432c7b1196b832a859Chih-Chung Chang        if (mCompensation % 180 != 0) {
522bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang            int tmp = w;
523bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang            w = h;
524bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang            h = tmp;
525bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang        }
5262ef46ed28b28b355d7f3f1432c7b1196b832a859Chih-Chung Chang        int l = mCameraRelativeFrame.left;
5272ef46ed28b28b355d7f3f1432c7b1196b832a859Chih-Chung Chang        int t = mCameraRelativeFrame.top;
5282ef46ed28b28b355d7f3f1432c7b1196b832a859Chih-Chung Chang        int r = mCameraRelativeFrame.right;
5292ef46ed28b28b355d7f3f1432c7b1196b832a859Chih-Chung Chang        int b = mCameraRelativeFrame.bottom;
530bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang
5312ef46ed28b28b355d7f3f1432c7b1196b832a859Chih-Chung Chang        // Now convert it to the coordinates we are using.
5322ef46ed28b28b355d7f3f1432c7b1196b832a859Chih-Chung Chang        switch (mCompensation) {
533bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang            case 0: mCameraRect.set(l, t, r, b); break;
534bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang            case 90: mCameraRect.set(h - b, l, h - t, r); break;
535bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang            case 180: mCameraRect.set(w - r, h - b, w - l, h - t); break;
536bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang            case 270: mCameraRect.set(t, w - r, b, w - l); break;
537bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang        }
538bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang
5392ef46ed28b28b355d7f3f1432c7b1196b832a859Chih-Chung Chang        Log.d(TAG, "compensation = " + mCompensation
5402ef46ed28b28b355d7f3f1432c7b1196b832a859Chih-Chung Chang                + ", CameraRelativeFrame = " + mCameraRelativeFrame
5412ef46ed28b28b355d7f3f1432c7b1196b832a859Chih-Chung Chang                + ", mCameraRect = " + mCameraRect);
542bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang    }
543bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang
5442ef46ed28b28b355d7f3f1432c7b1196b832a859Chih-Chung Chang    public void setCameraRelativeFrame(Rect frame) {
5452ef46ed28b28b355d7f3f1432c7b1196b832a859Chih-Chung Chang        mCameraRelativeFrame.set(frame);
5469f44f35c211229bc0c5ca17823f5d7b6bb3583a1Chih-Chung Chang        updateCameraRect();
5479f44f35c211229bc0c5ca17823f5d7b6bb3583a1Chih-Chung Chang        // Originally we do
5489f44f35c211229bc0c5ca17823f5d7b6bb3583a1Chih-Chung Chang        //     mPositionController.setConstrainedFrame(mCameraRect);
5499f44f35c211229bc0c5ca17823f5d7b6bb3583a1Chih-Chung Chang        // here, but it is moved to a parameter of the setImageSize() call, so
5509f44f35c211229bc0c5ca17823f5d7b6bb3583a1Chih-Chung Chang        // it can be updated atomically with the CameraScreenNail's size change.
551bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang    }
552bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang
553bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang    // Returns the rotation we need to do to the camera texture before drawing
554bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang    // it to the canvas, assuming the camera texture is correct when the device
555bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang    // is in its natural orientation.
556bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang    private int getCameraRotation() {
557bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang        return (mCompensation - mDisplayRotation + 360) % 360;
558bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang    }
559bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang
56043a80fd806247cb50e81bacda3ad21d210e9ae88Angus Kong    private int getPanoramaRotation() {
561b1a28418848ce0adeb8a845a78e246b01913d176John Reck        // This function is magic
562b1a28418848ce0adeb8a845a78e246b01913d176John Reck        // The issue here is that Pano makes bad assumptions about rotation and
563b1a28418848ce0adeb8a845a78e246b01913d176John Reck        // orientation. The first is it assumes only two rotations are possible,
564b1a28418848ce0adeb8a845a78e246b01913d176John Reck        // 0 and 90. Thus, if display rotation is >= 180, we invert the output.
565b1a28418848ce0adeb8a845a78e246b01913d176John Reck        // The second is that it assumes landscape is a 90 rotation from portrait,
566b1a28418848ce0adeb8a845a78e246b01913d176John Reck        // however on landscape devices this is not true. Thus, if we are in portrait
567b1a28418848ce0adeb8a845a78e246b01913d176John Reck        // on a landscape device, we need to invert the output
568e6c566e7e25b7f961f501c878e98d3ca640e63d8John Reck        int orientation = mContext.getResources().getConfiguration().orientation;
569b1a28418848ce0adeb8a845a78e246b01913d176John Reck        boolean invertPortrait = (orientation == Configuration.ORIENTATION_PORTRAIT
570b1a28418848ce0adeb8a845a78e246b01913d176John Reck                && (mDisplayRotation == 90 || mDisplayRotation == 270));
571b1a28418848ce0adeb8a845a78e246b01913d176John Reck        boolean invert = (mDisplayRotation >= 180);
572b1a28418848ce0adeb8a845a78e246b01913d176John Reck        if (invert != invertPortrait) {
573866ed7abd7960efc7986a3624414a730e17f33a6John Reck            return (mCompensation + 180) % 360;
574b1a28418848ce0adeb8a845a78e246b01913d176John Reck        }
57543a80fd806247cb50e81bacda3ad21d210e9ae88Angus Kong        return mCompensation;
57643a80fd806247cb50e81bacda3ad21d210e9ae88Angus Kong    }
57743a80fd806247cb50e81bacda3ad21d210e9ae88Angus Kong
578b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    ////////////////////////////////////////////////////////////////////////////
579b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    //  Pictures
580b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    ////////////////////////////////////////////////////////////////////////////
581b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang
582b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    private interface Picture {
583b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        void reload();
584b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        void draw(GLCanvas canvas, Rect r);
585b8be1e0ad76b6abc0da7ead39f7a9811195d001eChih-Chung Chang        void setScreenNail(ScreenNail s);
5862c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang        boolean isCamera();  // whether the picture is a camera preview
5876b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        boolean isDeletable();  // whether the picture can be deleted
5889f44f35c211229bc0c5ca17823f5d7b6bb3583a1Chih-Chung Chang        void forceSize();  // called when mCompensation changes
5896b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        Size getSize();
5907817979db0c52ffeacb951625b1e821eba303285Ahbong Chang    }
591f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
592b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    class FullPicture implements Picture {
593b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        private int mRotation;
5942c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang        private boolean mIsCamera;
59543a80fd806247cb50e81bacda3ad21d210e9ae88Angus Kong        private boolean mIsPanorama;
596dbb6acc204ba6f095eb0e49e1298e9ceda2c3096Wu-cheng Li        private boolean mIsStaticCamera;
597d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang        private boolean mIsVideo;
5986b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        private boolean mIsDeletable;
599f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang        private int mLoadingState = Model.LOADING_INIT;
6006b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        private Size mSize = new Size();
601f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
602b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        @Override
603b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        public void reload() {
604b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            // mImageWidth and mImageHeight will get updated
605b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            mTileView.notifyModelInvalidated();
606b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang
607bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang            mIsCamera = mModel.isCamera(0);
60843a80fd806247cb50e81bacda3ad21d210e9ae88Angus Kong            mIsPanorama = mModel.isPanorama(0);
609dbb6acc204ba6f095eb0e49e1298e9ceda2c3096Wu-cheng Li            mIsStaticCamera = mModel.isStaticCamera(0);
610d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang            mIsVideo = mModel.isVideo(0);
6116b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            mIsDeletable = mModel.isDeletable(0);
612f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang            mLoadingState = mModel.getLoadingState(0);
613bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang            setScreenNail(mModel.getScreenNail(0));
6146b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            updateSize();
6159f44f35c211229bc0c5ca17823f5d7b6bb3583a1Chih-Chung Chang        }
6169f44f35c211229bc0c5ca17823f5d7b6bb3583a1Chih-Chung Chang
6176b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        @Override
6186b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        public Size getSize() {
6196b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            return mSize;
620bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang        }
621bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang
622bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang        @Override
6239f44f35c211229bc0c5ca17823f5d7b6bb3583a1Chih-Chung Chang        public void forceSize() {
6249f44f35c211229bc0c5ca17823f5d7b6bb3583a1Chih-Chung Chang            updateSize();
6256b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            mPositionController.forceImageSize(0, mSize);
6269f44f35c211229bc0c5ca17823f5d7b6bb3583a1Chih-Chung Chang        }
6279f44f35c211229bc0c5ca17823f5d7b6bb3583a1Chih-Chung Chang
6289f44f35c211229bc0c5ca17823f5d7b6bb3583a1Chih-Chung Chang        private void updateSize() {
62943a80fd806247cb50e81bacda3ad21d210e9ae88Angus Kong            if (mIsPanorama) {
63043a80fd806247cb50e81bacda3ad21d210e9ae88Angus Kong                mRotation = getPanoramaRotation();
631dbb6acc204ba6f095eb0e49e1298e9ceda2c3096Wu-cheng Li            } else if (mIsCamera && !mIsStaticCamera) {
632bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang                mRotation = getCameraRotation();
633bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang            } else {
634bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang                mRotation = mModel.getImageRotation(0);
635bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang            }
636bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang
637c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang            int w = mTileView.mImageWidth;
638c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang            int h = mTileView.mImageHeight;
6396b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            mSize.width = getRotated(mRotation, w, h);
6406b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            mSize.height = getRotated(mRotation, h, w);
641f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
642f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
643b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        @Override
644b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        public void draw(GLCanvas canvas, Rect r) {
645f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang            drawTileView(canvas, r);
646cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang
647bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang            // We want to have the following transitions:
6482c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang            // (1) Move camera preview out of its place: switch to film mode
6492c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang            // (2) Move camera preview into its place: switch to page mode
6502c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang            // The extra mWasCenter check makes sure (1) does not apply if in
6512c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang            // page mode, we move _to_ the camera preview from another picture.
652bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang
653bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang            // Holdings except touch-down prevent the transitions.
65418958c51f1412d959d52500ceefc46f987d035f3Chih-Chung Chang            if ((mHolding & ~HOLD_TOUCH_DOWN) != 0) return;
655bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang
656f4e22eb44bfae0c2a3b6570f19480c8c3535d808Bobby Georgescu            if (mWantPictureCenterCallbacks && mPositionController.isCenter()) {
657639095c6bed84ab0bf9ebc7c4f8b8c944df285a1Bobby Georgescu                mListener.onPictureCenter(mIsCamera);
658bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang            }
659cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang        }
660cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang
661b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        @Override
662b8be1e0ad76b6abc0da7ead39f7a9811195d001eChih-Chung Chang        public void setScreenNail(ScreenNail s) {
663b8be1e0ad76b6abc0da7ead39f7a9811195d001eChih-Chung Chang            mTileView.setScreenNail(s);
664cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang        }
665f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
666b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        @Override
6672c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang        public boolean isCamera() {
6682c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang            return mIsCamera;
669f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
670f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
6716b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        @Override
6726b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        public boolean isDeletable() {
6736b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            return mIsDeletable;
6746b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        }
6756b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang
676d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang        private void drawTileView(GLCanvas canvas, Rect r) {
677ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang            float imageScale = mPositionController.getImageScale();
678b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            int viewW = getWidth();
679b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            int viewH = getHeight();
680d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang            float cx = r.exactCenterX();
681d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang            float cy = r.exactCenterY();
682ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang            float scale = 1f;  // the scaling factor due to card effect
683b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang
684d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang            canvas.save(GLCanvas.SAVE_FLAG_MATRIX | GLCanvas.SAVE_FLAG_ALPHA);
685ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang            float filmRatio = mPositionController.getFilmRatio();
686ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang            boolean wantsCardEffect = CARD_EFFECT && !mIsCamera
687f320b8429dd5c68c117b8e9b1a36a435f4f4ff13Yuli Huang                    && filmRatio != 1f && !mPictures.get(-1).isCamera()
688f320b8429dd5c68c117b8e9b1a36a435f4f4ff13Yuli Huang                    && !mPositionController.inOpeningAnimation();
6896b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            boolean wantsOffsetEffect = OFFSET_EFFECT && mIsDeletable
6906b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                    && filmRatio == 1f && r.centerY() != viewH / 2;
6912c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang            if (wantsCardEffect) {
692b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                // Calculate the move-out progress value.
693b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                int left = r.left;
694b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                int right = r.right;
695b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                float progress = calculateMoveOutProgress(left, right, viewW);
696b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                progress = Utils.clamp(progress, -1f, 1f);
697b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang
698b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                // We only want to apply the fading animation if the scrolling
699b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                // movement is to the right.
700bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang                if (progress < 0) {
701ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang                    scale = getScrollScale(progress);
702ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang                    float alpha = getScrollAlpha(progress);
703ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang                    scale = interpolate(filmRatio, scale, 1f);
704ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang                    alpha = interpolate(filmRatio, alpha, 1f);
705d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang
706ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang                    imageScale *= scale;
707ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang                    canvas.multiplyAlpha(alpha);
708ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang
709ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang                    float cxPage;  // the cx value in page mode
710d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang                    if (right - left <= viewW) {
711b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                        // If the picture is narrower than the view, keep it at
712b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                        // the center of the view.
713ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang                        cxPage = viewW / 2f;
714b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                    } else {
715b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                        // If the picture is wider than the view (it's
716b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                        // zoomed-in), keep the left edge of the object align
717b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                        // the the left edge of the view.
718ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang                        cxPage = (right - left) * scale / 2f;
719b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                    }
720ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang                    cx = interpolate(filmRatio, cxPage, cx);
721b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                }
7226b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            } else if (wantsOffsetEffect) {
7236b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                float offset = (float) (r.centerY() - viewH / 2) / viewH;
7246b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                float alpha = getOffsetAlpha(offset);
7256b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                canvas.multiplyAlpha(alpha);
726b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            }
727f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
728d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang            // Draw the tile view.
729ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang            setTileViewPosition(cx, cy, viewW, viewH, imageScale);
7306b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            renderChild(canvas, mTileView);
731d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang
732f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang            // Draw the play video icon and the message.
733f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang            canvas.translate((int) (cx + 0.5f), (int) (cy + 0.5f));
734f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang            int s = (int) (scale * Math.min(r.width(), r.height()) + 0.5f);
735c1c67ea813421a6173d1dab2ad75c11b51c7976dMangesh Ghiware            if (mIsVideo) drawVideoPlayIcon(canvas, s);
736f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang            if (mLoadingState == Model.LOADING_FAIL) {
737f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang                drawLoadingFailMessage(canvas);
738d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang            }
739d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang
740f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang            // Draw a debug indicator showing which picture has focus (index ==
741f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang            // 0).
742f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang            //canvas.fillRect(-10, -10, 20, 20, 0x80FF00FF);
743f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang
744d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang            canvas.restore();
745d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang        }
746d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang
747d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang        // Set the position of the tile view
748d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang        private void setTileViewPosition(float cx, float cy,
749d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang                int viewW, int viewH, float scale) {
750d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang            // Find out the bitmap coordinates of the center of the view
751d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang            int imageW = mPositionController.getImageWidth();
752d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang            int imageH = mPositionController.getImageHeight();
753d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang            int centerX = (int) (imageW / 2f + (viewW / 2f - cx) / scale + 0.5f);
754d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang            int centerY = (int) (imageH / 2f + (viewH / 2f - cy) / scale + 0.5f);
755d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang
756b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            int inverseX = imageW - centerX;
757b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            int inverseY = imageH - centerY;
758d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang            int x, y;
759d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang            switch (mRotation) {
760d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang                case 0: x = centerX; y = centerY; break;
761d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang                case 90: x = centerY; y = inverseX; break;
762d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang                case 180: x = inverseX; y = inverseY; break;
763d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang                case 270: x = inverseY; y = centerX; break;
764b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                default:
765d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang                    throw new RuntimeException(String.valueOf(mRotation));
766b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            }
767d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang            mTileView.setPosition(x, y, scale, mRotation);
768f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
769f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
770f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
771b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    private class ScreenNailPicture implements Picture {
772b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        private int mIndex;
773b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        private int mRotation;
774b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        private ScreenNail mScreenNail;
7752c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang        private boolean mIsCamera;
77643a80fd806247cb50e81bacda3ad21d210e9ae88Angus Kong        private boolean mIsPanorama;
777dbb6acc204ba6f095eb0e49e1298e9ceda2c3096Wu-cheng Li        private boolean mIsStaticCamera;
778d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang        private boolean mIsVideo;
7796b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        private boolean mIsDeletable;
780f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang        private int mLoadingState = Model.LOADING_INIT;
7816b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        private Size mSize = new Size();
782f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
783b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        public ScreenNailPicture(int index) {
784b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            mIndex = index;
785b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        }
786532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang
787b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        @Override
788b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        public void reload() {
789bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang            mIsCamera = mModel.isCamera(mIndex);
79043a80fd806247cb50e81bacda3ad21d210e9ae88Angus Kong            mIsPanorama = mModel.isPanorama(mIndex);
791dbb6acc204ba6f095eb0e49e1298e9ceda2c3096Wu-cheng Li            mIsStaticCamera = mModel.isStaticCamera(mIndex);
792d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang            mIsVideo = mModel.isVideo(mIndex);
7936b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            mIsDeletable = mModel.isDeletable(mIndex);
794f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang            mLoadingState = mModel.getLoadingState(mIndex);
795c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang            setScreenNail(mModel.getScreenNail(mIndex));
7966b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            updateSize();
7976b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        }
7986b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang
7996b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        @Override
8006b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        public Size getSize() {
8016b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            return mSize;
802b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        }
803b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang
804b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        @Override
805b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        public void draw(GLCanvas canvas, Rect r) {
806b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            if (mScreenNail == null) {
807f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang                // Draw a placeholder rectange if there should be a picture in
808f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang                // this position (but somehow there isn't).
809c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang                if (mIndex >= mPrevBound && mIndex <= mNextBound) {
810f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang                    drawPlaceHolder(canvas, r);
811c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang                }
812b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                return;
813f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            }
8146b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            int w = getWidth();
8156b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            int h = getHeight();
8166b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            if (r.left >= w || r.right <= 0 || r.top >= h || r.bottom <= 0) {
817b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                mScreenNail.noDraw();
818b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                return;
819f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            }
820f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
821ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang            float filmRatio = mPositionController.getFilmRatio();
822ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang            boolean wantsCardEffect = CARD_EFFECT && mIndex > 0
823ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang                    && filmRatio != 1f && !mPictures.get(0).isCamera();
8246b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            boolean wantsOffsetEffect = OFFSET_EFFECT && mIsDeletable
8256b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                    && filmRatio == 1f && r.centerY() != h / 2;
826ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang            int cx = wantsCardEffect
827ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang                    ? (int) (interpolate(filmRatio, w / 2, r.centerX()) + 0.5f)
828ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang                    : r.centerX();
829b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            int cy = r.centerY();
830ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang            canvas.save(GLCanvas.SAVE_FLAG_MATRIX | GLCanvas.SAVE_FLAG_ALPHA);
831b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            canvas.translate(cx, cy);
8322c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang            if (wantsCardEffect) {
833b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                float progress = (float) (w / 2 - r.centerX()) / w;
834b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                progress = Utils.clamp(progress, -1, 1);
835b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                float alpha = getScrollAlpha(progress);
836b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                float scale = getScrollScale(progress);
837ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang                alpha = interpolate(filmRatio, alpha, 1f);
838ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang                scale = interpolate(filmRatio, scale, 1f);
839b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                canvas.multiplyAlpha(alpha);
840b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                canvas.scale(scale, scale, 1);
8416b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            } else if (wantsOffsetEffect) {
8426b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                float offset = (float) (r.centerY() - h / 2) / h;
8436b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                float alpha = getOffsetAlpha(offset);
8446b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                canvas.multiplyAlpha(alpha);
845b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            }
846b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            if (mRotation != 0) {
847b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                canvas.rotate(mRotation, 0, 0, 1);
848b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            }
849ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang            int drawW = getRotated(mRotation, r.width(), r.height());
850ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang            int drawH = getRotated(mRotation, r.height(), r.width());
851b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            mScreenNail.draw(canvas, -drawW / 2, -drawH / 2, drawW, drawH);
852f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang            if (isScreenNailAnimating()) {
853f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang                invalidate();
854f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang            }
855f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang            int s = Math.min(drawW, drawH);
856c1c67ea813421a6173d1dab2ad75c11b51c7976dMangesh Ghiware            if (mIsVideo) drawVideoPlayIcon(canvas, s);
857f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang            if (mLoadingState == Model.LOADING_FAIL) {
858f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang                drawLoadingFailMessage(canvas);
859f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang            }
860b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            canvas.restore();
861b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        }
862f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
863f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang        private boolean isScreenNailAnimating() {
864030f8dad6aefc42d0af39bc1b93f370937d3e2abOwen Lin            return (mScreenNail instanceof TiledScreenNail)
865030f8dad6aefc42d0af39bc1b93f370937d3e2abOwen Lin                    && ((TiledScreenNail) mScreenNail).isAnimating();
866f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang        }
867f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang
868b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        @Override
869b8be1e0ad76b6abc0da7ead39f7a9811195d001eChih-Chung Chang        public void setScreenNail(ScreenNail s) {
870b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            mScreenNail = s;
8719f44f35c211229bc0c5ca17823f5d7b6bb3583a1Chih-Chung Chang        }
8729f44f35c211229bc0c5ca17823f5d7b6bb3583a1Chih-Chung Chang
873bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang        @Override
8749f44f35c211229bc0c5ca17823f5d7b6bb3583a1Chih-Chung Chang        public void forceSize() {
8759f44f35c211229bc0c5ca17823f5d7b6bb3583a1Chih-Chung Chang            updateSize();
8766b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            mPositionController.forceImageSize(mIndex, mSize);
8779f44f35c211229bc0c5ca17823f5d7b6bb3583a1Chih-Chung Chang        }
8789f44f35c211229bc0c5ca17823f5d7b6bb3583a1Chih-Chung Chang
8799f44f35c211229bc0c5ca17823f5d7b6bb3583a1Chih-Chung Chang        private void updateSize() {
88043a80fd806247cb50e81bacda3ad21d210e9ae88Angus Kong            if (mIsPanorama) {
88143a80fd806247cb50e81bacda3ad21d210e9ae88Angus Kong                mRotation = getPanoramaRotation();
882dbb6acc204ba6f095eb0e49e1298e9ceda2c3096Wu-cheng Li            } else if (mIsCamera && !mIsStaticCamera) {
883bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang                mRotation = getCameraRotation();
884bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang            } else {
885bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang                mRotation = mModel.getImageRotation(mIndex);
886bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang            }
887c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang
888b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            if (mScreenNail != null) {
8896b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                mSize.width = mScreenNail.getWidth();
8906b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                mSize.height = mScreenNail.getHeight();
8916b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            } else {
892c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang                // If we don't have ScreenNail available, we can still try to
893c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang                // get the size information of it.
894c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang                mModel.getImageSize(mIndex, mSize);
895b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            }
896c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang
8976b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            int w = mSize.width;
8986b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            int h = mSize.height;
8996b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            mSize.width = getRotated(mRotation, w, h);
9006b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            mSize.height = getRotated(mRotation, h, w);
901f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
902b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang
903b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        @Override
9042c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang        public boolean isCamera() {
9052c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang            return mIsCamera;
906b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        }
9076b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang
9086b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        @Override
9096b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        public boolean isDeletable() {
9106b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            return mIsDeletable;
9116b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        }
912b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    }
913b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang
914f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang    // Draw a gray placeholder in the specified rectangle.
915f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang    private void drawPlaceHolder(GLCanvas canvas, Rect r) {
916915c2c5b2c367df71599370613af0924bd7c4887Bobby Georgescu        canvas.fillRect(r.left, r.top, r.width(), r.height(), mPlaceholderColor);
917f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang    }
918f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang
919d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang    // Draw the video play icon (in the place where the spinner was)
920d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang    private void drawVideoPlayIcon(GLCanvas canvas, int side) {
921d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang        int s = side / ICON_RATIO;
922d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang        // Draw the video play icon at the center
923d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang        mVideoPlayIcon.draw(canvas, -s / 2, -s / 2, s, s);
924d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang    }
925d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang
926f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang    // Draw the "no thumbnail" message
927f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang    private void drawLoadingFailMessage(GLCanvas canvas) {
928f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang        StringTexture m = mNoThumbnailText;
929f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang        m.draw(canvas, -m.getWidth() / 2, -m.getHeight() / 2);
930f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang    }
931f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang
932b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    private static int getRotated(int degree, int original, int theother) {
933b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        return (degree % 180 == 0) ? original : theother;
934b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    }
935b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang
936b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    ////////////////////////////////////////////////////////////////////////////
937b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    //  Gestures Handling
938b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    ////////////////////////////////////////////////////////////////////////////
939b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang
940b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    @Override
941b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    protected boolean onTouch(MotionEvent event) {
942b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        mGestureRecognizer.onTouchEvent(event);
943b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        return true;
944f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
945f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
9463a02809c97669a157cf45bfd61d45272110d4091Chih-Chung Chang    private class MyGestureListener implements GestureRecognizer.Listener {
9473a02809c97669a157cf45bfd61d45272110d4091Chih-Chung Chang        private boolean mIgnoreUpEvent = false;
948099989b310d84fe13eff0cdf2902bb3fb0bcbd14Chih-Chung Chang        // If we can change mode for this scale gesture.
949099989b310d84fe13eff0cdf2902bb3fb0bcbd14Chih-Chung Chang        private boolean mCanChangeMode;
95018958c51f1412d959d52500ceefc46f987d035f3Chih-Chung Chang        // If we have changed the film mode in this scaling gesture.
951b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        private boolean mModeChanged;
95233f8567dd5003e4bb342683f3768ab7552648b02Chih-Chung Chang        // If this scaling gesture should be ignored.
95333f8567dd5003e4bb342683f3768ab7552648b02Chih-Chung Chang        private boolean mIgnoreScalingGesture;
95417ffedda6e3ed57b38afbb775594cf30d83fc652Chih-Chung Chang        // whether the down action happened while the view is scrolling.
95517ffedda6e3ed57b38afbb775594cf30d83fc652Chih-Chung Chang        private boolean mDownInScrolling;
9566575794a9c09f22d5721e212c093e0a2df376d0cChih-Chung Chang        // If we should ignore all gestures other than onSingleTapUp.
9576575794a9c09f22d5721e212c093e0a2df376d0cChih-Chung Chang        private boolean mIgnoreSwipingGesture;
9586b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        // If a scrolling has happened after a down gesture.
9596b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        private boolean mScrolledAfterDown;
9606b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        // If the first scrolling move is in X direction. In the film mode, X
9616b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        // direction scrolling is normal scrolling. but Y direction scrolling is
9626b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        // a delete gesture.
9636b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        private boolean mFirstScrollX;
9646b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        // The accumulated Y delta that has been sent to mPositionController.
9656b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        private int mDeltaY;
9662ce59cbd4368eaf0f30cfea28891bd47155182cbChih-Chung Chang        // The accumulated scaling change from a scaling gesture.
9672ce59cbd4368eaf0f30cfea28891bd47155182cbChih-Chung Chang        private float mAccScale;
968499ac9eae0eb26eaf7ebf79fb88ba929b71c8145Bobby Georgescu        // If an onFling happened after the last onDown
969499ac9eae0eb26eaf7ebf79fb88ba929b71c8145Bobby Georgescu        private boolean mHadFling;
970b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang
9713a02809c97669a157cf45bfd61d45272110d4091Chih-Chung Chang        @Override
9723a02809c97669a157cf45bfd61d45272110d4091Chih-Chung Chang        public boolean onSingleTapUp(float x, float y) {
9734d47b3472cccf84e8c71d8fb389a79bc83b321eeHung-ying Tyan            // On crespo running Android 2.3.6 (gingerbread), a pinch out gesture results in the
9744d47b3472cccf84e8c71d8fb389a79bc83b321eeHung-ying Tyan            // following call sequence: onDown(), onUp() and then onSingleTapUp(). The correct
9754d47b3472cccf84e8c71d8fb389a79bc83b321eeHung-ying Tyan            // sequence for a single-tap-up gesture should be: onDown(), onSingleTapUp() and onUp().
9764d47b3472cccf84e8c71d8fb389a79bc83b321eeHung-ying Tyan            // The call sequence for a pinch out gesture in JB is: onDown(), then onUp() and there's
9774d47b3472cccf84e8c71d8fb389a79bc83b321eeHung-ying Tyan            // no onSingleTapUp(). Base on these observations, the following condition is added to
9784d47b3472cccf84e8c71d8fb389a79bc83b321eeHung-ying Tyan            // filter out the false alarm where onSingleTapUp() is called within a pinch out
9794d47b3472cccf84e8c71d8fb389a79bc83b321eeHung-ying Tyan            // gesture. The framework fix went into ICS. Refer to b/4588114.
9804d47b3472cccf84e8c71d8fb389a79bc83b321eeHung-ying Tyan            if (Build.VERSION.SDK_INT < ApiHelper.VERSION_CODES.ICE_CREAM_SANDWICH) {
9814d47b3472cccf84e8c71d8fb389a79bc83b321eeHung-ying Tyan                if ((mHolding & HOLD_TOUCH_DOWN) == 0) {
9824d47b3472cccf84e8c71d8fb389a79bc83b321eeHung-ying Tyan                    return true;
9834d47b3472cccf84e8c71d8fb389a79bc83b321eeHung-ying Tyan                }
9844d47b3472cccf84e8c71d8fb389a79bc83b321eeHung-ying Tyan            }
9854d47b3472cccf84e8c71d8fb389a79bc83b321eeHung-ying Tyan
986ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang            // We do this in addition to onUp() because we want the snapback of
987ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang            // setFilmMode to happen.
988ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang            mHolding &= ~HOLD_TOUCH_DOWN;
989ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang
99017ffedda6e3ed57b38afbb775594cf30d83fc652Chih-Chung Chang            if (mFilmMode && !mDownInScrolling) {
99117ffedda6e3ed57b38afbb775594cf30d83fc652Chih-Chung Chang                switchToHitPicture((int) (x + 0.5f), (int) (y + 0.5f));
992743f8152a1dd6ea47bcabc6fd95b6e8d8f1cd3e5Wu-cheng Li
993743f8152a1dd6ea47bcabc6fd95b6e8d8f1cd3e5Wu-cheng Li                // If this is a lock screen photo, let the listener handle the
994743f8152a1dd6ea47bcabc6fd95b6e8d8f1cd3e5Wu-cheng Li                // event. Tapping on lock screen photo should take the user
995743f8152a1dd6ea47bcabc6fd95b6e8d8f1cd3e5Wu-cheng Li                // directly to the lock screen.
996743f8152a1dd6ea47bcabc6fd95b6e8d8f1cd3e5Wu-cheng Li                MediaItem item = mModel.getMediaItem(0);
997743f8152a1dd6ea47bcabc6fd95b6e8d8f1cd3e5Wu-cheng Li                int supported = 0;
998743f8152a1dd6ea47bcabc6fd95b6e8d8f1cd3e5Wu-cheng Li                if (item != null) supported = item.getSupportedOperations();
999c7e3c76d6965f520a9651d309052c5f01726cd58Bobby Georgescu                if ((supported & MediaItem.SUPPORT_ACTION) == 0) {
1000743f8152a1dd6ea47bcabc6fd95b6e8d8f1cd3e5Wu-cheng Li                    setFilmMode(false);
1001743f8152a1dd6ea47bcabc6fd95b6e8d8f1cd3e5Wu-cheng Li                    mIgnoreUpEvent = true;
1002743f8152a1dd6ea47bcabc6fd95b6e8d8f1cd3e5Wu-cheng Li                    return true;
1003743f8152a1dd6ea47bcabc6fd95b6e8d8f1cd3e5Wu-cheng Li                }
1004b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            }
1005b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang
1006bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang            if (mListener != null) {
10072ef46ed28b28b355d7f3f1432c7b1196b832a859Chih-Chung Chang                // Do the inverse transform of the touch coordinates.
10082ef46ed28b28b355d7f3f1432c7b1196b832a859Chih-Chung Chang                Matrix m = getGLRoot().getCompensationMatrix();
10092ef46ed28b28b355d7f3f1432c7b1196b832a859Chih-Chung Chang                Matrix inv = new Matrix();
10102ef46ed28b28b355d7f3f1432c7b1196b832a859Chih-Chung Chang                m.invert(inv);
10112ef46ed28b28b355d7f3f1432c7b1196b832a859Chih-Chung Chang                float[] pts = new float[] {x, y};
10122ef46ed28b28b355d7f3f1432c7b1196b832a859Chih-Chung Chang                inv.mapPoints(pts);
10132ef46ed28b28b355d7f3f1432c7b1196b832a859Chih-Chung Chang                mListener.onSingleTapUp((int) (pts[0] + 0.5f), (int) (pts[1] + 0.5f));
10143a02809c97669a157cf45bfd61d45272110d4091Chih-Chung Chang            }
10153a02809c97669a157cf45bfd61d45272110d4091Chih-Chung Chang            return true;
10163a02809c97669a157cf45bfd61d45272110d4091Chih-Chung Chang        }
10173a02809c97669a157cf45bfd61d45272110d4091Chih-Chung Chang
10183a02809c97669a157cf45bfd61d45272110d4091Chih-Chung Chang        @Override
10193a02809c97669a157cf45bfd61d45272110d4091Chih-Chung Chang        public boolean onDoubleTap(float x, float y) {
10206575794a9c09f22d5721e212c093e0a2df376d0cChih-Chung Chang            if (mIgnoreSwipingGesture) return true;
102161f94714c3702115d2f89bb5f8829697be0c3680Chih-Chung Chang            if (mPictures.get(0).isCamera()) return false;
10223a02809c97669a157cf45bfd61d45272110d4091Chih-Chung Chang            PositionController controller = mPositionController;
1023b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            float scale = controller.getImageScale();
10243a02809c97669a157cf45bfd61d45272110d4091Chih-Chung Chang            // onDoubleTap happened on the second ACTION_DOWN.
10253a02809c97669a157cf45bfd61d45272110d4091Chih-Chung Chang            // We need to ignore the next UP event.
10263a02809c97669a157cf45bfd61d45272110d4091Chih-Chung Chang            mIgnoreUpEvent = true;
102724958c5d4a63e1d7c49d9e7007ee10958ad23e8fJohn Reck            if (scale <= .75f || controller.isAtMinimalScale()) {
102824958c5d4a63e1d7c49d9e7007ee10958ad23e8fJohn Reck                controller.zoomIn(x, y, Math.max(1.0f, scale * 1.5f));
10293a02809c97669a157cf45bfd61d45272110d4091Chih-Chung Chang            } else {
10303a02809c97669a157cf45bfd61d45272110d4091Chih-Chung Chang                controller.resetToFullView();
10313a02809c97669a157cf45bfd61d45272110d4091Chih-Chung Chang            }
10323a02809c97669a157cf45bfd61d45272110d4091Chih-Chung Chang            return true;
10333a02809c97669a157cf45bfd61d45272110d4091Chih-Chung Chang        }
1034f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
1035f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        @Override
10366b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        public boolean onScroll(float dx, float dy, float totalX, float totalY) {
10376575794a9c09f22d5721e212c093e0a2df376d0cChih-Chung Chang            if (mIgnoreSwipingGesture) return true;
10386b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            if (!mScrolledAfterDown) {
10396b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                mScrolledAfterDown = true;
10406b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                mFirstScrollX = (Math.abs(dx) > Math.abs(dy));
10416b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            }
10426b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang
10436b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            int dxi = (int) (-dx + 0.5f);
10446b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            int dyi = (int) (-dy + 0.5f);
10456b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            if (mFilmMode) {
10466b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                if (mFirstScrollX) {
10476b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                    mPositionController.scrollFilmX(dxi);
10486b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                } else {
10496b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                    if (mTouchBoxIndex == Integer.MAX_VALUE) return true;
10506b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                    int newDeltaY = calculateDeltaY(totalY);
10516b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                    int d = newDeltaY - mDeltaY;
10526b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                    if (d != 0) {
10536b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                        mPositionController.scrollFilmY(mTouchBoxIndex, d);
10546b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                        mDeltaY = newDeltaY;
10556b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                    }
10566b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                }
10576b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            } else {
10586b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                mPositionController.scrollPage(dxi, dyi);
10596b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            }
1060f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            return true;
1061f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
1062f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
10636b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        private int calculateDeltaY(float delta) {
10646b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            if (mTouchBoxDeletable) return (int) (delta + 0.5f);
10656b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang
10666b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            // don't let items that can't be deleted be dragged more than
10676b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            // maxScrollDistance, and make it harder and harder to drag.
10686b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            int size = getHeight();
10696b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            float maxScrollDistance = 0.15f * size;
10706b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            if (Math.abs(delta) >= size) {
10716b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                delta = delta > 0 ? maxScrollDistance : -maxScrollDistance;
10726b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            } else {
10736b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                delta = maxScrollDistance *
10746b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                        FloatMath.sin((delta / size) * (float) (Math.PI / 2));
10756b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            }
10766b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            return (int) (delta + 0.5f);
10776b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        }
10786b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang
1079f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        @Override
1080137c6d060f9710f1a460326f27459e89b49820c2Bobby Georgescu        public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
10816575794a9c09f22d5721e212c093e0a2df376d0cChih-Chung Chang            if (mIgnoreSwipingGesture) return true;
1082e9494164bd7dd1c55c99b3ae1e842b88bf6abd78Chih-Chung Chang            if (mModeChanged) return true;
10832ce3c3bfe08fff5aee58007cc8ba8f4a50861ae2Yuli Huang            if (swipeImages(velocityX, velocityY)) {
1084b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang                mIgnoreUpEvent = true;
10856b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            } else {
1086137c6d060f9710f1a460326f27459e89b49820c2Bobby Georgescu                flingImages(velocityX, velocityY, Math.abs(e2.getY() - e1.getY()));
1087f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            }
1088499ac9eae0eb26eaf7ebf79fb88ba929b71c8145Bobby Georgescu            mHadFling = true;
1089f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            return true;
1090f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
1091f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
1092137c6d060f9710f1a460326f27459e89b49820c2Bobby Georgescu        private boolean flingImages(float velocityX, float velocityY, float dY) {
10936b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            int vx = (int) (velocityX + 0.5f);
10946b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            int vy = (int) (velocityY + 0.5f);
10956b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            if (!mFilmMode) {
10966b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                return mPositionController.flingPage(vx, vy);
10976b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            }
10986b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            if (Math.abs(velocityX) > Math.abs(velocityY)) {
10996b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                return mPositionController.flingFilmX(vx);
11006b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            }
11016b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            // If we scrolled in Y direction fast enough, treat it as a delete
11026b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            // gesture.
11036b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            if (!mFilmMode || mTouchBoxIndex == Integer.MAX_VALUE
11046b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                    || !mTouchBoxDeletable) {
11056b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                return false;
11066b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            }
11077817979db0c52ffeacb951625b1e821eba303285Ahbong Chang            int maxVelocity = GalleryUtils.dpToPixel(MAX_DISMISS_VELOCITY);
11087817979db0c52ffeacb951625b1e821eba303285Ahbong Chang            int escapeVelocity = GalleryUtils.dpToPixel(SWIPE_ESCAPE_VELOCITY);
1109137c6d060f9710f1a460326f27459e89b49820c2Bobby Georgescu            int escapeDistance = GalleryUtils.dpToPixel(SWIPE_ESCAPE_DISTANCE);
11106b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            int centerY = mPositionController.getPosition(mTouchBoxIndex)
11116b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                    .centerY();
11126b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            boolean fastEnough = (Math.abs(vy) > escapeVelocity)
11136b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                    && (Math.abs(vy) > Math.abs(vx))
1114137c6d060f9710f1a460326f27459e89b49820c2Bobby Georgescu                    && ((vy > 0) == (centerY > getHeight() / 2))
1115137c6d060f9710f1a460326f27459e89b49820c2Bobby Georgescu                    && dY >= escapeDistance;
11166b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            if (fastEnough) {
11176b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                vy = Math.min(vy, maxVelocity);
11186b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                int duration = mPositionController.flingFilmY(mTouchBoxIndex, vy);
11196b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                if (duration >= 0) {
11206b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                    mPositionController.setPopFromTop(vy < 0);
11216b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                    deleteAfterAnimation(duration);
11226b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                    // We reset mTouchBoxIndex, so up() won't check if Y
11236b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                    // scrolled far enough to be a delete gesture.
11246b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                    mTouchBoxIndex = Integer.MAX_VALUE;
11256b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                    return true;
11266b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                }
11276b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            }
11286b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            return false;
11296b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        }
11306b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang
11316b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        private void deleteAfterAnimation(int duration) {
11326b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            MediaItem item = mModel.getMediaItem(mTouchBoxIndex);
11336b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            if (item == null) return;
1134517e1bd25305d4e82d101a8c06be0119dde2eab3Chih-Chung Chang            mListener.onCommitDeleteImage();
1135517e1bd25305d4e82d101a8c06be0119dde2eab3Chih-Chung Chang            mUndoIndexHint = mModel.getCurrentIndex() + mTouchBoxIndex;
11366b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            mHolding |= HOLD_DELETE;
11376b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            Message m = mHandler.obtainMessage(MSG_DELETE_ANIMATION_DONE);
11386b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            m.obj = item.getPath();
11396b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            m.arg1 = mTouchBoxIndex;
11406b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            mHandler.sendMessageDelayed(m, duration);
11416b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        }
11426b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang
1143f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        @Override
11443a02809c97669a157cf45bfd61d45272110d4091Chih-Chung Chang        public boolean onScaleBegin(float focusX, float focusY) {
11456575794a9c09f22d5721e212c093e0a2df376d0cChih-Chung Chang            if (mIgnoreSwipingGesture) return true;
114633f8567dd5003e4bb342683f3768ab7552648b02Chih-Chung Chang            // We ignore the scaling gesture if it is a camera preview.
114733f8567dd5003e4bb342683f3768ab7552648b02Chih-Chung Chang            mIgnoreScalingGesture = mPictures.get(0).isCamera();
114833f8567dd5003e4bb342683f3768ab7552648b02Chih-Chung Chang            if (mIgnoreScalingGesture) {
114933f8567dd5003e4bb342683f3768ab7552648b02Chih-Chung Chang                return true;
115033f8567dd5003e4bb342683f3768ab7552648b02Chih-Chung Chang            }
11513a02809c97669a157cf45bfd61d45272110d4091Chih-Chung Chang            mPositionController.beginScale(focusX, focusY);
1152099989b310d84fe13eff0cdf2902bb3fb0bcbd14Chih-Chung Chang            // We can change mode if we are in film mode, or we are in page
1153099989b310d84fe13eff0cdf2902bb3fb0bcbd14Chih-Chung Chang            // mode and at minimal scale.
1154099989b310d84fe13eff0cdf2902bb3fb0bcbd14Chih-Chung Chang            mCanChangeMode = mFilmMode
1155099989b310d84fe13eff0cdf2902bb3fb0bcbd14Chih-Chung Chang                    || mPositionController.isAtMinimalScale();
11562ce59cbd4368eaf0f30cfea28891bd47155182cbChih-Chung Chang            mAccScale = 1f;
1157f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            return true;
1158f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
1159f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
1160f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        @Override
11613a02809c97669a157cf45bfd61d45272110d4091Chih-Chung Chang        public boolean onScale(float focusX, float focusY, float scale) {
11626575794a9c09f22d5721e212c093e0a2df376d0cChih-Chung Chang            if (mIgnoreSwipingGesture) return true;
11636575794a9c09f22d5721e212c093e0a2df376d0cChih-Chung Chang            if (mIgnoreScalingGesture) return true;
116418958c51f1412d959d52500ceefc46f987d035f3Chih-Chung Chang            if (mModeChanged) return true;
1165b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            if (Float.isNaN(scale) || Float.isInfinite(scale)) return false;
116633f8567dd5003e4bb342683f3768ab7552648b02Chih-Chung Chang
1167b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            int outOfRange = mPositionController.scaleBy(scale, focusX, focusY);
1168b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang
11692ce59cbd4368eaf0f30cfea28891bd47155182cbChih-Chung Chang            // We wait for a large enough scale change before changing mode.
11702ce59cbd4368eaf0f30cfea28891bd47155182cbChih-Chung Chang            // Otherwise we may mistakenly treat a zoom-in gesture as zoom-out
11712ce59cbd4368eaf0f30cfea28891bd47155182cbChih-Chung Chang            // or vice versa.
11722ce59cbd4368eaf0f30cfea28891bd47155182cbChih-Chung Chang            mAccScale *= scale;
11732ce59cbd4368eaf0f30cfea28891bd47155182cbChih-Chung Chang            boolean largeEnough = (mAccScale < 0.97f || mAccScale > 1.03f);
11742ce59cbd4368eaf0f30cfea28891bd47155182cbChih-Chung Chang
117518958c51f1412d959d52500ceefc46f987d035f3Chih-Chung Chang            // If mode changes, we treat this scaling gesture has ended.
11762ce59cbd4368eaf0f30cfea28891bd47155182cbChih-Chung Chang            if (mCanChangeMode && largeEnough) {
1177b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                if ((outOfRange < 0 && !mFilmMode) ||
1178b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                        (outOfRange > 0 && mFilmMode)) {
117918958c51f1412d959d52500ceefc46f987d035f3Chih-Chung Chang                    stopExtraScalingIfNeeded();
118018958c51f1412d959d52500ceefc46f987d035f3Chih-Chung Chang
118118958c51f1412d959d52500ceefc46f987d035f3Chih-Chung Chang                    // Removing the touch down flag allows snapback to happen
118233f8567dd5003e4bb342683f3768ab7552648b02Chih-Chung Chang                    // for film mode change.
118318958c51f1412d959d52500ceefc46f987d035f3Chih-Chung Chang                    mHolding &= ~HOLD_TOUCH_DOWN;
1184f37648b877bf6029d7afead31e965b473114c89cBobby Georgescu                    if (mFilmMode) {
1185f37648b877bf6029d7afead31e965b473114c89cBobby Georgescu                        UsageStatistics.setPendingTransitionCause(
1186f37648b877bf6029d7afead31e965b473114c89cBobby Georgescu                                UsageStatistics.TRANSITION_PINCH_OUT);
1187f37648b877bf6029d7afead31e965b473114c89cBobby Georgescu                    } else {
1188f37648b877bf6029d7afead31e965b473114c89cBobby Georgescu                        UsageStatistics.setPendingTransitionCause(
1189f37648b877bf6029d7afead31e965b473114c89cBobby Georgescu                                UsageStatistics.TRANSITION_PINCH_IN);
1190f37648b877bf6029d7afead31e965b473114c89cBobby Georgescu                    }
1191b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                    setFilmMode(!mFilmMode);
119218958c51f1412d959d52500ceefc46f987d035f3Chih-Chung Chang
1193f37648b877bf6029d7afead31e965b473114c89cBobby Georgescu
119418958c51f1412d959d52500ceefc46f987d035f3Chih-Chung Chang                    // We need to call onScaleEnd() before setting mModeChanged
119518958c51f1412d959d52500ceefc46f987d035f3Chih-Chung Chang                    // to true.
119618958c51f1412d959d52500ceefc46f987d035f3Chih-Chung Chang                    onScaleEnd();
1197b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                    mModeChanged = true;
1198b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                    return true;
1199534b12fd804610dd67b8109bc08ba76f31afb33eChih-Chung Chang                }
1200b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang           }
1201b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang
120218958c51f1412d959d52500ceefc46f987d035f3Chih-Chung Chang            if (outOfRange != 0) {
1203b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                startExtraScalingIfNeeded();
1204534b12fd804610dd67b8109bc08ba76f31afb33eChih-Chung Chang            } else {
1205b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                stopExtraScalingIfNeeded();
1206534b12fd804610dd67b8109bc08ba76f31afb33eChih-Chung Chang            }
1207f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            return true;
1208f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
1209f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
121033f8567dd5003e4bb342683f3768ab7552648b02Chih-Chung Chang        @Override
121133f8567dd5003e4bb342683f3768ab7552648b02Chih-Chung Chang        public void onScaleEnd() {
12126575794a9c09f22d5721e212c093e0a2df376d0cChih-Chung Chang            if (mIgnoreSwipingGesture) return;
12136575794a9c09f22d5721e212c093e0a2df376d0cChih-Chung Chang            if (mIgnoreScalingGesture) return;
121433f8567dd5003e4bb342683f3768ab7552648b02Chih-Chung Chang            if (mModeChanged) return;
121533f8567dd5003e4bb342683f3768ab7552648b02Chih-Chung Chang            mPositionController.endScale();
121633f8567dd5003e4bb342683f3768ab7552648b02Chih-Chung Chang        }
121733f8567dd5003e4bb342683f3768ab7552648b02Chih-Chung Chang
1218b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        private void startExtraScalingIfNeeded() {
1219b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            if (!mCancelExtraScalingPending) {
1220b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                mHandler.sendEmptyMessageDelayed(
1221b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                        MSG_CANCEL_EXTRA_SCALING, 700);
1222b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                mPositionController.setExtraScalingRange(true);
1223b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                mCancelExtraScalingPending = true;
1224b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            }
1225b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        }
1226b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang
1227b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        private void stopExtraScalingIfNeeded() {
1228b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            if (mCancelExtraScalingPending) {
1229b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                mHandler.removeMessages(MSG_CANCEL_EXTRA_SCALING);
1230b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                mPositionController.setExtraScalingRange(false);
1231b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                mCancelExtraScalingPending = false;
1232b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            }
1233b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        }
1234b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang
1235f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        @Override
12366b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        public void onDown(float x, float y) {
1237517e1bd25305d4e82d101a8c06be0119dde2eab3Chih-Chung Chang            checkHideUndoBar(UNDO_BAR_TOUCHED);
1238517e1bd25305d4e82d101a8c06be0119dde2eab3Chih-Chung Chang
12396b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            mDeltaY = 0;
1240e9494164bd7dd1c55c99b3ae1e842b88bf6abd78Chih-Chung Chang            mModeChanged = false;
12416b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang
12426575794a9c09f22d5721e212c093e0a2df376d0cChih-Chung Chang            if (mIgnoreSwipingGesture) return;
12436575794a9c09f22d5721e212c093e0a2df376d0cChih-Chung Chang
12442c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang            mHolding |= HOLD_TOUCH_DOWN;
124517ffedda6e3ed57b38afbb775594cf30d83fc652Chih-Chung Chang
124617ffedda6e3ed57b38afbb775594cf30d83fc652Chih-Chung Chang            if (mFilmMode && mPositionController.isScrolling()) {
124717ffedda6e3ed57b38afbb775594cf30d83fc652Chih-Chung Chang                mDownInScrolling = true;
124817ffedda6e3ed57b38afbb775594cf30d83fc652Chih-Chung Chang                mPositionController.stopScrolling();
124917ffedda6e3ed57b38afbb775594cf30d83fc652Chih-Chung Chang            } else {
125017ffedda6e3ed57b38afbb775594cf30d83fc652Chih-Chung Chang                mDownInScrolling = false;
125117ffedda6e3ed57b38afbb775594cf30d83fc652Chih-Chung Chang            }
1252499ac9eae0eb26eaf7ebf79fb88ba929b71c8145Bobby Georgescu            mHadFling = false;
12536b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            mScrolledAfterDown = false;
12546b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            if (mFilmMode) {
12556b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                int xi = (int) (x + 0.5f);
12566b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                int yi = (int) (y + 0.5f);
1257137c6d060f9710f1a460326f27459e89b49820c2Bobby Georgescu                // We only care about being within the x bounds, necessary for
1258137c6d060f9710f1a460326f27459e89b49820c2Bobby Georgescu                // handling very wide images which are otherwise very hard to fling
1259137c6d060f9710f1a460326f27459e89b49820c2Bobby Georgescu                mTouchBoxIndex = mPositionController.hitTest(xi, getHeight() / 2);
1260137c6d060f9710f1a460326f27459e89b49820c2Bobby Georgescu
12616b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                if (mTouchBoxIndex < mPrevBound || mTouchBoxIndex > mNextBound) {
12626b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                    mTouchBoxIndex = Integer.MAX_VALUE;
12636b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                } else {
12646b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                    mTouchBoxDeletable =
12656b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                            mPictures.get(mTouchBoxIndex).isDeletable();
12666b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                }
12676b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            } else {
12686b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                mTouchBoxIndex = Integer.MAX_VALUE;
12696b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            }
12703a02809c97669a157cf45bfd61d45272110d4091Chih-Chung Chang        }
12713a02809c97669a157cf45bfd61d45272110d4091Chih-Chung Chang
12723a02809c97669a157cf45bfd61d45272110d4091Chih-Chung Chang        @Override
12733a02809c97669a157cf45bfd61d45272110d4091Chih-Chung Chang        public void onUp() {
12746575794a9c09f22d5721e212c093e0a2df376d0cChih-Chung Chang            if (mIgnoreSwipingGesture) return;
12756575794a9c09f22d5721e212c093e0a2df376d0cChih-Chung Chang
127618958c51f1412d959d52500ceefc46f987d035f3Chih-Chung Chang            mHolding &= ~HOLD_TOUCH_DOWN;
12773a02809c97669a157cf45bfd61d45272110d4091Chih-Chung Chang            mEdgeView.onRelease();
12783a02809c97669a157cf45bfd61d45272110d4091Chih-Chung Chang
12796b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            // If we scrolled in Y direction far enough, treat it as a delete
12806b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            // gesture.
12816b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            if (mFilmMode && mScrolledAfterDown && !mFirstScrollX
12826b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                    && mTouchBoxIndex != Integer.MAX_VALUE) {
12836b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                Rect r = mPositionController.getPosition(mTouchBoxIndex);
12846b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                int h = getHeight();
12856b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                if (Math.abs(r.centerY() - h * 0.5f) > 0.4f * h) {
12866b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                    int duration = mPositionController
12876b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                            .flingFilmY(mTouchBoxIndex, 0);
12886b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                    if (duration >= 0) {
12896b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                        mPositionController.setPopFromTop(r.centerY() < h * 0.5f);
12906b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                        deleteAfterAnimation(duration);
12916b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                    }
12926b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                }
12936b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            }
12946b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang
12953a02809c97669a157cf45bfd61d45272110d4091Chih-Chung Chang            if (mIgnoreUpEvent) {
12963a02809c97669a157cf45bfd61d45272110d4091Chih-Chung Chang                mIgnoreUpEvent = false;
12973a02809c97669a157cf45bfd61d45272110d4091Chih-Chung Chang                return;
12983a02809c97669a157cf45bfd61d45272110d4091Chih-Chung Chang            }
1299b1a513974d59c1dc14e004338b6795d6c3c5e5e8Bobby Georgescu
1300dc55d3b1a614b95a36aab60c06f7961a800ec85aBobby Georgescu            if (!(mFilmMode && !mHadFling && mFirstScrollX
1301dc55d3b1a614b95a36aab60c06f7961a800ec85aBobby Georgescu                    && snapToNeighborImage())) {
1302b1a513974d59c1dc14e004338b6795d6c3c5e5e8Bobby Georgescu                snapback();
1303499ac9eae0eb26eaf7ebf79fb88ba929b71c8145Bobby Georgescu            }
1304f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
13056575794a9c09f22d5721e212c093e0a2df376d0cChih-Chung Chang
13066575794a9c09f22d5721e212c093e0a2df376d0cChih-Chung Chang        public void setSwipingEnabled(boolean enabled) {
13076575794a9c09f22d5721e212c093e0a2df376d0cChih-Chung Chang            mIgnoreSwipingGesture = !enabled;
13086575794a9c09f22d5721e212c093e0a2df376d0cChih-Chung Chang        }
13096575794a9c09f22d5721e212c093e0a2df376d0cChih-Chung Chang    }
13106575794a9c09f22d5721e212c093e0a2df376d0cChih-Chung Chang
13116575794a9c09f22d5721e212c093e0a2df376d0cChih-Chung Chang    public void setSwipingEnabled(boolean enabled) {
13126575794a9c09f22d5721e212c093e0a2df376d0cChih-Chung Chang        mGestureListener.setSwipingEnabled(enabled);
1313f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
1314f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
131500ccf35f8ec016b2cb8fdcf0c65ba643dca54a14Bobby Georgescu    private void updateActionBar() {
131600ccf35f8ec016b2cb8fdcf0c65ba643dca54a14Bobby Georgescu        boolean isCamera = mPictures.get(0).isCamera();
131700ccf35f8ec016b2cb8fdcf0c65ba643dca54a14Bobby Georgescu        if (isCamera && !mFilmMode) {
131800ccf35f8ec016b2cb8fdcf0c65ba643dca54a14Bobby Georgescu            // Move into camera in page mode, lock
131900ccf35f8ec016b2cb8fdcf0c65ba643dca54a14Bobby Georgescu            mListener.onActionBarAllowed(false);
132000ccf35f8ec016b2cb8fdcf0c65ba643dca54a14Bobby Georgescu        } else {
132100ccf35f8ec016b2cb8fdcf0c65ba643dca54a14Bobby Georgescu            mListener.onActionBarAllowed(true);
132200ccf35f8ec016b2cb8fdcf0c65ba643dca54a14Bobby Georgescu            if (mFilmMode) mListener.onActionBarWanted();
132300ccf35f8ec016b2cb8fdcf0c65ba643dca54a14Bobby Georgescu        }
132400ccf35f8ec016b2cb8fdcf0c65ba643dca54a14Bobby Georgescu    }
132500ccf35f8ec016b2cb8fdcf0c65ba643dca54a14Bobby Georgescu
13267eea4d3ac59aa88d327fc0d58f5e4052f43c54c9Bobby Georgescu    public void setFilmMode(boolean enabled) {
1327b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        if (mFilmMode == enabled) return;
1328b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        mFilmMode = enabled;
1329b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        mPositionController.setFilmMode(mFilmMode);
1330b8be1e0ad76b6abc0da7ead39f7a9811195d001eChih-Chung Chang        mModel.setNeedFullImage(!enabled);
133141b3cff60905a96ab5b6f8e5f42bcc3bab1eb605Bobby Georgescu        mModel.setFocusHintDirection(
133241b3cff60905a96ab5b6f8e5f42bcc3bab1eb605Bobby Georgescu                mFilmMode ? Model.FOCUS_HINT_PREVIOUS : Model.FOCUS_HINT_NEXT);
133300ccf35f8ec016b2cb8fdcf0c65ba643dca54a14Bobby Georgescu        updateActionBar();
13347eea4d3ac59aa88d327fc0d58f5e4052f43c54c9Bobby Georgescu        mListener.onFilmModeChanged(enabled);
1335bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang    }
1336bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang
1337bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang    public boolean getFilmMode() {
1338bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang        return mFilmMode;
1339f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
1340f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
1341b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    ////////////////////////////////////////////////////////////////////////////
1342b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    //  Framework events
1343b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    ////////////////////////////////////////////////////////////////////////////
1344b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang
1345b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    public void pause() {
1346b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        mPositionController.skipAnimation();
1347b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        mTileView.freeTextures();
1348b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        for (int i = -SCREEN_NAIL_MAX; i <= SCREEN_NAIL_MAX; i++) {
1349b8be1e0ad76b6abc0da7ead39f7a9811195d001eChih-Chung Chang            mPictures.get(i).setScreenNail(null);
1350b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        }
13516118af9ddad8acc050133154f8f60c842c7f9bfaChih-Chung Chang        hideUndoBar();
1352f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
1353f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
1354b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    public void resume() {
1355b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        mTileView.prepareTextures();
13563ed6ae23e43be73c83b6be3dcaafe7400a7b3355Bobby Georgescu        mPositionController.skipToFinalPosition();
1357f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
1358f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
135933f8567dd5003e4bb342683f3768ab7552648b02Chih-Chung Chang    // move to the camera preview and show controls after resume
136033f8567dd5003e4bb342683f3768ab7552648b02Chih-Chung Chang    public void resetToFirstPicture() {
136133f8567dd5003e4bb342683f3768ab7552648b02Chih-Chung Chang        mModel.moveTo(0);
136233f8567dd5003e4bb342683f3768ab7552648b02Chih-Chung Chang        setFilmMode(false);
136333f8567dd5003e4bb342683f3768ab7552648b02Chih-Chung Chang    }
136433f8567dd5003e4bb342683f3768ab7552648b02Chih-Chung Chang
1365517e1bd25305d4e82d101a8c06be0119dde2eab3Chih-Chung Chang    ////////////////////////////////////////////////////////////////////////////
1366517e1bd25305d4e82d101a8c06be0119dde2eab3Chih-Chung Chang    //  Undo Bar
1367517e1bd25305d4e82d101a8c06be0119dde2eab3Chih-Chung Chang    ////////////////////////////////////////////////////////////////////////////
1368517e1bd25305d4e82d101a8c06be0119dde2eab3Chih-Chung Chang
1369517e1bd25305d4e82d101a8c06be0119dde2eab3Chih-Chung Chang    private int mUndoBarState;
1370517e1bd25305d4e82d101a8c06be0119dde2eab3Chih-Chung Chang    private static final int UNDO_BAR_SHOW = 1;
1371517e1bd25305d4e82d101a8c06be0119dde2eab3Chih-Chung Chang    private static final int UNDO_BAR_TIMEOUT = 2;
1372517e1bd25305d4e82d101a8c06be0119dde2eab3Chih-Chung Chang    private static final int UNDO_BAR_TOUCHED = 4;
13736118af9ddad8acc050133154f8f60c842c7f9bfaChih-Chung Chang    private static final int UNDO_BAR_FULL_CAMERA = 8;
13746118af9ddad8acc050133154f8f60c842c7f9bfaChih-Chung Chang    private static final int UNDO_BAR_DELETE_LAST = 16;
1375517e1bd25305d4e82d101a8c06be0119dde2eab3Chih-Chung Chang
13766118af9ddad8acc050133154f8f60c842c7f9bfaChih-Chung Chang    // "deleteLast" means if the deletion is on the last remaining picture in
13776118af9ddad8acc050133154f8f60c842c7f9bfaChih-Chung Chang    // the album.
13786118af9ddad8acc050133154f8f60c842c7f9bfaChih-Chung Chang    private void showUndoBar(boolean deleteLast) {
13796118af9ddad8acc050133154f8f60c842c7f9bfaChih-Chung Chang        mHandler.removeMessages(MSG_UNDO_BAR_TIMEOUT);
1380517e1bd25305d4e82d101a8c06be0119dde2eab3Chih-Chung Chang        mUndoBarState = UNDO_BAR_SHOW;
13816118af9ddad8acc050133154f8f60c842c7f9bfaChih-Chung Chang        if(deleteLast) mUndoBarState |= UNDO_BAR_DELETE_LAST;
1382517e1bd25305d4e82d101a8c06be0119dde2eab3Chih-Chung Chang        mUndoBar.animateVisibility(GLView.VISIBLE);
13836118af9ddad8acc050133154f8f60c842c7f9bfaChih-Chung Chang        mHandler.sendEmptyMessageDelayed(MSG_UNDO_BAR_TIMEOUT, 3000);
1384949584340c9f5f41a6133ba06058af280490f0c8Bobby Georgescu        if (mListener != null) mListener.onUndoBarVisibilityChanged(true);
1385517e1bd25305d4e82d101a8c06be0119dde2eab3Chih-Chung Chang    }
1386517e1bd25305d4e82d101a8c06be0119dde2eab3Chih-Chung Chang
13876118af9ddad8acc050133154f8f60c842c7f9bfaChih-Chung Chang    private void hideUndoBar() {
13886118af9ddad8acc050133154f8f60c842c7f9bfaChih-Chung Chang        mHandler.removeMessages(MSG_UNDO_BAR_TIMEOUT);
1389517e1bd25305d4e82d101a8c06be0119dde2eab3Chih-Chung Chang        mListener.onCommitDeleteImage();
1390517e1bd25305d4e82d101a8c06be0119dde2eab3Chih-Chung Chang        mUndoBar.animateVisibility(GLView.INVISIBLE);
1391517e1bd25305d4e82d101a8c06be0119dde2eab3Chih-Chung Chang        mUndoBarState = 0;
1392517e1bd25305d4e82d101a8c06be0119dde2eab3Chih-Chung Chang        mUndoIndexHint = Integer.MAX_VALUE;
1393949584340c9f5f41a6133ba06058af280490f0c8Bobby Georgescu        mListener.onUndoBarVisibilityChanged(false);
1394517e1bd25305d4e82d101a8c06be0119dde2eab3Chih-Chung Chang    }
1395517e1bd25305d4e82d101a8c06be0119dde2eab3Chih-Chung Chang
13966118af9ddad8acc050133154f8f60c842c7f9bfaChih-Chung Chang    // Check if the one of the conditions for hiding the undo bar has been
13976118af9ddad8acc050133154f8f60c842c7f9bfaChih-Chung Chang    // met. The conditions are:
13986118af9ddad8acc050133154f8f60c842c7f9bfaChih-Chung Chang    //
13996118af9ddad8acc050133154f8f60c842c7f9bfaChih-Chung Chang    // 1. It has been three seconds since last showing, and (a) the user has
14006118af9ddad8acc050133154f8f60c842c7f9bfaChih-Chung Chang    // touched, or (b) the deleted picture is the last remaining picture in the
14016118af9ddad8acc050133154f8f60c842c7f9bfaChih-Chung Chang    // album.
14026118af9ddad8acc050133154f8f60c842c7f9bfaChih-Chung Chang    //
14036118af9ddad8acc050133154f8f60c842c7f9bfaChih-Chung Chang    // 2. The camera is shown in full screen.
1404517e1bd25305d4e82d101a8c06be0119dde2eab3Chih-Chung Chang    private void checkHideUndoBar(int addition) {
1405517e1bd25305d4e82d101a8c06be0119dde2eab3Chih-Chung Chang        mUndoBarState |= addition;
14066118af9ddad8acc050133154f8f60c842c7f9bfaChih-Chung Chang        if ((mUndoBarState & UNDO_BAR_SHOW) == 0) return;
14076118af9ddad8acc050133154f8f60c842c7f9bfaChih-Chung Chang        boolean timeout = (mUndoBarState & UNDO_BAR_TIMEOUT) != 0;
14086118af9ddad8acc050133154f8f60c842c7f9bfaChih-Chung Chang        boolean touched = (mUndoBarState & UNDO_BAR_TOUCHED) != 0;
14096118af9ddad8acc050133154f8f60c842c7f9bfaChih-Chung Chang        boolean fullCamera = (mUndoBarState & UNDO_BAR_FULL_CAMERA) != 0;
14106118af9ddad8acc050133154f8f60c842c7f9bfaChih-Chung Chang        boolean deleteLast = (mUndoBarState & UNDO_BAR_DELETE_LAST) != 0;
141141b3cff60905a96ab5b6f8e5f42bcc3bab1eb605Bobby Georgescu        if ((timeout && deleteLast) || fullCamera || touched) {
1412517e1bd25305d4e82d101a8c06be0119dde2eab3Chih-Chung Chang            hideUndoBar();
1413517e1bd25305d4e82d101a8c06be0119dde2eab3Chih-Chung Chang        }
14146b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang    }
14156b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang
1416639095c6bed84ab0bf9ebc7c4f8b8c944df285a1Bobby Georgescu    public boolean canUndo() {
1417639095c6bed84ab0bf9ebc7c4f8b8c944df285a1Bobby Georgescu        return (mUndoBarState & UNDO_BAR_SHOW) != 0;
14186118af9ddad8acc050133154f8f60c842c7f9bfaChih-Chung Chang    }
14196118af9ddad8acc050133154f8f60c842c7f9bfaChih-Chung Chang
1420b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    ////////////////////////////////////////////////////////////////////////////
1421b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    //  Rendering
1422b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    ////////////////////////////////////////////////////////////////////////////
1423b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang
1424b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    @Override
1425b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    protected void render(GLCanvas canvas) {
142658ed4d197795147f9f4a202da00b411a6ed53bd2Doris Liu        if (mFirst) {
142758ed4d197795147f9f4a202da00b411a6ed53bd2Doris Liu            // Make sure the fields are properly initialized before checking
142858ed4d197795147f9f4a202da00b411a6ed53bd2Doris Liu            // whether isCamera()
142958ed4d197795147f9f4a202da00b411a6ed53bd2Doris Liu            mPictures.get(0).reload();
143058ed4d197795147f9f4a202da00b411a6ed53bd2Doris Liu        }
14319f44f35c211229bc0c5ca17823f5d7b6bb3583a1Chih-Chung Chang        // Check if the camera preview occupies the full screen.
14329f44f35c211229bc0c5ca17823f5d7b6bb3583a1Chih-Chung Chang        boolean full = !mFilmMode && mPictures.get(0).isCamera()
14339f44f35c211229bc0c5ca17823f5d7b6bb3583a1Chih-Chung Chang                && mPositionController.isCenter()
14349f44f35c211229bc0c5ca17823f5d7b6bb3583a1Chih-Chung Chang                && mPositionController.isAtMinimalScale();
14356bf226e45ec542eae4f766a2417bfdda625af4f0Michael Kolb        if (mFirst || full != mFullScreenCamera) {
14369f44f35c211229bc0c5ca17823f5d7b6bb3583a1Chih-Chung Chang            mFullScreenCamera = full;
14376bf226e45ec542eae4f766a2417bfdda625af4f0Michael Kolb            mFirst = false;
14389f44f35c211229bc0c5ca17823f5d7b6bb3583a1Chih-Chung Chang            mListener.onFullScreenChanged(full);
14396118af9ddad8acc050133154f8f60c842c7f9bfaChih-Chung Chang            if (full) mHandler.sendEmptyMessage(MSG_UNDO_BAR_FULL_CAMERA);
14409f44f35c211229bc0c5ca17823f5d7b6bb3583a1Chih-Chung Chang        }
14419f44f35c211229bc0c5ca17823f5d7b6bb3583a1Chih-Chung Chang
14429f44f35c211229bc0c5ca17823f5d7b6bb3583a1Chih-Chung Chang        // Determine how many photos we need to draw in addition to the center
14439f44f35c211229bc0c5ca17823f5d7b6bb3583a1Chih-Chung Chang        // one.
14449f44f35c211229bc0c5ca17823f5d7b6bb3583a1Chih-Chung Chang        int neighbors;
14459f44f35c211229bc0c5ca17823f5d7b6bb3583a1Chih-Chung Chang        if (mFullScreenCamera) {
14469f44f35c211229bc0c5ca17823f5d7b6bb3583a1Chih-Chung Chang            neighbors = 0;
14479f44f35c211229bc0c5ca17823f5d7b6bb3583a1Chih-Chung Chang        } else {
14489f44f35c211229bc0c5ca17823f5d7b6bb3583a1Chih-Chung Chang            // In page mode, we draw only one previous/next photo. But if we are
14499f44f35c211229bc0c5ca17823f5d7b6bb3583a1Chih-Chung Chang            // doing capture animation, we want to draw all photos.
14509f44f35c211229bc0c5ca17823f5d7b6bb3583a1Chih-Chung Chang            boolean inPageMode = (mPositionController.getFilmRatio() == 0f);
14519f44f35c211229bc0c5ca17823f5d7b6bb3583a1Chih-Chung Chang            boolean inCaptureAnimation =
14529f44f35c211229bc0c5ca17823f5d7b6bb3583a1Chih-Chung Chang                    ((mHolding & HOLD_CAPTURE_ANIMATION) != 0);
14539f44f35c211229bc0c5ca17823f5d7b6bb3583a1Chih-Chung Chang            if (inPageMode && !inCaptureAnimation) {
14549f44f35c211229bc0c5ca17823f5d7b6bb3583a1Chih-Chung Chang                neighbors = 1;
14559f44f35c211229bc0c5ca17823f5d7b6bb3583a1Chih-Chung Chang            } else {
14569f44f35c211229bc0c5ca17823f5d7b6bb3583a1Chih-Chung Chang                neighbors = SCREEN_NAIL_MAX;
14579f44f35c211229bc0c5ca17823f5d7b6bb3583a1Chih-Chung Chang            }
14589f44f35c211229bc0c5ca17823f5d7b6bb3583a1Chih-Chung Chang        }
1459c4791b7721a8417be5be33a67c8ade6e82b03a2cChih-Chung Chang
1460c4791b7721a8417be5be33a67c8ade6e82b03a2cChih-Chung Chang        // Draw photos from back to front
1461c4791b7721a8417be5be33a67c8ade6e82b03a2cChih-Chung Chang        for (int i = neighbors; i >= -neighbors; i--) {
1462b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            Rect r = mPositionController.getPosition(i);
1463b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            mPictures.get(i).draw(canvas, r);
1464b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        }
1465b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang
14666b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        renderChild(canvas, mEdgeView);
14676b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        renderChild(canvas, mUndoBar);
14686b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang
1469b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        mPositionController.advanceAnimation();
1470b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        checkFocusSwitching();
1471ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang    }
1472ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang
1473b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    ////////////////////////////////////////////////////////////////////////////
1474b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    //  Film mode focus switching
1475b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    ////////////////////////////////////////////////////////////////////////////
1476f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
1477b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    // Runs in GL thread.
1478b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    private void checkFocusSwitching() {
1479b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        if (!mFilmMode) return;
1480b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        if (mHandler.hasMessages(MSG_SWITCH_FOCUS)) return;
1481b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        if (switchPosition() != 0) {
1482b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            mHandler.sendEmptyMessage(MSG_SWITCH_FOCUS);
1483f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
1484f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
1485f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
1486b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    // Runs in main thread.
1487b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    private void switchFocus() {
14882c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang        if (mHolding != 0) return;
1489b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        switch (switchPosition()) {
1490b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            case -1:
1491b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                switchToPrevImage();
1492b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                break;
1493b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            case 1:
1494b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                switchToNextImage();
1495b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                break;
1496b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        }
1497f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
1498f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
1499b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    // Returns -1 if we should switch focus to the previous picture, +1 if we
1500b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    // should switch to the next, 0 otherwise.
1501b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    private int switchPosition() {
1502b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        Rect curr = mPositionController.getPosition(0);
1503b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        int center = getWidth() / 2;
1504f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
15052c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang        if (curr.left > center && mPrevBound < 0) {
1506b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            Rect prev = mPositionController.getPosition(-1);
1507b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            int currDist = curr.left - center;
1508b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            int prevDist = center - prev.right;
1509b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            if (prevDist < currDist) {
1510b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                return -1;
1511b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            }
15122c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang        } else if (curr.right < center && mNextBound > 0) {
1513b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            Rect next = mPositionController.getPosition(1);
1514b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            int currDist = center - curr.right;
1515b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            int nextDist = next.left - center;
1516b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            if (nextDist < currDist) {
1517b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                return 1;
1518b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            }
1519b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        }
1520f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
1521b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        return 0;
1522f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
1523f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
152417ffedda6e3ed57b38afbb775594cf30d83fc652Chih-Chung Chang    // Switch to the previous or next picture if the hit position is inside
152517ffedda6e3ed57b38afbb775594cf30d83fc652Chih-Chung Chang    // one of their boxes. This runs in main thread.
152617ffedda6e3ed57b38afbb775594cf30d83fc652Chih-Chung Chang    private void switchToHitPicture(int x, int y) {
152717ffedda6e3ed57b38afbb775594cf30d83fc652Chih-Chung Chang        if (mPrevBound < 0) {
152817ffedda6e3ed57b38afbb775594cf30d83fc652Chih-Chung Chang            Rect r = mPositionController.getPosition(-1);
152917ffedda6e3ed57b38afbb775594cf30d83fc652Chih-Chung Chang            if (r.right >= x) {
153017ffedda6e3ed57b38afbb775594cf30d83fc652Chih-Chung Chang                slideToPrevPicture();
153117ffedda6e3ed57b38afbb775594cf30d83fc652Chih-Chung Chang                return;
153217ffedda6e3ed57b38afbb775594cf30d83fc652Chih-Chung Chang            }
153317ffedda6e3ed57b38afbb775594cf30d83fc652Chih-Chung Chang        }
153417ffedda6e3ed57b38afbb775594cf30d83fc652Chih-Chung Chang
153517ffedda6e3ed57b38afbb775594cf30d83fc652Chih-Chung Chang        if (mNextBound > 0) {
153617ffedda6e3ed57b38afbb775594cf30d83fc652Chih-Chung Chang            Rect r = mPositionController.getPosition(1);
153717ffedda6e3ed57b38afbb775594cf30d83fc652Chih-Chung Chang            if (r.left <= x) {
153817ffedda6e3ed57b38afbb775594cf30d83fc652Chih-Chung Chang                slideToNextPicture();
153917ffedda6e3ed57b38afbb775594cf30d83fc652Chih-Chung Chang                return;
154017ffedda6e3ed57b38afbb775594cf30d83fc652Chih-Chung Chang            }
154117ffedda6e3ed57b38afbb775594cf30d83fc652Chih-Chung Chang        }
154217ffedda6e3ed57b38afbb775594cf30d83fc652Chih-Chung Chang    }
154317ffedda6e3ed57b38afbb775594cf30d83fc652Chih-Chung Chang
1544b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    ////////////////////////////////////////////////////////////////////////////
1545b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    //  Page mode focus switching
1546b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    //
1547b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    //  We slide image to the next one or the previous one in two cases: 1: If
1548b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    //  the user did a fling gesture with enough velocity.  2 If the user has
1549b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    //  moved the picture a lot.
1550b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    ////////////////////////////////////////////////////////////////////////////
1551f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
1552b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    private boolean swipeImages(float velocityX, float velocityY) {
1553b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        if (mFilmMode) return false;
1554f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
1555b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        // Avoid swiping images if we're possibly flinging to view the
1556b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        // zoomed in picture vertically.
1557b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        PositionController controller = mPositionController;
1558b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        boolean isMinimal = controller.isAtMinimalScale();
1559b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        int edges = controller.getImageAtEdges();
1560b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        if (!isMinimal && Math.abs(velocityY) > Math.abs(velocityX))
1561b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            if ((edges & PositionController.IMAGE_AT_TOP_EDGE) == 0
1562b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                    || (edges & PositionController.IMAGE_AT_BOTTOM_EDGE) == 0)
1563b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                return false;
1564f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
1565b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        // If we are at the edge of the current photo and the sweeping velocity
1566b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        // exceeds the threshold, slide to the next / previous image.
1567b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        if (velocityX < -SWIPE_THRESHOLD && (isMinimal
1568b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                || (edges & PositionController.IMAGE_AT_RIGHT_EDGE) != 0)) {
1569b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            return slideToNextPicture();
1570b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        } else if (velocityX > SWIPE_THRESHOLD && (isMinimal
1571b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                || (edges & PositionController.IMAGE_AT_LEFT_EDGE) != 0)) {
1572b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            return slideToPrevPicture();
1573f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
1574f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
1575b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        return false;
1576b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    }
1577b29a27f475a2c449abdda8d4e03d30914feed8c6Chih-Chung Chang
15782c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang    private void snapback() {
15796b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        if ((mHolding & ~HOLD_DELETE) != 0) return;
1580499ac9eae0eb26eaf7ebf79fb88ba929b71c8145Bobby Georgescu        if (mFilmMode || !snapToNeighborImage()) {
15812c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang            mPositionController.snapback();
15822c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang        }
15832c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang    }
15842c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang
1585b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    private boolean snapToNeighborImage() {
1586b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        Rect r = mPositionController.getPosition(0);
1587b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        int viewW = getWidth();
1588cda9315a4b3fb0228cbcd3e9a18d8c90f0a8a3e0Doris Liu        // Setting the move threshold proportional to the width of the view
1589cda9315a4b3fb0228cbcd3e9a18d8c90f0a8a3e0Doris Liu        int moveThreshold = viewW / 5 ;
1590cda9315a4b3fb0228cbcd3e9a18d8c90f0a8a3e0Doris Liu        int threshold = moveThreshold + gapToSide(r.width(), viewW);
1591f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
1592b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        // If we have moved the picture a lot, switching.
1593b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        if (viewW - r.right > threshold) {
1594b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            return slideToNextPicture();
1595b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        } else if (r.left > threshold) {
1596b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            return slideToPrevPicture();
1597f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
1598f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
1599b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        return false;
1600b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    }
1601f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
1602b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    private boolean slideToNextPicture() {
16032c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang        if (mNextBound <= 0) return false;
1604b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        switchToNextImage();
16052c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang        mPositionController.startHorizontalSlide();
1606b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        return true;
1607b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    }
1608676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang
1609b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    private boolean slideToPrevPicture() {
16102c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang        if (mPrevBound >= 0) return false;
1611b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        switchToPrevImage();
16122c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang        mPositionController.startHorizontalSlide();
1613b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        return true;
1614b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    }
1615676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang
1616b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    private static int gapToSide(int imageWidth, int viewWidth) {
1617b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        return Math.max(0, (viewWidth - imageWidth) / 2);
1618b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    }
1619f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
1620b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    ////////////////////////////////////////////////////////////////////////////
1621b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    //  Focus switching
1622b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    ////////////////////////////////////////////////////////////////////////////
1623f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
16247eea4d3ac59aa88d327fc0d58f5e4052f43c54c9Bobby Georgescu    public void switchToImage(int index) {
16257eea4d3ac59aa88d327fc0d58f5e4052f43c54c9Bobby Georgescu        mModel.moveTo(index);
16267eea4d3ac59aa88d327fc0d58f5e4052f43c54c9Bobby Georgescu    }
16277eea4d3ac59aa88d327fc0d58f5e4052f43c54c9Bobby Georgescu
1628b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    private void switchToNextImage() {
1629bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang        mModel.moveTo(mModel.getCurrentIndex() + 1);
1630b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    }
163115b351a22d02e89d882fc9fe32b3f4c512080e0aChih-Chung Chang
1632b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    private void switchToPrevImage() {
1633bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang        mModel.moveTo(mModel.getCurrentIndex() - 1);
1634b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    }
1635cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang
1636160e6d776daab93610b3d12413ad9ff2dd867d8bChih-Chung Chang    private void switchToFirstImage() {
1637bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang        mModel.moveTo(0);
1638160e6d776daab93610b3d12413ad9ff2dd867d8bChih-Chung Chang    }
1639160e6d776daab93610b3d12413ad9ff2dd867d8bChih-Chung Chang
1640b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    ////////////////////////////////////////////////////////////////////////////
1641b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    //  Opening Animation
1642b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    ////////////////////////////////////////////////////////////////////////////
1643b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang
1644b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    public void setOpenAnimationRect(Rect rect) {
1645b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        mPositionController.setOpenAnimationRect(rect);
1646cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang    }
164715b351a22d02e89d882fc9fe32b3f4c512080e0aChih-Chung Chang
1648b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    ////////////////////////////////////////////////////////////////////////////
16492c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang    //  Capture Animation
16502c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang    ////////////////////////////////////////////////////////////////////////////
16512c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang
16522c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang    public boolean switchWithCaptureAnimation(int offset) {
16532c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang        GLRoot root = getGLRoot();
165414ce29ab5608daaa3d0074cdfc9fc85988537ff2Bobby Georgescu        if(root == null) return false;
16552c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang        root.lockRenderThread();
16562c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang        try {
16572c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang            return switchWithCaptureAnimationLocked(offset);
16582c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang        } finally {
16592c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang            root.unlockRenderThread();
16602c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang        }
16612c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang    }
16622c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang
16632c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang    private boolean switchWithCaptureAnimationLocked(int offset) {
16642c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang        if (mHolding != 0) return true;
16652c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang        if (offset == 1) {
16662c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang            if (mNextBound <= 0) return false;
166761f94714c3702115d2f89bb5f8829697be0c3680Chih-Chung Chang            // Temporary disable action bar until the capture animation is done.
166861f94714c3702115d2f89bb5f8829697be0c3680Chih-Chung Chang            if (!mFilmMode) mListener.onActionBarAllowed(false);
16692c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang            switchToNextImage();
16702c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang            mPositionController.startCaptureAnimationSlide(-1);
16712c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang        } else if (offset == -1) {
16722c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang            if (mPrevBound >= 0) return false;
1673c4791b7721a8417be5be33a67c8ade6e82b03a2cChih-Chung Chang            if (mFilmMode) setFilmMode(false);
1674c4791b7721a8417be5be33a67c8ade6e82b03a2cChih-Chung Chang
1675c4791b7721a8417be5be33a67c8ade6e82b03a2cChih-Chung Chang            // If we are too far away from the first image (so that we don't
1676c4791b7721a8417be5be33a67c8ade6e82b03a2cChih-Chung Chang            // have all the ScreenNails in-between), we go directly without
1677c4791b7721a8417be5be33a67c8ade6e82b03a2cChih-Chung Chang            // animation.
1678c4791b7721a8417be5be33a67c8ade6e82b03a2cChih-Chung Chang            if (mModel.getCurrentIndex() > SCREEN_NAIL_MAX) {
1679c4791b7721a8417be5be33a67c8ade6e82b03a2cChih-Chung Chang                switchToFirstImage();
168042e1fed4edd9ed1e326aab2f7969242c56812952Chih-Chung Chang                mPositionController.skipToFinalPosition();
1681c4791b7721a8417be5be33a67c8ade6e82b03a2cChih-Chung Chang                return true;
1682c4791b7721a8417be5be33a67c8ade6e82b03a2cChih-Chung Chang            }
1683c4791b7721a8417be5be33a67c8ade6e82b03a2cChih-Chung Chang
1684160e6d776daab93610b3d12413ad9ff2dd867d8bChih-Chung Chang            switchToFirstImage();
16852c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang            mPositionController.startCaptureAnimationSlide(1);
16862c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang        } else {
16872c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang            return false;
16882c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang        }
16892c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang        mHolding |= HOLD_CAPTURE_ANIMATION;
1690bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang        Message m = mHandler.obtainMessage(MSG_CAPTURE_ANIMATION_DONE, offset, 0);
1691f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang        mHandler.sendMessageDelayed(m, PositionController.CAPTURE_ANIMATION_TIME);
16922c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang        return true;
16932c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang    }
16942c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang
1695bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang    private void captureAnimationDone(int offset) {
16962c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang        mHolding &= ~HOLD_CAPTURE_ANIMATION;
1697e6251df06f0b3c8f556043f8b725b4b19be7474dChih-Chung Chang        if (offset == 1 && !mFilmMode) {
1698e6251df06f0b3c8f556043f8b725b4b19be7474dChih-Chung Chang            // Now the capture animation is done, enable the action bar.
1699e6251df06f0b3c8f556043f8b725b4b19be7474dChih-Chung Chang            mListener.onActionBarAllowed(true);
1700e6251df06f0b3c8f556043f8b725b4b19be7474dChih-Chung Chang            mListener.onActionBarWanted();
1701bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang        }
17022c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang        snapback();
17032c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang    }
17042c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang
17052c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang    ////////////////////////////////////////////////////////////////////////////
1706b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    //  Card deck effect calculation
1707b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    ////////////////////////////////////////////////////////////////////////////
1708b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang
1709cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang    // Returns the scrolling progress value for an object moving out of a
1710cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang    // view. The progress value measures how much the object has moving out of
1711cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang    // the view. The object currently displays in [left, right), and the view is
1712cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang    // at [0, viewWidth].
1713cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang    //
1714cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang    // The returned value is negative when the object is moving right, and
1715cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang    // positive when the object is moving left. The value goes to -1 or 1 when
1716cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang    // the object just moves out of the view completely. The value is 0 if the
1717cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang    // object currently fills the view.
1718cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang    private static float calculateMoveOutProgress(int left, int right,
1719cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang            int viewWidth) {
1720cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang        // w = object width
1721cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang        // viewWidth = view width
1722cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang        int w = right - left;
1723cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang
1724cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang        // If the object width is smaller than the view width,
1725cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang        //      |....view....|
1726cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang        //                   |<-->|      progress = -1 when left = viewWidth
1727bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang        //          |<-->|               progress = 0 when left = viewWidth / 2 - w / 2
1728cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang        // |<-->|                        progress = 1 when left = -w
1729cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang        if (w < viewWidth) {
1730bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang            int zx = viewWidth / 2 - w / 2;
1731bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang            if (left > zx) {
1732bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang                return -(left - zx) / (float) (viewWidth - zx);  // progress = (0, -1]
1733bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang            } else {
1734bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang                return (left - zx) / (float) (-w - zx);  // progress = [0, 1]
1735bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang            }
1736cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang        }
1737cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang
1738cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang        // If the object width is larger than the view width,
1739cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang        //             |..view..|
1740cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang        //                      |<--------->| progress = -1 when left = viewWidth
1741cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang        //             |<--------->|          progress = 0 between left = 0
1742cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang        //          |<--------->|                          and right = viewWidth
1743cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang        // |<--------->|                      progress = 1 when right = 0
1744cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang        if (left > 0) {
1745cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang            return -left / (float) viewWidth;
1746cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang        }
1747cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang
1748cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang        if (right < viewWidth) {
1749cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang            return (viewWidth - right) / (float) viewWidth;
1750cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang        }
1751cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang
1752cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang        return 0;
1753cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang    }
1754cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang
1755cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang    // Maps a scrolling progress value to the alpha factor in the fading
1756cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang    // animation.
1757cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang    private float getScrollAlpha(float scrollProgress) {
1758cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang        return scrollProgress < 0 ? mAlphaInterpolator.getInterpolation(
1759cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang                     1 - Math.abs(scrollProgress)) : 1.0f;
1760cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang    }
1761cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang
1762cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang    // Maps a scrolling progress value to the scaling factor in the fading
1763cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang    // animation.
1764cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang    private float getScrollScale(float scrollProgress) {
1765cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang        float interpolatedProgress = mScaleInterpolator.getInterpolation(
1766cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang                Math.abs(scrollProgress));
1767cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang        float scale = (1 - interpolatedProgress) +
1768cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang                interpolatedProgress * TRANSITION_SCALE_FACTOR;
1769cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang        return scale;
1770cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang    }
1771cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang
1772cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang
1773cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang    // This interpolator emulates the rate at which the perceived scale of an
1774cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang    // object changes as its distance from a camera increases. When this
1775cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang    // interpolator is applied to a scale animation on a view, it evokes the
1776cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang    // sense that the object is shrinking due to moving away from the camera.
1777cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang    private static class ZInterpolator {
1778cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang        private float focalLength;
1779cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang
1780cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang        public ZInterpolator(float foc) {
1781cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang            focalLength = foc;
1782cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang        }
1783cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang
1784cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang        public float getInterpolation(float input) {
1785cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang            return (1.0f - focalLength / (focalLength + input)) /
1786cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang                (1.0f - focalLength / (focalLength + 1.0f));
1787f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
1788f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
1789f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
1790ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang    // Returns an interpolated value for the page/film transition.
1791ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang    // When ratio = 0, the result is from.
1792ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang    // When ratio = 1, the result is to.
1793ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang    private static float interpolate(float ratio, float from, float to) {
1794ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang        return from + (to - from) * ratio * ratio;
1795ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang    }
1796ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang
17976b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang    // Returns the alpha factor in film mode if a picture is not in the center.
17986b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang    // The 0.03 lower bound is to make the item always visible a bit.
17996b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang    private float getOffsetAlpha(float offset) {
18006b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        offset /= 0.5f;
18016b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        float alpha = (offset > 0) ? (1 - offset) : (1 + offset);
18026b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        return Utils.clamp(alpha, 0.03f, 1f);
18036b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang    }
18046b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang
1805b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    ////////////////////////////////////////////////////////////////////////////
1806b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    //  Simple public utilities
1807b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    ////////////////////////////////////////////////////////////////////////////
1808f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
1809bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang    public void setListener(Listener listener) {
1810bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang        mListener = listener;
1811f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
1812616a70fdb4473d2fbd7b70772a3a82b908aeae1eOwen Lin
1813616a70fdb4473d2fbd7b70772a3a82b908aeae1eOwen Lin    public Rect getPhotoRect(int index) {
1814616a70fdb4473d2fbd7b70772a3a82b908aeae1eOwen Lin        return mPositionController.getPosition(index);
1815616a70fdb4473d2fbd7b70772a3a82b908aeae1eOwen Lin    }
1816616a70fdb4473d2fbd7b70772a3a82b908aeae1eOwen Lin
1817616a70fdb4473d2fbd7b70772a3a82b908aeae1eOwen Lin    public PhotoFallbackEffect buildFallbackEffect(GLView root, GLCanvas canvas) {
1818616a70fdb4473d2fbd7b70772a3a82b908aeae1eOwen Lin        Rect location = new Rect();
1819616a70fdb4473d2fbd7b70772a3a82b908aeae1eOwen Lin        Utils.assertTrue(root.getBoundsOf(this, location));
1820616a70fdb4473d2fbd7b70772a3a82b908aeae1eOwen Lin
1821616a70fdb4473d2fbd7b70772a3a82b908aeae1eOwen Lin        Rect fullRect = bounds();
1822616a70fdb4473d2fbd7b70772a3a82b908aeae1eOwen Lin        PhotoFallbackEffect effect = new PhotoFallbackEffect();
1823616a70fdb4473d2fbd7b70772a3a82b908aeae1eOwen Lin        for (int i = -SCREEN_NAIL_MAX; i <= SCREEN_NAIL_MAX; ++i) {
1824616a70fdb4473d2fbd7b70772a3a82b908aeae1eOwen Lin            MediaItem item = mModel.getMediaItem(i);
1825616a70fdb4473d2fbd7b70772a3a82b908aeae1eOwen Lin            if (item == null) continue;
1826616a70fdb4473d2fbd7b70772a3a82b908aeae1eOwen Lin            ScreenNail sc = mModel.getScreenNail(i);
1827030f8dad6aefc42d0af39bc1b93f370937d3e2abOwen Lin            if (!(sc instanceof TiledScreenNail)
1828030f8dad6aefc42d0af39bc1b93f370937d3e2abOwen Lin                    || ((TiledScreenNail) sc).isShowingPlaceholder()) continue;
182949affdc4e274098a34e4eb2dbe4a89a750f1ba7fOwen Lin
183049affdc4e274098a34e4eb2dbe4a89a750f1ba7fOwen Lin            // Now, sc is BitmapScreenNail and is not showing placeholder
1831616a70fdb4473d2fbd7b70772a3a82b908aeae1eOwen Lin            Rect rect = new Rect(getPhotoRect(i));
1832616a70fdb4473d2fbd7b70772a3a82b908aeae1eOwen Lin            if (!Rect.intersects(fullRect, rect)) continue;
1833616a70fdb4473d2fbd7b70772a3a82b908aeae1eOwen Lin            rect.offset(location.left, location.top);
1834616a70fdb4473d2fbd7b70772a3a82b908aeae1eOwen Lin
183538155c4f4dd6f0173337eb9f3fea54803192e229Owen Lin            int width = sc.getWidth();
183638155c4f4dd6f0173337eb9f3fea54803192e229Owen Lin            int height = sc.getHeight();
183738155c4f4dd6f0173337eb9f3fea54803192e229Owen Lin
183838155c4f4dd6f0173337eb9f3fea54803192e229Owen Lin            int rotation = mModel.getImageRotation(i);
183938155c4f4dd6f0173337eb9f3fea54803192e229Owen Lin            RawTexture texture;
184038155c4f4dd6f0173337eb9f3fea54803192e229Owen Lin            if ((rotation % 180) == 0) {
184138155c4f4dd6f0173337eb9f3fea54803192e229Owen Lin                texture = new RawTexture(width, height, true);
184238155c4f4dd6f0173337eb9f3fea54803192e229Owen Lin                canvas.beginRenderTarget(texture);
184338155c4f4dd6f0173337eb9f3fea54803192e229Owen Lin                canvas.translate(width / 2f, height / 2f);
184438155c4f4dd6f0173337eb9f3fea54803192e229Owen Lin            } else {
184538155c4f4dd6f0173337eb9f3fea54803192e229Owen Lin                texture = new RawTexture(height, width, true);
184638155c4f4dd6f0173337eb9f3fea54803192e229Owen Lin                canvas.beginRenderTarget(texture);
184738155c4f4dd6f0173337eb9f3fea54803192e229Owen Lin                canvas.translate(height / 2f, width / 2f);
184838155c4f4dd6f0173337eb9f3fea54803192e229Owen Lin            }
184938155c4f4dd6f0173337eb9f3fea54803192e229Owen Lin
185038155c4f4dd6f0173337eb9f3fea54803192e229Owen Lin            canvas.rotate(rotation, 0, 0, 1);
185138155c4f4dd6f0173337eb9f3fea54803192e229Owen Lin            canvas.translate(-width / 2f, -height / 2f);
185238155c4f4dd6f0173337eb9f3fea54803192e229Owen Lin            sc.draw(canvas, 0, 0, width, height);
1853616a70fdb4473d2fbd7b70772a3a82b908aeae1eOwen Lin            canvas.endRenderTarget();
1854616a70fdb4473d2fbd7b70772a3a82b908aeae1eOwen Lin            effect.addEntry(item.getPath(), rect, texture);
1855616a70fdb4473d2fbd7b70772a3a82b908aeae1eOwen Lin        }
1856616a70fdb4473d2fbd7b70772a3a82b908aeae1eOwen Lin        return effect;
1857616a70fdb4473d2fbd7b70772a3a82b908aeae1eOwen Lin    }
1858f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin}
1859