PhotoView.java revision 49affdc4e274098a34e4eb2dbe4a89a750f1ba7f
1f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin/*
2f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * Copyright (C) 2010 The Android Open Source Project
3f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin *
4f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * Licensed under the Apache License, Version 2.0 (the "License");
5f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * you may not use this file except in compliance with the License.
6f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * You may obtain a copy of the License at
7f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin *
8f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin *      http://www.apache.org/licenses/LICENSE-2.0
9f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin *
10f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * Unless required by applicable law or agreed to in writing, software
11f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * distributed under the License is distributed on an "AS IS" BASIS,
12f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * See the License for the specific language governing permissions and
14f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * limitations under the License.
15f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin */
16f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
17f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linpackage com.android.gallery3d.ui;
18f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
19f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport android.content.Context;
20f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport android.graphics.Color;
212ef46ed28b28b355d7f3f1432c7b1196b832a859Chih-Chung Changimport android.graphics.Matrix;
22cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Changimport android.graphics.Point;
2304ac045bf8da5082bbb0bdc9ea5f9c9b5b796ad0Yuli Huangimport android.graphics.Rect;
24f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport android.os.Message;
25f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport android.view.MotionEvent;
26cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Changimport android.view.animation.AccelerateInterpolator;
27f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
282b3ee0ea07246b859a5b75d8a6102a7cce7ec838Owen Linimport com.android.gallery3d.R;
292b3ee0ea07246b859a5b75d8a6102a7cce7ec838Owen Linimport com.android.gallery3d.app.GalleryActivity;
30cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Changimport com.android.gallery3d.common.Utils;
31616a70fdb4473d2fbd7b70772a3a82b908aeae1eOwen Linimport com.android.gallery3d.data.MediaItem;
32b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Changimport com.android.gallery3d.data.MediaObject;
33b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Changimport com.android.gallery3d.util.RangeArray;
34b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang
35f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linpublic class PhotoView extends GLView {
36f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    @SuppressWarnings("unused")
37f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static final String TAG = "PhotoView";
38c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang    private static final int PLACEHOLDER_COLOR = 0xFF222222;
39f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
40f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public static final int INVALID_SIZE = -1;
41b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    public static final long INVALID_DATA_VERSION =
42b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            MediaObject.INVALID_DATA_VERSION;
43f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
44c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang    public static class Size {
45c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang        public int width;
46c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang        public int height;
47c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang    }
48c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang
49c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang    public interface Model extends TileImageView.Model {
50bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang        public int getCurrentIndex();
51bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang        public void moveTo(int index);
52c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang
53c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang        // Returns the size for the specified picture. If the size information is
54c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang        // not avaiable, width = height = 0.
55c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang        public void getImageSize(int offset, Size size);
56c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang
57616a70fdb4473d2fbd7b70772a3a82b908aeae1eOwen Lin        // Returns the media item for the specified picture.
58616a70fdb4473d2fbd7b70772a3a82b908aeae1eOwen Lin        public MediaItem getMediaItem(int offset);
59616a70fdb4473d2fbd7b70772a3a82b908aeae1eOwen Lin
60c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang        // Returns the rotation for the specified picture.
61c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang        public int getImageRotation(int offset);
62f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
63b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        // This amends the getScreenNail() method of TileImageView.Model to get
64b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        // ScreenNail at previous (negative offset) or next (positive offset)
65b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        // positions. Returns null if the specified ScreenNail is unavailable.
66b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        public ScreenNail getScreenNail(int offset);
67c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang
68c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang        // Set this to true if we need the model to provide full images.
69b8be1e0ad76b6abc0da7ead39f7a9811195d001eChih-Chung Chang        public void setNeedFullImage(boolean enabled);
70bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang
71bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang        // Returns true if the item is the Camera preview.
72bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang        public boolean isCamera(int offset);
73d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang
7443a80fd806247cb50e81bacda3ad21d210e9ae88Angus Kong        // Returns true if the item is the Panorama.
7543a80fd806247cb50e81bacda3ad21d210e9ae88Angus Kong        public boolean isPanorama(int offset);
7643a80fd806247cb50e81bacda3ad21d210e9ae88Angus Kong
77d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang        // Returns true if the item is a Video.
78d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang        public boolean isVideo(int offset);
79f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang
80f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang        public static final int LOADING_INIT = 0;
81f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang        public static final int LOADING_COMPLETE = 1;
82f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang        public static final int LOADING_FAIL = 2;
83f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang
84f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang        public int getLoadingState(int offset);
85b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    }
86b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang
87bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang    public interface Listener {
88b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        public void onSingleTapUp(int x, int y);
89bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang        public void lockOrientation();
90bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang        public void unlockOrientation();
91bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang        public void onFullScreenChanged(boolean full);
9261f94714c3702115d2f89bb5f8829697be0c3680Chih-Chung Chang        public void onActionBarAllowed(boolean allowed);
93bd7c016d728853d38f98a3c6077b7bd9b08858b9Yuli Huang        public void onCurrentImageUpdated();
94b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    }
95f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
96bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang    // Here is a graph showing the places we need to lock/unlock device
97bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang    // orientation:
98bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang    //
99bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang    //           +------------+ A  +------------+
100bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang    // Page mode |   Camera   |<---|   Photo    |
101bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang    //           |  [locked]  |--->| [unlocked] |
102bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang    //           +------------+  B +------------+
103bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang    //                ^                  ^
104bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang    //                | C                | D
105bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang    //           +------------+    +------------+
106bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang    //           |   Camera   |    |   Photo    |
107bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang    // Film mode |    [*]     |    |    [*]     |
108bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang    //           +------------+    +------------+
109bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang    //
110bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang    // In Page mode, we want to lock in Camera because we don't want the system
111bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang    // rotation animation. We also want to unlock in Photo because we want to
112bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang    // show the system action bar in the right place.
113bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang    //
114bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang    // We don't show action bar in Film mode, so it's fine for it to be locked
115bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang    // or unlocked in Film mode.
116bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang    //
117bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang    // There are four transitions we need to check if we need to
118bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang    // lock/unlock. Marked as A to D above and in the code.
119bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang
120b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    private static final int MSG_CANCEL_EXTRA_SCALING = 2;
121b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    private static final int MSG_SWITCH_FOCUS = 3;
1222c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang    private static final int MSG_CAPTURE_ANIMATION_DONE = 4;
123f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
124b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    private static final int MOVE_THRESHOLD = 256;
125f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static final float SWIPE_THRESHOLD = 300f;
126f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
127f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static final float DEFAULT_TEXT_SIZE = 20;
128cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang    private static float TRANSITION_SCALE_FACTOR = 0.74f;
129d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang    private static final int ICON_RATIO = 6;
1302c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang
1312c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang    // whether we want to apply card deck effect in page mode.
132b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    private static final boolean CARD_EFFECT = true;
133cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang
134cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang    // Used to calculate the scaling factor for the fading animation.
135cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang    private ZInterpolator mScaleInterpolator = new ZInterpolator(0.5f);
136cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang
137cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang    // Used to calculate the alpha factor for the fading animation.
138cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang    private AccelerateInterpolator mAlphaInterpolator =
139cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang            new AccelerateInterpolator(0.9f);
140f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
141b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    // We keep this many previous ScreenNails. (also this many next ScreenNails)
142b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    public static final int SCREEN_NAIL_MAX = 3;
143b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang
144b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    // The picture entries, the valid index is from -SCREEN_NAIL_MAX to
145b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    // SCREEN_NAIL_MAX.
146b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    private final RangeArray<Picture> mPictures =
147b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            new RangeArray<Picture>(-SCREEN_NAIL_MAX, SCREEN_NAIL_MAX);
148f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
1496575794a9c09f22d5721e212c093e0a2df376d0cChih-Chung Chang    private final MyGestureListener mGestureListener;
1503a02809c97669a157cf45bfd61d45272110d4091Chih-Chung Chang    private final GestureRecognizer mGestureRecognizer;
151f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private final PositionController mPositionController;
152f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
153bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang    private Listener mListener;
154f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private Model mModel;
155f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private StringTexture mLoadingText;
156f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private StringTexture mNoThumbnailText;
157b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    private TileImageView mTileView;
158532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang    private EdgeView mEdgeView;
159f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private Texture mVideoPlayIcon;
160f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
161f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private SynchronizedHandler mHandler;
162f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
163cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang    private Point mImageCenter = new Point();
164534b12fd804610dd67b8109bc08ba76f31afb33eChih-Chung Chang    private boolean mCancelExtraScalingPending;
165b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    private boolean mFilmMode = false;
166bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang    private int mDisplayRotation = 0;
167bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang    private int mCompensation = 0;
168bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang    private boolean mFullScreen = true;
1692ef46ed28b28b355d7f3f1432c7b1196b832a859Chih-Chung Chang    private Rect mCameraRelativeFrame = new Rect();
170bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang    private Rect mCameraRect = new Rect();
171f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
172c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang    // [mPrevBound, mNextBound] is the range of index for all pictures in the
173c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang    // model, if we assume the index of current focused picture is 0.  So if
174c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang    // there are some previous pictures, mPrevBound < 0, and if there are some
175c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang    // next pictures, mNextBound > 0.
176c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang    private int mPrevBound;
177c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang    private int mNextBound;
178c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang
1792c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang    // This variable prevents us doing snapback until its values goes to 0. This
1802c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang    // happens if the user gesture is still in progress or we are in a capture
1812c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang    // animation.
1822c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang    private int mHolding;
1832c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang    private static final int HOLD_TOUCH_DOWN = 1;
18418958c51f1412d959d52500ceefc46f987d035f3Chih-Chung Chang    private static final int HOLD_CAPTURE_ANIMATION = 2;
1852c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang
186f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public PhotoView(GalleryActivity activity) {
187f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mTileView = new TileImageView(activity);
188f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        addComponent(mTileView);
189f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        Context context = activity.getAndroidContext();
190532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang        mEdgeView = new EdgeView(context);
191532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang        addComponent(mEdgeView);
192f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mLoadingText = StringTexture.newInstance(
193f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                context.getString(R.string.loading),
194f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                DEFAULT_TEXT_SIZE, Color.WHITE);
195f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mNoThumbnailText = StringTexture.newInstance(
196f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                context.getString(R.string.no_thumbnail),
197f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                DEFAULT_TEXT_SIZE, Color.WHITE);
198f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
199b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        mHandler = new MyHandler(activity.getGLRoot());
200f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
2016575794a9c09f22d5721e212c093e0a2df376d0cChih-Chung Chang        mGestureListener = new MyGestureListener();
2026575794a9c09f22d5721e212c093e0a2df376d0cChih-Chung Chang        mGestureRecognizer = new GestureRecognizer(context, mGestureListener);
203f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
204b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        mPositionController = new PositionController(context,
205b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                new PositionController.Listener() {
206b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                    public void invalidate() {
207b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                        PhotoView.this.invalidate();
208b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                    }
2092c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang                    public boolean isHolding() {
21018958c51f1412d959d52500ceefc46f987d035f3Chih-Chung Chang                        return mHolding != 0;
211b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                    }
212b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                    public void onPull(int offset, int direction) {
213b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                        mEdgeView.onPull(offset, direction);
214b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                    }
215b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                    public void onRelease() {
216b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                        mEdgeView.onRelease();
217b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                    }
218b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                    public void onAbsorb(int velocity, int direction) {
219b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                        mEdgeView.onAbsorb(velocity, direction);
220b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                    }
221b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                });
222f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mVideoPlayIcon = new ResourceTexture(context, R.drawable.ic_control_play);
223b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        for (int i = -SCREEN_NAIL_MAX; i <= SCREEN_NAIL_MAX; i++) {
224b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            if (i == 0) {
225b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                mPictures.put(i, new FullPicture());
226cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang            } else {
227b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                mPictures.put(i, new ScreenNailPicture(i));
228cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang            }
229f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
230f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
231f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
232b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    public void setModel(Model model) {
233b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        mModel = model;
234b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        mTileView.setModel(mModel);
235f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
236f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
237b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    class MyHandler extends SynchronizedHandler {
238b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        public MyHandler(GLRoot root) {
239b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            super(root);
240f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
241f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
242b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        @Override
243b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        public void handleMessage(Message message) {
244b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            switch (message.what) {
245b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                case MSG_CANCEL_EXTRA_SCALING: {
246b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                    mGestureRecognizer.cancelScale();
247b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                    mPositionController.setExtraScalingRange(false);
248b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                    mCancelExtraScalingPending = false;
249b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                    break;
250b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                }
251b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                case MSG_SWITCH_FOCUS: {
252b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                    switchFocus();
253b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                    break;
254b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                }
2552c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang                case MSG_CAPTURE_ANIMATION_DONE: {
256bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang                    // message.arg1 is the offset parameter passed to
257bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang                    // switchWithCaptureAnimation().
258bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang                    captureAnimationDone(message.arg1);
2592c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang                    break;
2602c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang                }
261b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                default: throw new AssertionError(message.what);
262f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            }
263f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
264b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    };
265f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
266b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    ////////////////////////////////////////////////////////////////////////////
267b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    //  Data/Image change notifications
268b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    ////////////////////////////////////////////////////////////////////////////
269b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang
270214993dc4abf87c386123af50e3c34184ba11cb6Chih-Chung Chang    public void notifyDataChange(int[] fromIndex, int prevBound, int nextBound) {
271c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang        mPrevBound = prevBound;
272c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang        mNextBound = nextBound;
273c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang
274b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        // Move the boxes
275214993dc4abf87c386123af50e3c34184ba11cb6Chih-Chung Chang        mPositionController.moveBox(fromIndex, mPrevBound < 0, mNextBound > 0,
276bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang                mModel.isCamera(0));
277b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang
278b8be1e0ad76b6abc0da7ead39f7a9811195d001eChih-Chung Chang        // Update the ScreenNails.
279b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        for (int i = -SCREEN_NAIL_MAX; i <= SCREEN_NAIL_MAX; i++) {
280b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            mPictures.get(i).reload();
281b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        }
282b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang
283b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        invalidate();
284f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
285f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
286b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    public void notifyImageChange(int index) {
287bd7c016d728853d38f98a3c6077b7bd9b08858b9Yuli Huang        if (index == 0) {
288bd7c016d728853d38f98a3c6077b7bd9b08858b9Yuli Huang            mListener.onCurrentImageUpdated();
289bd7c016d728853d38f98a3c6077b7bd9b08858b9Yuli Huang        }
290b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        mPictures.get(index).reload();
291b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        invalidate();
292f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
293f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
294bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang    @Override
2952ef46ed28b28b355d7f3f1432c7b1196b832a859Chih-Chung Chang    protected void onLayout(
2962ef46ed28b28b355d7f3f1432c7b1196b832a859Chih-Chung Chang            boolean changeSize, int left, int top, int right, int bottom) {
2972ef46ed28b28b355d7f3f1432c7b1196b832a859Chih-Chung Chang        int w = right - left;
2982ef46ed28b28b355d7f3f1432c7b1196b832a859Chih-Chung Chang        int h = bottom - top;
2992ef46ed28b28b355d7f3f1432c7b1196b832a859Chih-Chung Chang        mTileView.layout(0, 0, w, h);
3002ef46ed28b28b355d7f3f1432c7b1196b832a859Chih-Chung Chang        mEdgeView.layout(0, 0, w, h);
3012ef46ed28b28b355d7f3f1432c7b1196b832a859Chih-Chung Chang
3022ef46ed28b28b355d7f3f1432c7b1196b832a859Chih-Chung Chang        GLRoot root = getGLRoot();
3032ef46ed28b28b355d7f3f1432c7b1196b832a859Chih-Chung Chang        int displayRotation = root.getDisplayRotation();
3042ef46ed28b28b355d7f3f1432c7b1196b832a859Chih-Chung Chang        int compensation = root.getCompensation();
3052ef46ed28b28b355d7f3f1432c7b1196b832a859Chih-Chung Chang        if (mDisplayRotation != displayRotation
3062ef46ed28b28b355d7f3f1432c7b1196b832a859Chih-Chung Chang                || mCompensation != compensation) {
3072ef46ed28b28b355d7f3f1432c7b1196b832a859Chih-Chung Chang            mDisplayRotation = displayRotation;
3082ef46ed28b28b355d7f3f1432c7b1196b832a859Chih-Chung Chang            mCompensation = compensation;
3092ef46ed28b28b355d7f3f1432c7b1196b832a859Chih-Chung Chang
3103b4a8aeb0353fa18a2b5267b3952a80a6c6d4d13Chih-Chung Chang            // We need to change the size and rotation of the Camera ScreenNail,
3113b4a8aeb0353fa18a2b5267b3952a80a6c6d4d13Chih-Chung Chang            // but we don't want it to animate because the size doen't actually
3122ef46ed28b28b355d7f3f1432c7b1196b832a859Chih-Chung Chang            // change in the eye of the user.
3132ef46ed28b28b355d7f3f1432c7b1196b832a859Chih-Chung Chang            for (int i = -SCREEN_NAIL_MAX; i <= SCREEN_NAIL_MAX; i++) {
3142ef46ed28b28b355d7f3f1432c7b1196b832a859Chih-Chung Chang                Picture p = mPictures.get(i);
3152ef46ed28b28b355d7f3f1432c7b1196b832a859Chih-Chung Chang                if (p.isCamera()) {
3162ef46ed28b28b355d7f3f1432c7b1196b832a859Chih-Chung Chang                    p.updateSize(true);
3172ef46ed28b28b355d7f3f1432c7b1196b832a859Chih-Chung Chang                }
318bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang            }
319bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang        }
320bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang
321bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang        updateConstrainedFrame();
322bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang        if (changeSize) {
323bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang            mPositionController.setViewSize(getWidth(), getHeight());
324bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang        }
325bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang    }
326bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang
327bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang    // Update the constrained frame due to layout change.
328bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang    private void updateConstrainedFrame() {
3292ef46ed28b28b355d7f3f1432c7b1196b832a859Chih-Chung Chang        // Get the width and height in framework orientation because the given
3302ef46ed28b28b355d7f3f1432c7b1196b832a859Chih-Chung Chang        // mCameraRelativeFrame is in that coordinates.
331bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang        int w = getWidth();
332bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang        int h = getHeight();
3332ef46ed28b28b355d7f3f1432c7b1196b832a859Chih-Chung Chang        if (mCompensation % 180 != 0) {
334bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang            int tmp = w;
335bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang            w = h;
336bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang            h = tmp;
337bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang        }
3382ef46ed28b28b355d7f3f1432c7b1196b832a859Chih-Chung Chang        int l = mCameraRelativeFrame.left;
3392ef46ed28b28b355d7f3f1432c7b1196b832a859Chih-Chung Chang        int t = mCameraRelativeFrame.top;
3402ef46ed28b28b355d7f3f1432c7b1196b832a859Chih-Chung Chang        int r = mCameraRelativeFrame.right;
3412ef46ed28b28b355d7f3f1432c7b1196b832a859Chih-Chung Chang        int b = mCameraRelativeFrame.bottom;
342bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang
3432ef46ed28b28b355d7f3f1432c7b1196b832a859Chih-Chung Chang        // Now convert it to the coordinates we are using.
3442ef46ed28b28b355d7f3f1432c7b1196b832a859Chih-Chung Chang        switch (mCompensation) {
345bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang            case 0: mCameraRect.set(l, t, r, b); break;
346bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang            case 90: mCameraRect.set(h - b, l, h - t, r); break;
347bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang            case 180: mCameraRect.set(w - r, h - b, w - l, h - t); break;
348bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang            case 270: mCameraRect.set(t, w - r, b, w - l); break;
349bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang        }
350bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang
3512ef46ed28b28b355d7f3f1432c7b1196b832a859Chih-Chung Chang        Log.d(TAG, "compensation = " + mCompensation
3522ef46ed28b28b355d7f3f1432c7b1196b832a859Chih-Chung Chang                + ", CameraRelativeFrame = " + mCameraRelativeFrame
3532ef46ed28b28b355d7f3f1432c7b1196b832a859Chih-Chung Chang                + ", mCameraRect = " + mCameraRect);
354bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang        mPositionController.setConstrainedFrame(mCameraRect);
355bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang    }
356bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang
3572ef46ed28b28b355d7f3f1432c7b1196b832a859Chih-Chung Chang    public void setCameraRelativeFrame(Rect frame) {
3582ef46ed28b28b355d7f3f1432c7b1196b832a859Chih-Chung Chang        mCameraRelativeFrame.set(frame);
3592ef46ed28b28b355d7f3f1432c7b1196b832a859Chih-Chung Chang        updateConstrainedFrame();
360bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang    }
361bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang
362bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang    // Returns the rotation we need to do to the camera texture before drawing
363bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang    // it to the canvas, assuming the camera texture is correct when the device
364bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang    // is in its natural orientation.
365bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang    private int getCameraRotation() {
366bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang        return (mCompensation - mDisplayRotation + 360) % 360;
367bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang    }
368bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang
36943a80fd806247cb50e81bacda3ad21d210e9ae88Angus Kong    private int getPanoramaRotation() {
37043a80fd806247cb50e81bacda3ad21d210e9ae88Angus Kong        return mCompensation;
37143a80fd806247cb50e81bacda3ad21d210e9ae88Angus Kong    }
37243a80fd806247cb50e81bacda3ad21d210e9ae88Angus Kong
373b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    ////////////////////////////////////////////////////////////////////////////
374b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    //  Pictures
375b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    ////////////////////////////////////////////////////////////////////////////
376b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang
377b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    private interface Picture {
378b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        void reload();
379b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        void draw(GLCanvas canvas, Rect r);
380b8be1e0ad76b6abc0da7ead39f7a9811195d001eChih-Chung Chang        void setScreenNail(ScreenNail s);
3812c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang        boolean isCamera();  // whether the picture is a camera preview
382bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang        void updateSize(boolean force);  // called when mCompensation changes
383b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    };
384f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
385b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    class FullPicture implements Picture {
386b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        private int mRotation;
3872c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang        private boolean mIsCamera;
38843a80fd806247cb50e81bacda3ad21d210e9ae88Angus Kong        private boolean mIsPanorama;
389d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang        private boolean mIsVideo;
390f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang        private int mLoadingState = Model.LOADING_INIT;
391bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang        private boolean mWasCameraCenter;
392b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang
393b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        public void FullPicture(TileImageView tileView) {
394b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            mTileView = tileView;
395f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
396f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
397b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        @Override
398b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        public void reload() {
399b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            // mImageWidth and mImageHeight will get updated
400b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            mTileView.notifyModelInvalidated();
401b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang
402bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang            mIsCamera = mModel.isCamera(0);
40343a80fd806247cb50e81bacda3ad21d210e9ae88Angus Kong            mIsPanorama = mModel.isPanorama(0);
404d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang            mIsVideo = mModel.isVideo(0);
405f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang            mLoadingState = mModel.getLoadingState(0);
406bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang            setScreenNail(mModel.getScreenNail(0));
407bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang            updateSize(false);
408bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang        }
409bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang
410bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang        @Override
411bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang        public void updateSize(boolean force) {
41243a80fd806247cb50e81bacda3ad21d210e9ae88Angus Kong            if (mIsPanorama) {
41343a80fd806247cb50e81bacda3ad21d210e9ae88Angus Kong                mRotation = getPanoramaRotation();
41443a80fd806247cb50e81bacda3ad21d210e9ae88Angus Kong            } else if (mIsCamera) {
415bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang                mRotation = getCameraRotation();
416bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang            } else {
417bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang                mRotation = mModel.getImageRotation(0);
418bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang            }
419bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang
420c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang            int w = mTileView.mImageWidth;
421c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang            int h = mTileView.mImageHeight;
422c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang            mPositionController.setImageSize(0,
423c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang                    getRotated(mRotation, w, h),
424bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang                    getRotated(mRotation, h, w),
425bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang                    force);
426f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
427f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
428b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        @Override
429b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        public void draw(GLCanvas canvas, Rect r) {
430f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang            drawTileView(canvas, r);
431cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang
432f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang            boolean isCenter = mPositionController.isCenter();
433aeb3062e84ed28f0e25a73f511f287ae0a3e4294Chih-Chung Chang            if (mIsCamera) {
434aeb3062e84ed28f0e25a73f511f287ae0a3e4294Chih-Chung Chang                boolean full = !mFilmMode && isCenter
435aeb3062e84ed28f0e25a73f511f287ae0a3e4294Chih-Chung Chang                        && mPositionController.isAtMinimalScale();
436aeb3062e84ed28f0e25a73f511f287ae0a3e4294Chih-Chung Chang                if (full != mFullScreen) {
437aeb3062e84ed28f0e25a73f511f287ae0a3e4294Chih-Chung Chang                    mFullScreen = full;
438aeb3062e84ed28f0e25a73f511f287ae0a3e4294Chih-Chung Chang                    mListener.onFullScreenChanged(full);
439aeb3062e84ed28f0e25a73f511f287ae0a3e4294Chih-Chung Chang                }
440aeb3062e84ed28f0e25a73f511f287ae0a3e4294Chih-Chung Chang            }
441aeb3062e84ed28f0e25a73f511f287ae0a3e4294Chih-Chung Chang
442bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang            // We want to have the following transitions:
4432c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang            // (1) Move camera preview out of its place: switch to film mode
4442c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang            // (2) Move camera preview into its place: switch to page mode
4452c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang            // The extra mWasCenter check makes sure (1) does not apply if in
4462c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang            // page mode, we move _to_ the camera preview from another picture.
447bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang
448bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang            // Holdings except touch-down prevent the transitions.
44918958c51f1412d959d52500ceefc46f987d035f3Chih-Chung Chang            if ((mHolding & ~HOLD_TOUCH_DOWN) != 0) return;
450bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang
451bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang            boolean isCameraCenter = mIsCamera && isCenter;
452bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang
453bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang            if (mWasCameraCenter && mIsCamera && !isCenter && !mFilmMode) {
45418958c51f1412d959d52500ceefc46f987d035f3Chih-Chung Chang                // Temporary disabled to de-emphasize filmstrip.
45518958c51f1412d959d52500ceefc46f987d035f3Chih-Chung Chang                // setFilmMode(true);
45618958c51f1412d959d52500ceefc46f987d035f3Chih-Chung Chang            } else if (!mWasCameraCenter && isCameraCenter && mFilmMode) {
457bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang                setFilmMode(false);
458aeb3062e84ed28f0e25a73f511f287ae0a3e4294Chih-Chung Chang            }
459aeb3062e84ed28f0e25a73f511f287ae0a3e4294Chih-Chung Chang
460aeb3062e84ed28f0e25a73f511f287ae0a3e4294Chih-Chung Chang            if (isCenter && !mFilmMode) {
461aeb3062e84ed28f0e25a73f511f287ae0a3e4294Chih-Chung Chang                if (mIsCamera) {
462aeb3062e84ed28f0e25a73f511f287ae0a3e4294Chih-Chung Chang                    // move into camera, lock
463aeb3062e84ed28f0e25a73f511f287ae0a3e4294Chih-Chung Chang                    mListener.lockOrientation();  // Transition A
464aeb3062e84ed28f0e25a73f511f287ae0a3e4294Chih-Chung Chang                } else {
465aeb3062e84ed28f0e25a73f511f287ae0a3e4294Chih-Chung Chang                    // move out of camera, unlock
466aeb3062e84ed28f0e25a73f511f287ae0a3e4294Chih-Chung Chang                    mListener.unlockOrientation();  // Transition B
467aeb3062e84ed28f0e25a73f511f287ae0a3e4294Chih-Chung Chang                }
468bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang            }
469bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang
470bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang            mWasCameraCenter = isCameraCenter;
471cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang        }
472cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang
473b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        @Override
474b8be1e0ad76b6abc0da7ead39f7a9811195d001eChih-Chung Chang        public void setScreenNail(ScreenNail s) {
475b8be1e0ad76b6abc0da7ead39f7a9811195d001eChih-Chung Chang            mTileView.setScreenNail(s);
476cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang        }
477f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
478b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        @Override
4792c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang        public boolean isCamera() {
4802c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang            return mIsCamera;
481f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
482f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
483d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang        private void drawTileView(GLCanvas canvas, Rect r) {
484ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang            float imageScale = mPositionController.getImageScale();
485b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            int viewW = getWidth();
486b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            int viewH = getHeight();
487d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang            float cx = r.exactCenterX();
488d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang            float cy = r.exactCenterY();
489ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang            float scale = 1f;  // the scaling factor due to card effect
490b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang
491d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang            canvas.save(GLCanvas.SAVE_FLAG_MATRIX | GLCanvas.SAVE_FLAG_ALPHA);
492ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang            float filmRatio = mPositionController.getFilmRatio();
493ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang            boolean wantsCardEffect = CARD_EFFECT && !mIsCamera
494f320b8429dd5c68c117b8e9b1a36a435f4f4ff13Yuli Huang                    && filmRatio != 1f && !mPictures.get(-1).isCamera()
495f320b8429dd5c68c117b8e9b1a36a435f4f4ff13Yuli Huang                    && !mPositionController.inOpeningAnimation();
4962c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang            if (wantsCardEffect) {
497b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                // Calculate the move-out progress value.
498b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                int left = r.left;
499b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                int right = r.right;
500b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                float progress = calculateMoveOutProgress(left, right, viewW);
501b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                progress = Utils.clamp(progress, -1f, 1f);
502b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang
503b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                // We only want to apply the fading animation if the scrolling
504b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                // movement is to the right.
505bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang                if (progress < 0) {
506ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang                    scale = getScrollScale(progress);
507ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang                    float alpha = getScrollAlpha(progress);
508ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang                    scale = interpolate(filmRatio, scale, 1f);
509ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang                    alpha = interpolate(filmRatio, alpha, 1f);
510d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang
511ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang                    imageScale *= scale;
512ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang                    canvas.multiplyAlpha(alpha);
513ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang
514ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang                    float cxPage;  // the cx value in page mode
515d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang                    if (right - left <= viewW) {
516b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                        // If the picture is narrower than the view, keep it at
517b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                        // the center of the view.
518ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang                        cxPage = viewW / 2f;
519b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                    } else {
520b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                        // If the picture is wider than the view (it's
521b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                        // zoomed-in), keep the left edge of the object align
522b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                        // the the left edge of the view.
523ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang                        cxPage = (right - left) * scale / 2f;
524b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                    }
525ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang                    cx = interpolate(filmRatio, cxPage, cx);
526b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                }
527b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            }
528f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
529d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang            // Draw the tile view.
530ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang            setTileViewPosition(cx, cy, viewW, viewH, imageScale);
531d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang            PhotoView.super.render(canvas);
532d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang
533f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang            // Draw the play video icon and the message.
534f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang            canvas.translate((int) (cx + 0.5f), (int) (cy + 0.5f));
535f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang            int s = (int) (scale * Math.min(r.width(), r.height()) + 0.5f);
536f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang            if (mIsVideo) drawVideoPlayIcon(canvas, s);
537f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang            if (mLoadingState == Model.LOADING_FAIL) {
538f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang                drawLoadingFailMessage(canvas);
539d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang            }
540d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang
541f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang            // Draw a debug indicator showing which picture has focus (index ==
542f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang            // 0).
543f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang            //canvas.fillRect(-10, -10, 20, 20, 0x80FF00FF);
544f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang
545d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang            canvas.restore();
546d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang        }
547d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang
548d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang        // Set the position of the tile view
549d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang        private void setTileViewPosition(float cx, float cy,
550d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang                int viewW, int viewH, float scale) {
551d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang            // Find out the bitmap coordinates of the center of the view
552d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang            int imageW = mPositionController.getImageWidth();
553d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang            int imageH = mPositionController.getImageHeight();
554d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang            int centerX = (int) (imageW / 2f + (viewW / 2f - cx) / scale + 0.5f);
555d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang            int centerY = (int) (imageH / 2f + (viewH / 2f - cy) / scale + 0.5f);
556d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang
557b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            int inverseX = imageW - centerX;
558b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            int inverseY = imageH - centerY;
559d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang            int x, y;
560d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang            switch (mRotation) {
561d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang                case 0: x = centerX; y = centerY; break;
562d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang                case 90: x = centerY; y = inverseX; break;
563d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang                case 180: x = inverseX; y = inverseY; break;
564d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang                case 270: x = inverseY; y = centerX; break;
565b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                default:
566d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang                    throw new RuntimeException(String.valueOf(mRotation));
567b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            }
568d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang            mTileView.setPosition(x, y, scale, mRotation);
569f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
570f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
571f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
572b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    private class ScreenNailPicture implements Picture {
573b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        private int mIndex;
574b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        private int mRotation;
575b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        private ScreenNail mScreenNail;
576c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang        private Size mSize = new Size();
5772c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang        private boolean mIsCamera;
57843a80fd806247cb50e81bacda3ad21d210e9ae88Angus Kong        private boolean mIsPanorama;
579d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang        private boolean mIsVideo;
580f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang        private int mLoadingState = Model.LOADING_INIT;
581f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
582b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        public ScreenNailPicture(int index) {
583b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            mIndex = index;
584b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        }
585532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang
586b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        @Override
587b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        public void reload() {
588bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang            mIsCamera = mModel.isCamera(mIndex);
58943a80fd806247cb50e81bacda3ad21d210e9ae88Angus Kong            mIsPanorama = mModel.isPanorama(mIndex);
590d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang            mIsVideo = mModel.isVideo(mIndex);
591f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang            mLoadingState = mModel.getLoadingState(mIndex);
592c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang            setScreenNail(mModel.getScreenNail(mIndex));
593b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        }
594b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang
595b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        @Override
596b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        public void draw(GLCanvas canvas, Rect r) {
597b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            if (mScreenNail == null) {
598f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang                // Draw a placeholder rectange if there should be a picture in
599f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang                // this position (but somehow there isn't).
600c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang                if (mIndex >= mPrevBound && mIndex <= mNextBound) {
601f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang                    drawPlaceHolder(canvas, r);
602c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang                }
603b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                return;
604f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            }
605b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            if (r.left >= getWidth() || r.right <= 0 ||
606b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                    r.top >= getHeight() || r.bottom <= 0) {
607b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                mScreenNail.noDraw();
608b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                return;
609f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            }
610f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
611bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang            if (mIsCamera && mFullScreen != false) {
612bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang                mFullScreen = false;
613bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang                mListener.onFullScreenChanged(false);
614bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang            }
615bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang
616ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang            float filmRatio = mPositionController.getFilmRatio();
617ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang            boolean wantsCardEffect = CARD_EFFECT && mIndex > 0
618ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang                    && filmRatio != 1f && !mPictures.get(0).isCamera();
619b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            int w = getWidth();
620ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang            int cx = wantsCardEffect
621ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang                    ? (int) (interpolate(filmRatio, w / 2, r.centerX()) + 0.5f)
622ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang                    : r.centerX();
623b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            int cy = r.centerY();
624ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang            canvas.save(GLCanvas.SAVE_FLAG_MATRIX | GLCanvas.SAVE_FLAG_ALPHA);
625b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            canvas.translate(cx, cy);
6262c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang            if (wantsCardEffect) {
627b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                float progress = (float) (w / 2 - r.centerX()) / w;
628b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                progress = Utils.clamp(progress, -1, 1);
629b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                float alpha = getScrollAlpha(progress);
630b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                float scale = getScrollScale(progress);
631ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang                alpha = interpolate(filmRatio, alpha, 1f);
632ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang                scale = interpolate(filmRatio, scale, 1f);
633b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                canvas.multiplyAlpha(alpha);
634b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                canvas.scale(scale, scale, 1);
635b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            }
636b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            if (mRotation != 0) {
637b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                canvas.rotate(mRotation, 0, 0, 1);
638b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            }
639ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang            int drawW = getRotated(mRotation, r.width(), r.height());
640ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang            int drawH = getRotated(mRotation, r.height(), r.width());
641b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            mScreenNail.draw(canvas, -drawW / 2, -drawH / 2, drawW, drawH);
642f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang            if (isScreenNailAnimating()) {
643f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang                invalidate();
644f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang            }
645f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang            int s = Math.min(drawW, drawH);
646f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang            if (mIsVideo) drawVideoPlayIcon(canvas, s);
647f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang            if (mLoadingState == Model.LOADING_FAIL) {
648f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang                drawLoadingFailMessage(canvas);
649f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang            }
650b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            canvas.restore();
651b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        }
652f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
653f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang        private boolean isScreenNailAnimating() {
654f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang            return (mScreenNail instanceof BitmapScreenNail)
655f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang                    && ((BitmapScreenNail) mScreenNail).isAnimating();
656f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang        }
657f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang
658b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        @Override
659b8be1e0ad76b6abc0da7ead39f7a9811195d001eChih-Chung Chang        public void setScreenNail(ScreenNail s) {
660b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            if (mScreenNail == s) return;
661b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            mScreenNail = s;
662bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang            updateSize(false);
663bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang        }
664bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang
665bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang        @Override
666bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang        public void updateSize(boolean force) {
66743a80fd806247cb50e81bacda3ad21d210e9ae88Angus Kong            if (mIsPanorama) {
66843a80fd806247cb50e81bacda3ad21d210e9ae88Angus Kong                mRotation = getPanoramaRotation();
66943a80fd806247cb50e81bacda3ad21d210e9ae88Angus Kong            } else if (mIsCamera) {
670bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang                mRotation = getCameraRotation();
671bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang            } else {
672bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang                mRotation = mModel.getImageRotation(mIndex);
673bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang            }
674c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang
675c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang            int w = 0, h = 0;
676b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            if (mScreenNail != null) {
677bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang                w = mScreenNail.getWidth();
678bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang                h = mScreenNail.getHeight();
679c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang            } else if (mModel != null) {
680c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang                // If we don't have ScreenNail available, we can still try to
681c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang                // get the size information of it.
682c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang                mModel.getImageSize(mIndex, mSize);
683c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang                w = mSize.width;
684c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang                h = mSize.height;
685b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            }
686c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang
687c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang            if (w != 0 && h != 0)  {
688b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                mPositionController.setImageSize(mIndex,
689b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                        getRotated(mRotation, w, h),
690bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang                        getRotated(mRotation, h, w),
691bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang                        force);
692b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            }
693f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
694b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang
695b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        @Override
6962c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang        public boolean isCamera() {
6972c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang            return mIsCamera;
698b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        }
699b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    }
700b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang
701f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang    // Draw a gray placeholder in the specified rectangle.
702f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang    private void drawPlaceHolder(GLCanvas canvas, Rect r) {
703f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang        canvas.fillRect(r.left, r.top, r.width(), r.height(), PLACEHOLDER_COLOR);
704f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang    }
705f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang
706d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang    // Draw the video play icon (in the place where the spinner was)
707d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang    private void drawVideoPlayIcon(GLCanvas canvas, int side) {
708d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang        int s = side / ICON_RATIO;
709d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang        // Draw the video play icon at the center
710d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang        mVideoPlayIcon.draw(canvas, -s / 2, -s / 2, s, s);
711d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang    }
712d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang
713f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang    // Draw the "no thumbnail" message
714f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang    private void drawLoadingFailMessage(GLCanvas canvas) {
715f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang        StringTexture m = mNoThumbnailText;
716f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang        m.draw(canvas, -m.getWidth() / 2, -m.getHeight() / 2);
717f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang    }
718f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang
719b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    private static int getRotated(int degree, int original, int theother) {
720b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        return (degree % 180 == 0) ? original : theother;
721b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    }
722b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang
723b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    ////////////////////////////////////////////////////////////////////////////
724b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    //  Gestures Handling
725b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    ////////////////////////////////////////////////////////////////////////////
726b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang
727b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    @Override
728b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    protected boolean onTouch(MotionEvent event) {
729b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        mGestureRecognizer.onTouchEvent(event);
730b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        return true;
731f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
732f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
7333a02809c97669a157cf45bfd61d45272110d4091Chih-Chung Chang    private class MyGestureListener implements GestureRecognizer.Listener {
7343a02809c97669a157cf45bfd61d45272110d4091Chih-Chung Chang        private boolean mIgnoreUpEvent = false;
735099989b310d84fe13eff0cdf2902bb3fb0bcbd14Chih-Chung Chang        // If we can change mode for this scale gesture.
736099989b310d84fe13eff0cdf2902bb3fb0bcbd14Chih-Chung Chang        private boolean mCanChangeMode;
73718958c51f1412d959d52500ceefc46f987d035f3Chih-Chung Chang        // If we have changed the film mode in this scaling gesture.
738b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        private boolean mModeChanged;
73933f8567dd5003e4bb342683f3768ab7552648b02Chih-Chung Chang        // If this scaling gesture should be ignored.
74033f8567dd5003e4bb342683f3768ab7552648b02Chih-Chung Chang        private boolean mIgnoreScalingGesture;
74117ffedda6e3ed57b38afbb775594cf30d83fc652Chih-Chung Chang        // whether the down action happened while the view is scrolling.
74217ffedda6e3ed57b38afbb775594cf30d83fc652Chih-Chung Chang        private boolean mDownInScrolling;
7436575794a9c09f22d5721e212c093e0a2df376d0cChih-Chung Chang        // If we should ignore all gestures other than onSingleTapUp.
7446575794a9c09f22d5721e212c093e0a2df376d0cChih-Chung Chang        private boolean mIgnoreSwipingGesture;
745b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang
7463a02809c97669a157cf45bfd61d45272110d4091Chih-Chung Chang        @Override
7473a02809c97669a157cf45bfd61d45272110d4091Chih-Chung Chang        public boolean onSingleTapUp(float x, float y) {
748ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang            // We do this in addition to onUp() because we want the snapback of
749ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang            // setFilmMode to happen.
750ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang            mHolding &= ~HOLD_TOUCH_DOWN;
751ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang
75217ffedda6e3ed57b38afbb775594cf30d83fc652Chih-Chung Chang            if (mFilmMode && !mDownInScrolling) {
75317ffedda6e3ed57b38afbb775594cf30d83fc652Chih-Chung Chang                switchToHitPicture((int) (x + 0.5f), (int) (y + 0.5f));
754b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                setFilmMode(false);
75517ffedda6e3ed57b38afbb775594cf30d83fc652Chih-Chung Chang                mIgnoreUpEvent = true;
756b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                return true;
757b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            }
758b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang
759bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang            if (mListener != null) {
7602ef46ed28b28b355d7f3f1432c7b1196b832a859Chih-Chung Chang                // Do the inverse transform of the touch coordinates.
7612ef46ed28b28b355d7f3f1432c7b1196b832a859Chih-Chung Chang                Matrix m = getGLRoot().getCompensationMatrix();
7622ef46ed28b28b355d7f3f1432c7b1196b832a859Chih-Chung Chang                Matrix inv = new Matrix();
7632ef46ed28b28b355d7f3f1432c7b1196b832a859Chih-Chung Chang                m.invert(inv);
7642ef46ed28b28b355d7f3f1432c7b1196b832a859Chih-Chung Chang                float[] pts = new float[] {x, y};
7652ef46ed28b28b355d7f3f1432c7b1196b832a859Chih-Chung Chang                inv.mapPoints(pts);
7662ef46ed28b28b355d7f3f1432c7b1196b832a859Chih-Chung Chang                mListener.onSingleTapUp((int) (pts[0] + 0.5f), (int) (pts[1] + 0.5f));
7673a02809c97669a157cf45bfd61d45272110d4091Chih-Chung Chang            }
7683a02809c97669a157cf45bfd61d45272110d4091Chih-Chung Chang            return true;
7693a02809c97669a157cf45bfd61d45272110d4091Chih-Chung Chang        }
7703a02809c97669a157cf45bfd61d45272110d4091Chih-Chung Chang
7713a02809c97669a157cf45bfd61d45272110d4091Chih-Chung Chang        @Override
7723a02809c97669a157cf45bfd61d45272110d4091Chih-Chung Chang        public boolean onDoubleTap(float x, float y) {
7736575794a9c09f22d5721e212c093e0a2df376d0cChih-Chung Chang            if (mIgnoreSwipingGesture) return true;
77461f94714c3702115d2f89bb5f8829697be0c3680Chih-Chung Chang            if (mPictures.get(0).isCamera()) return false;
7753a02809c97669a157cf45bfd61d45272110d4091Chih-Chung Chang            PositionController controller = mPositionController;
776b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            float scale = controller.getImageScale();
7773a02809c97669a157cf45bfd61d45272110d4091Chih-Chung Chang            // onDoubleTap happened on the second ACTION_DOWN.
7783a02809c97669a157cf45bfd61d45272110d4091Chih-Chung Chang            // We need to ignore the next UP event.
7793a02809c97669a157cf45bfd61d45272110d4091Chih-Chung Chang            mIgnoreUpEvent = true;
7803a02809c97669a157cf45bfd61d45272110d4091Chih-Chung Chang            if (scale <= 1.0f || controller.isAtMinimalScale()) {
7813a02809c97669a157cf45bfd61d45272110d4091Chih-Chung Chang                controller.zoomIn(x, y, Math.max(1.5f, scale * 1.5f));
7823a02809c97669a157cf45bfd61d45272110d4091Chih-Chung Chang            } else {
7833a02809c97669a157cf45bfd61d45272110d4091Chih-Chung Chang                controller.resetToFullView();
7843a02809c97669a157cf45bfd61d45272110d4091Chih-Chung Chang            }
7853a02809c97669a157cf45bfd61d45272110d4091Chih-Chung Chang            return true;
7863a02809c97669a157cf45bfd61d45272110d4091Chih-Chung Chang        }
787f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
788f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        @Override
7893a02809c97669a157cf45bfd61d45272110d4091Chih-Chung Chang        public boolean onScroll(float dx, float dy) {
7906575794a9c09f22d5721e212c093e0a2df376d0cChih-Chung Chang            if (mIgnoreSwipingGesture) return true;
791b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            mPositionController.startScroll(-dx, -dy);
792f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            return true;
793f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
794f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
795f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        @Override
7963a02809c97669a157cf45bfd61d45272110d4091Chih-Chung Chang        public boolean onFling(float velocityX, float velocityY) {
7976575794a9c09f22d5721e212c093e0a2df376d0cChih-Chung Chang            if (mIgnoreSwipingGesture) return true;
7982ce3c3bfe08fff5aee58007cc8ba8f4a50861ae2Yuli Huang            if (swipeImages(velocityX, velocityY)) {
799b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang                mIgnoreUpEvent = true;
800b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang            } else if (mPositionController.fling(velocityX, velocityY)) {
801b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang                mIgnoreUpEvent = true;
802f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            }
803f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            return true;
804f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
805f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
806f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        @Override
8073a02809c97669a157cf45bfd61d45272110d4091Chih-Chung Chang        public boolean onScaleBegin(float focusX, float focusY) {
8086575794a9c09f22d5721e212c093e0a2df376d0cChih-Chung Chang            if (mIgnoreSwipingGesture) return true;
80933f8567dd5003e4bb342683f3768ab7552648b02Chih-Chung Chang            // We ignore the scaling gesture if it is a camera preview.
81033f8567dd5003e4bb342683f3768ab7552648b02Chih-Chung Chang            mIgnoreScalingGesture = mPictures.get(0).isCamera();
81133f8567dd5003e4bb342683f3768ab7552648b02Chih-Chung Chang            if (mIgnoreScalingGesture) {
81233f8567dd5003e4bb342683f3768ab7552648b02Chih-Chung Chang                return true;
81333f8567dd5003e4bb342683f3768ab7552648b02Chih-Chung Chang            }
8143a02809c97669a157cf45bfd61d45272110d4091Chih-Chung Chang            mPositionController.beginScale(focusX, focusY);
815099989b310d84fe13eff0cdf2902bb3fb0bcbd14Chih-Chung Chang            // We can change mode if we are in film mode, or we are in page
816099989b310d84fe13eff0cdf2902bb3fb0bcbd14Chih-Chung Chang            // mode and at minimal scale.
817099989b310d84fe13eff0cdf2902bb3fb0bcbd14Chih-Chung Chang            mCanChangeMode = mFilmMode
818099989b310d84fe13eff0cdf2902bb3fb0bcbd14Chih-Chung Chang                    || mPositionController.isAtMinimalScale();
819b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            mModeChanged = false;
820f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            return true;
821f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
822f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
823f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        @Override
8243a02809c97669a157cf45bfd61d45272110d4091Chih-Chung Chang        public boolean onScale(float focusX, float focusY, float scale) {
8256575794a9c09f22d5721e212c093e0a2df376d0cChih-Chung Chang            if (mIgnoreSwipingGesture) return true;
8266575794a9c09f22d5721e212c093e0a2df376d0cChih-Chung Chang            if (mIgnoreScalingGesture) return true;
82718958c51f1412d959d52500ceefc46f987d035f3Chih-Chung Chang            if (mModeChanged) return true;
828b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            if (Float.isNaN(scale) || Float.isInfinite(scale)) return false;
82933f8567dd5003e4bb342683f3768ab7552648b02Chih-Chung Chang
83033f8567dd5003e4bb342683f3768ab7552648b02Chih-Chung Chang            // We wait for the scale change accumulated to a large enough change
83133f8567dd5003e4bb342683f3768ab7552648b02Chih-Chung Chang            // before reacting to it. Otherwise we may mistakenly treat a
83233f8567dd5003e4bb342683f3768ab7552648b02Chih-Chung Chang            // zoom-in gesture as zoom-out or vice versa.
83318958c51f1412d959d52500ceefc46f987d035f3Chih-Chung Chang            if (scale > 0.99f && scale < 1.01f) return false;
83433f8567dd5003e4bb342683f3768ab7552648b02Chih-Chung Chang
835b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            int outOfRange = mPositionController.scaleBy(scale, focusX, focusY);
836b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang
83718958c51f1412d959d52500ceefc46f987d035f3Chih-Chung Chang            // If mode changes, we treat this scaling gesture has ended.
83818958c51f1412d959d52500ceefc46f987d035f3Chih-Chung Chang            if (mCanChangeMode) {
839b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                if ((outOfRange < 0 && !mFilmMode) ||
840b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                        (outOfRange > 0 && mFilmMode)) {
84118958c51f1412d959d52500ceefc46f987d035f3Chih-Chung Chang                    stopExtraScalingIfNeeded();
84218958c51f1412d959d52500ceefc46f987d035f3Chih-Chung Chang
84318958c51f1412d959d52500ceefc46f987d035f3Chih-Chung Chang                    // Removing the touch down flag allows snapback to happen
84433f8567dd5003e4bb342683f3768ab7552648b02Chih-Chung Chang                    // for film mode change.
84518958c51f1412d959d52500ceefc46f987d035f3Chih-Chung Chang                    mHolding &= ~HOLD_TOUCH_DOWN;
846b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                    setFilmMode(!mFilmMode);
84718958c51f1412d959d52500ceefc46f987d035f3Chih-Chung Chang
84818958c51f1412d959d52500ceefc46f987d035f3Chih-Chung Chang                    // We need to call onScaleEnd() before setting mModeChanged
84918958c51f1412d959d52500ceefc46f987d035f3Chih-Chung Chang                    // to true.
85018958c51f1412d959d52500ceefc46f987d035f3Chih-Chung Chang                    onScaleEnd();
851b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                    mModeChanged = true;
852b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                    return true;
853534b12fd804610dd67b8109bc08ba76f31afb33eChih-Chung Chang                }
854b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang           }
855b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang
85618958c51f1412d959d52500ceefc46f987d035f3Chih-Chung Chang            if (outOfRange != 0) {
857b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                startExtraScalingIfNeeded();
858534b12fd804610dd67b8109bc08ba76f31afb33eChih-Chung Chang            } else {
859b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                stopExtraScalingIfNeeded();
860534b12fd804610dd67b8109bc08ba76f31afb33eChih-Chung Chang            }
861f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            return true;
862f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
863f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
86433f8567dd5003e4bb342683f3768ab7552648b02Chih-Chung Chang        @Override
86533f8567dd5003e4bb342683f3768ab7552648b02Chih-Chung Chang        public void onScaleEnd() {
8666575794a9c09f22d5721e212c093e0a2df376d0cChih-Chung Chang            if (mIgnoreSwipingGesture) return;
8676575794a9c09f22d5721e212c093e0a2df376d0cChih-Chung Chang            if (mIgnoreScalingGesture) return;
86833f8567dd5003e4bb342683f3768ab7552648b02Chih-Chung Chang            if (mModeChanged) return;
86933f8567dd5003e4bb342683f3768ab7552648b02Chih-Chung Chang            mPositionController.endScale();
87033f8567dd5003e4bb342683f3768ab7552648b02Chih-Chung Chang        }
87133f8567dd5003e4bb342683f3768ab7552648b02Chih-Chung Chang
872b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        private void startExtraScalingIfNeeded() {
873b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            if (!mCancelExtraScalingPending) {
874b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                mHandler.sendEmptyMessageDelayed(
875b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                        MSG_CANCEL_EXTRA_SCALING, 700);
876b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                mPositionController.setExtraScalingRange(true);
877b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                mCancelExtraScalingPending = true;
878b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            }
879b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        }
880b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang
881b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        private void stopExtraScalingIfNeeded() {
882b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            if (mCancelExtraScalingPending) {
883b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                mHandler.removeMessages(MSG_CANCEL_EXTRA_SCALING);
884b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                mPositionController.setExtraScalingRange(false);
885b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                mCancelExtraScalingPending = false;
886b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            }
887b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        }
888b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang
889f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        @Override
8903a02809c97669a157cf45bfd61d45272110d4091Chih-Chung Chang        public void onDown() {
8916575794a9c09f22d5721e212c093e0a2df376d0cChih-Chung Chang            if (mIgnoreSwipingGesture) return;
8926575794a9c09f22d5721e212c093e0a2df376d0cChih-Chung Chang
8932c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang            mHolding |= HOLD_TOUCH_DOWN;
89417ffedda6e3ed57b38afbb775594cf30d83fc652Chih-Chung Chang
89517ffedda6e3ed57b38afbb775594cf30d83fc652Chih-Chung Chang            if (mFilmMode && mPositionController.isScrolling()) {
89617ffedda6e3ed57b38afbb775594cf30d83fc652Chih-Chung Chang                mDownInScrolling = true;
89717ffedda6e3ed57b38afbb775594cf30d83fc652Chih-Chung Chang                mPositionController.stopScrolling();
89817ffedda6e3ed57b38afbb775594cf30d83fc652Chih-Chung Chang            } else {
89917ffedda6e3ed57b38afbb775594cf30d83fc652Chih-Chung Chang                mDownInScrolling = false;
90017ffedda6e3ed57b38afbb775594cf30d83fc652Chih-Chung Chang            }
9013a02809c97669a157cf45bfd61d45272110d4091Chih-Chung Chang        }
9023a02809c97669a157cf45bfd61d45272110d4091Chih-Chung Chang
9033a02809c97669a157cf45bfd61d45272110d4091Chih-Chung Chang        @Override
9043a02809c97669a157cf45bfd61d45272110d4091Chih-Chung Chang        public void onUp() {
9056575794a9c09f22d5721e212c093e0a2df376d0cChih-Chung Chang            if (mIgnoreSwipingGesture) return;
9066575794a9c09f22d5721e212c093e0a2df376d0cChih-Chung Chang
90718958c51f1412d959d52500ceefc46f987d035f3Chih-Chung Chang            mHolding &= ~HOLD_TOUCH_DOWN;
9083a02809c97669a157cf45bfd61d45272110d4091Chih-Chung Chang            mEdgeView.onRelease();
9093a02809c97669a157cf45bfd61d45272110d4091Chih-Chung Chang
9103a02809c97669a157cf45bfd61d45272110d4091Chih-Chung Chang            if (mIgnoreUpEvent) {
9113a02809c97669a157cf45bfd61d45272110d4091Chih-Chung Chang                mIgnoreUpEvent = false;
9123a02809c97669a157cf45bfd61d45272110d4091Chih-Chung Chang                return;
9133a02809c97669a157cf45bfd61d45272110d4091Chih-Chung Chang            }
914b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang
9152c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang            snapback();
916f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
9176575794a9c09f22d5721e212c093e0a2df376d0cChih-Chung Chang
9186575794a9c09f22d5721e212c093e0a2df376d0cChih-Chung Chang        public void setSwipingEnabled(boolean enabled) {
9196575794a9c09f22d5721e212c093e0a2df376d0cChih-Chung Chang            mIgnoreSwipingGesture = !enabled;
9206575794a9c09f22d5721e212c093e0a2df376d0cChih-Chung Chang        }
9216575794a9c09f22d5721e212c093e0a2df376d0cChih-Chung Chang    }
9226575794a9c09f22d5721e212c093e0a2df376d0cChih-Chung Chang
9236575794a9c09f22d5721e212c093e0a2df376d0cChih-Chung Chang    public void setSwipingEnabled(boolean enabled) {
9246575794a9c09f22d5721e212c093e0a2df376d0cChih-Chung Chang        mGestureListener.setSwipingEnabled(enabled);
925f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
926f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
927b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    private void setFilmMode(boolean enabled) {
928b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        if (mFilmMode == enabled) return;
929b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        mFilmMode = enabled;
930b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        mPositionController.setFilmMode(mFilmMode);
931b8be1e0ad76b6abc0da7ead39f7a9811195d001eChih-Chung Chang        mModel.setNeedFullImage(!enabled);
93261f94714c3702115d2f89bb5f8829697be0c3680Chih-Chung Chang        mListener.onActionBarAllowed(!enabled);
933bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang
934bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang        // If we leave filmstrip mode, we should lock/unlock
935bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang        if (!enabled) {
936bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang            if (mPictures.get(0).isCamera()) {
937bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang                mListener.lockOrientation();  // Transition C
938bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang            } else {
939bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang                mListener.unlockOrientation();  // Transition D
940bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang            }
941bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang        }
942bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang    }
943bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang
944bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang    public boolean getFilmMode() {
945bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang        return mFilmMode;
946f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
947f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
948b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    ////////////////////////////////////////////////////////////////////////////
949b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    //  Framework events
950b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    ////////////////////////////////////////////////////////////////////////////
951b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang
952b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    public void pause() {
953b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        mPositionController.skipAnimation();
954b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        mTileView.freeTextures();
955b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        for (int i = -SCREEN_NAIL_MAX; i <= SCREEN_NAIL_MAX; i++) {
956b8be1e0ad76b6abc0da7ead39f7a9811195d001eChih-Chung Chang            mPictures.get(i).setScreenNail(null);
957b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        }
958f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
959f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
960b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    public void resume() {
961b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        mTileView.prepareTextures();
962f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
963f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
96433f8567dd5003e4bb342683f3768ab7552648b02Chih-Chung Chang    // move to the camera preview and show controls after resume
96533f8567dd5003e4bb342683f3768ab7552648b02Chih-Chung Chang    public void resetToFirstPicture() {
96633f8567dd5003e4bb342683f3768ab7552648b02Chih-Chung Chang        mModel.moveTo(0);
96733f8567dd5003e4bb342683f3768ab7552648b02Chih-Chung Chang        setFilmMode(false);
96833f8567dd5003e4bb342683f3768ab7552648b02Chih-Chung Chang    }
96933f8567dd5003e4bb342683f3768ab7552648b02Chih-Chung Chang
970b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    ////////////////////////////////////////////////////////////////////////////
971b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    //  Rendering
972b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    ////////////////////////////////////////////////////////////////////////////
973b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang
974b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    @Override
975b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    protected void render(GLCanvas canvas) {
976c4791b7721a8417be5be33a67c8ade6e82b03a2cChih-Chung Chang        // In page mode, we draw only one previous/next photo. But if we are
977c4791b7721a8417be5be33a67c8ade6e82b03a2cChih-Chung Chang        // doing capture animation, we want to draw all photos.
978c4791b7721a8417be5be33a67c8ade6e82b03a2cChih-Chung Chang        boolean inPageMode = (mPositionController.getFilmRatio() == 0f);
979c4791b7721a8417be5be33a67c8ade6e82b03a2cChih-Chung Chang        boolean inCaptureAnimation = ((mHolding & HOLD_CAPTURE_ANIMATION) != 0);
980c4791b7721a8417be5be33a67c8ade6e82b03a2cChih-Chung Chang        boolean drawOneNeighborOnly = inPageMode && !inCaptureAnimation;
981c4791b7721a8417be5be33a67c8ade6e82b03a2cChih-Chung Chang        int neighbors = drawOneNeighborOnly ? 1 : SCREEN_NAIL_MAX;
982c4791b7721a8417be5be33a67c8ade6e82b03a2cChih-Chung Chang
983c4791b7721a8417be5be33a67c8ade6e82b03a2cChih-Chung Chang        // Draw photos from back to front
984c4791b7721a8417be5be33a67c8ade6e82b03a2cChih-Chung Chang        for (int i = neighbors; i >= -neighbors; i--) {
985b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            Rect r = mPositionController.getPosition(i);
986b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            mPictures.get(i).draw(canvas, r);
987b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        }
988b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang
989b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        mPositionController.advanceAnimation();
990b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        checkFocusSwitching();
991ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang    }
992ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang
993b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    ////////////////////////////////////////////////////////////////////////////
994b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    //  Film mode focus switching
995b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    ////////////////////////////////////////////////////////////////////////////
996f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
997b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    // Runs in GL thread.
998b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    private void checkFocusSwitching() {
999b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        if (!mFilmMode) return;
1000b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        if (mHandler.hasMessages(MSG_SWITCH_FOCUS)) return;
1001b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        if (switchPosition() != 0) {
1002b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            mHandler.sendEmptyMessage(MSG_SWITCH_FOCUS);
1003f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
1004f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
1005f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
1006b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    // Runs in main thread.
1007b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    private void switchFocus() {
10082c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang        if (mHolding != 0) return;
1009b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        switch (switchPosition()) {
1010b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            case -1:
1011b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                switchToPrevImage();
1012b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                break;
1013b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            case 1:
1014b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                switchToNextImage();
1015b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                break;
1016b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        }
1017f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
1018f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
1019b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    // Returns -1 if we should switch focus to the previous picture, +1 if we
1020b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    // should switch to the next, 0 otherwise.
1021b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    private int switchPosition() {
1022b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        Rect curr = mPositionController.getPosition(0);
1023b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        int center = getWidth() / 2;
1024f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
10252c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang        if (curr.left > center && mPrevBound < 0) {
1026b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            Rect prev = mPositionController.getPosition(-1);
1027b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            int currDist = curr.left - center;
1028b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            int prevDist = center - prev.right;
1029b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            if (prevDist < currDist) {
1030b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                return -1;
1031b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            }
10322c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang        } else if (curr.right < center && mNextBound > 0) {
1033b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            Rect next = mPositionController.getPosition(1);
1034b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            int currDist = center - curr.right;
1035b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            int nextDist = next.left - center;
1036b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            if (nextDist < currDist) {
1037b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                return 1;
1038b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            }
1039b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        }
1040f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
1041b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        return 0;
1042f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
1043f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
104417ffedda6e3ed57b38afbb775594cf30d83fc652Chih-Chung Chang    // Switch to the previous or next picture if the hit position is inside
104517ffedda6e3ed57b38afbb775594cf30d83fc652Chih-Chung Chang    // one of their boxes. This runs in main thread.
104617ffedda6e3ed57b38afbb775594cf30d83fc652Chih-Chung Chang    private void switchToHitPicture(int x, int y) {
104717ffedda6e3ed57b38afbb775594cf30d83fc652Chih-Chung Chang        if (mPrevBound < 0) {
104817ffedda6e3ed57b38afbb775594cf30d83fc652Chih-Chung Chang            Rect r = mPositionController.getPosition(-1);
104917ffedda6e3ed57b38afbb775594cf30d83fc652Chih-Chung Chang            if (r.right >= x) {
105017ffedda6e3ed57b38afbb775594cf30d83fc652Chih-Chung Chang                slideToPrevPicture();
105117ffedda6e3ed57b38afbb775594cf30d83fc652Chih-Chung Chang                return;
105217ffedda6e3ed57b38afbb775594cf30d83fc652Chih-Chung Chang            }
105317ffedda6e3ed57b38afbb775594cf30d83fc652Chih-Chung Chang        }
105417ffedda6e3ed57b38afbb775594cf30d83fc652Chih-Chung Chang
105517ffedda6e3ed57b38afbb775594cf30d83fc652Chih-Chung Chang        if (mNextBound > 0) {
105617ffedda6e3ed57b38afbb775594cf30d83fc652Chih-Chung Chang            Rect r = mPositionController.getPosition(1);
105717ffedda6e3ed57b38afbb775594cf30d83fc652Chih-Chung Chang            if (r.left <= x) {
105817ffedda6e3ed57b38afbb775594cf30d83fc652Chih-Chung Chang                slideToNextPicture();
105917ffedda6e3ed57b38afbb775594cf30d83fc652Chih-Chung Chang                return;
106017ffedda6e3ed57b38afbb775594cf30d83fc652Chih-Chung Chang            }
106117ffedda6e3ed57b38afbb775594cf30d83fc652Chih-Chung Chang        }
106217ffedda6e3ed57b38afbb775594cf30d83fc652Chih-Chung Chang    }
106317ffedda6e3ed57b38afbb775594cf30d83fc652Chih-Chung Chang
1064b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    ////////////////////////////////////////////////////////////////////////////
1065b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    //  Page mode focus switching
1066b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    //
1067b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    //  We slide image to the next one or the previous one in two cases: 1: If
1068b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    //  the user did a fling gesture with enough velocity.  2 If the user has
1069b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    //  moved the picture a lot.
1070b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    ////////////////////////////////////////////////////////////////////////////
1071f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
1072b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    private boolean swipeImages(float velocityX, float velocityY) {
1073b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        if (mFilmMode) return false;
1074f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
1075b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        // Avoid swiping images if we're possibly flinging to view the
1076b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        // zoomed in picture vertically.
1077b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        PositionController controller = mPositionController;
1078b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        boolean isMinimal = controller.isAtMinimalScale();
1079b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        int edges = controller.getImageAtEdges();
1080b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        if (!isMinimal && Math.abs(velocityY) > Math.abs(velocityX))
1081b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            if ((edges & PositionController.IMAGE_AT_TOP_EDGE) == 0
1082b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                    || (edges & PositionController.IMAGE_AT_BOTTOM_EDGE) == 0)
1083b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                return false;
1084f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
1085b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        // If we are at the edge of the current photo and the sweeping velocity
1086b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        // exceeds the threshold, slide to the next / previous image.
1087b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        if (velocityX < -SWIPE_THRESHOLD && (isMinimal
1088b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                || (edges & PositionController.IMAGE_AT_RIGHT_EDGE) != 0)) {
1089b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            return slideToNextPicture();
1090b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        } else if (velocityX > SWIPE_THRESHOLD && (isMinimal
1091b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                || (edges & PositionController.IMAGE_AT_LEFT_EDGE) != 0)) {
1092b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            return slideToPrevPicture();
1093f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
1094f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
1095b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        return false;
1096b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    }
1097b29a27f475a2c449abdda8d4e03d30914feed8c6Chih-Chung Chang
10982c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang    private void snapback() {
10992c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang        if (mHolding != 0) return;
11002c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang        if (!snapToNeighborImage()) {
11012c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang            mPositionController.snapback();
11022c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang        }
11032c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang    }
11042c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang
1105b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    private boolean snapToNeighborImage() {
1106b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        if (mFilmMode) return false;
1107f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
1108b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        Rect r = mPositionController.getPosition(0);
1109b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        int viewW = getWidth();
1110b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        int threshold = MOVE_THRESHOLD + gapToSide(r.width(), viewW);
1111f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
1112b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        // If we have moved the picture a lot, switching.
1113b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        if (viewW - r.right > threshold) {
1114b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            return slideToNextPicture();
1115b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        } else if (r.left > threshold) {
1116b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            return slideToPrevPicture();
1117f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
1118f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
1119b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        return false;
1120b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    }
1121f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
1122b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    private boolean slideToNextPicture() {
11232c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang        if (mNextBound <= 0) return false;
1124b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        switchToNextImage();
11252c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang        mPositionController.startHorizontalSlide();
1126b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        return true;
1127b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    }
1128676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang
1129b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    private boolean slideToPrevPicture() {
11302c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang        if (mPrevBound >= 0) return false;
1131b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        switchToPrevImage();
11322c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang        mPositionController.startHorizontalSlide();
1133b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        return true;
1134b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    }
1135676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang
1136b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    private static int gapToSide(int imageWidth, int viewWidth) {
1137b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        return Math.max(0, (viewWidth - imageWidth) / 2);
1138b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    }
1139f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
1140b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    ////////////////////////////////////////////////////////////////////////////
1141b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    //  Focus switching
1142b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    ////////////////////////////////////////////////////////////////////////////
1143f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
1144b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    private void switchToNextImage() {
1145bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang        mModel.moveTo(mModel.getCurrentIndex() + 1);
1146b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    }
114715b351a22d02e89d882fc9fe32b3f4c512080e0aChih-Chung Chang
1148b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    private void switchToPrevImage() {
1149bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang        mModel.moveTo(mModel.getCurrentIndex() - 1);
1150b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    }
1151cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang
1152160e6d776daab93610b3d12413ad9ff2dd867d8bChih-Chung Chang    private void switchToFirstImage() {
1153bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang        mModel.moveTo(0);
1154160e6d776daab93610b3d12413ad9ff2dd867d8bChih-Chung Chang    }
1155160e6d776daab93610b3d12413ad9ff2dd867d8bChih-Chung Chang
1156b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    ////////////////////////////////////////////////////////////////////////////
1157b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    //  Opening Animation
1158b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    ////////////////////////////////////////////////////////////////////////////
1159b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang
1160b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    public void setOpenAnimationRect(Rect rect) {
1161b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        mPositionController.setOpenAnimationRect(rect);
1162cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang    }
116315b351a22d02e89d882fc9fe32b3f4c512080e0aChih-Chung Chang
1164b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    ////////////////////////////////////////////////////////////////////////////
11652c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang    //  Capture Animation
11662c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang    ////////////////////////////////////////////////////////////////////////////
11672c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang
11682c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang    public boolean switchWithCaptureAnimation(int offset) {
11692c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang        GLRoot root = getGLRoot();
11702c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang        root.lockRenderThread();
11712c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang        try {
11722c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang            return switchWithCaptureAnimationLocked(offset);
11732c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang        } finally {
11742c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang            root.unlockRenderThread();
11752c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang        }
11762c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang    }
11772c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang
11782c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang    private boolean switchWithCaptureAnimationLocked(int offset) {
11792c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang        if (mHolding != 0) return true;
11802c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang        if (offset == 1) {
11812c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang            if (mNextBound <= 0) return false;
118261f94714c3702115d2f89bb5f8829697be0c3680Chih-Chung Chang            // Temporary disable action bar until the capture animation is done.
118361f94714c3702115d2f89bb5f8829697be0c3680Chih-Chung Chang            if (!mFilmMode) mListener.onActionBarAllowed(false);
11842c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang            switchToNextImage();
11852c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang            mPositionController.startCaptureAnimationSlide(-1);
11862c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang        } else if (offset == -1) {
11872c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang            if (mPrevBound >= 0) return false;
1188c4791b7721a8417be5be33a67c8ade6e82b03a2cChih-Chung Chang            if (mFilmMode) setFilmMode(false);
1189c4791b7721a8417be5be33a67c8ade6e82b03a2cChih-Chung Chang
1190c4791b7721a8417be5be33a67c8ade6e82b03a2cChih-Chung Chang            // If we are too far away from the first image (so that we don't
1191c4791b7721a8417be5be33a67c8ade6e82b03a2cChih-Chung Chang            // have all the ScreenNails in-between), we go directly without
1192c4791b7721a8417be5be33a67c8ade6e82b03a2cChih-Chung Chang            // animation.
1193c4791b7721a8417be5be33a67c8ade6e82b03a2cChih-Chung Chang            if (mModel.getCurrentIndex() > SCREEN_NAIL_MAX) {
1194c4791b7721a8417be5be33a67c8ade6e82b03a2cChih-Chung Chang                switchToFirstImage();
1195c4791b7721a8417be5be33a67c8ade6e82b03a2cChih-Chung Chang                mPositionController.skipAnimation();
1196c4791b7721a8417be5be33a67c8ade6e82b03a2cChih-Chung Chang                return true;
1197c4791b7721a8417be5be33a67c8ade6e82b03a2cChih-Chung Chang            }
1198c4791b7721a8417be5be33a67c8ade6e82b03a2cChih-Chung Chang
1199160e6d776daab93610b3d12413ad9ff2dd867d8bChih-Chung Chang            switchToFirstImage();
12002c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang            mPositionController.startCaptureAnimationSlide(1);
12012c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang        } else {
12022c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang            return false;
12032c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang        }
12042c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang        mHolding |= HOLD_CAPTURE_ANIMATION;
1205bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang        Message m = mHandler.obtainMessage(MSG_CAPTURE_ANIMATION_DONE, offset, 0);
1206f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang        mHandler.sendMessageDelayed(m, PositionController.CAPTURE_ANIMATION_TIME);
12072c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang        return true;
12082c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang    }
12092c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang
1210bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang    private void captureAnimationDone(int offset) {
12112c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang        mHolding &= ~HOLD_CAPTURE_ANIMATION;
1212bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang        if (offset == 1) {
1213bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang            // move out of camera, unlock
121461f94714c3702115d2f89bb5f8829697be0c3680Chih-Chung Chang            if (!mFilmMode) {
121561f94714c3702115d2f89bb5f8829697be0c3680Chih-Chung Chang                // Now the capture animation is done, enable the action bar.
121661f94714c3702115d2f89bb5f8829697be0c3680Chih-Chung Chang                mListener.onActionBarAllowed(true);
121761f94714c3702115d2f89bb5f8829697be0c3680Chih-Chung Chang            }
1218bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang        }
12192c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang        snapback();
12202c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang    }
12212c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang
12222c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang    ////////////////////////////////////////////////////////////////////////////
1223b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    //  Card deck effect calculation
1224b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    ////////////////////////////////////////////////////////////////////////////
1225b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang
1226cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang    // Returns the scrolling progress value for an object moving out of a
1227cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang    // view. The progress value measures how much the object has moving out of
1228cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang    // the view. The object currently displays in [left, right), and the view is
1229cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang    // at [0, viewWidth].
1230cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang    //
1231cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang    // The returned value is negative when the object is moving right, and
1232cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang    // positive when the object is moving left. The value goes to -1 or 1 when
1233cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang    // the object just moves out of the view completely. The value is 0 if the
1234cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang    // object currently fills the view.
1235cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang    private static float calculateMoveOutProgress(int left, int right,
1236cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang            int viewWidth) {
1237cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang        // w = object width
1238cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang        // viewWidth = view width
1239cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang        int w = right - left;
1240cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang
1241cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang        // If the object width is smaller than the view width,
1242cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang        //      |....view....|
1243cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang        //                   |<-->|      progress = -1 when left = viewWidth
1244bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang        //          |<-->|               progress = 0 when left = viewWidth / 2 - w / 2
1245cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang        // |<-->|                        progress = 1 when left = -w
1246cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang        if (w < viewWidth) {
1247bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang            int zx = viewWidth / 2 - w / 2;
1248bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang            if (left > zx) {
1249bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang                return -(left - zx) / (float) (viewWidth - zx);  // progress = (0, -1]
1250bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang            } else {
1251bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang                return (left - zx) / (float) (-w - zx);  // progress = [0, 1]
1252bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang            }
1253cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang        }
1254cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang
1255cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang        // If the object width is larger than the view width,
1256cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang        //             |..view..|
1257cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang        //                      |<--------->| progress = -1 when left = viewWidth
1258cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang        //             |<--------->|          progress = 0 between left = 0
1259cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang        //          |<--------->|                          and right = viewWidth
1260cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang        // |<--------->|                      progress = 1 when right = 0
1261cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang        if (left > 0) {
1262cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang            return -left / (float) viewWidth;
1263cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang        }
1264cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang
1265cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang        if (right < viewWidth) {
1266cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang            return (viewWidth - right) / (float) viewWidth;
1267cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang        }
1268cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang
1269cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang        return 0;
1270cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang    }
1271cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang
1272cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang    // Maps a scrolling progress value to the alpha factor in the fading
1273cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang    // animation.
1274cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang    private float getScrollAlpha(float scrollProgress) {
1275cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang        return scrollProgress < 0 ? mAlphaInterpolator.getInterpolation(
1276cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang                     1 - Math.abs(scrollProgress)) : 1.0f;
1277cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang    }
1278cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang
1279cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang    // Maps a scrolling progress value to the scaling factor in the fading
1280cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang    // animation.
1281cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang    private float getScrollScale(float scrollProgress) {
1282cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang        float interpolatedProgress = mScaleInterpolator.getInterpolation(
1283cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang                Math.abs(scrollProgress));
1284cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang        float scale = (1 - interpolatedProgress) +
1285cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang                interpolatedProgress * TRANSITION_SCALE_FACTOR;
1286cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang        return scale;
1287cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang    }
1288cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang
1289cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang
1290cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang    // This interpolator emulates the rate at which the perceived scale of an
1291cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang    // object changes as its distance from a camera increases. When this
1292cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang    // interpolator is applied to a scale animation on a view, it evokes the
1293cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang    // sense that the object is shrinking due to moving away from the camera.
1294cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang    private static class ZInterpolator {
1295cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang        private float focalLength;
1296cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang
1297cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang        public ZInterpolator(float foc) {
1298cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang            focalLength = foc;
1299cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang        }
1300cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang
1301cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang        public float getInterpolation(float input) {
1302cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang            return (1.0f - focalLength / (focalLength + input)) /
1303cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang                (1.0f - focalLength / (focalLength + 1.0f));
1304f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
1305f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
1306f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
1307ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang    // Returns an interpolated value for the page/film transition.
1308ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang    // When ratio = 0, the result is from.
1309ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang    // When ratio = 1, the result is to.
1310ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang    private static float interpolate(float ratio, float from, float to) {
1311ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang        return from + (to - from) * ratio * ratio;
1312ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang    }
1313ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang
1314b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    ////////////////////////////////////////////////////////////////////////////
1315b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    //  Simple public utilities
1316b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    ////////////////////////////////////////////////////////////////////////////
1317f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
1318bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang    public void setListener(Listener listener) {
1319bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang        mListener = listener;
1320f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
1321616a70fdb4473d2fbd7b70772a3a82b908aeae1eOwen Lin
1322616a70fdb4473d2fbd7b70772a3a82b908aeae1eOwen Lin    public Rect getPhotoRect(int index) {
1323616a70fdb4473d2fbd7b70772a3a82b908aeae1eOwen Lin        return mPositionController.getPosition(index);
1324616a70fdb4473d2fbd7b70772a3a82b908aeae1eOwen Lin    }
1325616a70fdb4473d2fbd7b70772a3a82b908aeae1eOwen Lin
1326616a70fdb4473d2fbd7b70772a3a82b908aeae1eOwen Lin
1327616a70fdb4473d2fbd7b70772a3a82b908aeae1eOwen Lin    public PhotoFallbackEffect buildFallbackEffect(GLView root, GLCanvas canvas) {
1328616a70fdb4473d2fbd7b70772a3a82b908aeae1eOwen Lin        Rect location = new Rect();
1329616a70fdb4473d2fbd7b70772a3a82b908aeae1eOwen Lin        Utils.assertTrue(root.getBoundsOf(this, location));
1330616a70fdb4473d2fbd7b70772a3a82b908aeae1eOwen Lin
1331616a70fdb4473d2fbd7b70772a3a82b908aeae1eOwen Lin        Rect fullRect = bounds();
1332616a70fdb4473d2fbd7b70772a3a82b908aeae1eOwen Lin        PhotoFallbackEffect effect = new PhotoFallbackEffect();
1333616a70fdb4473d2fbd7b70772a3a82b908aeae1eOwen Lin        for (int i = -SCREEN_NAIL_MAX; i <= SCREEN_NAIL_MAX; ++i) {
1334616a70fdb4473d2fbd7b70772a3a82b908aeae1eOwen Lin            MediaItem item = mModel.getMediaItem(i);
1335616a70fdb4473d2fbd7b70772a3a82b908aeae1eOwen Lin            if (item == null) continue;
1336616a70fdb4473d2fbd7b70772a3a82b908aeae1eOwen Lin            ScreenNail sc = mModel.getScreenNail(i);
133749affdc4e274098a34e4eb2dbe4a89a750f1ba7fOwen Lin            if (!(sc instanceof BitmapScreenNail)
133849affdc4e274098a34e4eb2dbe4a89a750f1ba7fOwen Lin                    || ((BitmapScreenNail) sc).isShowingPlaceholder()) continue;
133949affdc4e274098a34e4eb2dbe4a89a750f1ba7fOwen Lin
134049affdc4e274098a34e4eb2dbe4a89a750f1ba7fOwen Lin            // Now, sc is BitmapScreenNail and is not showing placeholder
1341616a70fdb4473d2fbd7b70772a3a82b908aeae1eOwen Lin            Rect rect = new Rect(getPhotoRect(i));
1342616a70fdb4473d2fbd7b70772a3a82b908aeae1eOwen Lin            if (!Rect.intersects(fullRect, rect)) continue;
1343616a70fdb4473d2fbd7b70772a3a82b908aeae1eOwen Lin            rect.offset(location.left, location.top);
1344616a70fdb4473d2fbd7b70772a3a82b908aeae1eOwen Lin
1345616a70fdb4473d2fbd7b70772a3a82b908aeae1eOwen Lin            RawTexture texture = new RawTexture(sc.getWidth(), sc.getHeight(), true);
1346616a70fdb4473d2fbd7b70772a3a82b908aeae1eOwen Lin            canvas.beginRenderTarget(texture);
1347616a70fdb4473d2fbd7b70772a3a82b908aeae1eOwen Lin            sc.draw(canvas, 0, 0, sc.getWidth(), sc.getHeight());
1348616a70fdb4473d2fbd7b70772a3a82b908aeae1eOwen Lin            canvas.endRenderTarget();
1349616a70fdb4473d2fbd7b70772a3a82b908aeae1eOwen Lin            effect.addEntry(item.getPath(), rect, texture);
1350616a70fdb4473d2fbd7b70772a3a82b908aeae1eOwen Lin        }
1351616a70fdb4473d2fbd7b70772a3a82b908aeae1eOwen Lin        return effect;
1352616a70fdb4473d2fbd7b70772a3a82b908aeae1eOwen Lin    }
1353f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin}
1354