149b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong/*
249b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong * Copyright (C) 2013 The Android Open Source Project
349b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong *
449b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong * Licensed under the Apache License, Version 2.0 (the "License");
549b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong * you may not use this file except in compliance with the License.
649b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong * You may obtain a copy of the License at
749b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong *
849b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong *      http://www.apache.org/licenses/LICENSE-2.0
949b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong *
1049b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong * Unless required by applicable law or agreed to in writing, software
1149b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong * distributed under the License is distributed on an "AS IS" BASIS,
1249b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1349b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong * See the License for the specific language governing permissions and
1449b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong * limitations under the License.
1549b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong */
1649b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong
1701054e922aa547b937a71131ad04c6bd15356240Angus Kongpackage com.android.camera.widget;
1849b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong
1949b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kongimport android.animation.Animator;
20d70f0fb68064e170fb2cf041a707ed04d68040f9Doris Liuimport android.animation.AnimatorSet;
21e78541f2edf2f02660c34660114e76d53b32ab8bAngus Kongimport android.animation.TimeInterpolator;
2249b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kongimport android.animation.ValueAnimator;
2349b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kongimport android.content.Context;
2449b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kongimport android.graphics.Canvas;
25c195e7a9208c90d7a55e184d4b2b9c747e9c90f1Angus Kongimport android.graphics.Point;
2649b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kongimport android.graphics.Rect;
27a6a6a730074e9fedd56c41cd8fa8f1212cfac108Angus Kongimport android.graphics.RectF;
288de13111cc4e62da3462ea321d18c7951282e0d0Doris Liuimport android.net.Uri;
29893ca79bdea899fe3f3ed700c22235cc5b460527Spike Spragueimport android.os.Bundle;
30b21215a1a59812592380f31891becedbdec7468bAngus Kongimport android.os.Handler;
31bcd1152e8214b1fb4a17963ffe8c292681c81688Sam Juddimport android.os.SystemClock;
3249b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kongimport android.util.AttributeSet;
333179f6a49516833761be5155c4e1244ac0aa17feDoris Liuimport android.util.DisplayMetrics;
3443bf03f6e7f9a71b7553e7282b5799798997caf7Sam Juddimport android.util.SparseArray;
3549b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kongimport android.view.MotionEvent;
3649b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kongimport android.view.View;
3749b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kongimport android.view.ViewGroup;
383f192ccf03d0d25521b96304c4464b4bad28150aSascha Haeberlingimport android.view.accessibility.AccessibilityNodeInfo;
3949b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kongimport android.view.animation.DecelerateInterpolator;
4049b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kongimport android.widget.Scroller;
4149b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong
4288ef7664ba6a888fa5da8693091674e152b56192Sascha Haeberlingimport com.android.camera.CameraActivity;
438ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohdeimport com.android.camera.data.FilmstripItem;
448ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohdeimport com.android.camera.data.FilmstripItem.VideoClickedCallback;
452bca210e5fc8a77685775ffb403096167b017dceAngus Kongimport com.android.camera.debug.Log;
46628481532e1e58a0a26330d238e850aec5657bceAngus Kongimport com.android.camera.filmstrip.FilmstripController;
478ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohdeimport com.android.camera.filmstrip.FilmstripDataAdapter;
4801054e922aa547b937a71131ad04c6bd15356240Angus Kongimport com.android.camera.ui.FilmstripGestureRecognizer;
4901054e922aa547b937a71131ad04c6bd15356240Angus Kongimport com.android.camera.ui.ZoomView;
50c195e7a9208c90d7a55e184d4b2b9c747e9c90f1Angus Kongimport com.android.camera.util.CameraUtil;
518e963a5a6016d246184ed65906f9d103e92b17e2Sascha Haeberlingimport com.android.camera2.R;
52f1f5186f7d3fd8bf35009d9e65a43914c664d82bSascha Haeberling
53770c2b3e82326d6272b1d2f65a65e5330c951abbSascha Haeberlingimport java.lang.ref.WeakReference;
5443bf03f6e7f9a71b7553e7282b5799798997caf7Sam Juddimport java.util.ArrayDeque;
553f969c1735e2636bf22dfe44104d0e99924cca97Alan Newbergerimport java.util.Arrays;
5643bf03f6e7f9a71b7553e7282b5799798997caf7Sam Juddimport java.util.Queue;
573f969c1735e2636bf22dfe44104d0e99924cca97Alan Newberger
58b2510252fb020f3ccb88787c870930427377b9dfAngus Kongpublic class FilmstripView extends ViewGroup {
59770c2b3e82326d6272b1d2f65a65e5330c951abbSascha Haeberling    /**
60770c2b3e82326d6272b1d2f65a65e5330c951abbSascha Haeberling     * An action callback to be used for actions on the local media data items.
61770c2b3e82326d6272b1d2f65a65e5330c951abbSascha Haeberling     */
628ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde    public static class PlayVideoIntent implements VideoClickedCallback {
638ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde        private final WeakReference<CameraActivity> mActivity;
64770c2b3e82326d6272b1d2f65a65e5330c951abbSascha Haeberling
65770c2b3e82326d6272b1d2f65a65e5330c951abbSascha Haeberling        /**
66770c2b3e82326d6272b1d2f65a65e5330c951abbSascha Haeberling         * The given activity is used to start intents. It is wrapped in a weak
67770c2b3e82326d6272b1d2f65a65e5330c951abbSascha Haeberling         * reference to prevent leaks.
68770c2b3e82326d6272b1d2f65a65e5330c951abbSascha Haeberling         */
698ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde        public PlayVideoIntent(CameraActivity activity) {
708ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde            mActivity = new WeakReference<CameraActivity>(activity);
71770c2b3e82326d6272b1d2f65a65e5330c951abbSascha Haeberling        }
72770c2b3e82326d6272b1d2f65a65e5330c951abbSascha Haeberling
73770c2b3e82326d6272b1d2f65a65e5330c951abbSascha Haeberling        /**
74770c2b3e82326d6272b1d2f65a65e5330c951abbSascha Haeberling         * Fires an intent to play the video with the given URI and title.
75770c2b3e82326d6272b1d2f65a65e5330c951abbSascha Haeberling         */
76770c2b3e82326d6272b1d2f65a65e5330c951abbSascha Haeberling        @Override
77770c2b3e82326d6272b1d2f65a65e5330c951abbSascha Haeberling        public void playVideo(Uri uri, String title) {
788ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde            CameraActivity activity = mActivity.get();
79770c2b3e82326d6272b1d2f65a65e5330c951abbSascha Haeberling            if (activity != null) {
80770c2b3e82326d6272b1d2f65a65e5330c951abbSascha Haeberling              CameraUtil.playVideo(activity, uri, title);
81770c2b3e82326d6272b1d2f65a65e5330c951abbSascha Haeberling            }
82770c2b3e82326d6272b1d2f65a65e5330c951abbSascha Haeberling        }
83770c2b3e82326d6272b1d2f65a65e5330c951abbSascha Haeberling    }
84770c2b3e82326d6272b1d2f65a65e5330c951abbSascha Haeberling
85770c2b3e82326d6272b1d2f65a65e5330c951abbSascha Haeberling
862bca210e5fc8a77685775ffb403096167b017dceAngus Kong    private static final Log.Tag TAG = new Log.Tag("FilmstripView");
87af24d28f8938a05d2e3c40155e39cbc7ff4d64b7Angus Kong
8849b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong    private static final int BUFFER_SIZE = 5;
898ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde    private static final int BUFFER_CENTER = (BUFFER_SIZE - 1) / 2;
90734598c39e6a9d809982dae2907dc7d10e0b0e9fAngus Kong    private static final int GEOMETRY_ADJUST_TIME_MS = 400;
918969a67c926608737f3e49f2d2240298372e8416Angus Kong    private static final int SNAP_IN_CENTER_TIME_MS = 600;
92d70f0fb68064e170fb2cf041a707ed04d68040f9Doris Liu    private static final float FLING_COASTING_DURATION_S = 0.05f;
93121022cb590955e37f1264f77190ce4711159976Doris Liu    private static final int ZOOM_ANIMATION_DURATION_MS = 200;
947d2388d96522975307cf5c40f7ac911ad963c44dAngus Kong    private static final int CAMERA_PREVIEW_SWIPE_THRESHOLD = 300;
9543596cd34e7020d6a0aafcdc31297237ad98496cAngus Kong    private static final float FILM_STRIP_SCALE = 0.7f;
964ff5a1ab360577c6a7ec953f9197778c84c9d334Angus Kong    private static final float FULL_SCREEN_SCALE = 1f;
97121022cb590955e37f1264f77190ce4711159976Doris Liu
98bcd1152e8214b1fb4a17963ffe8c292681c81688Sam Judd    // The min velocity at which the user must have moved their finger in
99bcd1152e8214b1fb4a17963ffe8c292681c81688Sam Judd    // pixels per millisecond to count a vertical gesture as a promote/demote
100bcd1152e8214b1fb4a17963ffe8c292681c81688Sam Judd    // at short vertical distances.
101bcd1152e8214b1fb4a17963ffe8c292681c81688Sam Judd    private static final float PROMOTE_VELOCITY = 3.5f;
102bcd1152e8214b1fb4a17963ffe8c292681c81688Sam Judd    // The min distance relative to this view's height the user must have
103bcd1152e8214b1fb4a17963ffe8c292681c81688Sam Judd    // moved their finger to count a vertical gesture as a promote/demote if
104bcd1152e8214b1fb4a17963ffe8c292681c81688Sam Judd    // they moved their finger at least at PROMOTE_VELOCITY.
105bcd1152e8214b1fb4a17963ffe8c292681c81688Sam Judd    private static final float VELOCITY_PROMOTE_HEIGHT_RATIO = 1/10f;
106bcd1152e8214b1fb4a17963ffe8c292681c81688Sam Judd    // The min distance relative to this view's height the user must have
107bcd1152e8214b1fb4a17963ffe8c292681c81688Sam Judd    // moved their finger to count a vertical gesture as a promote/demote if
108bcd1152e8214b1fb4a17963ffe8c292681c81688Sam Judd    // they moved their finger at less than PROMOTE_VELOCITY.
109bcd1152e8214b1fb4a17963ffe8c292681c81688Sam Judd    private static final float PROMOTE_HEIGHT_RATIO = 1/2f;
110bcd1152e8214b1fb4a17963ffe8c292681c81688Sam Judd
111121022cb590955e37f1264f77190ce4711159976Doris Liu    private static final float TOLERANCE = 0.1f;
112fb57df15a87f95ce8b2f861e54d960602912e03cDoris Liu    // Only check for intercepting touch events within first 500ms
113fb57df15a87f95ce8b2f861e54d960602912e03cDoris Liu    private static final int SWIPE_TIME_OUT = 500;
114d70f0fb68064e170fb2cf041a707ed04d68040f9Doris Liu    private static final int DECELERATION_FACTOR = 4;
1159652134e6e1b312e348ed3565343741239907f11Alan Newberger    private static final float MOUSE_SCROLL_FACTOR = 128f;
11649b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong
11788ef7664ba6a888fa5da8693091674e152b56192Sascha Haeberling    private CameraActivity mActivity;
1188ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde    private VideoClickedCallback mVideoClickedCallback;
119166e36fb6e04d40a1bef0459ee6b96c9c736039bAngus Kong    private FilmstripGestureRecognizer mGestureRecognizer;
120166e36fb6e04d40a1bef0459ee6b96c9c736039bAngus Kong    private FilmstripGestureRecognizer.Listener mGestureListener;
1218ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde    private FilmstripDataAdapter mDataAdapter;
122faaee012acc80ad369cb03df9c196e48140f1e7bAngus Kong    private int mViewGapInPixel;
12349b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong    private final Rect mDrawArea = new Rect();
12449b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong
12549b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong    private float mScale;
1268ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde    private FilmstripControllerImpl mController;
127af24d28f8938a05d2e3c40155e39cbc7ff4d64b7Angus Kong    private int mCenterX = -1;
1288ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde    private final ViewItem[] mViewItems = new ViewItem[BUFFER_SIZE];
12949b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong
13001054e922aa547b937a71131ad04c6bd15356240Angus Kong    private FilmstripController.FilmstripListener mListener;
1318de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu    private ZoomView mZoomView = null;
13287f9a62f473a3c57505e2877b0a46ab3cb5e62acAngus Kong
133fb57df15a87f95ce8b2f861e54d960602912e03cDoris Liu    private MotionEvent mDown;
134fb57df15a87f95ce8b2f861e54d960602912e03cDoris Liu    private boolean mCheckToIntercept = true;
135fb57df15a87f95ce8b2f861e54d960602912e03cDoris Liu    private int mSlop;
136e78541f2edf2f02660c34660114e76d53b32ab8bAngus Kong    private TimeInterpolator mViewAnimInterpolator;
137e78541f2edf2f02660c34660114e76d53b32ab8bAngus Kong
13847721fa8f2dc13cb565ae5629a417dc4ed298e81Angus Kong    // This is true if and only if the user is scrolling,
13947721fa8f2dc13cb565ae5629a417dc4ed298e81Angus Kong    private boolean mIsUserScrolling;
1408ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde    private int mAdapterIndexUserIsScrollingOver;
1413179f6a49516833761be5155c4e1244ac0aa17feDoris Liu    private float mOverScaleFactor = 1f;
14247721fa8f2dc13cb565ae5629a417dc4ed298e81Angus Kong
1434567160a43fa5787012bf0b618c216cf18ac0457Angus Kong    private boolean mFullScreenUIHidden = false;
1448ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde    private final SparseArray<Queue<View>> recycledViews = new SparseArray<>();
14543bf03f6e7f9a71b7553e7282b5799798997caf7Sam Judd
146f1f5186f7d3fd8bf35009d9e65a43914c664d82bSascha Haeberling    /**
147f1f5186f7d3fd8bf35009d9e65a43914c664d82bSascha Haeberling     * A helper class to tract and calculate the view coordination.
148f1f5186f7d3fd8bf35009d9e65a43914c664d82bSascha Haeberling     */
1498ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde    private static class ViewItem {
15077d9f023e76816e5da7bd067ad46cc0e9c98623fPaul Rohde        private static enum RenderSize {
15177d9f023e76816e5da7bd067ad46cc0e9c98623fPaul Rohde            TINY,
15277d9f023e76816e5da7bd067ad46cc0e9c98623fPaul Rohde            THUMBNAIL,
15377d9f023e76816e5da7bd067ad46cc0e9c98623fPaul Rohde            FULL_RES
15477d9f023e76816e5da7bd067ad46cc0e9c98623fPaul Rohde        }
15577d9f023e76816e5da7bd067ad46cc0e9c98623fPaul Rohde
15677d9f023e76816e5da7bd067ad46cc0e9c98623fPaul Rohde        private final FilmstripView mFilmstrip;
15777d9f023e76816e5da7bd067ad46cc0e9c98623fPaul Rohde        private final View mView;
15877d9f023e76816e5da7bd067ad46cc0e9c98623fPaul Rohde        private final RectF mViewArea;
15977d9f023e76816e5da7bd067ad46cc0e9c98623fPaul Rohde
1608ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde        private int mIndex;
161f1f5186f7d3fd8bf35009d9e65a43914c664d82bSascha Haeberling        /** The position of the left of the view in the whole filmstrip. */
16249b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong        private int mLeftPosition;
1638e29072ca69229a25dc4a856e3635d131613f4caAlan Newberger        private FilmstripItem mData;
16477d9f023e76816e5da7bd067ad46cc0e9c98623fPaul Rohde        private RenderSize mRenderSize;
1651f9db2dfe59c33228103c54523281501ef52c9baAngus Kong
1661f9db2dfe59c33228103c54523281501ef52c9baAngus Kong        private ValueAnimator mTranslationXAnimator;
1671f9db2dfe59c33228103c54523281501ef52c9baAngus Kong        private ValueAnimator mTranslationYAnimator;
1681f9db2dfe59c33228103c54523281501ef52c9baAngus Kong        private ValueAnimator mAlphaAnimator;
169edbba628dc2e31057b304c6a431f44dbca125891Angus Kong
170e7263f7ea45cf31d3948984ba8514e03ef86627ePaul Rohde        private boolean mLockAtFullOpacity;
171e7263f7ea45cf31d3948984ba8514e03ef86627ePaul Rohde
1724ff5a1ab360577c6a7ec953f9197778c84c9d334Angus Kong        /**
1734ff5a1ab360577c6a7ec953f9197778c84c9d334Angus Kong         * Constructor.
17488ef7664ba6a888fa5da8693091674e152b56192Sascha Haeberling         *
1758ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde         * @param index The index of the data from
1768ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde         *            {@link com.android.camera.filmstrip.FilmstripDataAdapter}.
1774ff5a1ab360577c6a7ec953f9197778c84c9d334Angus Kong         * @param v The {@code View} representing the data.
1784ff5a1ab360577c6a7ec953f9197778c84c9d334Angus Kong         */
1798ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde        public ViewItem(int index, View v, FilmstripItem data, FilmstripView filmstrip) {
18077d9f023e76816e5da7bd067ad46cc0e9c98623fPaul Rohde            mFilmstrip = filmstrip;
18177d9f023e76816e5da7bd067ad46cc0e9c98623fPaul Rohde            mView = v;
18277d9f023e76816e5da7bd067ad46cc0e9c98623fPaul Rohde            mViewArea = new RectF();
18377d9f023e76816e5da7bd067ad46cc0e9c98623fPaul Rohde
1848ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde            mIndex = index;
1851f9db2dfe59c33228103c54523281501ef52c9baAngus Kong            mData = data;
18649b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong            mLeftPosition = -1;
18777d9f023e76816e5da7bd067ad46cc0e9c98623fPaul Rohde            mRenderSize = RenderSize.TINY;
188e7263f7ea45cf31d3948984ba8514e03ef86627ePaul Rohde            mLockAtFullOpacity = false;
18977d9f023e76816e5da7bd067ad46cc0e9c98623fPaul Rohde
19077d9f023e76816e5da7bd067ad46cc0e9c98623fPaul Rohde            mView.setPivotX(0f);
19177d9f023e76816e5da7bd067ad46cc0e9c98623fPaul Rohde            mView.setPivotY(0f);
19249b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong        }
19349b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong
1945a0be52d3db2398cfe01d18adf779d9e443edf82Alan Newberger        public FilmstripItem getData() {
1955a0be52d3db2398cfe01d18adf779d9e443edf82Alan Newberger            return mData;
1965a0be52d3db2398cfe01d18adf779d9e443edf82Alan Newberger        }
1975a0be52d3db2398cfe01d18adf779d9e443edf82Alan Newberger
1988e29072ca69229a25dc4a856e3635d131613f4caAlan Newberger        public void setData(FilmstripItem item) {
1998e29072ca69229a25dc4a856e3635d131613f4caAlan Newberger            mData = item;
20077d9f023e76816e5da7bd067ad46cc0e9c98623fPaul Rohde
20177d9f023e76816e5da7bd067ad46cc0e9c98623fPaul Rohde            renderTiny();
2028e29072ca69229a25dc4a856e3635d131613f4caAlan Newberger        }
2038e29072ca69229a25dc4a856e3635d131613f4caAlan Newberger
20477d9f023e76816e5da7bd067ad46cc0e9c98623fPaul Rohde        public void renderTiny() {
20577d9f023e76816e5da7bd067ad46cc0e9c98623fPaul Rohde            if (mRenderSize != RenderSize.TINY) {
20677d9f023e76816e5da7bd067ad46cc0e9c98623fPaul Rohde                mRenderSize = RenderSize.TINY;
20777d9f023e76816e5da7bd067ad46cc0e9c98623fPaul Rohde
20877d9f023e76816e5da7bd067ad46cc0e9c98623fPaul Rohde                Log.i(TAG, "[ViewItem:" + mIndex + "] mData.renderTiny()");
20977d9f023e76816e5da7bd067ad46cc0e9c98623fPaul Rohde                mData.renderTiny(mView);
21077d9f023e76816e5da7bd067ad46cc0e9c98623fPaul Rohde            }
211caca8c7b2e97bd76e4df1ea98c08acaa48b21557Andy Huibers        }
212caca8c7b2e97bd76e4df1ea98c08acaa48b21557Andy Huibers
21377d9f023e76816e5da7bd067ad46cc0e9c98623fPaul Rohde        public void renderThumbnail() {
21477d9f023e76816e5da7bd067ad46cc0e9c98623fPaul Rohde            if (mRenderSize != RenderSize.THUMBNAIL) {
21577d9f023e76816e5da7bd067ad46cc0e9c98623fPaul Rohde                mRenderSize = RenderSize.THUMBNAIL;
21677d9f023e76816e5da7bd067ad46cc0e9c98623fPaul Rohde
21777d9f023e76816e5da7bd067ad46cc0e9c98623fPaul Rohde                Log.i(TAG, "[ViewItem:" + mIndex + "] mData.renderThumbnail()");
21877d9f023e76816e5da7bd067ad46cc0e9c98623fPaul Rohde                mData.renderThumbnail(mView);
21977d9f023e76816e5da7bd067ad46cc0e9c98623fPaul Rohde            }
22077d9f023e76816e5da7bd067ad46cc0e9c98623fPaul Rohde        }
22177d9f023e76816e5da7bd067ad46cc0e9c98623fPaul Rohde
22277d9f023e76816e5da7bd067ad46cc0e9c98623fPaul Rohde        public void renderFullRes() {
22377d9f023e76816e5da7bd067ad46cc0e9c98623fPaul Rohde            if (mRenderSize != RenderSize.FULL_RES) {
22477d9f023e76816e5da7bd067ad46cc0e9c98623fPaul Rohde                mRenderSize = RenderSize.FULL_RES;
22577d9f023e76816e5da7bd067ad46cc0e9c98623fPaul Rohde
22677d9f023e76816e5da7bd067ad46cc0e9c98623fPaul Rohde                Log.i(TAG, "[ViewItem:" + mIndex + "] mData.renderFullRes()");
22777d9f023e76816e5da7bd067ad46cc0e9c98623fPaul Rohde                mData.renderFullRes(mView);
22877d9f023e76816e5da7bd067ad46cc0e9c98623fPaul Rohde            }
229caca8c7b2e97bd76e4df1ea98c08acaa48b21557Andy Huibers        }
230caca8c7b2e97bd76e4df1ea98c08acaa48b21557Andy Huibers
231e7263f7ea45cf31d3948984ba8514e03ef86627ePaul Rohde        public void lockAtFullOpacity() {
232e7263f7ea45cf31d3948984ba8514e03ef86627ePaul Rohde            if (!mLockAtFullOpacity) {
233e7263f7ea45cf31d3948984ba8514e03ef86627ePaul Rohde                mLockAtFullOpacity = true;
234e7263f7ea45cf31d3948984ba8514e03ef86627ePaul Rohde                mView.setAlpha(1.0f);
235e7263f7ea45cf31d3948984ba8514e03ef86627ePaul Rohde            }
236e7263f7ea45cf31d3948984ba8514e03ef86627ePaul Rohde        }
237e7263f7ea45cf31d3948984ba8514e03ef86627ePaul Rohde
238e7263f7ea45cf31d3948984ba8514e03ef86627ePaul Rohde        public void unlockOpacity() {
239e7263f7ea45cf31d3948984ba8514e03ef86627ePaul Rohde            mLockAtFullOpacity = false;
240e7263f7ea45cf31d3948984ba8514e03ef86627ePaul Rohde        }
241e7263f7ea45cf31d3948984ba8514e03ef86627ePaul Rohde
242280fd3edae1dc35ac1015bcc9532477169fad3aaSascha Haeberling        /**
2438ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde         * Returns the index from
2448ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde         * {@link com.android.camera.filmstrip.FilmstripDataAdapter}.
245280fd3edae1dc35ac1015bcc9532477169fad3aaSascha Haeberling         */
2468ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde        public int getAdapterIndex() {
2478ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde            return mIndex;
24849b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong        }
24949b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong
250280fd3edae1dc35ac1015bcc9532477169fad3aaSascha Haeberling        /**
2518ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde         * Sets the index used in the
2528ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde         * {@link com.android.camera.filmstrip.FilmstripDataAdapter}.
253280fd3edae1dc35ac1015bcc9532477169fad3aaSascha Haeberling         */
2548ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde        public void setIndex(int index) {
2558ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde            mIndex = index;
25687f9a62f473a3c57505e2877b0a46ab3cb5e62acAngus Kong        }
25787f9a62f473a3c57505e2877b0a46ab3cb5e62acAngus Kong
2584ff5a1ab360577c6a7ec953f9197778c84c9d334Angus Kong        /** Sets the left position of the view in the whole filmstrip. */
25949b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong        public void setLeftPosition(int pos) {
26049b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong            mLeftPosition = pos;
26149b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong        }
26249b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong
2634ff5a1ab360577c6a7ec953f9197778c84c9d334Angus Kong        /** Returns the left position of the view in the whole filmstrip. */
26449b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong        public int getLeftPosition() {
26549b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong            return mLeftPosition;
26649b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong        }
26749b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong
2684ff5a1ab360577c6a7ec953f9197778c84c9d334Angus Kong        /** Returns the translation of Y regarding the view scale. */
2691f9db2dfe59c33228103c54523281501ef52c9baAngus Kong        public float getTranslationY() {
2708ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde            return mView.getTranslationY() / mFilmstrip.mScale;
27187f9a62f473a3c57505e2877b0a46ab3cb5e62acAngus Kong        }
27287f9a62f473a3c57505e2877b0a46ab3cb5e62acAngus Kong
2734ff5a1ab360577c6a7ec953f9197778c84c9d334Angus Kong        /** Returns the translation of X regarding the view scale. */
2741f9db2dfe59c33228103c54523281501ef52c9baAngus Kong        public float getTranslationX() {
2758ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde            return mView.getTranslationX() / mFilmstrip.mScale;
2761f9db2dfe59c33228103c54523281501ef52c9baAngus Kong        }
2771f9db2dfe59c33228103c54523281501ef52c9baAngus Kong
2781f9db2dfe59c33228103c54523281501ef52c9baAngus Kong        /** Sets the translation of Y regarding the view scale. */
2791f9db2dfe59c33228103c54523281501ef52c9baAngus Kong        public void setTranslationY(float transY) {
2808ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde            mView.setTranslationY(transY * mFilmstrip.mScale);
2811f9db2dfe59c33228103c54523281501ef52c9baAngus Kong        }
2821f9db2dfe59c33228103c54523281501ef52c9baAngus Kong
2831f9db2dfe59c33228103c54523281501ef52c9baAngus Kong        /** Sets the translation of X regarding the view scale. */
2841f9db2dfe59c33228103c54523281501ef52c9baAngus Kong        public void setTranslationX(float transX) {
2858ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde            mView.setTranslationX(transX * mFilmstrip.mScale);
2861f9db2dfe59c33228103c54523281501ef52c9baAngus Kong        }
2871f9db2dfe59c33228103c54523281501ef52c9baAngus Kong
2881f9db2dfe59c33228103c54523281501ef52c9baAngus Kong        /** Forwarding of {@link android.view.View#setAlpha(float)}. */
2891f9db2dfe59c33228103c54523281501ef52c9baAngus Kong        public void setAlpha(float alpha) {
290e7263f7ea45cf31d3948984ba8514e03ef86627ePaul Rohde            if (!mLockAtFullOpacity) {
291e7263f7ea45cf31d3948984ba8514e03ef86627ePaul Rohde                mView.setAlpha(alpha);
292e7263f7ea45cf31d3948984ba8514e03ef86627ePaul Rohde            }
2931f9db2dfe59c33228103c54523281501ef52c9baAngus Kong        }
2941f9db2dfe59c33228103c54523281501ef52c9baAngus Kong
2951f9db2dfe59c33228103c54523281501ef52c9baAngus Kong        /** Forwarding of {@link android.view.View#getAlpha()}. */
2961f9db2dfe59c33228103c54523281501ef52c9baAngus Kong        public float getAlpha() {
2971f9db2dfe59c33228103c54523281501ef52c9baAngus Kong            return mView.getAlpha();
2981f9db2dfe59c33228103c54523281501ef52c9baAngus Kong        }
2991f9db2dfe59c33228103c54523281501ef52c9baAngus Kong
3001f9db2dfe59c33228103c54523281501ef52c9baAngus Kong        /** Forwarding of {@link android.view.View#getMeasuredWidth()}. */
3011f9db2dfe59c33228103c54523281501ef52c9baAngus Kong        public int getMeasuredWidth() {
3021f9db2dfe59c33228103c54523281501ef52c9baAngus Kong            return mView.getMeasuredWidth();
30349b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong        }
30449b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong
3058de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu        /**
3061f9db2dfe59c33228103c54523281501ef52c9baAngus Kong         * Animates the X translation of the view. Note: the animated value is
3071f9db2dfe59c33228103c54523281501ef52c9baAngus Kong         * not set directly by {@link android.view.View#setTranslationX(float)}
3081f9db2dfe59c33228103c54523281501ef52c9baAngus Kong         * because the value might be changed during in {@code onLayout()}.
3091f9db2dfe59c33228103c54523281501ef52c9baAngus Kong         * The animated value of X translation is specially handled in {@code
3101f9db2dfe59c33228103c54523281501ef52c9baAngus Kong         * layoutIn()}.
3118de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu         *
3121f9db2dfe59c33228103c54523281501ef52c9baAngus Kong         * @param targetX The final value.
3131f9db2dfe59c33228103c54523281501ef52c9baAngus Kong         * @param duration_ms The duration of the animation.
3141f9db2dfe59c33228103c54523281501ef52c9baAngus Kong         * @param interpolator Time interpolator.
3158de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu         */
3161f9db2dfe59c33228103c54523281501ef52c9baAngus Kong        public void animateTranslationX(
3171f9db2dfe59c33228103c54523281501ef52c9baAngus Kong                float targetX, long duration_ms, TimeInterpolator interpolator) {
3181f9db2dfe59c33228103c54523281501ef52c9baAngus Kong            if (mTranslationXAnimator == null) {
3191f9db2dfe59c33228103c54523281501ef52c9baAngus Kong                mTranslationXAnimator = new ValueAnimator();
3201f9db2dfe59c33228103c54523281501ef52c9baAngus Kong                mTranslationXAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
3211f9db2dfe59c33228103c54523281501ef52c9baAngus Kong                    @Override
3221f9db2dfe59c33228103c54523281501ef52c9baAngus Kong                    public void onAnimationUpdate(ValueAnimator valueAnimator) {
3231f9db2dfe59c33228103c54523281501ef52c9baAngus Kong                        // We invalidate the filmstrip view instead of setting the
3241f9db2dfe59c33228103c54523281501ef52c9baAngus Kong                        // translation X because the translation X of the view is
3251f9db2dfe59c33228103c54523281501ef52c9baAngus Kong                        // touched in onLayout(). See the documentation of
3261f9db2dfe59c33228103c54523281501ef52c9baAngus Kong                        // animateTranslationX().
3278ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde                        mFilmstrip.invalidate();
3281f9db2dfe59c33228103c54523281501ef52c9baAngus Kong                    }
3291f9db2dfe59c33228103c54523281501ef52c9baAngus Kong                });
3301f9db2dfe59c33228103c54523281501ef52c9baAngus Kong            }
3311f9db2dfe59c33228103c54523281501ef52c9baAngus Kong            runAnimation(mTranslationXAnimator, getTranslationX(), targetX, duration_ms,
3321f9db2dfe59c33228103c54523281501ef52c9baAngus Kong                    interpolator);
3338de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu        }
3348de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu
3358de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu        /**
3361f9db2dfe59c33228103c54523281501ef52c9baAngus Kong         * Animates the Y translation of the view.
3378de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu         *
3381f9db2dfe59c33228103c54523281501ef52c9baAngus Kong         * @param targetY The final value.
3391f9db2dfe59c33228103c54523281501ef52c9baAngus Kong         * @param duration_ms The duration of the animation.
3401f9db2dfe59c33228103c54523281501ef52c9baAngus Kong         * @param interpolator Time interpolator.
3418de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu         */
3421f9db2dfe59c33228103c54523281501ef52c9baAngus Kong        public void animateTranslationY(
3431f9db2dfe59c33228103c54523281501ef52c9baAngus Kong                float targetY, long duration_ms, TimeInterpolator interpolator) {
3441f9db2dfe59c33228103c54523281501ef52c9baAngus Kong            if (mTranslationYAnimator == null) {
3451f9db2dfe59c33228103c54523281501ef52c9baAngus Kong                mTranslationYAnimator = new ValueAnimator();
3461f9db2dfe59c33228103c54523281501ef52c9baAngus Kong                mTranslationYAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
3471f9db2dfe59c33228103c54523281501ef52c9baAngus Kong                    @Override
3481f9db2dfe59c33228103c54523281501ef52c9baAngus Kong                    public void onAnimationUpdate(ValueAnimator valueAnimator) {
3491f9db2dfe59c33228103c54523281501ef52c9baAngus Kong                        setTranslationY((Float) valueAnimator.getAnimatedValue());
3501f9db2dfe59c33228103c54523281501ef52c9baAngus Kong                    }
3511f9db2dfe59c33228103c54523281501ef52c9baAngus Kong                });
3521f9db2dfe59c33228103c54523281501ef52c9baAngus Kong            }
3531f9db2dfe59c33228103c54523281501ef52c9baAngus Kong            runAnimation(mTranslationYAnimator, getTranslationY(), targetY, duration_ms,
3541f9db2dfe59c33228103c54523281501ef52c9baAngus Kong                    interpolator);
3558de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu        }
3568de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu
3571f9db2dfe59c33228103c54523281501ef52c9baAngus Kong        /**
3581f9db2dfe59c33228103c54523281501ef52c9baAngus Kong         * Animates the alpha value of the view.
3591f9db2dfe59c33228103c54523281501ef52c9baAngus Kong         *
3601f9db2dfe59c33228103c54523281501ef52c9baAngus Kong         * @param targetAlpha The final value.
3611f9db2dfe59c33228103c54523281501ef52c9baAngus Kong         * @param duration_ms The duration of the animation.
3621f9db2dfe59c33228103c54523281501ef52c9baAngus Kong         * @param interpolator Time interpolator.
3631f9db2dfe59c33228103c54523281501ef52c9baAngus Kong         */
3641f9db2dfe59c33228103c54523281501ef52c9baAngus Kong        public void animateAlpha(float targetAlpha, long duration_ms,
3651f9db2dfe59c33228103c54523281501ef52c9baAngus Kong                TimeInterpolator interpolator) {
3661f9db2dfe59c33228103c54523281501ef52c9baAngus Kong            if (mAlphaAnimator == null) {
3671f9db2dfe59c33228103c54523281501ef52c9baAngus Kong                mAlphaAnimator = new ValueAnimator();
3681f9db2dfe59c33228103c54523281501ef52c9baAngus Kong                mAlphaAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
3691f9db2dfe59c33228103c54523281501ef52c9baAngus Kong                    @Override
3701f9db2dfe59c33228103c54523281501ef52c9baAngus Kong                    public void onAnimationUpdate(ValueAnimator valueAnimator) {
3711f9db2dfe59c33228103c54523281501ef52c9baAngus Kong                        ViewItem.this.setAlpha((Float) valueAnimator.getAnimatedValue());
3721f9db2dfe59c33228103c54523281501ef52c9baAngus Kong                    }
3731f9db2dfe59c33228103c54523281501ef52c9baAngus Kong                });
3741f9db2dfe59c33228103c54523281501ef52c9baAngus Kong            }
3751f9db2dfe59c33228103c54523281501ef52c9baAngus Kong            runAnimation(mAlphaAnimator, getAlpha(), targetAlpha, duration_ms, interpolator);
37687f9a62f473a3c57505e2877b0a46ab3cb5e62acAngus Kong        }
37787f9a62f473a3c57505e2877b0a46ab3cb5e62acAngus Kong
3781f9db2dfe59c33228103c54523281501ef52c9baAngus Kong        private void runAnimation(final ValueAnimator animator, final float startValue,
3791f9db2dfe59c33228103c54523281501ef52c9baAngus Kong                final float targetValue, final long duration_ms,
3801f9db2dfe59c33228103c54523281501ef52c9baAngus Kong                final TimeInterpolator interpolator) {
3811f9db2dfe59c33228103c54523281501ef52c9baAngus Kong            if (startValue == targetValue) {
3821f9db2dfe59c33228103c54523281501ef52c9baAngus Kong                return;
3831f9db2dfe59c33228103c54523281501ef52c9baAngus Kong            }
3841f9db2dfe59c33228103c54523281501ef52c9baAngus Kong            animator.setInterpolator(interpolator);
3851f9db2dfe59c33228103c54523281501ef52c9baAngus Kong            animator.setDuration(duration_ms);
3861f9db2dfe59c33228103c54523281501ef52c9baAngus Kong            animator.setFloatValues(startValue, targetValue);
3871f9db2dfe59c33228103c54523281501ef52c9baAngus Kong            animator.start();
38849b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong        }
38949b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong
3901f9db2dfe59c33228103c54523281501ef52c9baAngus Kong        /** Adjusts the translation of X regarding the view scale. */
3911f9db2dfe59c33228103c54523281501ef52c9baAngus Kong        public void translateXScaledBy(float transX) {
3928ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde            setTranslationX(getTranslationX() + transX * mFilmstrip.mScale);
393edbba628dc2e31057b304c6a431f44dbca125891Angus Kong        }
394edbba628dc2e31057b304c6a431f44dbca125891Angus Kong
3951f9db2dfe59c33228103c54523281501ef52c9baAngus Kong        /**
3961f9db2dfe59c33228103c54523281501ef52c9baAngus Kong         * Forwarding of {@link android.view.View#getHitRect(android.graphics.Rect)}.
3971f9db2dfe59c33228103c54523281501ef52c9baAngus Kong         */
3981f9db2dfe59c33228103c54523281501ef52c9baAngus Kong        public void getHitRect(Rect rect) {
3991f9db2dfe59c33228103c54523281501ef52c9baAngus Kong            mView.getHitRect(rect);
4009f02c87cfb5039a72f516ea5edc0378c45ac5779Angus Kong        }
4019f02c87cfb5039a72f516ea5edc0378c45ac5779Angus Kong
40249b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong        public int getCenterX() {
4037d2388d96522975307cf5c40f7ac911ad963c44dAngus Kong            return mLeftPosition + mView.getMeasuredWidth() / 2;
40449b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong        }
40549b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong
4061f9db2dfe59c33228103c54523281501ef52c9baAngus Kong        /** Forwarding of {@link android.view.View#getVisibility()}. */
4071f9db2dfe59c33228103c54523281501ef52c9baAngus Kong        public int getVisibility() {
4081f9db2dfe59c33228103c54523281501ef52c9baAngus Kong            return mView.getVisibility();
4091f9db2dfe59c33228103c54523281501ef52c9baAngus Kong        }
4101f9db2dfe59c33228103c54523281501ef52c9baAngus Kong
4111f9db2dfe59c33228103c54523281501ef52c9baAngus Kong        /** Forwarding of {@link android.view.View#setVisibility(int)}. */
4121f9db2dfe59c33228103c54523281501ef52c9baAngus Kong        public void setVisibility(int visibility) {
4131f9db2dfe59c33228103c54523281501ef52c9baAngus Kong            mView.setVisibility(visibility);
4141f9db2dfe59c33228103c54523281501ef52c9baAngus Kong        }
4151f9db2dfe59c33228103c54523281501ef52c9baAngus Kong
4161f9db2dfe59c33228103c54523281501ef52c9baAngus Kong        /**
4171f9db2dfe59c33228103c54523281501ef52c9baAngus Kong         * Adds the view of the data to the view hierarchy if necessary.
4181f9db2dfe59c33228103c54523281501ef52c9baAngus Kong         */
4191f9db2dfe59c33228103c54523281501ef52c9baAngus Kong        public void addViewToHierarchy() {
4208ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde            if (mFilmstrip.indexOfChild(mView) < 0) {
4218ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde                mFilmstrip.addView(mView);
4221f9db2dfe59c33228103c54523281501ef52c9baAngus Kong            }
4238670da6b3631e82d864e55d36a1ccaf9b2c7a8ceSascha Haeberling
424ec980898dd0dc712a3d6373c06a13f8646a0eebcAlan Newberger            // all new views added should not display until layout positions
425ec980898dd0dc712a3d6373c06a13f8646a0eebcAlan Newberger            // them and sets them visible
426ec980898dd0dc712a3d6373c06a13f8646a0eebcAlan Newberger            setVisibility(View.INVISIBLE);
4278670da6b3631e82d864e55d36a1ccaf9b2c7a8ceSascha Haeberling            setAlpha(1f);
4288670da6b3631e82d864e55d36a1ccaf9b2c7a8ceSascha Haeberling            setTranslationX(0);
4298670da6b3631e82d864e55d36a1ccaf9b2c7a8ceSascha Haeberling            setTranslationY(0);
4301f9db2dfe59c33228103c54523281501ef52c9baAngus Kong        }
4311f9db2dfe59c33228103c54523281501ef52c9baAngus Kong
4321f9db2dfe59c33228103c54523281501ef52c9baAngus Kong        /**
4335a0be52d3db2398cfe01d18adf779d9e443edf82Alan Newberger         * Removes from the hierarchy.
4341f9db2dfe59c33228103c54523281501ef52c9baAngus Kong         */
4355a0be52d3db2398cfe01d18adf779d9e443edf82Alan Newberger        public void removeViewFromHierarchy() {
4365a0be52d3db2398cfe01d18adf779d9e443edf82Alan Newberger            mFilmstrip.removeView(mView);
4375a0be52d3db2398cfe01d18adf779d9e443edf82Alan Newberger            mData.recycle(mView);
4385a0be52d3db2398cfe01d18adf779d9e443edf82Alan Newberger            mFilmstrip.recycleView(mView, mIndex);
4391f9db2dfe59c33228103c54523281501ef52c9baAngus Kong        }
4401f9db2dfe59c33228103c54523281501ef52c9baAngus Kong
4411f9db2dfe59c33228103c54523281501ef52c9baAngus Kong        /**
4421f9db2dfe59c33228103c54523281501ef52c9baAngus Kong         * Brings the view to front by
4431f9db2dfe59c33228103c54523281501ef52c9baAngus Kong         * {@link #bringChildToFront(android.view.View)}
4441f9db2dfe59c33228103c54523281501ef52c9baAngus Kong         */
4451f9db2dfe59c33228103c54523281501ef52c9baAngus Kong        public void bringViewToFront() {
4468ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde            mFilmstrip.bringChildToFront(mView);
44749b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong        }
44849b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong
4498de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu        /**
4508de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu         * The visual x position of this view, in pixels.
4518de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu         */
4528de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu        public float getX() {
4538de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu            return mView.getX();
4548de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu        }
4558de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu
4568de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu        /**
4578de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu         * The visual y position of this view, in pixels.
4588de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu         */
4598de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu        public float getY() {
4608de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu            return mView.getY();
4618de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu        }
4628de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu
4631f9db2dfe59c33228103c54523281501ef52c9baAngus Kong        /**
4641f9db2dfe59c33228103c54523281501ef52c9baAngus Kong         * Forwarding of {@link android.view.View#measure(int, int)}.
4651f9db2dfe59c33228103c54523281501ef52c9baAngus Kong         */
4661f9db2dfe59c33228103c54523281501ef52c9baAngus Kong        public void measure(int widthSpec, int heightSpec) {
4671f9db2dfe59c33228103c54523281501ef52c9baAngus Kong            mView.measure(widthSpec, heightSpec);
4681f9db2dfe59c33228103c54523281501ef52c9baAngus Kong        }
4691f9db2dfe59c33228103c54523281501ef52c9baAngus Kong
47049b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong        private void layoutAt(int left, int top) {
47149b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong            mView.layout(left, top, left + mView.getMeasuredWidth(),
47249b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong                    top + mView.getMeasuredHeight());
47349b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong        }
47449b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong
4754ff5a1ab360577c6a7ec953f9197778c84c9d334Angus Kong        /**
4768de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu         * The bounding rect of the view.
4778de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu         */
4788de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu        public RectF getViewRect() {
4798de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu            RectF r = new RectF();
4808de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu            r.left = mView.getX();
4818de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu            r.top = mView.getY();
4828de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu            r.right = r.left + mView.getWidth() * mView.getScaleX();
4838de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu            r.bottom = r.top + mView.getHeight() * mView.getScaleY();
4848de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu            return r;
4858de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu        }
4868de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu
487ae5fa1499ab1d6d1d421228859d13ac45ad1d437Spike Sprague        private View getView() {
488ae5fa1499ab1d6d1d421228859d13ac45ad1d437Spike Sprague            return mView;
489ae5fa1499ab1d6d1d421228859d13ac45ad1d437Spike Sprague        }
490ae5fa1499ab1d6d1d421228859d13ac45ad1d437Spike Sprague
4918de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu        /**
49288ef7664ba6a888fa5da8693091674e152b56192Sascha Haeberling         * Layouts the view in the area assuming the center of the area is at a
49388ef7664ba6a888fa5da8693091674e152b56192Sascha Haeberling         * specific point of the whole filmstrip.
4944ff5a1ab360577c6a7ec953f9197778c84c9d334Angus Kong         *
4954ff5a1ab360577c6a7ec953f9197778c84c9d334Angus Kong         * @param drawArea The area when filmstrip will show in.
4964ff5a1ab360577c6a7ec953f9197778c84c9d334Angus Kong         * @param refCenter The absolute X coordination in the whole filmstrip
49788ef7664ba6a888fa5da8693091674e152b56192Sascha Haeberling         *            of the center of {@code drawArea}.
4981f9db2dfe59c33228103c54523281501ef52c9baAngus Kong         * @param scale The scale of the view on the filmstrip.
4994ff5a1ab360577c6a7ec953f9197778c84c9d334Angus Kong         */
5001f9db2dfe59c33228103c54523281501ef52c9baAngus Kong        public void layoutWithTranslationX(Rect drawArea, int refCenter, float scale) {
5011f9db2dfe59c33228103c54523281501ef52c9baAngus Kong            final float translationX =
5021f9db2dfe59c33228103c54523281501ef52c9baAngus Kong                    ((mTranslationXAnimator != null && mTranslationXAnimator.isRunning()) ?
5031f9db2dfe59c33228103c54523281501ef52c9baAngus Kong                            (Float) mTranslationXAnimator.getAnimatedValue() : 0);
5041f9db2dfe59c33228103c54523281501ef52c9baAngus Kong            int left =
5051f9db2dfe59c33228103c54523281501ef52c9baAngus Kong                    (int) (drawArea.centerX() + (mLeftPosition - refCenter + translationX) * scale);
50687f9a62f473a3c57505e2877b0a46ab3cb5e62acAngus Kong            int top = (int) (drawArea.centerY() - (mView.getMeasuredHeight() / 2) * scale);
50749b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong            layoutAt(left, top);
50849b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong            mView.setScaleX(scale);
50949b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong            mView.setScaleY(scale);
510a6a6a730074e9fedd56c41cd8fa8f1212cfac108Angus Kong
511a6a6a730074e9fedd56c41cd8fa8f1212cfac108Angus Kong            // update mViewArea for touch detection.
512a6a6a730074e9fedd56c41cd8fa8f1212cfac108Angus Kong            int l = mView.getLeft();
513a6a6a730074e9fedd56c41cd8fa8f1212cfac108Angus Kong            int t = mView.getTop();
514a6a6a730074e9fedd56c41cd8fa8f1212cfac108Angus Kong            mViewArea.set(l, t,
515f1582c9c075e9cd5790b4bbe1101c94a45ac15d2Angus Kong                    l + mView.getMeasuredWidth() * scale,
516f1582c9c075e9cd5790b4bbe1101c94a45ac15d2Angus Kong                    t + mView.getMeasuredHeight() * scale);
517a6a6a730074e9fedd56c41cd8fa8f1212cfac108Angus Kong        }
518a6a6a730074e9fedd56c41cd8fa8f1212cfac108Angus Kong
5194ff5a1ab360577c6a7ec953f9197778c84c9d334Angus Kong        /** Returns true if the point is in the view. */
520a6a6a730074e9fedd56c41cd8fa8f1212cfac108Angus Kong        public boolean areaContains(float x, float y) {
521a6a6a730074e9fedd56c41cd8fa8f1212cfac108Angus Kong            return mViewArea.contains(x, y);
52249b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong        }
5238969a67c926608737f3e49f2d2240298372e8416Angus Kong
5248de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu        /**
5258de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu         * Return the width of the view.
5268de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu         */
5278de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu        public int getWidth() {
5288de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu            return mView.getWidth();
5298de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu        }
5308de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu
53129d9b3cd08a7b31b8ccecf4792d5ae981faa205cSam Judd        /**
53229d9b3cd08a7b31b8ccecf4792d5ae981faa205cSam Judd         * Returns the position of the left edge of the view area content is drawn in.
53329d9b3cd08a7b31b8ccecf4792d5ae981faa205cSam Judd         */
53429d9b3cd08a7b31b8ccecf4792d5ae981faa205cSam Judd        public int getDrawAreaLeft() {
53529d9b3cd08a7b31b8ccecf4792d5ae981faa205cSam Judd            return Math.round(mViewArea.left);
53629d9b3cd08a7b31b8ccecf4792d5ae981faa205cSam Judd        }
53729d9b3cd08a7b31b8ccecf4792d5ae981faa205cSam Judd
538121022cb590955e37f1264f77190ce4711159976Doris Liu        /**
539280fd3edae1dc35ac1015bcc9532477169fad3aaSascha Haeberling         * Apply a scale factor (i.e. {@code postScale}) on top of current scale at
540280fd3edae1dc35ac1015bcc9532477169fad3aaSascha Haeberling         * pivot point ({@code focusX}, {@code focusY}). Visually it should be the
541121022cb590955e37f1264f77190ce4711159976Doris Liu         * same as post concatenating current view's matrix with specified scale.
542121022cb590955e37f1264f77190ce4711159976Doris Liu         */
543121022cb590955e37f1264f77190ce4711159976Doris Liu        void postScale(float focusX, float focusY, float postScale, int viewportWidth,
544280fd3edae1dc35ac1015bcc9532477169fad3aaSascha Haeberling                int viewportHeight) {
5451f9db2dfe59c33228103c54523281501ef52c9baAngus Kong            float transX = mView.getTranslationX();
5461f9db2dfe59c33228103c54523281501ef52c9baAngus Kong            float transY = mView.getTranslationY();
547121022cb590955e37f1264f77190ce4711159976Doris Liu            // Pivot point is top left of the view, so we need to translate
548121022cb590955e37f1264f77190ce4711159976Doris Liu            // to scale around focus point
549121022cb590955e37f1264f77190ce4711159976Doris Liu            transX -= (focusX - getX()) * (postScale - 1f);
550121022cb590955e37f1264f77190ce4711159976Doris Liu            transY -= (focusY - getY()) * (postScale - 1f);
551121022cb590955e37f1264f77190ce4711159976Doris Liu            float scaleX = mView.getScaleX() * postScale;
552121022cb590955e37f1264f77190ce4711159976Doris Liu            float scaleY = mView.getScaleY() * postScale;
553121022cb590955e37f1264f77190ce4711159976Doris Liu            updateTransform(transX, transY, scaleX, scaleY, viewportWidth,
554121022cb590955e37f1264f77190ce4711159976Doris Liu                    viewportHeight);
555121022cb590955e37f1264f77190ce4711159976Doris Liu        }
5568de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu
5578de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu        void updateTransform(float transX, float transY, float scaleX, float scaleY,
558280fd3edae1dc35ac1015bcc9532477169fad3aaSascha Haeberling                int viewportWidth, int viewportHeight) {
559121022cb590955e37f1264f77190ce4711159976Doris Liu            float left = transX + mView.getLeft();
560121022cb590955e37f1264f77190ce4711159976Doris Liu            float top = transY + mView.getTop();
561121022cb590955e37f1264f77190ce4711159976Doris Liu            RectF r = ZoomView.adjustToFitInBounds(new RectF(left, top,
562121022cb590955e37f1264f77190ce4711159976Doris Liu                    left + mView.getWidth() * scaleX,
563121022cb590955e37f1264f77190ce4711159976Doris Liu                    top + mView.getHeight() * scaleY),
5648de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu                    viewportWidth, viewportHeight);
5658de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu            mView.setScaleX(scaleX);
5668de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu            mView.setScaleY(scaleY);
5678de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu            transX = r.left - mView.getLeft();
5688de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu            transY = r.top - mView.getTop();
5698de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu            mView.setTranslationX(transX);
5708de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu            mView.setTranslationY(transY);
5718de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu        }
5728de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu
573f9ddb6034e560684e4d3ddd460ef9dff345e4e91Doris Liu        void resetTransform() {
574f9ddb6034e560684e4d3ddd460ef9dff345e4e91Doris Liu            mView.setScaleX(FULL_SCREEN_SCALE);
575f9ddb6034e560684e4d3ddd460ef9dff345e4e91Doris Liu            mView.setScaleY(FULL_SCREEN_SCALE);
576f9ddb6034e560684e4d3ddd460ef9dff345e4e91Doris Liu            mView.setTranslationX(0f);
577f9ddb6034e560684e4d3ddd460ef9dff345e4e91Doris Liu            mView.setTranslationY(0f);
578f9ddb6034e560684e4d3ddd460ef9dff345e4e91Doris Liu        }
579f1582c9c075e9cd5790b4bbe1101c94a45ac15d2Angus Kong
580f1582c9c075e9cd5790b4bbe1101c94a45ac15d2Angus Kong        @Override
581f1582c9c075e9cd5790b4bbe1101c94a45ac15d2Angus Kong        public String toString() {
5828ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde            return "AdapterIndex = " + mIndex + "\n\t left = " + mLeftPosition
583f1582c9c075e9cd5790b4bbe1101c94a45ac15d2Angus Kong                    + "\n\t viewArea = " + mViewArea
584f1582c9c075e9cd5790b4bbe1101c94a45ac15d2Angus Kong                    + "\n\t centerX = " + getCenterX()
585f1582c9c075e9cd5790b4bbe1101c94a45ac15d2Angus Kong                    + "\n\t view MeasuredSize = "
586f1582c9c075e9cd5790b4bbe1101c94a45ac15d2Angus Kong                    + mView.getMeasuredWidth() + ',' + mView.getMeasuredHeight()
587f1582c9c075e9cd5790b4bbe1101c94a45ac15d2Angus Kong                    + "\n\t view Size = " + mView.getWidth() + ',' + mView.getHeight()
588f1582c9c075e9cd5790b4bbe1101c94a45ac15d2Angus Kong                    + "\n\t view scale = " + mView.getScaleX();
589f1582c9c075e9cd5790b4bbe1101c94a45ac15d2Angus Kong        }
59049b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong    }
59149b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong
592628481532e1e58a0a26330d238e850aec5657bceAngus Kong    /** Constructor. */
593628481532e1e58a0a26330d238e850aec5657bceAngus Kong    public FilmstripView(Context context) {
59449b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong        super(context);
59588ef7664ba6a888fa5da8693091674e152b56192Sascha Haeberling        init((CameraActivity) context);
59649b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong    }
59749b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong
5988e5e4eedb25a4589342660b928935acca4375fc7Angus Kong    /** Constructor. */
599628481532e1e58a0a26330d238e850aec5657bceAngus Kong    public FilmstripView(Context context, AttributeSet attrs) {
60049b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong        super(context, attrs);
60188ef7664ba6a888fa5da8693091674e152b56192Sascha Haeberling        init((CameraActivity) context);
60249b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong    }
60349b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong
6048e5e4eedb25a4589342660b928935acca4375fc7Angus Kong    /** Constructor. */
605628481532e1e58a0a26330d238e850aec5657bceAngus Kong    public FilmstripView(Context context, AttributeSet attrs, int defStyle) {
60649b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong        super(context, attrs, defStyle);
60788ef7664ba6a888fa5da8693091674e152b56192Sascha Haeberling        init((CameraActivity) context);
60849b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong    }
60949b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong
61088ef7664ba6a888fa5da8693091674e152b56192Sascha Haeberling    private void init(CameraActivity cameraActivity) {
61149b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong        setWillNotDraw(false);
61288ef7664ba6a888fa5da8693091674e152b56192Sascha Haeberling        mActivity = cameraActivity;
6138ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde        mVideoClickedCallback = new PlayVideoIntent(mActivity);
61449b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong        mScale = 1.0f;
6158ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde        mAdapterIndexUserIsScrollingOver = 0;
6168ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde        mController = new FilmstripControllerImpl();
617a6a6a730074e9fedd56c41cd8fa8f1212cfac108Angus Kong        mViewAnimInterpolator = new DecelerateInterpolator();
6188de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu        mZoomView = new ZoomView(cameraActivity);
6198de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu        mZoomView.setVisibility(GONE);
6208de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu        addView(mZoomView);
6218de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu
6228ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde        mGestureListener = new FilmstripGestures();
62349b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong        mGestureRecognizer =
624166e36fb6e04d40a1bef0459ee6b96c9c736039bAngus Kong                new FilmstripGestureRecognizer(cameraActivity, mGestureListener);
625fb57df15a87f95ce8b2f861e54d960602912e03cDoris Liu        mSlop = (int) getContext().getResources().getDimension(R.dimen.pie_touch_slop);
6263179f6a49516833761be5155c4e1244ac0aa17feDoris Liu        DisplayMetrics metrics = new DisplayMetrics();
6273179f6a49516833761be5155c4e1244ac0aa17feDoris Liu        mActivity.getWindowManager().getDefaultDisplay().getMetrics(metrics);
6283179f6a49516833761be5155c4e1244ac0aa17feDoris Liu        // Allow over scaling because on high density screens, pixels are too
6293179f6a49516833761be5155c4e1244ac0aa17feDoris Liu        // tiny to clearly see the details at 1:1 zoom. We should not scale
6303179f6a49516833761be5155c4e1244ac0aa17feDoris Liu        // beyond what 1:1 would look like on a medium density screen, as
6313179f6a49516833761be5155c4e1244ac0aa17feDoris Liu        // scaling beyond that would only yield blur.
632caca8c7b2e97bd76e4df1ea98c08acaa48b21557Andy Huibers        mOverScaleFactor = (float) metrics.densityDpi / (float) DisplayMetrics.DENSITY_HIGH;
6333179f6a49516833761be5155c4e1244ac0aa17feDoris Liu        if (mOverScaleFactor < 1f) {
6343179f6a49516833761be5155c4e1244ac0aa17feDoris Liu            mOverScaleFactor = 1f;
6353179f6a49516833761be5155c4e1244ac0aa17feDoris Liu        }
6365c92c9ec5e21a57646d6eca4360c0f6bb141a237Spike Sprague
637ae5fa1499ab1d6d1d421228859d13ac45ad1d437Spike Sprague        setAccessibilityDelegate(new AccessibilityDelegate() {
638ae5fa1499ab1d6d1d421228859d13ac45ad1d437Spike Sprague            @Override
639ae5fa1499ab1d6d1d421228859d13ac45ad1d437Spike Sprague            public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
640ae5fa1499ab1d6d1d421228859d13ac45ad1d437Spike Sprague                super.onInitializeAccessibilityNodeInfo(host, info);
641ae5fa1499ab1d6d1d421228859d13ac45ad1d437Spike Sprague
642ae5fa1499ab1d6d1d421228859d13ac45ad1d437Spike Sprague                info.setClassName(FilmstripView.class.getName());
643ae5fa1499ab1d6d1d421228859d13ac45ad1d437Spike Sprague                info.setScrollable(true);
644ae5fa1499ab1d6d1d421228859d13ac45ad1d437Spike Sprague                info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD);
645ae5fa1499ab1d6d1d421228859d13ac45ad1d437Spike Sprague                info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD);
646ae5fa1499ab1d6d1d421228859d13ac45ad1d437Spike Sprague            }
647ae5fa1499ab1d6d1d421228859d13ac45ad1d437Spike Sprague
648ae5fa1499ab1d6d1d421228859d13ac45ad1d437Spike Sprague            @Override
649ae5fa1499ab1d6d1d421228859d13ac45ad1d437Spike Sprague            public boolean performAccessibilityAction(View host, int action, Bundle args) {
650ae5fa1499ab1d6d1d421228859d13ac45ad1d437Spike Sprague                if (!mController.isScrolling()) {
651ae5fa1499ab1d6d1d421228859d13ac45ad1d437Spike Sprague                    switch (action) {
652ae5fa1499ab1d6d1d421228859d13ac45ad1d437Spike Sprague                        case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD: {
653ae5fa1499ab1d6d1d421228859d13ac45ad1d437Spike Sprague                            mController.goToNextItem();
654ae5fa1499ab1d6d1d421228859d13ac45ad1d437Spike Sprague                            return true;
655ae5fa1499ab1d6d1d421228859d13ac45ad1d437Spike Sprague                        }
656ae5fa1499ab1d6d1d421228859d13ac45ad1d437Spike Sprague                        case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD: {
657ae5fa1499ab1d6d1d421228859d13ac45ad1d437Spike Sprague                            boolean wentToPrevious = mController.goToPreviousItem();
658ae5fa1499ab1d6d1d421228859d13ac45ad1d437Spike Sprague                            if (!wentToPrevious) {
659ae5fa1499ab1d6d1d421228859d13ac45ad1d437Spike Sprague                                // at beginning of filmstrip, hide and go back to preview
660ae5fa1499ab1d6d1d421228859d13ac45ad1d437Spike Sprague                                mActivity.getCameraAppUI().hideFilmstrip();
661ae5fa1499ab1d6d1d421228859d13ac45ad1d437Spike Sprague                            }
662ae5fa1499ab1d6d1d421228859d13ac45ad1d437Spike Sprague                            return true;
663ae5fa1499ab1d6d1d421228859d13ac45ad1d437Spike Sprague                        }
664ae5fa1499ab1d6d1d421228859d13ac45ad1d437Spike Sprague                        case AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS: {
665ae5fa1499ab1d6d1d421228859d13ac45ad1d437Spike Sprague                            // Prevent the view group itself from being selected.
666ae5fa1499ab1d6d1d421228859d13ac45ad1d437Spike Sprague                            // Instead, select the item in the center
6678ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde                            final ViewItem currentItem = mViewItems[BUFFER_CENTER];
668ae5fa1499ab1d6d1d421228859d13ac45ad1d437Spike Sprague                            currentItem.getView().performAccessibilityAction(action, args);
669ae5fa1499ab1d6d1d421228859d13ac45ad1d437Spike Sprague                            return true;
670ae5fa1499ab1d6d1d421228859d13ac45ad1d437Spike Sprague                        }
671ae5fa1499ab1d6d1d421228859d13ac45ad1d437Spike Sprague                    }
672ae5fa1499ab1d6d1d421228859d13ac45ad1d437Spike Sprague                }
673ae5fa1499ab1d6d1d421228859d13ac45ad1d437Spike Sprague                return super.performAccessibilityAction(host, action, args);
674ae5fa1499ab1d6d1d421228859d13ac45ad1d437Spike Sprague            }
675ae5fa1499ab1d6d1d421228859d13ac45ad1d437Spike Sprague        });
67649b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong    }
67749b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong
6788ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde    private void recycleView(View view, int index) {
6795a0be52d3db2398cfe01d18adf779d9e443edf82Alan Newberger        Log.v(TAG, "recycleView");
680b7d0a227fb0f842135dbc92a7e15c571f0e1a946Spike Sprague        final int viewType = (Integer) view.getTag(R.id.mediadata_tag_viewtype);
681bb3b39b13a7097a55e585e8d6197057500847708Spike Sprague        if (viewType > 0) {
682bb3b39b13a7097a55e585e8d6197057500847708Spike Sprague            Queue<View> recycledViewsForType = recycledViews.get(viewType);
683bb3b39b13a7097a55e585e8d6197057500847708Spike Sprague            if (recycledViewsForType == null) {
684bb3b39b13a7097a55e585e8d6197057500847708Spike Sprague                recycledViewsForType = new ArrayDeque<View>();
685bb3b39b13a7097a55e585e8d6197057500847708Spike Sprague                recycledViews.put(viewType, recycledViewsForType);
686bb3b39b13a7097a55e585e8d6197057500847708Spike Sprague            }
687bb3b39b13a7097a55e585e8d6197057500847708Spike Sprague            recycledViewsForType.offer(view);
68843bf03f6e7f9a71b7553e7282b5799798997caf7Sam Judd        }
68943bf03f6e7f9a71b7553e7282b5799798997caf7Sam Judd    }
69043bf03f6e7f9a71b7553e7282b5799798997caf7Sam Judd
6918ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde    private View getRecycledView(int index) {
6928ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde        final int viewType = mDataAdapter.getItemViewType(index);
69343bf03f6e7f9a71b7553e7282b5799798997caf7Sam Judd        Queue<View> recycledViewsForType = recycledViews.get(viewType);
6948ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde        View view = null;
69543bf03f6e7f9a71b7553e7282b5799798997caf7Sam Judd        if (recycledViewsForType != null) {
6968ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde            view = recycledViewsForType.poll();
69743bf03f6e7f9a71b7553e7282b5799798997caf7Sam Judd        }
6988ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde        if (view != null) {
6998ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde            view.setVisibility(View.GONE);
700987ee64612e2510004fdf08536746c87234d01c1Paul Rohde        }
7015a0be52d3db2398cfe01d18adf779d9e443edf82Alan Newberger        Log.v(TAG, "getRecycledView, recycled=" + (view != null));
7028ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde        return view;
70343bf03f6e7f9a71b7553e7282b5799798997caf7Sam Judd    }
70443bf03f6e7f9a71b7553e7282b5799798997caf7Sam Judd
7058e5e4eedb25a4589342660b928935acca4375fc7Angus Kong    /**
7068e5e4eedb25a4589342660b928935acca4375fc7Angus Kong     * Returns the controller.
7078e5e4eedb25a4589342660b928935acca4375fc7Angus Kong     *
7088e5e4eedb25a4589342660b928935acca4375fc7Angus Kong     * @return The {@code Controller}.
7098e5e4eedb25a4589342660b928935acca4375fc7Angus Kong     */
710628481532e1e58a0a26330d238e850aec5657bceAngus Kong    public FilmstripController getController() {
7116d23f4c77fde7ddf70e0edd48f46cc22b7f7320eAngus Kong        return mController;
7126d23f4c77fde7ddf70e0edd48f46cc22b7f7320eAngus Kong    }
7136d23f4c77fde7ddf70e0edd48f46cc22b7f7320eAngus Kong
71429d9b3cd08a7b31b8ccecf4792d5ae981faa205cSam Judd    /**
71529d9b3cd08a7b31b8ccecf4792d5ae981faa205cSam Judd     * Returns the draw area width of the current item.
71629d9b3cd08a7b31b8ccecf4792d5ae981faa205cSam Judd     */
71729d9b3cd08a7b31b8ccecf4792d5ae981faa205cSam Judd    public int  getCurrentItemLeft() {
7188ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde        return mViewItems[BUFFER_CENTER].getDrawAreaLeft();
71929d9b3cd08a7b31b8ccecf4792d5ae981faa205cSam Judd    }
72029d9b3cd08a7b31b8ccecf4792d5ae981faa205cSam Judd
72101054e922aa547b937a71131ad04c6bd15356240Angus Kong    private void setListener(FilmstripController.FilmstripListener l) {
72287f9a62f473a3c57505e2877b0a46ab3cb5e62acAngus Kong        mListener = l;
72387f9a62f473a3c57505e2877b0a46ab3cb5e62acAngus Kong    }
72487f9a62f473a3c57505e2877b0a46ab3cb5e62acAngus Kong
725628481532e1e58a0a26330d238e850aec5657bceAngus Kong    private void setViewGap(int viewGap) {
726faaee012acc80ad369cb03df9c196e48140f1e7bAngus Kong        mViewGapInPixel = viewGap;
727e78541f2edf2f02660c34660114e76d53b32ab8bAngus Kong    }
728e78541f2edf2f02660c34660114e76d53b32ab8bAngus Kong
72917e669d695ef2a38d28d5ba2512937836293e9caAngus Kong    /**
730c8924b2ec3788ca6d0f0af5ca749e5fe7be125ceAndy Huibers     * Called after current item or zoom level has changed.
731c8924b2ec3788ca6d0f0af5ca749e5fe7be125ceAndy Huibers     */
732c8924b2ec3788ca6d0f0af5ca749e5fe7be125ceAndy Huibers    public void zoomAtIndexChanged() {
7338ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde        if (mViewItems[BUFFER_CENTER] == null) {
734c17a56643186dee671bf6e7dec54cd558a7ecb2fSascha Haeberling            return;
735c17a56643186dee671bf6e7dec54cd558a7ecb2fSascha Haeberling        }
7368ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde        int index = mViewItems[BUFFER_CENTER].getAdapterIndex();
7378ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde        mListener.onZoomAtIndexChanged(index, mScale);
738c8924b2ec3788ca6d0f0af5ca749e5fe7be125ceAndy Huibers    }
739c8924b2ec3788ca6d0f0af5ca749e5fe7be125ceAndy Huibers
740c8924b2ec3788ca6d0f0af5ca749e5fe7be125ceAndy Huibers    /**
74117e669d695ef2a38d28d5ba2512937836293e9caAngus Kong     * Checks if the data is at the center.
74217e669d695ef2a38d28d5ba2512937836293e9caAngus Kong     *
7438ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde     * @param index The index of the item in the data adapter to check.
74417e669d695ef2a38d28d5ba2512937836293e9caAngus Kong     * @return {@code True} if the data is currently at the center.
74517e669d695ef2a38d28d5ba2512937836293e9caAngus Kong     */
7468ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde    private boolean isItemAtIndexCentered(int index) {
7478ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde        if (mViewItems[BUFFER_CENTER] == null) {
7483d0b691b8f7a844093e8de9267ffa2ce16d0c5a9Angus Kong            return false;
7493d0b691b8f7a844093e8de9267ffa2ce16d0c5a9Angus Kong        }
7508ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde        if (mViewItems[BUFFER_CENTER].getAdapterIndex() == index
751ec2fb473c094f8dfd1cc0cf5205d470e412d3919Angus Kong                && isCurrentItemCentered()) {
75249b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong            return true;
75349b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong        }
75449b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong        return false;
75549b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong    }
75649b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong
757653c43be6e58be54cf2ac3c8efb2d65ac20bd03bAngus Kong    private void measureViewItem(ViewItem item, int boundWidth, int boundHeight) {
7588ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde        int index = item.getAdapterIndex();
7598ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde        FilmstripItem imageData = mDataAdapter.getFilmstripItemAt(index);
76001054e922aa547b937a71131ad04c6bd15356240Angus Kong        if (imageData == null) {
761c14b6ba434575b7d795a8cd681821f86a39bc371Paul Rohde            Log.w(TAG, "measureViewItem() - Trying to measure a null item!");
762f9ef1ae9d31977c106eefc7ab1a74158bcdcfd39Erin Dahlgren            return;
763f9ef1ae9d31977c106eefc7ab1a74158bcdcfd39Erin Dahlgren        }
764f9ef1ae9d31977c106eefc7ab1a74158bcdcfd39Erin Dahlgren
7658ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde        Point dim = CameraUtil.resizeToFill(
76651cafa0a35546a42c573357aa7a031a79cf9ba1bAlan Newberger              imageData.getDimensions().getWidth(),
76751cafa0a35546a42c573357aa7a031a79cf9ba1bAlan Newberger              imageData.getDimensions().getHeight(),
76851cafa0a35546a42c573357aa7a031a79cf9ba1bAlan Newberger              imageData.getOrientation(),
7698ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde              boundWidth,
7708ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde              boundHeight);
771653c43be6e58be54cf2ac3c8efb2d65ac20bd03bAngus Kong
772c195e7a9208c90d7a55e184d4b2b9c747e9c90f1Angus Kong        item.measure(MeasureSpec.makeMeasureSpec(dim.x, MeasureSpec.EXACTLY),
773c195e7a9208c90d7a55e184d4b2b9c747e9c90f1Angus Kong                MeasureSpec.makeMeasureSpec(dim.y, MeasureSpec.EXACTLY));
774653c43be6e58be54cf2ac3c8efb2d65ac20bd03bAngus Kong    }
775653c43be6e58be54cf2ac3c8efb2d65ac20bd03bAngus Kong
77649b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong    @Override
77749b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
77849b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
77949b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong
78049b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong        int boundWidth = MeasureSpec.getSize(widthMeasureSpec);
78149b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong        int boundHeight = MeasureSpec.getSize(heightMeasureSpec);
782b477b42906a3f8b299ddaeede3940e6344fb6825Angus Kong        if (boundWidth == 0 || boundHeight == 0) {
783b477b42906a3f8b299ddaeede3940e6344fb6825Angus Kong            // Either width or height is unknown, can't measure children yet.
784b477b42906a3f8b299ddaeede3940e6344fb6825Angus Kong            return;
785b477b42906a3f8b299ddaeede3940e6344fb6825Angus Kong        }
786b477b42906a3f8b299ddaeede3940e6344fb6825Angus Kong
7878ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde        for (ViewItem item : mViewItems) {
788653c43be6e58be54cf2ac3c8efb2d65ac20bd03bAngus Kong            if (item != null) {
789653c43be6e58be54cf2ac3c8efb2d65ac20bd03bAngus Kong                measureViewItem(item, boundWidth, boundHeight);
79088ef7664ba6a888fa5da8693091674e152b56192Sascha Haeberling            }
79149b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong        }
7927d2388d96522975307cf5c40f7ac911ad963c44dAngus Kong        clampCenterX();
7938de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu        // Measure zoom view
794c02b13af4c44fe23d4a563d3c2df1ca50e44affcAngus Kong        mZoomView.measure(MeasureSpec.makeMeasureSpec(widthMeasureSpec, MeasureSpec.EXACTLY),
795c02b13af4c44fe23d4a563d3c2df1ca50e44affcAngus Kong                MeasureSpec.makeMeasureSpec(heightMeasureSpec, MeasureSpec.EXACTLY));
79649b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong    }
79749b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong
7988ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde    private int findTheNearestView(int viewX) {
79949b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong        int nearest = 0;
8008969a67c926608737f3e49f2d2240298372e8416Angus Kong        // Find the first non-null ViewItem.
801f1f5186f7d3fd8bf35009d9e65a43914c664d82bSascha Haeberling        while (nearest < BUFFER_SIZE
8028ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde                && (mViewItems[nearest] == null || mViewItems[nearest].getLeftPosition() == -1)) {
803f1f5186f7d3fd8bf35009d9e65a43914c664d82bSascha Haeberling            nearest++;
804f1f5186f7d3fd8bf35009d9e65a43914c664d82bSascha Haeberling        }
8058969a67c926608737f3e49f2d2240298372e8416Angus Kong        // No existing available ViewItem
806f1f5186f7d3fd8bf35009d9e65a43914c664d82bSascha Haeberling        if (nearest == BUFFER_SIZE) {
807f1f5186f7d3fd8bf35009d9e65a43914c664d82bSascha Haeberling            return -1;
808f1f5186f7d3fd8bf35009d9e65a43914c664d82bSascha Haeberling        }
8093044d8c577432d6e9721fc8b26ac2afbbaf21266Erin Dahlgren
8108ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde        int min = Math.abs(viewX - mViewItems[nearest].getCenterX());
81149b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong
8128ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde        for (int i = nearest + 1; i < BUFFER_SIZE && mViewItems[i] != null; i++) {
813f1f5186f7d3fd8bf35009d9e65a43914c664d82bSascha Haeberling            // Not measured yet.
8148ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde            if (mViewItems[i].getLeftPosition() == -1) {
815f1f5186f7d3fd8bf35009d9e65a43914c664d82bSascha Haeberling                continue;
8163f192ccf03d0d25521b96304c4464b4bad28150aSascha Haeberling            }
81749b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong
8188ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde            int centerX = mViewItems[i].getCenterX();
8198ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde            int dist = Math.abs(viewX - centerX);
82049b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong            if (dist < min) {
82149b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong                min = dist;
8228ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde                nearest = i;
82349b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong            }
82449b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong        }
82549b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong        return nearest;
82649b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong    }
82749b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong
8288ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde    private ViewItem buildViewItemAt(int index) {
829281624c713fb29d4be690fc980d2001efcf20dafAlan Newberger        if (mActivity.isDestroyed()) {
830281624c713fb29d4be690fc980d2001efcf20dafAlan Newberger            // Loading item data is call from multiple AsyncTasks and the
8318ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde            // activity may be finished when buildViewItemAt is called.
832281624c713fb29d4be690fc980d2001efcf20dafAlan Newberger            Log.d(TAG, "Activity destroyed, don't load data");
833281624c713fb29d4be690fc980d2001efcf20dafAlan Newberger            return null;
834281624c713fb29d4be690fc980d2001efcf20dafAlan Newberger        }
8358ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde        FilmstripItem data = mDataAdapter.getFilmstripItemAt(index);
836f1f5186f7d3fd8bf35009d9e65a43914c664d82bSascha Haeberling        if (data == null) {
837f1f5186f7d3fd8bf35009d9e65a43914c664d82bSascha Haeberling            return null;
838f1f5186f7d3fd8bf35009d9e65a43914c664d82bSascha Haeberling        }
839caca8c7b2e97bd76e4df1ea98c08acaa48b21557Andy Huibers
840a62ed04fc1af31c1fdfa0e9ac9cc0acfec1b1f12Alan Newberger        // Always scale by fixed filmstrip scale, since we only show items when
841a62ed04fc1af31c1fdfa0e9ac9cc0acfec1b1f12Alan Newberger        // in filmstrip. Preloading images with a different scale and bounds
842a62ed04fc1af31c1fdfa0e9ac9cc0acfec1b1f12Alan Newberger        // interferes with caching.
84377d9f023e76816e5da7bd067ad46cc0e9c98623fPaul Rohde        int width = Math.round(FULL_SCREEN_SCALE * getWidth());
84477d9f023e76816e5da7bd067ad46cc0e9c98623fPaul Rohde        int height = Math.round(FULL_SCREEN_SCALE * getHeight());
84577d9f023e76816e5da7bd067ad46cc0e9c98623fPaul Rohde
846a62ed04fc1af31c1fdfa0e9ac9cc0acfec1b1f12Alan Newberger        Log.v(TAG, "suggesting item bounds: " + width + "x" + height);
8474021c896985d9ba43db0199f259ce4b8bfc0dc88Sam Judd        mDataAdapter.suggestViewSizeBound(width, height);
8484021c896985d9ba43db0199f259ce4b8bfc0dc88Sam Judd
8498ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde        View recycled = getRecycledView(index);
85077d9f023e76816e5da7bd067ad46cc0e9c98623fPaul Rohde        View v = mDataAdapter.getView(recycled, index, mVideoClickedCallback);
851f1f5186f7d3fd8bf35009d9e65a43914c664d82bSascha Haeberling        if (v == null) {
852f1f5186f7d3fd8bf35009d9e65a43914c664d82bSascha Haeberling            return null;
853f1f5186f7d3fd8bf35009d9e65a43914c664d82bSascha Haeberling        }
8548ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde        ViewItem item = new ViewItem(index, v, data, this);
8551f9db2dfe59c33228103c54523281501ef52c9baAngus Kong        item.addViewToHierarchy();
8568969a67c926608737f3e49f2d2240298372e8416Angus Kong        return item;
85749b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong    }
85849b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong
85977d9f023e76816e5da7bd067ad46cc0e9c98623fPaul Rohde    private void renderFullRes(int bufferIndex) {
8608ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde        ViewItem item = mViewItems[bufferIndex];
86177d9f023e76816e5da7bd067ad46cc0e9c98623fPaul Rohde        if (item == null) {
862caca8c7b2e97bd76e4df1ea98c08acaa48b21557Andy Huibers            return;
8638ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde        }
86477d9f023e76816e5da7bd067ad46cc0e9c98623fPaul Rohde
86577d9f023e76816e5da7bd067ad46cc0e9c98623fPaul Rohde        item.renderFullRes();
86677d9f023e76816e5da7bd067ad46cc0e9c98623fPaul Rohde    }
86777d9f023e76816e5da7bd067ad46cc0e9c98623fPaul Rohde
86877d9f023e76816e5da7bd067ad46cc0e9c98623fPaul Rohde    private void renderThumbnail(int bufferIndex) {
86977d9f023e76816e5da7bd067ad46cc0e9c98623fPaul Rohde        ViewItem item = mViewItems[bufferIndex];
87077d9f023e76816e5da7bd067ad46cc0e9c98623fPaul Rohde        if (item == null) {
87177d9f023e76816e5da7bd067ad46cc0e9c98623fPaul Rohde            return;
87277d9f023e76816e5da7bd067ad46cc0e9c98623fPaul Rohde        }
87377d9f023e76816e5da7bd067ad46cc0e9c98623fPaul Rohde
87477d9f023e76816e5da7bd067ad46cc0e9c98623fPaul Rohde        item.renderThumbnail();
875caca8c7b2e97bd76e4df1ea98c08acaa48b21557Andy Huibers    }
876caca8c7b2e97bd76e4df1ea98c08acaa48b21557Andy Huibers
87777d9f023e76816e5da7bd067ad46cc0e9c98623fPaul Rohde    private void renderAllThumbnails() {
8788ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde        for(int i = 0; i < BUFFER_SIZE; i++) {
87977d9f023e76816e5da7bd067ad46cc0e9c98623fPaul Rohde            renderThumbnail(i);
8808ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde        }
8818ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde    }
8828ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde
8838ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde    private void removeItem(int bufferIndex) {
8848ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde        if (bufferIndex >= mViewItems.length || mViewItems[bufferIndex] == null) {
885f1f5186f7d3fd8bf35009d9e65a43914c664d82bSascha Haeberling            return;
886f1f5186f7d3fd8bf35009d9e65a43914c664d82bSascha Haeberling        }
8878ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde        FilmstripItem data = mDataAdapter.getFilmstripItemAt(
8888ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde              mViewItems[bufferIndex].getAdapterIndex());
889f9ef1ae9d31977c106eefc7ab1a74158bcdcfd39Erin Dahlgren        if (data == null) {
890c14b6ba434575b7d795a8cd681821f86a39bc371Paul Rohde            Log.w(TAG, "removeItem() - Trying to remove a null item!");
891f9ef1ae9d31977c106eefc7ab1a74158bcdcfd39Erin Dahlgren            return;
892f9ef1ae9d31977c106eefc7ab1a74158bcdcfd39Erin Dahlgren        }
8935a0be52d3db2398cfe01d18adf779d9e443edf82Alan Newberger        mViewItems[bufferIndex].removeViewFromHierarchy();
8948ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde        mViewItems[bufferIndex] = null;
895750e8ec8af168afd318d47082b326b95f1cca517Angus Kong    }
896750e8ec8af168afd318d47082b326b95f1cca517Angus Kong
897f1f5186f7d3fd8bf35009d9e65a43914c664d82bSascha Haeberling    /**
898f1f5186f7d3fd8bf35009d9e65a43914c664d82bSascha Haeberling     * We try to keep the one closest to the center of the screen at position
8998ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde     * BUFFER_CENTER.
900f1f5186f7d3fd8bf35009d9e65a43914c664d82bSascha Haeberling     */
90149b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong    private void stepIfNeeded() {
902628481532e1e58a0a26330d238e850aec5657bceAngus Kong        if (!inFilmstrip() && !inFullScreen()) {
903f1f5186f7d3fd8bf35009d9e65a43914c664d82bSascha Haeberling            // The good timing to step to the next view is when everything is
90437f3611bbdc787c996a99894dcef7d8fb77e3f7cSascha Haeberling            // not in transition.
905a6a6a730074e9fedd56c41cd8fa8f1212cfac108Angus Kong            return;
906a6a6a730074e9fedd56c41cd8fa8f1212cfac108Angus Kong        }
9078ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde        final int nearestBufferIndex = findTheNearestView(mCenterX);
9088ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde        // if the nearest view is the current view, or there is no nearest
9098ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde        // view, then we do not need to adjust the view buffers.
9108ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde        if (nearestBufferIndex == -1 || nearestBufferIndex == BUFFER_CENTER) {
911f1f5186f7d3fd8bf35009d9e65a43914c664d82bSascha Haeberling            return;
91253aedc0ee671c2d11341a66c444bbb6cdc97b150Angus Kong        }
9138ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde        int prevIndex = (mViewItems[BUFFER_CENTER] == null ? -1 :
9148ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde              mViewItems[BUFFER_CENTER].getAdapterIndex());
9158ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde        final int adjust = nearestBufferIndex - BUFFER_CENTER;
91649b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong        if (adjust > 0) {
9178ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde            // Remove from beginning of the buffer.
91849b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong            for (int k = 0; k < adjust; k++) {
9198969a67c926608737f3e49f2d2240298372e8416Angus Kong                removeItem(k);
92049b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong            }
9218ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde            // Shift items inside the buffer
92249b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong            for (int k = 0; k + adjust < BUFFER_SIZE; k++) {
9238ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde                mViewItems[k] = mViewItems[k + adjust];
92449b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong            }
9258ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde            // Fill the end with new items.
92649b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong            for (int k = BUFFER_SIZE - adjust; k < BUFFER_SIZE; k++) {
9278ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde                mViewItems[k] = null;
9288ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde                if (mViewItems[k - 1] != null) {
9298ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde                    mViewItems[k] = buildViewItemAt(mViewItems[k - 1].getAdapterIndex() + 1);
930750e8ec8af168afd318d47082b326b95f1cca517Angus Kong                }
93149b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong            }
93253aedc0ee671c2d11341a66c444bbb6cdc97b150Angus Kong            adjustChildZOrder();
93349b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong        } else {
9348ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde            // Remove from the end of the buffer
93549b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong            for (int k = BUFFER_SIZE - 1; k >= BUFFER_SIZE + adjust; k--) {
9368969a67c926608737f3e49f2d2240298372e8416Angus Kong                removeItem(k);
93749b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong            }
9388ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde            // Shift items inside the buffer
93949b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong            for (int k = BUFFER_SIZE - 1; k + adjust >= 0; k--) {
9408ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde                mViewItems[k] = mViewItems[k + adjust];
94149b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong            }
9428ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde            // Fill the beginning with new items.
94349b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong            for (int k = -1 - adjust; k >= 0; k--) {
9448ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde                mViewItems[k] = null;
9458ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde                if (mViewItems[k + 1] != null) {
9468ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde                    mViewItems[k] = buildViewItemAt(mViewItems[k + 1].getAdapterIndex() - 1);
947750e8ec8af168afd318d47082b326b95f1cca517Angus Kong                }
94849b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong            }
94949b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong        }
95053aedc0ee671c2d11341a66c444bbb6cdc97b150Angus Kong        invalidate();
95137f3611bbdc787c996a99894dcef7d8fb77e3f7cSascha Haeberling        if (mListener != null) {
9528ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde            mListener.onDataFocusChanged(prevIndex, mViewItems[BUFFER_CENTER]
9538ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde                  .getAdapterIndex());
9548ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde            final int firstVisible = mViewItems[BUFFER_CENTER].getAdapterIndex() - 2;
955de3e9abaa241dc2aa66e5d02ba8b7bd35e0d8f00Sam Judd            final int visibleItemCount = firstVisible + BUFFER_SIZE;
956de3e9abaa241dc2aa66e5d02ba8b7bd35e0d8f00Sam Judd            final int totalItemCount = mDataAdapter.getTotalNumber();
957de3e9abaa241dc2aa66e5d02ba8b7bd35e0d8f00Sam Judd            mListener.onScroll(firstVisible, visibleItemCount, totalItemCount);
95837f3611bbdc787c996a99894dcef7d8fb77e3f7cSascha Haeberling        }
959c8924b2ec3788ca6d0f0af5ca749e5fe7be125ceAndy Huibers        zoomAtIndexChanged();
96049b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong    }
96149b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong
9627d2388d96522975307cf5c40f7ac911ad963c44dAngus Kong    /**
963280fd3edae1dc35ac1015bcc9532477169fad3aaSascha Haeberling     * Check the bounds of {@code mCenterX}. Always call this function after: 1.
964280fd3edae1dc35ac1015bcc9532477169fad3aaSascha Haeberling     * Any changes to {@code mCenterX}. 2. Any size change of the view items.
9651f36cf1c97faf2b7824bbecd62942456847f0e13Angus Kong     *
9661f36cf1c97faf2b7824bbecd62942456847f0e13Angus Kong     * @return Whether clamp happened.
9677d2388d96522975307cf5c40f7ac911ad963c44dAngus Kong     */
9681f36cf1c97faf2b7824bbecd62942456847f0e13Angus Kong    private boolean clampCenterX() {
9698ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde        ViewItem currentItem = mViewItems[BUFFER_CENTER];
9708ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde        if (currentItem == null) {
9711f36cf1c97faf2b7824bbecd62942456847f0e13Angus Kong            return false;
972f1f5186f7d3fd8bf35009d9e65a43914c664d82bSascha Haeberling        }
97349b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong
9745fbe134c3cf1f7c2c82ccef223cab7eb43cd92f1Angus Kong        boolean stopScroll = false;
97586f1d1f3664c909d1d949951056c7bb1e9627324Alan Newberger        if (currentItem.getAdapterIndex() == 0 && mCenterX < currentItem.getCenterX()) {
97686f1d1f3664c909d1d949951056c7bb1e9627324Alan Newberger            // Stop at the first ViewItem.
9777d2388d96522975307cf5c40f7ac911ad963c44dAngus Kong            stopScroll = true;
97886f1d1f3664c909d1d949951056c7bb1e9627324Alan Newberger        } else if (currentItem.getAdapterIndex() == mDataAdapter.getTotalNumber() - 1
9798ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde                && mCenterX > currentItem.getCenterX()) {
9805fbe134c3cf1f7c2c82ccef223cab7eb43cd92f1Angus Kong            // Stop at the end.
9815fbe134c3cf1f7c2c82ccef223cab7eb43cd92f1Angus Kong            stopScroll = true;
9825fbe134c3cf1f7c2c82ccef223cab7eb43cd92f1Angus Kong        }
9835fbe134c3cf1f7c2c82ccef223cab7eb43cd92f1Angus Kong
9847d2388d96522975307cf5c40f7ac911ad963c44dAngus Kong        if (stopScroll) {
9858ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde            mCenterX = currentItem.getCenterX();
98649b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong        }
9873044d8c577432d6e9721fc8b26ac2afbbaf21266Erin Dahlgren
9881f36cf1c97faf2b7824bbecd62942456847f0e13Angus Kong        return stopScroll;
98949b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong    }
99049b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong
99153aedc0ee671c2d11341a66c444bbb6cdc97b150Angus Kong    /**
9928ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde     * Reorders the child views to be consistent with their index. This method
993280fd3edae1dc35ac1015bcc9532477169fad3aaSascha Haeberling     * should be called after adding/removing views.
99453aedc0ee671c2d11341a66c444bbb6cdc97b150Angus Kong     */
995a6a6a730074e9fedd56c41cd8fa8f1212cfac108Angus Kong    private void adjustChildZOrder() {
996a6a6a730074e9fedd56c41cd8fa8f1212cfac108Angus Kong        for (int i = BUFFER_SIZE - 1; i >= 0; i--) {
9978ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde            if (mViewItems[i] == null) {
998f1f5186f7d3fd8bf35009d9e65a43914c664d82bSascha Haeberling                continue;
9993f192ccf03d0d25521b96304c4464b4bad28150aSascha Haeberling            }
10008ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde            mViewItems[i].bringViewToFront();
1001a6a6a730074e9fedd56c41cd8fa8f1212cfac108Angus Kong        }
1002d70b22919f8d212bf11abd6e6724b3a395ef02c1Alan Newberger        // ZoomView is a special case to always be in the front.
1003563a289f1aed4dd7bf760959851ff6ad72c7627fAngus Kong        bringChildToFront(mZoomView);
1004a6a6a730074e9fedd56c41cd8fa8f1212cfac108Angus Kong    }
1005a6a6a730074e9fedd56c41cd8fa8f1212cfac108Angus Kong
1006f1f5186f7d3fd8bf35009d9e65a43914c664d82bSascha Haeberling    /**
10078ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde     * Returns the index of the current item, or -1 if there is no data.
1008c02b13af4c44fe23d4a563d3c2df1ca50e44affcAngus Kong     */
10098ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde    private int getCurrentItemAdapterIndex() {
10108ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde        ViewItem current = mViewItems[BUFFER_CENTER];
1011f1f5186f7d3fd8bf35009d9e65a43914c664d82bSascha Haeberling        if (current == null) {
1012f1f5186f7d3fd8bf35009d9e65a43914c664d82bSascha Haeberling            return -1;
1013f1f5186f7d3fd8bf35009d9e65a43914c664d82bSascha Haeberling        }
10148ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde        return current.getAdapterIndex();
1015f1f5186f7d3fd8bf35009d9e65a43914c664d82bSascha Haeberling    }
1016f1f5186f7d3fd8bf35009d9e65a43914c664d82bSascha Haeberling
1017f1f5186f7d3fd8bf35009d9e65a43914c664d82bSascha Haeberling    /**
1018280fd3edae1dc35ac1015bcc9532477169fad3aaSascha Haeberling     * Keep the current item in the center. This functions does not check if the
1019280fd3edae1dc35ac1015bcc9532477169fad3aaSascha Haeberling     * current item is null.
10207d2388d96522975307cf5c40f7ac911ad963c44dAngus Kong     */
10218ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde    private void scrollCurrentItemToCenter() {
10228ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde        final ViewItem currItem = mViewItems[BUFFER_CENTER];
1023ec2fb473c094f8dfd1cc0cf5205d470e412d3919Angus Kong        if (currItem == null) {
1024ec2fb473c094f8dfd1cc0cf5205d470e412d3919Angus Kong            return;
1025ec2fb473c094f8dfd1cc0cf5205d470e412d3919Angus Kong        }
1026ec2fb473c094f8dfd1cc0cf5205d470e412d3919Angus Kong        final int currentViewCenter = currItem.getCenterX();
10277d2388d96522975307cf5c40f7ac911ad963c44dAngus Kong        if (mController.isScrolling() || mIsUserScrolling
1028ec2fb473c094f8dfd1cc0cf5205d470e412d3919Angus Kong                || isCurrentItemCentered()) {
10298d3552d334196a09e20c83a5296f0b373041032cPaul Rohde            Log.d(TAG, "[fling] mController.isScrolling() - " + mController.isScrolling());
103047721fa8f2dc13cb565ae5629a417dc4ed298e81Angus Kong            return;
103147721fa8f2dc13cb565ae5629a417dc4ed298e81Angus Kong        }
10327d2388d96522975307cf5c40f7ac911ad963c44dAngus Kong
10337d2388d96522975307cf5c40f7ac911ad963c44dAngus Kong        int snapInTime = (int) (SNAP_IN_CENTER_TIME_MS
10347d2388d96522975307cf5c40f7ac911ad963c44dAngus Kong                * ((float) Math.abs(mCenterX - currentViewCenter))
1035280fd3edae1dc35ac1015bcc9532477169fad3aaSascha Haeberling                / mDrawArea.width());
10368ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde
1037c14b6ba434575b7d795a8cd681821f86a39bc371Paul Rohde        Log.d(TAG, "[fling] Scroll to center.");
10387d2388d96522975307cf5c40f7ac911ad963c44dAngus Kong        mController.scrollToPosition(currentViewCenter,
10398ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde              snapInTime, false);
104047721fa8f2dc13cb565ae5629a417dc4ed298e81Angus Kong    }
104147721fa8f2dc13cb565ae5629a417dc4ed298e81Angus Kong
104280af7dd871547d03ee9ff5023cbec00b1d8677eeAngus Kong    /**
104380af7dd871547d03ee9ff5023cbec00b1d8677eeAngus Kong     * Translates the {@link ViewItem} on the left of the current one to match
104480af7dd871547d03ee9ff5023cbec00b1d8677eeAngus Kong     * the full-screen layout. In full-screen, we show only one {@link ViewItem}
104580af7dd871547d03ee9ff5023cbec00b1d8677eeAngus Kong     * which occupies the whole screen. The other left ones are put on the left
1046d028dd4908e68c7d4d0e7438efa74fa2c67e5f2aAngus Kong     * side in full scales. Does nothing if there's no next item.
104780af7dd871547d03ee9ff5023cbec00b1d8677eeAngus Kong     *
10488ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde     * @param index The index of the current one to be translated.
104980af7dd871547d03ee9ff5023cbec00b1d8677eeAngus Kong     * @param drawAreaWidth The width of the current draw area.
105080af7dd871547d03ee9ff5023cbec00b1d8677eeAngus Kong     * @param scaleFraction A {@code float} between 0 and 1. 0 if the current
10511f9db2dfe59c33228103c54523281501ef52c9baAngus Kong     *            scale is {@code FILM_STRIP_SCALE}. 1 if the current scale is
10521f9db2dfe59c33228103c54523281501ef52c9baAngus Kong     *            {@code FULL_SCREEN_SCALE}.
105380af7dd871547d03ee9ff5023cbec00b1d8677eeAngus Kong     */
105480af7dd871547d03ee9ff5023cbec00b1d8677eeAngus Kong    private void translateLeftViewItem(
10558ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde            int index, int drawAreaWidth, float scaleFraction) {
10568ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde        if (index < 0 || index > BUFFER_SIZE - 1) {
1057c14b6ba434575b7d795a8cd681821f86a39bc371Paul Rohde            Log.w(TAG, "translateLeftViewItem() - Index out of bound!");
105880af7dd871547d03ee9ff5023cbec00b1d8677eeAngus Kong            return;
105980af7dd871547d03ee9ff5023cbec00b1d8677eeAngus Kong        }
106080af7dd871547d03ee9ff5023cbec00b1d8677eeAngus Kong
10618ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde        final ViewItem curr = mViewItems[index];
10628ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde        final ViewItem next = mViewItems[index + 1];
106380af7dd871547d03ee9ff5023cbec00b1d8677eeAngus Kong        if (curr == null || next == null) {
1064c14b6ba434575b7d795a8cd681821f86a39bc371Paul Rohde            Log.w(TAG, "translateLeftViewItem() - Invalid view item (curr or next == null). curr = "
10658ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde                    + index);
106680af7dd871547d03ee9ff5023cbec00b1d8677eeAngus Kong            return;
106780af7dd871547d03ee9ff5023cbec00b1d8677eeAngus Kong        }
106880af7dd871547d03ee9ff5023cbec00b1d8677eeAngus Kong
106980af7dd871547d03ee9ff5023cbec00b1d8677eeAngus Kong        final int currCenterX = curr.getCenterX();
107080af7dd871547d03ee9ff5023cbec00b1d8677eeAngus Kong        final int nextCenterX = next.getCenterX();
107180af7dd871547d03ee9ff5023cbec00b1d8677eeAngus Kong        final int translate = (int) ((nextCenterX - drawAreaWidth
1072280fd3edae1dc35ac1015bcc9532477169fad3aaSascha Haeberling                - currCenterX) * scaleFraction);
107380af7dd871547d03ee9ff5023cbec00b1d8677eeAngus Kong
10741f9db2dfe59c33228103c54523281501ef52c9baAngus Kong        curr.layoutWithTranslationX(mDrawArea, mCenterX, mScale);
10751f9db2dfe59c33228103c54523281501ef52c9baAngus Kong        curr.setAlpha(1f);
10764021c896985d9ba43db0199f259ce4b8bfc0dc88Sam Judd        curr.setVisibility(VISIBLE);
107780af7dd871547d03ee9ff5023cbec00b1d8677eeAngus Kong
107880af7dd871547d03ee9ff5023cbec00b1d8677eeAngus Kong        if (inFullScreen()) {
10798ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde            curr.setTranslationX(translate * (mCenterX - currCenterX) /
10808ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde                  (nextCenterX - currCenterX));
108180af7dd871547d03ee9ff5023cbec00b1d8677eeAngus Kong        } else {
10821f9db2dfe59c33228103c54523281501ef52c9baAngus Kong            curr.setTranslationX(translate);
108380af7dd871547d03ee9ff5023cbec00b1d8677eeAngus Kong        }
108480af7dd871547d03ee9ff5023cbec00b1d8677eeAngus Kong    }
108580af7dd871547d03ee9ff5023cbec00b1d8677eeAngus Kong
108680af7dd871547d03ee9ff5023cbec00b1d8677eeAngus Kong    /**
108780af7dd871547d03ee9ff5023cbec00b1d8677eeAngus Kong     * Fade out the {@link ViewItem} on the right of the current one in
1088d028dd4908e68c7d4d0e7438efa74fa2c67e5f2aAngus Kong     * full-screen layout. Does nothing if there's no previous item.
108980af7dd871547d03ee9ff5023cbec00b1d8677eeAngus Kong     *
10908ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde     * @param bufferIndex The index of the item in the buffer to fade.
109180af7dd871547d03ee9ff5023cbec00b1d8677eeAngus Kong     */
10928ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde    private void fadeAndScaleRightViewItem(int bufferIndex) {
10938ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde        if (bufferIndex < 1 || bufferIndex > BUFFER_SIZE) {
1094c14b6ba434575b7d795a8cd681821f86a39bc371Paul Rohde            Log.w(TAG, "fadeAndScaleRightViewItem() - bufferIndex out of bound!");
109580af7dd871547d03ee9ff5023cbec00b1d8677eeAngus Kong            return;
109680af7dd871547d03ee9ff5023cbec00b1d8677eeAngus Kong        }
109780af7dd871547d03ee9ff5023cbec00b1d8677eeAngus Kong
10988ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde        final ViewItem item = mViewItems[bufferIndex];
10998ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde        final ViewItem previousItem = mViewItems[bufferIndex - 1];
11008ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde        if (item == null || previousItem == null) {
1101c14b6ba434575b7d795a8cd681821f86a39bc371Paul Rohde            Log.w(TAG, "fadeAndScaleRightViewItem() - Invalid view item (curr or prev == null)."
1102c14b6ba434575b7d795a8cd681821f86a39bc371Paul Rohde                  + "curr = " + bufferIndex);
110380af7dd871547d03ee9ff5023cbec00b1d8677eeAngus Kong            return;
110480af7dd871547d03ee9ff5023cbec00b1d8677eeAngus Kong        }
110580af7dd871547d03ee9ff5023cbec00b1d8677eeAngus Kong
11068ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde        if (bufferIndex > BUFFER_CENTER + 1) {
11078ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde            // Every item not right next to the BUFFER_CENTER is invisible.
11088ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde            item.setVisibility(INVISIBLE);
110980af7dd871547d03ee9ff5023cbec00b1d8677eeAngus Kong            return;
111080af7dd871547d03ee9ff5023cbec00b1d8677eeAngus Kong        }
11118ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde        final int prevCenterX = previousItem.getCenterX();
111280af7dd871547d03ee9ff5023cbec00b1d8677eeAngus Kong        if (mCenterX <= prevCenterX) {
111380af7dd871547d03ee9ff5023cbec00b1d8677eeAngus Kong            // Shortcut. If the position is at the center of the previous one,
111480af7dd871547d03ee9ff5023cbec00b1d8677eeAngus Kong            // set to invisible too.
11158ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde            item.setVisibility(INVISIBLE);
111680af7dd871547d03ee9ff5023cbec00b1d8677eeAngus Kong            return;
111780af7dd871547d03ee9ff5023cbec00b1d8677eeAngus Kong        }
11188ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde        final int currCenterX = item.getCenterX();
111980af7dd871547d03ee9ff5023cbec00b1d8677eeAngus Kong        final float fadeDownFraction =
112080af7dd871547d03ee9ff5023cbec00b1d8677eeAngus Kong                ((float) mCenterX - prevCenterX) / (currCenterX - prevCenterX);
11218ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde        item.layoutWithTranslationX(mDrawArea, currCenterX,
11228ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde              FILM_STRIP_SCALE + (1f - FILM_STRIP_SCALE) * fadeDownFraction);
1123e7263f7ea45cf31d3948984ba8514e03ef86627ePaul Rohde
11248ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde        item.setAlpha(fadeDownFraction);
11258ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde        item.setTranslationX(0);
11268ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde        item.setVisibility(VISIBLE);
112780af7dd871547d03ee9ff5023cbec00b1d8677eeAngus Kong    }
112880af7dd871547d03ee9ff5023cbec00b1d8677eeAngus Kong
1129f1582c9c075e9cd5790b4bbe1101c94a45ac15d2Angus Kong    private void layoutViewItems(boolean layoutChanged) {
11308ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde        if (mViewItems[BUFFER_CENTER] == null ||
11315fbe134c3cf1f7c2c82ccef223cab7eb43cd92f1Angus Kong                mDrawArea.width() == 0 ||
11325fbe134c3cf1f7c2c82ccef223cab7eb43cd92f1Angus Kong                mDrawArea.height() == 0) {
11333d0b691b8f7a844093e8de9267ffa2ce16d0c5a9Angus Kong            return;
11343d0b691b8f7a844093e8de9267ffa2ce16d0c5a9Angus Kong        }
113549b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong
1136f1582c9c075e9cd5790b4bbe1101c94a45ac15d2Angus Kong        // If the layout changed, we need to adjust the current position so
1137f1582c9c075e9cd5790b4bbe1101c94a45ac15d2Angus Kong        // that if an item is centered before the change, it's still centered.
1138f1582c9c075e9cd5790b4bbe1101c94a45ac15d2Angus Kong        if (layoutChanged) {
11398ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde            mViewItems[BUFFER_CENTER].setLeftPosition(
11408ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde                  mCenterX - mViewItems[BUFFER_CENTER].getMeasuredWidth() / 2);
114149b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong        }
114249b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong
11434567160a43fa5787012bf0b618c216cf18ac0457Angus Kong        if (inZoomView()) {
11447d2388d96522975307cf5c40f7ac911ad963c44dAngus Kong            return;
11457d2388d96522975307cf5c40f7ac911ad963c44dAngus Kong        }
11464ff5a1ab360577c6a7ec953f9197778c84c9d334Angus Kong        /**
11474ff5a1ab360577c6a7ec953f9197778c84c9d334Angus Kong         * Transformed scale fraction between 0 and 1. 0 if the scale is
114888ef7664ba6a888fa5da8693091674e152b56192Sascha Haeberling         * {@link FILM_STRIP_SCALE}. 1 if the scale is {@link FULL_SCREEN_SCALE}
114988ef7664ba6a888fa5da8693091674e152b56192Sascha Haeberling         * .
11504ff5a1ab360577c6a7ec953f9197778c84c9d334Angus Kong         */
115180af7dd871547d03ee9ff5023cbec00b1d8677eeAngus Kong        final float scaleFraction = mViewAnimInterpolator.getInterpolation(
11524ff5a1ab360577c6a7ec953f9197778c84c9d334Angus Kong                (mScale - FILM_STRIP_SCALE) / (FULL_SCREEN_SCALE - FILM_STRIP_SCALE));
1153faaee012acc80ad369cb03df9c196e48140f1e7bAngus Kong        final int fullScreenWidth = mDrawArea.width() + mViewGapInPixel;
1154a6a6a730074e9fedd56c41cd8fa8f1212cfac108Angus Kong
1155280fd3edae1dc35ac1015bcc9532477169fad3aaSascha Haeberling        // Decide the position for all view items on the left and the right
1156280fd3edae1dc35ac1015bcc9532477169fad3aaSascha Haeberling        // first.
1157d028dd4908e68c7d4d0e7438efa74fa2c67e5f2aAngus Kong
1158d028dd4908e68c7d4d0e7438efa74fa2c67e5f2aAngus Kong        // Left items.
11598ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde        for (int i = BUFFER_CENTER - 1; i >= 0; i--) {
11608ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde            final ViewItem curr = mViewItems[i];
1161d028dd4908e68c7d4d0e7438efa74fa2c67e5f2aAngus Kong            if (curr == null) {
1162d028dd4908e68c7d4d0e7438efa74fa2c67e5f2aAngus Kong                break;
1163d028dd4908e68c7d4d0e7438efa74fa2c67e5f2aAngus Kong            }
1164d028dd4908e68c7d4d0e7438efa74fa2c67e5f2aAngus Kong
1165d028dd4908e68c7d4d0e7438efa74fa2c67e5f2aAngus Kong            // First, layout relatively to the next one.
11668ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde            final int currLeft = mViewItems[i + 1].getLeftPosition()
11671f9db2dfe59c33228103c54523281501ef52c9baAngus Kong                    - curr.getMeasuredWidth() - mViewGapInPixel;
1168d028dd4908e68c7d4d0e7438efa74fa2c67e5f2aAngus Kong            curr.setLeftPosition(currLeft);
1169d028dd4908e68c7d4d0e7438efa74fa2c67e5f2aAngus Kong        }
1170d028dd4908e68c7d4d0e7438efa74fa2c67e5f2aAngus Kong        // Right items.
11718ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde        for (int i = BUFFER_CENTER + 1; i < BUFFER_SIZE; i++) {
11728ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde            final ViewItem curr = mViewItems[i];
1173d028dd4908e68c7d4d0e7438efa74fa2c67e5f2aAngus Kong            if (curr == null) {
1174d028dd4908e68c7d4d0e7438efa74fa2c67e5f2aAngus Kong                break;
1175d028dd4908e68c7d4d0e7438efa74fa2c67e5f2aAngus Kong            }
1176d028dd4908e68c7d4d0e7438efa74fa2c67e5f2aAngus Kong
1177d028dd4908e68c7d4d0e7438efa74fa2c67e5f2aAngus Kong            // First, layout relatively to the previous one.
11788ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde            final ViewItem prev = mViewItems[i - 1];
1179da568b23002859e9672271bb135808303c2e07cbAngus Kong            final int currLeft =
11801f9db2dfe59c33228103c54523281501ef52c9baAngus Kong                    prev.getLeftPosition() + prev.getMeasuredWidth()
1181faaee012acc80ad369cb03df9c196e48140f1e7bAngus Kong                            + mViewGapInPixel;
1182d028dd4908e68c7d4d0e7438efa74fa2c67e5f2aAngus Kong            curr.setLeftPosition(currLeft);
1183d028dd4908e68c7d4d0e7438efa74fa2c67e5f2aAngus Kong        }
1184d028dd4908e68c7d4d0e7438efa74fa2c67e5f2aAngus Kong
118586f1d1f3664c909d1d949951056c7bb1e9627324Alan Newberger        if (scaleFraction == 1f) {
11868ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde            final ViewItem currItem = mViewItems[BUFFER_CENTER];
1187d028dd4908e68c7d4d0e7438efa74fa2c67e5f2aAngus Kong            final int currCenterX = currItem.getCenterX();
1188d028dd4908e68c7d4d0e7438efa74fa2c67e5f2aAngus Kong            if (mCenterX < currCenterX) {
1189d028dd4908e68c7d4d0e7438efa74fa2c67e5f2aAngus Kong                // In full-screen and mCenterX is on the left of the center,
1190d028dd4908e68c7d4d0e7438efa74fa2c67e5f2aAngus Kong                // we draw the current one to "fade down".
11918ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde                fadeAndScaleRightViewItem(BUFFER_CENTER);
1192280fd3edae1dc35ac1015bcc9532477169fad3aaSascha Haeberling            } else if (mCenterX > currCenterX) {
1193d028dd4908e68c7d4d0e7438efa74fa2c67e5f2aAngus Kong                // In full-screen and mCenterX is on the right of the center,
1194d028dd4908e68c7d4d0e7438efa74fa2c67e5f2aAngus Kong                // we draw the current one translated.
11958ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde                translateLeftViewItem(BUFFER_CENTER, fullScreenWidth, scaleFraction);
1196563a289f1aed4dd7bf760959851ff6ad72c7627fAngus Kong            } else {
11971f9db2dfe59c33228103c54523281501ef52c9baAngus Kong                currItem.layoutWithTranslationX(mDrawArea, mCenterX, mScale);
11981f9db2dfe59c33228103c54523281501ef52c9baAngus Kong                currItem.setTranslationX(0f);
11991f9db2dfe59c33228103c54523281501ef52c9baAngus Kong                currItem.setAlpha(1f);
1200563a289f1aed4dd7bf760959851ff6ad72c7627fAngus Kong            }
1201025136bb4639d84b2a5a7b95367fabcc92b86415Angus Kong        } else {
12028ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde            final ViewItem currItem = mViewItems[BUFFER_CENTER];
1203ec980898dd0dc712a3d6373c06a13f8646a0eebcAlan Newberger            currItem.setVisibility(View.VISIBLE);
1204280fd3edae1dc35ac1015bcc9532477169fad3aaSascha Haeberling            // The normal filmstrip has no translation for the current item. If
1205280fd3edae1dc35ac1015bcc9532477169fad3aaSascha Haeberling            // it has translation before, gradually set it to zero.
12061f9db2dfe59c33228103c54523281501ef52c9baAngus Kong            currItem.setTranslationX(currItem.getTranslationX() * scaleFraction);
12071f9db2dfe59c33228103c54523281501ef52c9baAngus Kong            currItem.layoutWithTranslationX(mDrawArea, mCenterX, mScale);
12088ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde            if (mViewItems[BUFFER_CENTER - 1] == null) {
12091f9db2dfe59c33228103c54523281501ef52c9baAngus Kong                currItem.setAlpha(1f);
1210d028dd4908e68c7d4d0e7438efa74fa2c67e5f2aAngus Kong            } else {
1211d028dd4908e68c7d4d0e7438efa74fa2c67e5f2aAngus Kong                final int currCenterX = currItem.getCenterX();
12128ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde                final int prevCenterX = mViewItems[BUFFER_CENTER - 1].getCenterX();
1213d028dd4908e68c7d4d0e7438efa74fa2c67e5f2aAngus Kong                final float fadeDownFraction =
1214d028dd4908e68c7d4d0e7438efa74fa2c67e5f2aAngus Kong                        ((float) mCenterX - prevCenterX) / (currCenterX - prevCenterX);
12151f9db2dfe59c33228103c54523281501ef52c9baAngus Kong                currItem.setAlpha(
1216d028dd4908e68c7d4d0e7438efa74fa2c67e5f2aAngus Kong                        (1 - fadeDownFraction) * (1 - scaleFraction) + fadeDownFraction);
1217d028dd4908e68c7d4d0e7438efa74fa2c67e5f2aAngus Kong            }
1218025136bb4639d84b2a5a7b95367fabcc92b86415Angus Kong        }
1219025136bb4639d84b2a5a7b95367fabcc92b86415Angus Kong
1220025136bb4639d84b2a5a7b95367fabcc92b86415Angus Kong        // Layout the rest dependent on the current scale.
1221025136bb4639d84b2a5a7b95367fabcc92b86415Angus Kong
1222d028dd4908e68c7d4d0e7438efa74fa2c67e5f2aAngus Kong        // Items on the left
12238ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde        for (int i = BUFFER_CENTER - 1; i >= 0; i--) {
12248ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde            final ViewItem curr = mViewItems[i];
1225f1f5186f7d3fd8bf35009d9e65a43914c664d82bSascha Haeberling            if (curr == null) {
122680af7dd871547d03ee9ff5023cbec00b1d8677eeAngus Kong                break;
1227f1f5186f7d3fd8bf35009d9e65a43914c664d82bSascha Haeberling            }
12288ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde            translateLeftViewItem(i, fullScreenWidth, scaleFraction);
122949b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong        }
123049b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong
1231d028dd4908e68c7d4d0e7438efa74fa2c67e5f2aAngus Kong        // Items on the right
12328ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde        for (int i = BUFFER_CENTER + 1; i < BUFFER_SIZE; i++) {
12338ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde            final ViewItem curr = mViewItems[i];
1234f1f5186f7d3fd8bf35009d9e65a43914c664d82bSascha Haeberling            if (curr == null) {
1235d028dd4908e68c7d4d0e7438efa74fa2c67e5f2aAngus Kong                break;
1236f1f5186f7d3fd8bf35009d9e65a43914c664d82bSascha Haeberling            }
1237a6a6a730074e9fedd56c41cd8fa8f1212cfac108Angus Kong
12381f9db2dfe59c33228103c54523281501ef52c9baAngus Kong            curr.layoutWithTranslationX(mDrawArea, mCenterX, mScale);
1239da568b23002859e9672271bb135808303c2e07cbAngus Kong
12404ff5a1ab360577c6a7ec953f9197778c84c9d334Angus Kong            if (scaleFraction == 1) {
1241025136bb4639d84b2a5a7b95367fabcc92b86415Angus Kong                // It's in full-screen mode.
12428ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde                fadeAndScaleRightViewItem(i);
1243a6a6a730074e9fedd56c41cd8fa8f1212cfac108Angus Kong            } else {
124434ac66e6d0bb849fb24a6255328390aba204abe3Alan Newberger                boolean isVisible = (curr.getVisibility() == VISIBLE);
124534ac66e6d0bb849fb24a6255328390aba204abe3Alan Newberger                boolean setToVisible = !isVisible;
12469351e87558b7cb6cbc21d817a585ef769567e347Spike Sprague
12478ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde                if (i == BUFFER_CENTER + 1) {
124834ac66e6d0bb849fb24a6255328390aba204abe3Alan Newberger                    // right hand neighbor needs to fade based on scale of
124934ac66e6d0bb849fb24a6255328390aba204abe3Alan Newberger                    // center
12501f9db2dfe59c33228103c54523281501ef52c9baAngus Kong                    curr.setAlpha(1f - scaleFraction);
1251a6a6a730074e9fedd56c41cd8fa8f1212cfac108Angus Kong                } else {
12524ff5a1ab360577c6a7ec953f9197778c84c9d334Angus Kong                    if (scaleFraction == 0f) {
12531f9db2dfe59c33228103c54523281501ef52c9baAngus Kong                        curr.setAlpha(1f);
12544ff5a1ab360577c6a7ec953f9197778c84c9d334Angus Kong                    } else {
125534ac66e6d0bb849fb24a6255328390aba204abe3Alan Newberger                        // further right items should not display when center
125634ac66e6d0bb849fb24a6255328390aba204abe3Alan Newberger                        // is being scaled
12579351e87558b7cb6cbc21d817a585ef769567e347Spike Sprague                        setToVisible = false;
125834ac66e6d0bb849fb24a6255328390aba204abe3Alan Newberger                        if (isVisible) {
125934ac66e6d0bb849fb24a6255328390aba204abe3Alan Newberger                            curr.setVisibility(INVISIBLE);
126034ac66e6d0bb849fb24a6255328390aba204abe3Alan Newberger                        }
12614ff5a1ab360577c6a7ec953f9197778c84c9d334Angus Kong                    }
1262a6a6a730074e9fedd56c41cd8fa8f1212cfac108Angus Kong                }
12639351e87558b7cb6cbc21d817a585ef769567e347Spike Sprague
126434ac66e6d0bb849fb24a6255328390aba204abe3Alan Newberger                if (setToVisible && !isVisible) {
12659351e87558b7cb6cbc21d817a585ef769567e347Spike Sprague                    curr.setVisibility(VISIBLE);
12669351e87558b7cb6cbc21d817a585ef769567e347Spike Sprague                }
12679351e87558b7cb6cbc21d817a585ef769567e347Spike Sprague
12688ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde                curr.setTranslationX((mViewItems[BUFFER_CENTER].getLeftPosition() -
12698ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde                      curr.getLeftPosition()) * scaleFraction);
127049b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong            }
127149b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong        }
127249b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong
127349b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong        stepIfNeeded();
12747d2388d96522975307cf5c40f7ac911ad963c44dAngus Kong    }
1275aa2a243bdc91b809dbfe01a9933b02e6b95d983fAngus Kong
12767d2388d96522975307cf5c40f7ac911ad963c44dAngus Kong    @Override
12777d2388d96522975307cf5c40f7ac911ad963c44dAngus Kong    public void onDraw(Canvas c) {
1278f1582c9c075e9cd5790b4bbe1101c94a45ac15d2Angus Kong        // TODO: remove layoutViewItems() here.
1279f1582c9c075e9cd5790b4bbe1101c94a45ac15d2Angus Kong        layoutViewItems(false);
12807d2388d96522975307cf5c40f7ac911ad963c44dAngus Kong        super.onDraw(c);
128149b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong    }
128249b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong
128349b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong    @Override
128449b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong    protected void onLayout(boolean changed, int l, int t, int r, int b) {
12857539e49296b993be37709f45385a6286618d464dAngus Kong        mDrawArea.left = 0;
12867539e49296b993be37709f45385a6286618d464dAngus Kong        mDrawArea.top = 0;
12877539e49296b993be37709f45385a6286618d464dAngus Kong        mDrawArea.right = r - l;
12887539e49296b993be37709f45385a6286618d464dAngus Kong        mDrawArea.bottom = b - t;
1289563a289f1aed4dd7bf760959851ff6ad72c7627fAngus Kong        mZoomView.layout(mDrawArea.left, mDrawArea.top, mDrawArea.right, mDrawArea.bottom);
12907d2388d96522975307cf5c40f7ac911ad963c44dAngus Kong        // TODO: Need a more robust solution to decide when to re-layout
1291280fd3edae1dc35ac1015bcc9532477169fad3aaSascha Haeberling        // If in the middle of zooming, only re-layout when the layout has
1292280fd3edae1dc35ac1015bcc9532477169fad3aaSascha Haeberling        // changed.
12934567160a43fa5787012bf0b618c216cf18ac0457Angus Kong        if (!inZoomView() || changed) {
1294121022cb590955e37f1264f77190ce4711159976Doris Liu            resetZoomView();
1295f1582c9c075e9cd5790b4bbe1101c94a45ac15d2Angus Kong            layoutViewItems(changed);
1296121022cb590955e37f1264f77190ce4711159976Doris Liu        }
129749b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong    }
129849b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong
1299f9ddb6034e560684e4d3ddd460ef9dff345e4e91Doris Liu    /**
1300280fd3edae1dc35ac1015bcc9532477169fad3aaSascha Haeberling     * Clears the translation and scale that has been set on the view, cancels
1301280fd3edae1dc35ac1015bcc9532477169fad3aaSascha Haeberling     * any loading request for image partial decoding, and hides zoom view. This
1302280fd3edae1dc35ac1015bcc9532477169fad3aaSascha Haeberling     * is needed for when there is a layout change (e.g. when users re-enter the
1303280fd3edae1dc35ac1015bcc9532477169fad3aaSascha Haeberling     * app, or rotate the device, etc).
1304f9ddb6034e560684e4d3ddd460ef9dff345e4e91Doris Liu     */
1305f9ddb6034e560684e4d3ddd460ef9dff345e4e91Doris Liu    private void resetZoomView() {
13064567160a43fa5787012bf0b618c216cf18ac0457Angus Kong        if (!inZoomView()) {
1307f9ddb6034e560684e4d3ddd460ef9dff345e4e91Doris Liu            return;
1308f9ddb6034e560684e4d3ddd460ef9dff345e4e91Doris Liu        }
13098ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde        ViewItem current = mViewItems[BUFFER_CENTER];
1310f9ddb6034e560684e4d3ddd460ef9dff345e4e91Doris Liu        if (current == null) {
1311f9ddb6034e560684e4d3ddd460ef9dff345e4e91Doris Liu            return;
1312f9ddb6034e560684e4d3ddd460ef9dff345e4e91Doris Liu        }
1313f9ddb6034e560684e4d3ddd460ef9dff345e4e91Doris Liu        mScale = FULL_SCREEN_SCALE;
1314d70f0fb68064e170fb2cf041a707ed04d68040f9Doris Liu        mController.cancelZoomAnimation();
1315d70f0fb68064e170fb2cf041a707ed04d68040f9Doris Liu        mController.cancelFlingAnimation();
1316f9ddb6034e560684e4d3ddd460ef9dff345e4e91Doris Liu        current.resetTransform();
1317f9ddb6034e560684e4d3ddd460ef9dff345e4e91Doris Liu        mController.cancelLoadingZoomedImage();
1318f9ddb6034e560684e4d3ddd460ef9dff345e4e91Doris Liu        mZoomView.setVisibility(GONE);
131903b75411538c60021799ccc7acc8ed4798253b6dDoris Liu        mController.setSurroundingViewsVisible(true);
1320f9ddb6034e560684e4d3ddd460ef9dff345e4e91Doris Liu    }
1321f9ddb6034e560684e4d3ddd460ef9dff345e4e91Doris Liu
1322b0288ec84e06b83923180a04e1b546724fcfd258Doris Liu    private void hideZoomView() {
13234567160a43fa5787012bf0b618c216cf18ac0457Angus Kong        if (inZoomView()) {
1324b0288ec84e06b83923180a04e1b546724fcfd258Doris Liu            mController.cancelLoadingZoomedImage();
1325b0288ec84e06b83923180a04e1b546724fcfd258Doris Liu            mZoomView.setVisibility(GONE);
1326b0288ec84e06b83923180a04e1b546724fcfd258Doris Liu        }
1327b0288ec84e06b83923180a04e1b546724fcfd258Doris Liu    }
1328b0288ec84e06b83923180a04e1b546724fcfd258Doris Liu
1329edbba628dc2e31057b304c6a431f44dbca125891Angus Kong    private void slideViewBack(ViewItem item) {
1330ec2fb473c094f8dfd1cc0cf5205d470e412d3919Angus Kong        item.animateTranslationX(0, GEOMETRY_ADJUST_TIME_MS, mViewAnimInterpolator);
13311f9db2dfe59c33228103c54523281501ef52c9baAngus Kong        item.animateTranslationY(0, GEOMETRY_ADJUST_TIME_MS, mViewAnimInterpolator);
13321f9db2dfe59c33228103c54523281501ef52c9baAngus Kong        item.animateAlpha(1f, GEOMETRY_ADJUST_TIME_MS, mViewAnimInterpolator);
133387f9a62f473a3c57505e2877b0a46ab3cb5e62acAngus Kong    }
133487f9a62f473a3c57505e2877b0a46ab3cb5e62acAngus Kong
13358ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde    private void animateItemRemoval(int index) {
1336c6953a4de0005456cd50bbbb0acb27162c9b3c8dAngus Kong        if (mScale > FULL_SCREEN_SCALE) {
1337c6953a4de0005456cd50bbbb0acb27162c9b3c8dAngus Kong            resetZoomView();
1338c6953a4de0005456cd50bbbb0acb27162c9b3c8dAngus Kong        }
13398ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde        int removeAtBufferIndex = findItemInBufferByAdapterIndex(index);
1340c31569ce211414f6db2447cc84ca71115f916b6fAngus Kong
13418ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde        // adjust the buffer to be consistent
1342c31569ce211414f6db2447cc84ca71115f916b6fAngus Kong        for (int i = 0; i < BUFFER_SIZE; i++) {
13438ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde            if (mViewItems[i] == null || mViewItems[i].getAdapterIndex() <= index) {
1344f1f5186f7d3fd8bf35009d9e65a43914c664d82bSascha Haeberling                continue;
1345f1f5186f7d3fd8bf35009d9e65a43914c664d82bSascha Haeberling            }
13468ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde            mViewItems[i].setIndex(mViewItems[i].getAdapterIndex() - 1);
1347c31569ce211414f6db2447cc84ca71115f916b6fAngus Kong        }
13488ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde        if (removeAtBufferIndex == -1) {
1349f1f5186f7d3fd8bf35009d9e65a43914c664d82bSascha Haeberling            return;
1350f1f5186f7d3fd8bf35009d9e65a43914c664d82bSascha Haeberling        }
1351c31569ce211414f6db2447cc84ca71115f916b6fAngus Kong
13528ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde        final ViewItem removedItem = mViewItems[removeAtBufferIndex];
13531f9db2dfe59c33228103c54523281501ef52c9baAngus Kong        final int offsetX = removedItem.getMeasuredWidth() + mViewGapInPixel;
135487f9a62f473a3c57505e2877b0a46ab3cb5e62acAngus Kong
13558ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde        for (int i = removeAtBufferIndex + 1; i < BUFFER_SIZE; i++) {
13568ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde            if (mViewItems[i] != null) {
13578ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde                mViewItems[i].setLeftPosition(mViewItems[i].getLeftPosition() - offsetX);
135887f9a62f473a3c57505e2877b0a46ab3cb5e62acAngus Kong            }
135987f9a62f473a3c57505e2877b0a46ab3cb5e62acAngus Kong        }
136087f9a62f473a3c57505e2877b0a46ab3cb5e62acAngus Kong
13618ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde        if (removeAtBufferIndex >= BUFFER_CENTER
13628ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde                && mViewItems[removeAtBufferIndex].getAdapterIndex() < mDataAdapter.getTotalNumber()) {
13638969a67c926608737f3e49f2d2240298372e8416Angus Kong            // Fill the removed item by left shift when the current one or
1364f1f5186f7d3fd8bf35009d9e65a43914c664d82bSascha Haeberling            // anyone on the right is removed, and there's more data on the
1365f1f5186f7d3fd8bf35009d9e65a43914c664d82bSascha Haeberling            // right available.
13668ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde            for (int i = removeAtBufferIndex; i < BUFFER_SIZE - 1; i++) {
13678ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde                mViewItems[i] = mViewItems[i + 1];
136887f9a62f473a3c57505e2877b0a46ab3cb5e62acAngus Kong            }
136987f9a62f473a3c57505e2877b0a46ab3cb5e62acAngus Kong
137087f9a62f473a3c57505e2877b0a46ab3cb5e62acAngus Kong            // pull data out from the DataAdapter for the last one.
137187f9a62f473a3c57505e2877b0a46ab3cb5e62acAngus Kong            int curr = BUFFER_SIZE - 1;
137287f9a62f473a3c57505e2877b0a46ab3cb5e62acAngus Kong            int prev = curr - 1;
13738ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde            if (mViewItems[prev] != null) {
13748ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde                mViewItems[curr] = buildViewItemAt(mViewItems[prev].getAdapterIndex() + 1);
137587f9a62f473a3c57505e2877b0a46ab3cb5e62acAngus Kong            }
13769f02c87cfb5039a72f516ea5edc0378c45ac5779Angus Kong
1377aa2a243bdc91b809dbfe01a9933b02e6b95d983fAngus Kong            // The animation part.
1378aa2a243bdc91b809dbfe01a9933b02e6b95d983fAngus Kong            if (inFullScreen()) {
13798ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde                mViewItems[BUFFER_CENTER].setVisibility(VISIBLE);
13808ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde                ViewItem nextItem = mViewItems[BUFFER_CENTER + 1];
1381aa2a243bdc91b809dbfe01a9933b02e6b95d983fAngus Kong                if (nextItem != null) {
13821f9db2dfe59c33228103c54523281501ef52c9baAngus Kong                    nextItem.setVisibility(INVISIBLE);
1383aa2a243bdc91b809dbfe01a9933b02e6b95d983fAngus Kong                }
1384aa2a243bdc91b809dbfe01a9933b02e6b95d983fAngus Kong            }
1385aa2a243bdc91b809dbfe01a9933b02e6b95d983fAngus Kong
13860980fe89b2c0d2d8f75cb43a5fe06c00b8c8901eAngus Kong            // Translate the views to their original places.
13878ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde            for (int i = removeAtBufferIndex; i < BUFFER_SIZE; i++) {
13888ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde                if (mViewItems[i] != null) {
13898ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde                    mViewItems[i].setTranslationX(offsetX);
13909f02c87cfb5039a72f516ea5edc0378c45ac5779Angus Kong                }
13919f02c87cfb5039a72f516ea5edc0378c45ac5779Angus Kong            }
13929f02c87cfb5039a72f516ea5edc0378c45ac5779Angus Kong
13939f02c87cfb5039a72f516ea5edc0378c45ac5779Angus Kong            // The end of the filmstrip might have been changed.
1394af24d28f8938a05d2e3c40155e39cbc7ff4d64b7Angus Kong            // The mCenterX might be out of the bound.
13958ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde            ViewItem currItem = mViewItems[BUFFER_CENTER];
13968ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde            if (currItem!=null) {
13978ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde                if (currItem.getAdapterIndex() == mDataAdapter.getTotalNumber() - 1
13989efa64a16a691e6aeb56dc844aa00c8ea057ef99I-Jong Lin                        && mCenterX > currItem.getCenterX()) {
13999efa64a16a691e6aeb56dc844aa00c8ea057ef99I-Jong Lin                    int adjustDiff = currItem.getCenterX() - mCenterX;
14009efa64a16a691e6aeb56dc844aa00c8ea057ef99I-Jong Lin                    mCenterX = currItem.getCenterX();
14019efa64a16a691e6aeb56dc844aa00c8ea057ef99I-Jong Lin                    for (int i = 0; i < BUFFER_SIZE; i++) {
14028ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde                        if (mViewItems[i] != null) {
14038ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde                            mViewItems[i].translateXScaledBy(adjustDiff);
14049efa64a16a691e6aeb56dc844aa00c8ea057ef99I-Jong Lin                        }
14059f02c87cfb5039a72f516ea5edc0378c45ac5779Angus Kong                    }
14069f02c87cfb5039a72f516ea5edc0378c45ac5779Angus Kong                }
14079efa64a16a691e6aeb56dc844aa00c8ea057ef99I-Jong Lin            } else {
14089efa64a16a691e6aeb56dc844aa00c8ea057ef99I-Jong Lin                // CurrItem should NOT be NULL, but if is, at least don't crash.
14099efa64a16a691e6aeb56dc844aa00c8ea057ef99I-Jong Lin                Log.w(TAG,"Caught invalid update in removal animation.");
14109f02c87cfb5039a72f516ea5edc0378c45ac5779Angus Kong            }
141187f9a62f473a3c57505e2877b0a46ab3cb5e62acAngus Kong        } else {
141287f9a62f473a3c57505e2877b0a46ab3cb5e62acAngus Kong            // fill the removed place by right shift
1413af24d28f8938a05d2e3c40155e39cbc7ff4d64b7Angus Kong            mCenterX -= offsetX;
14149f02c87cfb5039a72f516ea5edc0378c45ac5779Angus Kong
14158ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde            for (int i = removeAtBufferIndex; i > 0; i--) {
14168ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde                mViewItems[i] = mViewItems[i - 1];
141787f9a62f473a3c57505e2877b0a46ab3cb5e62acAngus Kong            }
141887f9a62f473a3c57505e2877b0a46ab3cb5e62acAngus Kong
141987f9a62f473a3c57505e2877b0a46ab3cb5e62acAngus Kong            // pull data out from the DataAdapter for the first one.
142087f9a62f473a3c57505e2877b0a46ab3cb5e62acAngus Kong            int curr = 0;
142187f9a62f473a3c57505e2877b0a46ab3cb5e62acAngus Kong            int next = curr + 1;
14228ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde            if (mViewItems[next] != null) {
14238ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde                mViewItems[curr] = buildViewItemAt(mViewItems[next].getAdapterIndex() - 1);
14248ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde
142587f9a62f473a3c57505e2877b0a46ab3cb5e62acAngus Kong            }
14260980fe89b2c0d2d8f75cb43a5fe06c00b8c8901eAngus Kong
14270980fe89b2c0d2d8f75cb43a5fe06c00b8c8901eAngus Kong            // Translate the views to their original places.
14288ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde            for (int i = removeAtBufferIndex; i >= 0; i--) {
14298ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde                if (mViewItems[i] != null) {
14308ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde                    mViewItems[i].setTranslationX(-offsetX);
14310980fe89b2c0d2d8f75cb43a5fe06c00b8c8901eAngus Kong                }
14320980fe89b2c0d2d8f75cb43a5fe06c00b8c8901eAngus Kong            }
143387f9a62f473a3c57505e2877b0a46ab3cb5e62acAngus Kong        }
143487f9a62f473a3c57505e2877b0a46ab3cb5e62acAngus Kong
14351f9db2dfe59c33228103c54523281501ef52c9baAngus Kong        int transY = getHeight() / 8;
14361f9db2dfe59c33228103c54523281501ef52c9baAngus Kong        if (removedItem.getTranslationY() < 0) {
14371f9db2dfe59c33228103c54523281501ef52c9baAngus Kong            transY = -transY;
14381f9db2dfe59c33228103c54523281501ef52c9baAngus Kong        }
14391f9db2dfe59c33228103c54523281501ef52c9baAngus Kong        removedItem.animateTranslationY(removedItem.getTranslationY() + transY,
14401f9db2dfe59c33228103c54523281501ef52c9baAngus Kong                GEOMETRY_ADJUST_TIME_MS, mViewAnimInterpolator);
14411f9db2dfe59c33228103c54523281501ef52c9baAngus Kong        removedItem.animateAlpha(0f, GEOMETRY_ADJUST_TIME_MS, mViewAnimInterpolator);
14421f9db2dfe59c33228103c54523281501ef52c9baAngus Kong        postDelayed(new Runnable() {
14431f9db2dfe59c33228103c54523281501ef52c9baAngus Kong            @Override
14441f9db2dfe59c33228103c54523281501ef52c9baAngus Kong            public void run() {
14455a0be52d3db2398cfe01d18adf779d9e443edf82Alan Newberger                removedItem.removeViewFromHierarchy();
14461f9db2dfe59c33228103c54523281501ef52c9baAngus Kong            }
14471f9db2dfe59c33228103c54523281501ef52c9baAngus Kong        }, GEOMETRY_ADJUST_TIME_MS);
14481f9db2dfe59c33228103c54523281501ef52c9baAngus Kong
14491f9db2dfe59c33228103c54523281501ef52c9baAngus Kong        adjustChildZOrder();
14501f9db2dfe59c33228103c54523281501ef52c9baAngus Kong        invalidate();
14511f9db2dfe59c33228103c54523281501ef52c9baAngus Kong
14529f02c87cfb5039a72f516ea5edc0378c45ac5779Angus Kong        // Now, slide every one back.
14538ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde        if (mViewItems[BUFFER_CENTER] == null) {
14541f9db2dfe59c33228103c54523281501ef52c9baAngus Kong            return;
14551f9db2dfe59c33228103c54523281501ef52c9baAngus Kong        }
14569f02c87cfb5039a72f516ea5edc0378c45ac5779Angus Kong        for (int i = 0; i < BUFFER_SIZE; i++) {
14578ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde            if (mViewItems[i] != null
14588ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde                    && mViewItems[i].getTranslationX() != 0f) {
14598ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde                slideViewBack(mViewItems[i]);
14609f02c87cfb5039a72f516ea5edc0378c45ac5779Angus Kong            }
14619f02c87cfb5039a72f516ea5edc0378c45ac5779Angus Kong        }
146287f9a62f473a3c57505e2877b0a46ab3cb5e62acAngus Kong    }
146387f9a62f473a3c57505e2877b0a46ab3cb5e62acAngus Kong
1464e78541f2edf2f02660c34660114e76d53b32ab8bAngus Kong    // returns -1 on failure.
14658ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde    private int findItemInBufferByAdapterIndex(int index) {
1466e78541f2edf2f02660c34660114e76d53b32ab8bAngus Kong        for (int i = 0; i < BUFFER_SIZE; i++) {
14678ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde            if (mViewItems[i] != null
14688ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde                    && mViewItems[i].getAdapterIndex() == index) {
1469f1f5186f7d3fd8bf35009d9e65a43914c664d82bSascha Haeberling                return i;
1470f1f5186f7d3fd8bf35009d9e65a43914c664d82bSascha Haeberling            }
1471e78541f2edf2f02660c34660114e76d53b32ab8bAngus Kong        }
1472e78541f2edf2f02660c34660114e76d53b32ab8bAngus Kong        return -1;
1473e78541f2edf2f02660c34660114e76d53b32ab8bAngus Kong    }
1474e78541f2edf2f02660c34660114e76d53b32ab8bAngus Kong
14758ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde    private void updateInsertion(int index) {
14768ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde        int bufferIndex = findItemInBufferByAdapterIndex(index);
14778ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde        if (bufferIndex == -1) {
14788969a67c926608737f3e49f2d2240298372e8416Angus Kong            // Not in the current item buffers. Check if it's inserted
1479e78541f2edf2f02660c34660114e76d53b32ab8bAngus Kong            // at the end.
14808ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde            if (index == mDataAdapter.getTotalNumber() - 1) {
14818ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde                int prev = findItemInBufferByAdapterIndex(index - 1);
1482e78541f2edf2f02660c34660114e76d53b32ab8bAngus Kong                if (prev >= 0 && prev < BUFFER_SIZE - 1) {
1483e78541f2edf2f02660c34660114e76d53b32ab8bAngus Kong                    // The previous data is in the buffer and we still
1484e78541f2edf2f02660c34660114e76d53b32ab8bAngus Kong                    // have room for the inserted data.
14858ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde                    bufferIndex = prev + 1;
1486e78541f2edf2f02660c34660114e76d53b32ab8bAngus Kong                }
1487e78541f2edf2f02660c34660114e76d53b32ab8bAngus Kong            }
1488e78541f2edf2f02660c34660114e76d53b32ab8bAngus Kong        }
1489e78541f2edf2f02660c34660114e76d53b32ab8bAngus Kong
14908ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde        // adjust the indexes to be consistent
1491e78541f2edf2f02660c34660114e76d53b32ab8bAngus Kong        for (int i = 0; i < BUFFER_SIZE; i++) {
14928ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde            if (mViewItems[i] == null || mViewItems[i].getAdapterIndex() < index) {
1493f1f5186f7d3fd8bf35009d9e65a43914c664d82bSascha Haeberling                continue;
1494f1f5186f7d3fd8bf35009d9e65a43914c664d82bSascha Haeberling            }
14958ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde            mViewItems[i].setIndex(mViewItems[i].getAdapterIndex() + 1);
1496e78541f2edf2f02660c34660114e76d53b32ab8bAngus Kong        }
14978ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde        if (bufferIndex == -1) {
1498f1f5186f7d3fd8bf35009d9e65a43914c664d82bSascha Haeberling            return;
1499f1f5186f7d3fd8bf35009d9e65a43914c664d82bSascha Haeberling        }
1500e78541f2edf2f02660c34660114e76d53b32ab8bAngus Kong
15018ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde        final FilmstripItem data = mDataAdapter.getFilmstripItemAt(index);
1502c195e7a9208c90d7a55e184d4b2b9c747e9c90f1Angus Kong        Point dim = CameraUtil
15038ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde                .resizeToFill(
150451cafa0a35546a42c573357aa7a031a79cf9ba1bAlan Newberger                      data.getDimensions().getWidth(),
150551cafa0a35546a42c573357aa7a031a79cf9ba1bAlan Newberger                      data.getDimensions().getHeight(),
150651cafa0a35546a42c573357aa7a031a79cf9ba1bAlan Newberger                      data.getOrientation(),
15078ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde                      getMeasuredWidth(),
15088ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde                      getMeasuredHeight());
1509c195e7a9208c90d7a55e184d4b2b9c747e9c90f1Angus Kong        final int offsetX = dim.x + mViewGapInPixel;
15108ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde        ViewItem viewItem = buildViewItemAt(index);
151161c5f9f86b5fd8937fac3d082398086bac0d8db6Alan Newberger        if (viewItem == null) {
151261c5f9f86b5fd8937fac3d082398086bac0d8db6Alan Newberger            Log.w(TAG, "unable to build inserted item from data");
151361c5f9f86b5fd8937fac3d082398086bac0d8db6Alan Newberger            return;
151461c5f9f86b5fd8937fac3d082398086bac0d8db6Alan Newberger        }
1515e78541f2edf2f02660c34660114e76d53b32ab8bAngus Kong
15168ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde        if (bufferIndex >= BUFFER_CENTER) {
15178ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde            if (bufferIndex == BUFFER_CENTER) {
15188ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde                viewItem.setLeftPosition(mViewItems[BUFFER_CENTER].getLeftPosition());
1519e78541f2edf2f02660c34660114e76d53b32ab8bAngus Kong            }
1520e78541f2edf2f02660c34660114e76d53b32ab8bAngus Kong            // Shift right to make rooms for newly inserted item.
15218969a67c926608737f3e49f2d2240298372e8416Angus Kong            removeItem(BUFFER_SIZE - 1);
15228ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde            for (int i = BUFFER_SIZE - 1; i > bufferIndex; i--) {
15238ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde                mViewItems[i] = mViewItems[i - 1];
15248ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde                if (mViewItems[i] != null) {
15258ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde                    mViewItems[i].setTranslationX(-offsetX);
15268ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde                    slideViewBack(mViewItems[i]);
1527e78541f2edf2f02660c34660114e76d53b32ab8bAngus Kong                }
1528e78541f2edf2f02660c34660114e76d53b32ab8bAngus Kong            }
1529e78541f2edf2f02660c34660114e76d53b32ab8bAngus Kong        } else {
1530f1f5186f7d3fd8bf35009d9e65a43914c664d82bSascha Haeberling            // Shift left. Put the inserted data on the left instead of the
1531f1f5186f7d3fd8bf35009d9e65a43914c664d82bSascha Haeberling            // found position.
15328ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde            --bufferIndex;
15338ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde            if (bufferIndex < 0) {
1534f1f5186f7d3fd8bf35009d9e65a43914c664d82bSascha Haeberling                return;
1535f1f5186f7d3fd8bf35009d9e65a43914c664d82bSascha Haeberling            }
15368969a67c926608737f3e49f2d2240298372e8416Angus Kong            removeItem(0);
15378ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde            for (int i = 1; i <= bufferIndex; i++) {
15388ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde                if (mViewItems[i] != null) {
15398ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde                    mViewItems[i].setTranslationX(offsetX);
15408ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde                    slideViewBack(mViewItems[i]);
15418ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde                    mViewItems[i - 1] = mViewItems[i];
1542e78541f2edf2f02660c34660114e76d53b32ab8bAngus Kong                }
1543e78541f2edf2f02660c34660114e76d53b32ab8bAngus Kong            }
1544e78541f2edf2f02660c34660114e76d53b32ab8bAngus Kong        }
1545e78541f2edf2f02660c34660114e76d53b32ab8bAngus Kong
15468ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde        mViewItems[bufferIndex] = viewItem;
154777d9f023e76816e5da7bd067ad46cc0e9c98623fPaul Rohde        renderThumbnail(bufferIndex);
15481f9db2dfe59c33228103c54523281501ef52c9baAngus Kong        viewItem.setAlpha(0f);
15491f9db2dfe59c33228103c54523281501ef52c9baAngus Kong        viewItem.setTranslationY(getHeight() / 8);
15501f9db2dfe59c33228103c54523281501ef52c9baAngus Kong        slideViewBack(viewItem);
155153aedc0ee671c2d11341a66c444bbb6cdc97b150Angus Kong        adjustChildZOrder();
1552c14b6ba434575b7d795a8cd681821f86a39bc371Paul Rohde
1553e78541f2edf2f02660c34660114e76d53b32ab8bAngus Kong        invalidate();
1554e78541f2edf2f02660c34660114e76d53b32ab8bAngus Kong    }
1555e78541f2edf2f02660c34660114e76d53b32ab8bAngus Kong
15568ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde    private void setDataAdapter(FilmstripDataAdapter adapter) {
155749b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong        mDataAdapter = adapter;
15583f192ccf03d0d25521b96304c4464b4bad28150aSascha Haeberling        int maxEdge = (int) (Math.max(this.getHeight(), this.getWidth())
15594021c896985d9ba43db0199f259ce4b8bfc0dc88Sam Judd                * FILM_STRIP_SCALE);
15604021c896985d9ba43db0199f259ce4b8bfc0dc88Sam Judd        mDataAdapter.suggestViewSizeBound(maxEdge, maxEdge);
15618ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde        mDataAdapter.setListener(new FilmstripDataAdapter.Listener() {
156249b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong            @Override
15638ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde            public void onFilmstripItemLoaded() {
156449b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong                reload();
156549b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong            }
156649b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong
156749b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong            @Override
15688ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde            public void onFilmstripItemUpdated(FilmstripDataAdapter.UpdateReporter reporter) {
156949b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong                update(reporter);
157049b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong            }
157149b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong
157249b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong            @Override
15738ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde            public void onFilmstripItemInserted(int index, FilmstripItem item) {
15748ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde                if (mViewItems[BUFFER_CENTER] == null) {
1575e78541f2edf2f02660c34660114e76d53b32ab8bAngus Kong                    // empty now, simply do a reload.
1576e78541f2edf2f02660c34660114e76d53b32ab8bAngus Kong                    reload();
1577faaee012acc80ad369cb03df9c196e48140f1e7bAngus Kong                } else {
15788ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde                    updateInsertion(index);
1579faaee012acc80ad369cb03df9c196e48140f1e7bAngus Kong                }
1580faaee012acc80ad369cb03df9c196e48140f1e7bAngus Kong                if (mListener != null) {
15818ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde                    mListener.onDataFocusChanged(index, getCurrentItemAdapterIndex());
1582e78541f2edf2f02660c34660114e76d53b32ab8bAngus Kong                }
1583c14b6ba434575b7d795a8cd681821f86a39bc371Paul Rohde                Log.d(TAG, "onFilmstripItemInserted()");
158477d9f023e76816e5da7bd067ad46cc0e9c98623fPaul Rohde                renderAllThumbnails();
158549b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong            }
158649b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong
158749b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong            @Override
15888ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde            public void onFilmstripItemRemoved(int index, FilmstripItem item) {
15898ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde                animateItemRemoval(index);
1590faaee012acc80ad369cb03df9c196e48140f1e7bAngus Kong                if (mListener != null) {
15918ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde                    mListener.onDataFocusChanged(index, getCurrentItemAdapterIndex());
1592faaee012acc80ad369cb03df9c196e48140f1e7bAngus Kong                }
1593c14b6ba434575b7d795a8cd681821f86a39bc371Paul Rohde                Log.d(TAG, "onFilmstripItemRemoved()");
159477d9f023e76816e5da7bd067ad46cc0e9c98623fPaul Rohde                renderAllThumbnails();
159549b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong            }
159649b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong        });
159749b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong    }
159849b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong
1599628481532e1e58a0a26330d238e850aec5657bceAngus Kong    private boolean inFilmstrip() {
1600a6a6a730074e9fedd56c41cd8fa8f1212cfac108Angus Kong        return (mScale == FILM_STRIP_SCALE);
1601a6a6a730074e9fedd56c41cd8fa8f1212cfac108Angus Kong    }
1602a6a6a730074e9fedd56c41cd8fa8f1212cfac108Angus Kong
1603628481532e1e58a0a26330d238e850aec5657bceAngus Kong    private boolean inFullScreen() {
16044ff5a1ab360577c6a7ec953f9197778c84c9d334Angus Kong        return (mScale == FULL_SCREEN_SCALE);
1605a6a6a730074e9fedd56c41cd8fa8f1212cfac108Angus Kong    }
1606a6a6a730074e9fedd56c41cd8fa8f1212cfac108Angus Kong
16074567160a43fa5787012bf0b618c216cf18ac0457Angus Kong    private boolean inZoomView() {
16084567160a43fa5787012bf0b618c216cf18ac0457Angus Kong        return (mScale > FULL_SCREEN_SCALE);
16094567160a43fa5787012bf0b618c216cf18ac0457Angus Kong    }
16104567160a43fa5787012bf0b618c216cf18ac0457Angus Kong
161149b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong    @Override
161249b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong    public boolean onInterceptTouchEvent(MotionEvent ev) {
16137e9e4b7bdf371afd37f43a331cef1672fac1a230Angus Kong        if (mController.isScrolling()) {
1614f1f5186f7d3fd8bf35009d9e65a43914c664d82bSascha Haeberling            return true;
1615f1f5186f7d3fd8bf35009d9e65a43914c664d82bSascha Haeberling        }
1616a6a6a730074e9fedd56c41cd8fa8f1212cfac108Angus Kong
1617fb57df15a87f95ce8b2f861e54d960602912e03cDoris Liu        if (ev.getActionMasked() == MotionEvent.ACTION_DOWN) {
1618fb57df15a87f95ce8b2f861e54d960602912e03cDoris Liu            mCheckToIntercept = true;
1619fb57df15a87f95ce8b2f861e54d960602912e03cDoris Liu            mDown = MotionEvent.obtain(ev);
1620fb57df15a87f95ce8b2f861e54d960602912e03cDoris Liu            return false;
1621fb57df15a87f95ce8b2f861e54d960602912e03cDoris Liu        } else if (ev.getActionMasked() == MotionEvent.ACTION_POINTER_DOWN) {
1622fb57df15a87f95ce8b2f861e54d960602912e03cDoris Liu            // Do not intercept touch once child is in zoom mode
1623fb57df15a87f95ce8b2f861e54d960602912e03cDoris Liu            mCheckToIntercept = false;
1624fb57df15a87f95ce8b2f861e54d960602912e03cDoris Liu            return false;
1625fb57df15a87f95ce8b2f861e54d960602912e03cDoris Liu        } else {
1626f1f5186f7d3fd8bf35009d9e65a43914c664d82bSascha Haeberling            if (!mCheckToIntercept) {
1627f1f5186f7d3fd8bf35009d9e65a43914c664d82bSascha Haeberling                return false;
1628f1f5186f7d3fd8bf35009d9e65a43914c664d82bSascha Haeberling            }
1629f1f5186f7d3fd8bf35009d9e65a43914c664d82bSascha Haeberling            if (ev.getEventTime() - ev.getDownTime() > SWIPE_TIME_OUT) {
1630f1f5186f7d3fd8bf35009d9e65a43914c664d82bSascha Haeberling                return false;
1631f1f5186f7d3fd8bf35009d9e65a43914c664d82bSascha Haeberling            }
1632fb57df15a87f95ce8b2f861e54d960602912e03cDoris Liu            int deltaX = (int) (ev.getX() - mDown.getX());
1633fb57df15a87f95ce8b2f861e54d960602912e03cDoris Liu            int deltaY = (int) (ev.getY() - mDown.getY());
1634fb57df15a87f95ce8b2f861e54d960602912e03cDoris Liu            if (ev.getActionMasked() == MotionEvent.ACTION_MOVE
1635fb57df15a87f95ce8b2f861e54d960602912e03cDoris Liu                    && deltaX < mSlop * (-1)) {
1636fb57df15a87f95ce8b2f861e54d960602912e03cDoris Liu                // intercept left swipe
1637fb57df15a87f95ce8b2f861e54d960602912e03cDoris Liu                if (Math.abs(deltaX) >= Math.abs(deltaY) * 2) {
1638fb57df15a87f95ce8b2f861e54d960602912e03cDoris Liu                    return true;
1639fb57df15a87f95ce8b2f861e54d960602912e03cDoris Liu                }
1640fb57df15a87f95ce8b2f861e54d960602912e03cDoris Liu            }
1641fb57df15a87f95ce8b2f861e54d960602912e03cDoris Liu        }
1642fb57df15a87f95ce8b2f861e54d960602912e03cDoris Liu        return false;
164349b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong    }
164449b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong
164549b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong    @Override
164649b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong    public boolean onTouchEvent(MotionEvent ev) {
1647166e36fb6e04d40a1bef0459ee6b96c9c736039bAngus Kong        return mGestureRecognizer.onTouchEvent(ev);
1648166e36fb6e04d40a1bef0459ee6b96c9c736039bAngus Kong    }
1649166e36fb6e04d40a1bef0459ee6b96c9c736039bAngus Kong
16509652134e6e1b312e348ed3565343741239907f11Alan Newberger    @Override
16519652134e6e1b312e348ed3565343741239907f11Alan Newberger    public boolean onGenericMotionEvent(MotionEvent ev) {
16529652134e6e1b312e348ed3565343741239907f11Alan Newberger        mGestureRecognizer.onGenericMotionEvent(ev);
16539652134e6e1b312e348ed3565343741239907f11Alan Newberger        return true;
16549652134e6e1b312e348ed3565343741239907f11Alan Newberger    }
16559652134e6e1b312e348ed3565343741239907f11Alan Newberger
1656166e36fb6e04d40a1bef0459ee6b96c9c736039bAngus Kong    FilmstripGestureRecognizer.Listener getGestureListener() {
1657166e36fb6e04d40a1bef0459ee6b96c9c736039bAngus Kong        return mGestureListener;
165849b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong    }
165949b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong
16608ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde    private void updateViewItem(int bufferIndex) {
16618ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde        ViewItem item = mViewItems[bufferIndex];
16628969a67c926608737f3e49f2d2240298372e8416Angus Kong        if (item == null) {
1663c14b6ba434575b7d795a8cd681821f86a39bc371Paul Rohde            Log.w(TAG, "updateViewItem() - Trying to update an null item!");
16648969a67c926608737f3e49f2d2240298372e8416Angus Kong            return;
16658969a67c926608737f3e49f2d2240298372e8416Angus Kong        }
16668969a67c926608737f3e49f2d2240298372e8416Angus Kong
16678e29072ca69229a25dc4a856e3635d131613f4caAlan Newberger        int adapterIndex = item.getAdapterIndex();
16688e29072ca69229a25dc4a856e3635d131613f4caAlan Newberger        FilmstripItem filmstripItem = mDataAdapter.getFilmstripItemAt(adapterIndex);
16698e29072ca69229a25dc4a856e3635d131613f4caAlan Newberger        if (filmstripItem == null) {
16708e29072ca69229a25dc4a856e3635d131613f4caAlan Newberger            Log.w(TAG, "updateViewItem() - Trying to update item with null FilmstripItem!");
16718969a67c926608737f3e49f2d2240298372e8416Angus Kong            return;
16728969a67c926608737f3e49f2d2240298372e8416Angus Kong        }
16738e29072ca69229a25dc4a856e3635d131613f4caAlan Newberger
16745a0be52d3db2398cfe01d18adf779d9e443edf82Alan Newberger        FilmstripItem oldFilmstripItem = item.getData();
16755a0be52d3db2398cfe01d18adf779d9e443edf82Alan Newberger
16768e29072ca69229a25dc4a856e3635d131613f4caAlan Newberger        // In case the underlying data item is changed (commonly from
16778e29072ca69229a25dc4a856e3635d131613f4caAlan Newberger        // SessionItem to PhotoItem for an image requiring processing), set the
16788e29072ca69229a25dc4a856e3635d131613f4caAlan Newberger        // new FilmstripItem on the ViewItem
16795a0be52d3db2398cfe01d18adf779d9e443edf82Alan Newberger        if (!filmstripItem.equals(oldFilmstripItem)) {
16805a0be52d3db2398cfe01d18adf779d9e443edf82Alan Newberger            oldFilmstripItem.recycle(item.getView());
16815a0be52d3db2398cfe01d18adf779d9e443edf82Alan Newberger            item.setData(filmstripItem);
16825a0be52d3db2398cfe01d18adf779d9e443edf82Alan Newberger            Log.v(TAG, "updateViewItem() - recycling old data item and setting new");
16835a0be52d3db2398cfe01d18adf779d9e443edf82Alan Newberger        } else {
16845a0be52d3db2398cfe01d18adf779d9e443edf82Alan Newberger            Log.v(TAG, "updateViewItem() - updating data with the same item");
16855a0be52d3db2398cfe01d18adf779d9e443edf82Alan Newberger        }
16868e29072ca69229a25dc4a856e3635d131613f4caAlan Newberger
16878e29072ca69229a25dc4a856e3635d131613f4caAlan Newberger        // In case state changed from a new FilmStripItem or the existing one,
16888e29072ca69229a25dc4a856e3635d131613f4caAlan Newberger        // redraw the View contents. We call getView here as it will refill the
16898e29072ca69229a25dc4a856e3635d131613f4caAlan Newberger        // view contents, but it is not clear as we are not using the documented
16908e29072ca69229a25dc4a856e3635d131613f4caAlan Newberger        // method intent to get a View, we know that this always uses the view
16918e29072ca69229a25dc4a856e3635d131613f4caAlan Newberger        // passed in to populate it.
16928e29072ca69229a25dc4a856e3635d131613f4caAlan Newberger        // TODO: refactor 'getView' to more explicitly just update view contents
16938e29072ca69229a25dc4a856e3635d131613f4caAlan Newberger        mDataAdapter.getView(item.getView(), adapterIndex, mVideoClickedCallback);
16948e29072ca69229a25dc4a856e3635d131613f4caAlan Newberger
1695d1d28254c1413b4d7725df7d7b38e2a1f2f42e28Angus Kong        mZoomView.resetDecoder();
16963044d8c577432d6e9721fc8b26ac2afbbaf21266Erin Dahlgren
16973044d8c577432d6e9721fc8b26ac2afbbaf21266Erin Dahlgren        boolean stopScroll = clampCenterX();
16983044d8c577432d6e9721fc8b26ac2afbbaf21266Erin Dahlgren        if (stopScroll) {
16991f36cf1c97faf2b7824bbecd62942456847f0e13Angus Kong            mController.stopScrolling(true);
17001f36cf1c97faf2b7824bbecd62942456847f0e13Angus Kong        }
17018ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde
1702c14b6ba434575b7d795a8cd681821f86a39bc371Paul Rohde        Log.d(TAG, "updateViewItem(bufferIndex: " + bufferIndex + ")");
1703c14b6ba434575b7d795a8cd681821f86a39bc371Paul Rohde        Log.d(TAG, "updateViewItem() - mIsUserScrolling: " + mIsUserScrolling);
17048d3552d334196a09e20c83a5296f0b373041032cPaul Rohde        Log.d(TAG, "updateViewItem() - mController.isScrolling() - " + mController.isScrolling());
1705c14b6ba434575b7d795a8cd681821f86a39bc371Paul Rohde
17068d3552d334196a09e20c83a5296f0b373041032cPaul Rohde        // Relying on only isScrolling or isUserScrolling independently
17078d3552d334196a09e20c83a5296f0b373041032cPaul Rohde        // is unreliable. Load the full resolution if either value
17088d3552d334196a09e20c83a5296f0b373041032cPaul Rohde        // reports that the item is not scrolling.
17098d3552d334196a09e20c83a5296f0b373041032cPaul Rohde        if (!mController.isScrolling() || !mIsUserScrolling) {
171077d9f023e76816e5da7bd067ad46cc0e9c98623fPaul Rohde            renderThumbnail(bufferIndex);
1711c14b6ba434575b7d795a8cd681821f86a39bc371Paul Rohde        }
17128ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde
171353aedc0ee671c2d11341a66c444bbb6cdc97b150Angus Kong        adjustChildZOrder();
171453aedc0ee671c2d11341a66c444bbb6cdc97b150Angus Kong        invalidate();
17158a2350a3d557465b53445685db0f9ac838cf90c5Angus Kong        if (mListener != null) {
17168e29072ca69229a25dc4a856e3635d131613f4caAlan Newberger            mListener.onDataUpdated(adapterIndex);
17178a2350a3d557465b53445685db0f9ac838cf90c5Angus Kong        }
171849b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong    }
171949b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong
1720f1f5186f7d3fd8bf35009d9e65a43914c664d82bSascha Haeberling    /** Some of the data is changed. */
17218ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde    private void update(FilmstripDataAdapter.UpdateReporter reporter) {
172249b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong        // No data yet.
17238ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde        if (mViewItems[BUFFER_CENTER] == null) {
172449b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong            reload();
172549b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong            return;
172649b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong        }
172749b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong
172849b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong        // Check the current one.
17298ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde        ViewItem curr = mViewItems[BUFFER_CENTER];
17308ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde        int index = curr.getAdapterIndex();
17318ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde        if (reporter.isDataRemoved(index)) {
173249b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong            reload();
173349b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong            return;
173449b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong        }
17358ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde        if (reporter.isDataUpdated(index)) {
17368ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde            updateViewItem(BUFFER_CENTER);
17378ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde            final FilmstripItem data = mDataAdapter.getFilmstripItemAt(index);
1738a5d6b243d3d742cf9dd2ffdac88d386abacd123aAngus Kong            if (!mIsUserScrolling && !mController.isScrolling()) {
1739a5d6b243d3d742cf9dd2ffdac88d386abacd123aAngus Kong                // If there is no scrolling at all, adjust mCenterX to place
1740a5d6b243d3d742cf9dd2ffdac88d386abacd123aAngus Kong                // the current item at the center.
17418ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde                Point dim = CameraUtil.resizeToFill(
174251cafa0a35546a42c573357aa7a031a79cf9ba1bAlan Newberger                      data.getDimensions().getWidth(),
174351cafa0a35546a42c573357aa7a031a79cf9ba1bAlan Newberger                      data.getDimensions().getHeight(),
174451cafa0a35546a42c573357aa7a031a79cf9ba1bAlan Newberger                      data.getOrientation(),
17458ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde                      getMeasuredWidth(),
17468ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde                      getMeasuredHeight());
1747c195e7a9208c90d7a55e184d4b2b9c747e9c90f1Angus Kong                mCenterX = curr.getLeftPosition() + dim.x / 2;
1748a5d6b243d3d742cf9dd2ffdac88d386abacd123aAngus Kong            }
174949b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong        }
175049b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong
175149b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong        // Check left
17528ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde        for (int i = BUFFER_CENTER - 1; i >= 0; i--) {
17538ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde            curr = mViewItems[i];
175449b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong            if (curr != null) {
17558ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde                index = curr.getAdapterIndex();
17568ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde                if (reporter.isDataRemoved(index) || reporter.isDataUpdated(index)) {
17578969a67c926608737f3e49f2d2240298372e8416Angus Kong                    updateViewItem(i);
175849b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong                }
17598ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde
176049b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong            } else {
17618ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde                ViewItem next = mViewItems[i + 1];
176287f9a62f473a3c57505e2877b0a46ab3cb5e62acAngus Kong                if (next != null) {
17638ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde                    mViewItems[i] = buildViewItemAt(next.getAdapterIndex() - 1);
176487f9a62f473a3c57505e2877b0a46ab3cb5e62acAngus Kong                }
176549b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong            }
176649b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong        }
176749b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong
176849b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong        // Check right
17698ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde        for (int i = BUFFER_CENTER + 1; i < BUFFER_SIZE; i++) {
17708ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde            curr = mViewItems[i];
177149b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong            if (curr != null) {
17728ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde                index = curr.getAdapterIndex();
17738ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde                if (reporter.isDataRemoved(index) || reporter.isDataUpdated(index)) {
17748969a67c926608737f3e49f2d2240298372e8416Angus Kong                    updateViewItem(i);
177549b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong                }
177649b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong            } else {
17778ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde                ViewItem prev = mViewItems[i - 1];
177887f9a62f473a3c57505e2877b0a46ab3cb5e62acAngus Kong                if (prev != null) {
17798ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde                    mViewItems[i] = buildViewItemAt(prev.getAdapterIndex() + 1);
178087f9a62f473a3c57505e2877b0a46ab3cb5e62acAngus Kong                }
178149b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong            }
178249b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong        }
178353aedc0ee671c2d11341a66c444bbb6cdc97b150Angus Kong        adjustChildZOrder();
1784b7a52a4caafd270dbb6aaa41f890a516359e44b8Erin Dahlgren        // Request a layout to find the measured width/height of the view first.
17858969a67c926608737f3e49f2d2240298372e8416Angus Kong        requestLayout();
178649b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong    }
178749b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong
1788f1f5186f7d3fd8bf35009d9e65a43914c664d82bSascha Haeberling    /**
1789f1f5186f7d3fd8bf35009d9e65a43914c664d82bSascha Haeberling     * The whole data might be totally different. Flush all and load from the
17903f969c1735e2636bf22dfe44104d0e99924cca97Alan Newberger     * start. Filmstrip will be centered on the first item, i.e. the camera
17913f969c1735e2636bf22dfe44104d0e99924cca97Alan Newberger     * preview.
1792f1f5186f7d3fd8bf35009d9e65a43914c664d82bSascha Haeberling     */
179349b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong    private void reload() {
17947d2388d96522975307cf5c40f7ac911ad963c44dAngus Kong        mController.stopScrolling(true);
17957d2388d96522975307cf5c40f7ac911ad963c44dAngus Kong        mController.stopScale();
17968ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde        mAdapterIndexUserIsScrollingOver = 0;
179756e44af257d65d730f04d1e90c87f5674abd7f26ztenghui
1798faaee012acc80ad369cb03df9c196e48140f1e7bAngus Kong        int prevId = -1;
17998ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde        if (mViewItems[BUFFER_CENTER] != null) {
18008ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde            prevId = mViewItems[BUFFER_CENTER].getAdapterIndex();
18017d2388d96522975307cf5c40f7ac911ad963c44dAngus Kong        }
180256e44af257d65d730f04d1e90c87f5674abd7f26ztenghui
18038ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde        // Remove all views from the mViewItems buffer, except the camera view.
18048ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde        for (int i = 0; i < mViewItems.length; i++) {
18058ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde            if (mViewItems[i] == null) {
18063f969c1735e2636bf22dfe44104d0e99924cca97Alan Newberger                continue;
18073f969c1735e2636bf22dfe44104d0e99924cca97Alan Newberger            }
18085a0be52d3db2398cfe01d18adf779d9e443edf82Alan Newberger            mViewItems[i].removeViewFromHierarchy();
18093f969c1735e2636bf22dfe44104d0e99924cca97Alan Newberger        }
18103f969c1735e2636bf22dfe44104d0e99924cca97Alan Newberger
18113f969c1735e2636bf22dfe44104d0e99924cca97Alan Newberger        // Clear out the mViewItems and rebuild with camera in the center.
18128ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde        Arrays.fill(mViewItems, null);
181349b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong        int dataNumber = mDataAdapter.getTotalNumber();
1814f1f5186f7d3fd8bf35009d9e65a43914c664d82bSascha Haeberling        if (dataNumber == 0) {
1815f1f5186f7d3fd8bf35009d9e65a43914c664d82bSascha Haeberling            return;
1816f1f5186f7d3fd8bf35009d9e65a43914c664d82bSascha Haeberling        }
181749b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong
18188ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde        mViewItems[BUFFER_CENTER] = buildViewItemAt(0);
18198ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde        if (mViewItems[BUFFER_CENTER] == null) {
18203d0b691b8f7a844093e8de9267ffa2ce16d0c5a9Angus Kong            return;
18213d0b691b8f7a844093e8de9267ffa2ce16d0c5a9Angus Kong        }
18228ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde        mViewItems[BUFFER_CENTER].setLeftPosition(0);
18238ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde        for (int i = BUFFER_CENTER + 1; i < BUFFER_SIZE; i++) {
18248ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde            mViewItems[i] = buildViewItemAt(mViewItems[i - 1].getAdapterIndex() + 1);
18258ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde            if (mViewItems[i] == null) {
18263f969c1735e2636bf22dfe44104d0e99924cca97Alan Newberger                break;
182749b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong            }
182849b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong        }
18293f969c1735e2636bf22dfe44104d0e99924cca97Alan Newberger
18308ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde        // Ensure that the views in mViewItems will layout the first in the
18313f969c1735e2636bf22dfe44104d0e99924cca97Alan Newberger        // center of the display upon a reload.
18323f969c1735e2636bf22dfe44104d0e99924cca97Alan Newberger        mCenterX = -1;
1833166e36fb6e04d40a1bef0459ee6b96c9c736039bAngus Kong        mScale = FILM_STRIP_SCALE;
18343f969c1735e2636bf22dfe44104d0e99924cca97Alan Newberger
183553aedc0ee671c2d11341a66c444bbb6cdc97b150Angus Kong        adjustChildZOrder();
18368ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde
1837c14b6ba434575b7d795a8cd681821f86a39bc371Paul Rohde        Log.d(TAG, "reload() - Ensure all items are loaded at max size.");
183877d9f023e76816e5da7bd067ad46cc0e9c98623fPaul Rohde        renderAllThumbnails();
18397d2388d96522975307cf5c40f7ac911ad963c44dAngus Kong        invalidate();
1840742cd5b18e1899bee1d430b61891238c0f3502b6Doris Liu
18417d2388d96522975307cf5c40f7ac911ad963c44dAngus Kong        if (mListener != null) {
1842c02b13af4c44fe23d4a563d3c2df1ca50e44affcAngus Kong            mListener.onDataReloaded();
18438ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde            mListener.onDataFocusChanged(prevId, mViewItems[BUFFER_CENTER].getAdapterIndex());
18447d2388d96522975307cf5c40f7ac911ad963c44dAngus Kong        }
184549b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong    }
184649b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong
18478ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde    private void promoteData(int index) {
184887f9a62f473a3c57505e2877b0a46ab3cb5e62acAngus Kong        if (mListener != null) {
18498ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde            mListener.onFocusedDataPromoted(index);
185087f9a62f473a3c57505e2877b0a46ab3cb5e62acAngus Kong        }
185187f9a62f473a3c57505e2877b0a46ab3cb5e62acAngus Kong    }
185287f9a62f473a3c57505e2877b0a46ab3cb5e62acAngus Kong
18538ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde    private void demoteData(int index) {
185487f9a62f473a3c57505e2877b0a46ab3cb5e62acAngus Kong        if (mListener != null) {
18558ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde            mListener.onFocusedDataDemoted(index);
185687f9a62f473a3c57505e2877b0a46ab3cb5e62acAngus Kong        }
185787f9a62f473a3c57505e2877b0a46ab3cb5e62acAngus Kong    }
185887f9a62f473a3c57505e2877b0a46ab3cb5e62acAngus Kong
1859faaee012acc80ad369cb03df9c196e48140f1e7bAngus Kong    private void onEnterFilmstrip() {
1860c14b6ba434575b7d795a8cd681821f86a39bc371Paul Rohde        Log.d(TAG, "onEnterFilmstrip()");
1861faaee012acc80ad369cb03df9c196e48140f1e7bAngus Kong        if (mListener != null) {
18628ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde            mListener.onEnterFilmstrip(getCurrentItemAdapterIndex());
1863faaee012acc80ad369cb03df9c196e48140f1e7bAngus Kong        }
1864faaee012acc80ad369cb03df9c196e48140f1e7bAngus Kong    }
1865faaee012acc80ad369cb03df9c196e48140f1e7bAngus Kong
1866faaee012acc80ad369cb03df9c196e48140f1e7bAngus Kong    private void onLeaveFilmstrip() {
1867faaee012acc80ad369cb03df9c196e48140f1e7bAngus Kong        if (mListener != null) {
18688ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde            mListener.onLeaveFilmstrip(getCurrentItemAdapterIndex());
1869faaee012acc80ad369cb03df9c196e48140f1e7bAngus Kong        }
1870faaee012acc80ad369cb03df9c196e48140f1e7bAngus Kong    }
1871faaee012acc80ad369cb03df9c196e48140f1e7bAngus Kong
1872faaee012acc80ad369cb03df9c196e48140f1e7bAngus Kong    private void onEnterFullScreen() {
18734567160a43fa5787012bf0b618c216cf18ac0457Angus Kong        mFullScreenUIHidden = false;
1874faaee012acc80ad369cb03df9c196e48140f1e7bAngus Kong        if (mListener != null) {
18758ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde            mListener.onEnterFullScreenUiShown(getCurrentItemAdapterIndex());
1876faaee012acc80ad369cb03df9c196e48140f1e7bAngus Kong        }
1877faaee012acc80ad369cb03df9c196e48140f1e7bAngus Kong    }
1878faaee012acc80ad369cb03df9c196e48140f1e7bAngus Kong
1879faaee012acc80ad369cb03df9c196e48140f1e7bAngus Kong    private void onLeaveFullScreen() {
1880faaee012acc80ad369cb03df9c196e48140f1e7bAngus Kong        if (mListener != null) {
18818ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde            mListener.onLeaveFullScreenUiShown(getCurrentItemAdapterIndex());
18824567160a43fa5787012bf0b618c216cf18ac0457Angus Kong        }
18834567160a43fa5787012bf0b618c216cf18ac0457Angus Kong    }
18844567160a43fa5787012bf0b618c216cf18ac0457Angus Kong
18854567160a43fa5787012bf0b618c216cf18ac0457Angus Kong    private void onEnterFullScreenUiHidden() {
18864567160a43fa5787012bf0b618c216cf18ac0457Angus Kong        mFullScreenUIHidden = true;
18874567160a43fa5787012bf0b618c216cf18ac0457Angus Kong        if (mListener != null) {
18888ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde            mListener.onEnterFullScreenUiHidden(getCurrentItemAdapterIndex());
18894567160a43fa5787012bf0b618c216cf18ac0457Angus Kong        }
18904567160a43fa5787012bf0b618c216cf18ac0457Angus Kong    }
18914567160a43fa5787012bf0b618c216cf18ac0457Angus Kong
18924567160a43fa5787012bf0b618c216cf18ac0457Angus Kong    private void onLeaveFullScreenUiHidden() {
18934567160a43fa5787012bf0b618c216cf18ac0457Angus Kong        mFullScreenUIHidden = false;
18944567160a43fa5787012bf0b618c216cf18ac0457Angus Kong        if (mListener != null) {
18958ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde            mListener.onLeaveFullScreenUiHidden(getCurrentItemAdapterIndex());
1896faaee012acc80ad369cb03df9c196e48140f1e7bAngus Kong        }
1897faaee012acc80ad369cb03df9c196e48140f1e7bAngus Kong    }
1898faaee012acc80ad369cb03df9c196e48140f1e7bAngus Kong
1899faaee012acc80ad369cb03df9c196e48140f1e7bAngus Kong    private void onEnterZoomView() {
1900faaee012acc80ad369cb03df9c196e48140f1e7bAngus Kong        if (mListener != null) {
19018ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde            mListener.onEnterZoomView(getCurrentItemAdapterIndex());
1902faaee012acc80ad369cb03df9c196e48140f1e7bAngus Kong        }
1903faaee012acc80ad369cb03df9c196e48140f1e7bAngus Kong    }
1904faaee012acc80ad369cb03df9c196e48140f1e7bAngus Kong
19054567160a43fa5787012bf0b618c216cf18ac0457Angus Kong    private void onLeaveZoomView() {
19064567160a43fa5787012bf0b618c216cf18ac0457Angus Kong        mController.setSurroundingViewsVisible(true);
19074567160a43fa5787012bf0b618c216cf18ac0457Angus Kong    }
19084567160a43fa5787012bf0b618c216cf18ac0457Angus Kong
1909f1f5186f7d3fd8bf35009d9e65a43914c664d82bSascha Haeberling    /**
1910f1f5186f7d3fd8bf35009d9e65a43914c664d82bSascha Haeberling     * MyController controls all the geometry animations. It passively tells the
1911f1f5186f7d3fd8bf35009d9e65a43914c664d82bSascha Haeberling     * geometry information on demand.
1912f1f5186f7d3fd8bf35009d9e65a43914c664d82bSascha Haeberling     */
19138ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde    private class FilmstripControllerImpl implements FilmstripController {
191449b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong
1915734598c39e6a9d809982dae2907dc7d10e0b0e9fAngus Kong        private final ValueAnimator mScaleAnimator;
1916121022cb590955e37f1264f77190ce4711159976Doris Liu        private ValueAnimator mZoomAnimator;
1917d70f0fb68064e170fb2cf041a707ed04d68040f9Doris Liu        private AnimatorSet mFlingAnimator;
191849b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong
19198ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde        private final FilmstripScrollGesture mScrollGesture;
19207d2388d96522975307cf5c40f7ac911ad963c44dAngus Kong        private boolean mCanStopScroll;
1921734598c39e6a9d809982dae2907dc7d10e0b0e9fAngus Kong
19228ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde        private final FilmstripScrollGesture.Listener mScrollListener =
19238ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde                new FilmstripScrollGesture.Listener() {
1924734598c39e6a9d809982dae2907dc7d10e0b0e9fAngus Kong                    @Override
1925b21215a1a59812592380f31891becedbdec7468bAngus Kong                    public void onScrollUpdate(int currX, int currY) {
1926b21215a1a59812592380f31891becedbdec7468bAngus Kong                        mCenterX = currX;
19273044d8c577432d6e9721fc8b26ac2afbbaf21266Erin Dahlgren
19283044d8c577432d6e9721fc8b26ac2afbbaf21266Erin Dahlgren                        boolean stopScroll = clampCenterX();
19293044d8c577432d6e9721fc8b26ac2afbbaf21266Erin Dahlgren                        if (stopScroll) {
1930c14b6ba434575b7d795a8cd681821f86a39bc371Paul Rohde                            Log.d(TAG, "[fling] onScrollUpdate() - stopScrolling!");
19311f36cf1c97faf2b7824bbecd62942456847f0e13Angus Kong                            mController.stopScrolling(true);
19321f36cf1c97faf2b7824bbecd62942456847f0e13Angus Kong                        }
1933b21215a1a59812592380f31891becedbdec7468bAngus Kong                        invalidate();
1934734598c39e6a9d809982dae2907dc7d10e0b0e9fAngus Kong                    }
193549b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong
1936b21215a1a59812592380f31891becedbdec7468bAngus Kong                    @Override
1937b21215a1a59812592380f31891becedbdec7468bAngus Kong                    public void onScrollEnd() {
1938b21215a1a59812592380f31891becedbdec7468bAngus Kong                        mCanStopScroll = true;
1939c14b6ba434575b7d795a8cd681821f86a39bc371Paul Rohde                        Log.d(TAG, "[fling] onScrollEnd() - onScrollEnd");
19408ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde                        if (mViewItems[BUFFER_CENTER] == null) {
1941b21215a1a59812592380f31891becedbdec7468bAngus Kong                            return;
1942b21215a1a59812592380f31891becedbdec7468bAngus Kong                        }
19438ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde                        scrollCurrentItemToCenter();
19448ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde
19458ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde                        // onScrollEnd will get called twice, once when
19468ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde                        // the fling part ends, and once when the manual
19478ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde                        // scroll center animation finishes. Once everything
19488ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde                        // stops moving ensure that the items are loaded at
19498ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde                        // full resolution.
19508ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde                        if (isCurrentItemCentered()) {
19518ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde                            // Since these are getting pushed into a queue,
19528ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde                            // we want to ensure the item that is "most in view" is
19538ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde                            // the first one rendered at max size.
1954c14b6ba434575b7d795a8cd681821f86a39bc371Paul Rohde
1955c14b6ba434575b7d795a8cd681821f86a39bc371Paul Rohde                            Log.d(TAG, "[fling] onScrollEnd() - Ensuring that items are at"
1956c14b6ba434575b7d795a8cd681821f86a39bc371Paul Rohde                                  + " full resolution.");
195777d9f023e76816e5da7bd067ad46cc0e9c98623fPaul Rohde                            renderThumbnail(BUFFER_CENTER);
195877d9f023e76816e5da7bd067ad46cc0e9c98623fPaul Rohde                            renderThumbnail(BUFFER_CENTER + 1);
195977d9f023e76816e5da7bd067ad46cc0e9c98623fPaul Rohde                            renderThumbnail(BUFFER_CENTER - 1);
196077d9f023e76816e5da7bd067ad46cc0e9c98623fPaul Rohde                            renderThumbnail(BUFFER_CENTER + 2);
19618ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde                        }
19627d2388d96522975307cf5c40f7ac911ad963c44dAngus Kong                    }
1963b21215a1a59812592380f31891becedbdec7468bAngus Kong                };
1964b21215a1a59812592380f31891becedbdec7468bAngus Kong
1965280fd3edae1dc35ac1015bcc9532477169fad3aaSascha Haeberling        private final ValueAnimator.AnimatorUpdateListener mScaleAnimatorUpdateListener =
1966b21215a1a59812592380f31891becedbdec7468bAngus Kong                new ValueAnimator.AnimatorUpdateListener() {
1967b21215a1a59812592380f31891becedbdec7468bAngus Kong                    @Override
1968b21215a1a59812592380f31891becedbdec7468bAngus Kong                    public void onAnimationUpdate(ValueAnimator animation) {
19698ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde                        if (mViewItems[BUFFER_CENTER] == null) {
1970b21215a1a59812592380f31891becedbdec7468bAngus Kong                            return;
1971b21215a1a59812592380f31891becedbdec7468bAngus Kong                        }
1972b21215a1a59812592380f31891becedbdec7468bAngus Kong                        mScale = (Float) animation.getAnimatedValue();
1973b21215a1a59812592380f31891becedbdec7468bAngus Kong                        invalidate();
1974b21215a1a59812592380f31891becedbdec7468bAngus Kong                    }
1975b21215a1a59812592380f31891becedbdec7468bAngus Kong                };
197649b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong
19778ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde        FilmstripControllerImpl() {
19781f36cf1c97faf2b7824bbecd62942456847f0e13Angus Kong            TimeInterpolator decelerateInterpolator = new DecelerateInterpolator(1.5f);
19798ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde            mScrollGesture = new FilmstripScrollGesture(mActivity.getAndroidContext(),
19801f36cf1c97faf2b7824bbecd62942456847f0e13Angus Kong                    new Handler(mActivity.getMainLooper()),
19818ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde                  mScrollListener, decelerateInterpolator);
1982734598c39e6a9d809982dae2907dc7d10e0b0e9fAngus Kong            mCanStopScroll = true;
1983734598c39e6a9d809982dae2907dc7d10e0b0e9fAngus Kong
198449b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong            mScaleAnimator = new ValueAnimator();
1985b21215a1a59812592380f31891becedbdec7468bAngus Kong            mScaleAnimator.addUpdateListener(mScaleAnimatorUpdateListener);
19861f36cf1c97faf2b7824bbecd62942456847f0e13Angus Kong            mScaleAnimator.setInterpolator(decelerateInterpolator);
1987faaee012acc80ad369cb03df9c196e48140f1e7bAngus Kong            mScaleAnimator.addListener(new Animator.AnimatorListener() {
1988faaee012acc80ad369cb03df9c196e48140f1e7bAngus Kong                @Override
1989faaee012acc80ad369cb03df9c196e48140f1e7bAngus Kong                public void onAnimationStart(Animator animator) {
1990faaee012acc80ad369cb03df9c196e48140f1e7bAngus Kong                    if (mScale == FULL_SCREEN_SCALE) {
1991faaee012acc80ad369cb03df9c196e48140f1e7bAngus Kong                        onLeaveFullScreen();
1992faaee012acc80ad369cb03df9c196e48140f1e7bAngus Kong                    } else {
1993faaee012acc80ad369cb03df9c196e48140f1e7bAngus Kong                        if (mScale == FILM_STRIP_SCALE) {
1994faaee012acc80ad369cb03df9c196e48140f1e7bAngus Kong                            onLeaveFilmstrip();
1995faaee012acc80ad369cb03df9c196e48140f1e7bAngus Kong                        }
1996faaee012acc80ad369cb03df9c196e48140f1e7bAngus Kong                    }
1997faaee012acc80ad369cb03df9c196e48140f1e7bAngus Kong                }
1998faaee012acc80ad369cb03df9c196e48140f1e7bAngus Kong
1999faaee012acc80ad369cb03df9c196e48140f1e7bAngus Kong                @Override
2000faaee012acc80ad369cb03df9c196e48140f1e7bAngus Kong                public void onAnimationEnd(Animator animator) {
2001faaee012acc80ad369cb03df9c196e48140f1e7bAngus Kong                    if (mScale == FULL_SCREEN_SCALE) {
2002faaee012acc80ad369cb03df9c196e48140f1e7bAngus Kong                        onEnterFullScreen();
2003faaee012acc80ad369cb03df9c196e48140f1e7bAngus Kong                    } else {
2004faaee012acc80ad369cb03df9c196e48140f1e7bAngus Kong                        if (mScale == FILM_STRIP_SCALE) {
2005faaee012acc80ad369cb03df9c196e48140f1e7bAngus Kong                            onEnterFilmstrip();
2006faaee012acc80ad369cb03df9c196e48140f1e7bAngus Kong                        }
2007faaee012acc80ad369cb03df9c196e48140f1e7bAngus Kong                    }
2008c8924b2ec3788ca6d0f0af5ca749e5fe7be125ceAndy Huibers                    zoomAtIndexChanged();
2009faaee012acc80ad369cb03df9c196e48140f1e7bAngus Kong                }
2010faaee012acc80ad369cb03df9c196e48140f1e7bAngus Kong
2011faaee012acc80ad369cb03df9c196e48140f1e7bAngus Kong                @Override
2012faaee012acc80ad369cb03df9c196e48140f1e7bAngus Kong                public void onAnimationCancel(Animator animator) {
2013faaee012acc80ad369cb03df9c196e48140f1e7bAngus Kong
2014faaee012acc80ad369cb03df9c196e48140f1e7bAngus Kong                }
2015faaee012acc80ad369cb03df9c196e48140f1e7bAngus Kong
2016faaee012acc80ad369cb03df9c196e48140f1e7bAngus Kong                @Override
2017faaee012acc80ad369cb03df9c196e48140f1e7bAngus Kong                public void onAnimationRepeat(Animator animator) {
2018faaee012acc80ad369cb03df9c196e48140f1e7bAngus Kong
2019faaee012acc80ad369cb03df9c196e48140f1e7bAngus Kong                }
2020faaee012acc80ad369cb03df9c196e48140f1e7bAngus Kong            });
202149b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong        }
202249b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong
20236d23f4c77fde7ddf70e0edd48f46cc22b7f7320eAngus Kong        @Override
2024faaee012acc80ad369cb03df9c196e48140f1e7bAngus Kong        public void setImageGap(int imageGap) {
2025faaee012acc80ad369cb03df9c196e48140f1e7bAngus Kong            FilmstripView.this.setViewGap(imageGap);
2026628481532e1e58a0a26330d238e850aec5657bceAngus Kong        }
2027628481532e1e58a0a26330d238e850aec5657bceAngus Kong
2028628481532e1e58a0a26330d238e850aec5657bceAngus Kong        @Override
20298ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde        public int getCurrentAdapterIndex() {
20308ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde            return FilmstripView.this.getCurrentItemAdapterIndex();
2031628481532e1e58a0a26330d238e850aec5657bceAngus Kong        }
2032628481532e1e58a0a26330d238e850aec5657bceAngus Kong
2033628481532e1e58a0a26330d238e850aec5657bceAngus Kong        @Override
20348ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde        public void setDataAdapter(FilmstripDataAdapter adapter) {
2035628481532e1e58a0a26330d238e850aec5657bceAngus Kong            FilmstripView.this.setDataAdapter(adapter);
2036628481532e1e58a0a26330d238e850aec5657bceAngus Kong        }
2037628481532e1e58a0a26330d238e850aec5657bceAngus Kong
2038628481532e1e58a0a26330d238e850aec5657bceAngus Kong        @Override
2039628481532e1e58a0a26330d238e850aec5657bceAngus Kong        public boolean inFilmstrip() {
2040628481532e1e58a0a26330d238e850aec5657bceAngus Kong            return FilmstripView.this.inFilmstrip();
2041628481532e1e58a0a26330d238e850aec5657bceAngus Kong        }
2042628481532e1e58a0a26330d238e850aec5657bceAngus Kong
2043628481532e1e58a0a26330d238e850aec5657bceAngus Kong        @Override
2044628481532e1e58a0a26330d238e850aec5657bceAngus Kong        public boolean inFullScreen() {
2045628481532e1e58a0a26330d238e850aec5657bceAngus Kong            return FilmstripView.this.inFullScreen();
2046628481532e1e58a0a26330d238e850aec5657bceAngus Kong        }
2047628481532e1e58a0a26330d238e850aec5657bceAngus Kong
2048628481532e1e58a0a26330d238e850aec5657bceAngus Kong        @Override
20498ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde        public void setListener(FilmstripListener listener) {
20508ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde            FilmstripView.this.setListener(listener);
2051628481532e1e58a0a26330d238e850aec5657bceAngus Kong        }
2052628481532e1e58a0a26330d238e850aec5657bceAngus Kong
2053628481532e1e58a0a26330d238e850aec5657bceAngus Kong        @Override
20546d23f4c77fde7ddf70e0edd48f46cc22b7f7320eAngus Kong        public boolean isScrolling() {
20558ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde            return !mScrollGesture.isFinished();
2056af24d28f8938a05d2e3c40155e39cbc7ff4d64b7Angus Kong        }
2057af24d28f8938a05d2e3c40155e39cbc7ff4d64b7Angus Kong
20586d23f4c77fde7ddf70e0edd48f46cc22b7f7320eAngus Kong        @Override
20598de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu        public boolean isScaling() {
20608de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu            return mScaleAnimator.isRunning();
2061af24d28f8938a05d2e3c40155e39cbc7ff4d64b7Angus Kong        }
2062af24d28f8938a05d2e3c40155e39cbc7ff4d64b7Angus Kong
20638ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde        private int estimateMinX(int index, int leftPos, int viewWidth) {
20648ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde            return leftPos - (index + 100) * (viewWidth + mViewGapInPixel);
206549b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong        }
206649b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong
20678ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde        private int estimateMaxX(int index, int leftPos, int viewWidth) {
2068f1f5186f7d3fd8bf35009d9e65a43914c664d82bSascha Haeberling            return leftPos
20698ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde                    + (mDataAdapter.getTotalNumber() - index + 100)
2070faaee012acc80ad369cb03df9c196e48140f1e7bAngus Kong                    * (viewWidth + mViewGapInPixel);
20716d23f4c77fde7ddf70e0edd48f46cc22b7f7320eAngus Kong        }
20726d23f4c77fde7ddf70e0edd48f46cc22b7f7320eAngus Kong
2073121022cb590955e37f1264f77190ce4711159976Doris Liu        /** Zoom all the way in or out on the image at the given pivot point. */
2074121022cb590955e37f1264f77190ce4711159976Doris Liu        private void zoomAt(final ViewItem current, final float focusX, final float focusY) {
2075121022cb590955e37f1264f77190ce4711159976Doris Liu            // End previous zoom animation, if any
2076121022cb590955e37f1264f77190ce4711159976Doris Liu            if (mZoomAnimator != null) {
2077121022cb590955e37f1264f77190ce4711159976Doris Liu                mZoomAnimator.end();
2078121022cb590955e37f1264f77190ce4711159976Doris Liu            }
2079121022cb590955e37f1264f77190ce4711159976Doris Liu            // Calculate end scale
20803179f6a49516833761be5155c4e1244ac0aa17feDoris Liu            final float maxScale = getCurrentDataMaxScale(false);
2081121022cb590955e37f1264f77190ce4711159976Doris Liu            final float endScale = mScale < maxScale - maxScale * TOLERANCE
2082121022cb590955e37f1264f77190ce4711159976Doris Liu                    ? maxScale : FULL_SCREEN_SCALE;
2083121022cb590955e37f1264f77190ce4711159976Doris Liu
2084121022cb590955e37f1264f77190ce4711159976Doris Liu            mZoomAnimator = new ValueAnimator();
2085121022cb590955e37f1264f77190ce4711159976Doris Liu            mZoomAnimator.setFloatValues(mScale, endScale);
2086121022cb590955e37f1264f77190ce4711159976Doris Liu            mZoomAnimator.setDuration(ZOOM_ANIMATION_DURATION_MS);
2087121022cb590955e37f1264f77190ce4711159976Doris Liu            mZoomAnimator.addListener(new Animator.AnimatorListener() {
2088121022cb590955e37f1264f77190ce4711159976Doris Liu                @Override
2089121022cb590955e37f1264f77190ce4711159976Doris Liu                public void onAnimationStart(Animator animation) {
2090121022cb590955e37f1264f77190ce4711159976Doris Liu                    if (mScale == FULL_SCREEN_SCALE) {
20914567160a43fa5787012bf0b618c216cf18ac0457Angus Kong                        if (mFullScreenUIHidden) {
20924567160a43fa5787012bf0b618c216cf18ac0457Angus Kong                            onLeaveFullScreenUiHidden();
20934567160a43fa5787012bf0b618c216cf18ac0457Angus Kong                        } else {
20944567160a43fa5787012bf0b618c216cf18ac0457Angus Kong                            onLeaveFullScreen();
20954567160a43fa5787012bf0b618c216cf18ac0457Angus Kong                        }
2096121022cb590955e37f1264f77190ce4711159976Doris Liu                        setSurroundingViewsVisible(false);
20974567160a43fa5787012bf0b618c216cf18ac0457Angus Kong                    } else if (inZoomView()) {
20984567160a43fa5787012bf0b618c216cf18ac0457Angus Kong                        onLeaveZoomView();
2099121022cb590955e37f1264f77190ce4711159976Doris Liu                    }
2100121022cb590955e37f1264f77190ce4711159976Doris Liu                    cancelLoadingZoomedImage();
2101121022cb590955e37f1264f77190ce4711159976Doris Liu                }
2102121022cb590955e37f1264f77190ce4711159976Doris Liu
2103121022cb590955e37f1264f77190ce4711159976Doris Liu                @Override
2104121022cb590955e37f1264f77190ce4711159976Doris Liu                public void onAnimationEnd(Animator animation) {
2105121022cb590955e37f1264f77190ce4711159976Doris Liu                    // Make sure animation ends up having the correct scale even
2106121022cb590955e37f1264f77190ce4711159976Doris Liu                    // if it is cancelled before it finishes
2107121022cb590955e37f1264f77190ce4711159976Doris Liu                    if (mScale != endScale) {
2108280fd3edae1dc35ac1015bcc9532477169fad3aaSascha Haeberling                        current.postScale(focusX, focusY, endScale / mScale, mDrawArea.width(),
2109121022cb590955e37f1264f77190ce4711159976Doris Liu                                mDrawArea.height());
2110121022cb590955e37f1264f77190ce4711159976Doris Liu                        mScale = endScale;
2111121022cb590955e37f1264f77190ce4711159976Doris Liu                    }
2112121022cb590955e37f1264f77190ce4711159976Doris Liu
21134567160a43fa5787012bf0b618c216cf18ac0457Angus Kong                    if (inFullScreen()) {
2114121022cb590955e37f1264f77190ce4711159976Doris Liu                        setSurroundingViewsVisible(true);
2115121022cb590955e37f1264f77190ce4711159976Doris Liu                        mZoomView.setVisibility(GONE);
2116121022cb590955e37f1264f77190ce4711159976Doris Liu                        current.resetTransform();
21174567160a43fa5787012bf0b618c216cf18ac0457Angus Kong                        onEnterFullScreenUiHidden();
2118121022cb590955e37f1264f77190ce4711159976Doris Liu                    } else {
2119121022cb590955e37f1264f77190ce4711159976Doris Liu                        mController.loadZoomedImage();
2120faaee012acc80ad369cb03df9c196e48140f1e7bAngus Kong                        onEnterZoomView();
2121121022cb590955e37f1264f77190ce4711159976Doris Liu                    }
2122121022cb590955e37f1264f77190ce4711159976Doris Liu                    mZoomAnimator = null;
2123c8924b2ec3788ca6d0f0af5ca749e5fe7be125ceAndy Huibers                    zoomAtIndexChanged();
2124121022cb590955e37f1264f77190ce4711159976Doris Liu                }
2125121022cb590955e37f1264f77190ce4711159976Doris Liu
2126121022cb590955e37f1264f77190ce4711159976Doris Liu                @Override
2127121022cb590955e37f1264f77190ce4711159976Doris Liu                public void onAnimationCancel(Animator animation) {
2128121022cb590955e37f1264f77190ce4711159976Doris Liu                    // Do nothing.
2129121022cb590955e37f1264f77190ce4711159976Doris Liu                }
2130121022cb590955e37f1264f77190ce4711159976Doris Liu
2131121022cb590955e37f1264f77190ce4711159976Doris Liu                @Override
2132121022cb590955e37f1264f77190ce4711159976Doris Liu                public void onAnimationRepeat(Animator animation) {
2133121022cb590955e37f1264f77190ce4711159976Doris Liu                    // Do nothing.
2134121022cb590955e37f1264f77190ce4711159976Doris Liu                }
2135121022cb590955e37f1264f77190ce4711159976Doris Liu            });
2136121022cb590955e37f1264f77190ce4711159976Doris Liu
2137121022cb590955e37f1264f77190ce4711159976Doris Liu            mZoomAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
2138121022cb590955e37f1264f77190ce4711159976Doris Liu                @Override
2139121022cb590955e37f1264f77190ce4711159976Doris Liu                public void onAnimationUpdate(ValueAnimator animation) {
2140121022cb590955e37f1264f77190ce4711159976Doris Liu                    float newScale = (Float) animation.getAnimatedValue();
2141121022cb590955e37f1264f77190ce4711159976Doris Liu                    float postScale = newScale / mScale;
2142121022cb590955e37f1264f77190ce4711159976Doris Liu                    mScale = newScale;
2143121022cb590955e37f1264f77190ce4711159976Doris Liu                    current.postScale(focusX, focusY, postScale, mDrawArea.width(),
2144121022cb590955e37f1264f77190ce4711159976Doris Liu                            mDrawArea.height());
2145121022cb590955e37f1264f77190ce4711159976Doris Liu                }
2146121022cb590955e37f1264f77190ce4711159976Doris Liu            });
2147121022cb590955e37f1264f77190ce4711159976Doris Liu            mZoomAnimator.start();
2148121022cb590955e37f1264f77190ce4711159976Doris Liu        }
2149121022cb590955e37f1264f77190ce4711159976Doris Liu
21506d23f4c77fde7ddf70e0edd48f46cc22b7f7320eAngus Kong        @Override
2151a6a6a730074e9fedd56c41cd8fa8f1212cfac108Angus Kong        public void scroll(float deltaX) {
21527d2388d96522975307cf5c40f7ac911ad963c44dAngus Kong            if (!stopScrolling(false)) {
2153a6a6a730074e9fedd56c41cd8fa8f1212cfac108Angus Kong                return;
2154a6a6a730074e9fedd56c41cd8fa8f1212cfac108Angus Kong            }
2155a6a6a730074e9fedd56c41cd8fa8f1212cfac108Angus Kong            mCenterX += deltaX;
21563044d8c577432d6e9721fc8b26ac2afbbaf21266Erin Dahlgren
21573044d8c577432d6e9721fc8b26ac2afbbaf21266Erin Dahlgren            boolean stopScroll = clampCenterX();
21583044d8c577432d6e9721fc8b26ac2afbbaf21266Erin Dahlgren            if (stopScroll) {
21591f36cf1c97faf2b7824bbecd62942456847f0e13Angus Kong                mController.stopScrolling(true);
21601f36cf1c97faf2b7824bbecd62942456847f0e13Angus Kong            }
216117e669d695ef2a38d28d5ba2512937836293e9caAngus Kong            invalidate();
2162a6a6a730074e9fedd56c41cd8fa8f1212cfac108Angus Kong        }
2163a6a6a730074e9fedd56c41cd8fa8f1212cfac108Angus Kong
2164a6a6a730074e9fedd56c41cd8fa8f1212cfac108Angus Kong        @Override
21656d23f4c77fde7ddf70e0edd48f46cc22b7f7320eAngus Kong        public void fling(float velocityX) {
21667d2388d96522975307cf5c40f7ac911ad963c44dAngus Kong            if (!stopScrolling(false)) {
2167f1f5186f7d3fd8bf35009d9e65a43914c664d82bSascha Haeberling                return;
2168f1f5186f7d3fd8bf35009d9e65a43914c664d82bSascha Haeberling            }
21698ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde            final ViewItem item = mViewItems[BUFFER_CENTER];
21708969a67c926608737f3e49f2d2240298372e8416Angus Kong            if (item == null) {
2171f1f5186f7d3fd8bf35009d9e65a43914c664d82bSascha Haeberling                return;
2172f1f5186f7d3fd8bf35009d9e65a43914c664d82bSascha Haeberling            }
21736d23f4c77fde7ddf70e0edd48f46cc22b7f7320eAngus Kong
21746d23f4c77fde7ddf70e0edd48f46cc22b7f7320eAngus Kong            float scaledVelocityX = velocityX / mScale;
217586f1d1f3664c909d1d949951056c7bb1e9627324Alan Newberger            if (inFullScreen() && scaledVelocityX < 0) {
2176a6a6a730074e9fedd56c41cd8fa8f1212cfac108Angus Kong                // Swipe left in camera preview.
2177faaee012acc80ad369cb03df9c196e48140f1e7bAngus Kong                goToFilmstrip();
21786d23f4c77fde7ddf70e0edd48f46cc22b7f7320eAngus Kong            }
21796d23f4c77fde7ddf70e0edd48f46cc22b7f7320eAngus Kong
21806d23f4c77fde7ddf70e0edd48f46cc22b7f7320eAngus Kong            int w = getWidth();
21816d23f4c77fde7ddf70e0edd48f46cc22b7f7320eAngus Kong            // Estimation of possible length on the left. To ensure the
21826d23f4c77fde7ddf70e0edd48f46cc22b7f7320eAngus Kong            // velocity doesn't become too slow eventually, we add a huge number
21836d23f4c77fde7ddf70e0edd48f46cc22b7f7320eAngus Kong            // to the estimated maximum.
21848ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde            int minX = estimateMinX(item.getAdapterIndex(), item.getLeftPosition(), w);
21856d23f4c77fde7ddf70e0edd48f46cc22b7f7320eAngus Kong            // Estimation of possible length on the right. Likewise, exaggerate
21866d23f4c77fde7ddf70e0edd48f46cc22b7f7320eAngus Kong            // the possible maximum too.
21878ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde            int maxX = estimateMaxX(item.getAdapterIndex(), item.getLeftPosition(), w);
2188b6a8810b7a5ac0df1e19efd26628c01bcb32b97bPaul Rohde
21898ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde            mScrollGesture.fling(mCenterX, 0, (int) -velocityX, 0, minX, maxX, 0, 0);
21906d23f4c77fde7ddf70e0edd48f46cc22b7f7320eAngus Kong        }
21916d23f4c77fde7ddf70e0edd48f46cc22b7f7320eAngus Kong
2192c02b13af4c44fe23d4a563d3c2df1ca50e44affcAngus Kong        void flingInsideZoomView(float velocityX, float velocityY) {
21934567160a43fa5787012bf0b618c216cf18ac0457Angus Kong            if (!inZoomView()) {
2194d70f0fb68064e170fb2cf041a707ed04d68040f9Doris Liu                return;
2195d70f0fb68064e170fb2cf041a707ed04d68040f9Doris Liu            }
2196d70f0fb68064e170fb2cf041a707ed04d68040f9Doris Liu
21978ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde            final ViewItem current = mViewItems[BUFFER_CENTER];
2198d70f0fb68064e170fb2cf041a707ed04d68040f9Doris Liu            if (current == null) {
2199d70f0fb68064e170fb2cf041a707ed04d68040f9Doris Liu                return;
2200d70f0fb68064e170fb2cf041a707ed04d68040f9Doris Liu            }
2201d70f0fb68064e170fb2cf041a707ed04d68040f9Doris Liu
2202d70f0fb68064e170fb2cf041a707ed04d68040f9Doris Liu            final int factor = DECELERATION_FACTOR;
2203d70f0fb68064e170fb2cf041a707ed04d68040f9Doris Liu            // Deceleration curve for distance:
2204d70f0fb68064e170fb2cf041a707ed04d68040f9Doris Liu            // S(t) = s + (e - s) * (1 - (1 - t/T) ^ factor)
2205280fd3edae1dc35ac1015bcc9532477169fad3aaSascha Haeberling            // Need to find the ending distance (e), so that the starting
2206280fd3edae1dc35ac1015bcc9532477169fad3aaSascha Haeberling            // velocity is the velocity of fling.
2207d70f0fb68064e170fb2cf041a707ed04d68040f9Doris Liu            // Velocity is the derivative of distance
2208d70f0fb68064e170fb2cf041a707ed04d68040f9Doris Liu            // V(t) = (e - s) * factor * (-1) * (1 - t/T) ^ (factor - 1) * (-1/T)
2209d70f0fb68064e170fb2cf041a707ed04d68040f9Doris Liu            //      = (e - s) * factor * (1 - t/T) ^ (factor - 1) / T
2210d70f0fb68064e170fb2cf041a707ed04d68040f9Doris Liu            // Since V(0) = V0, we have e = T / factor * V0 + s
2211d70f0fb68064e170fb2cf041a707ed04d68040f9Doris Liu
2212d70f0fb68064e170fb2cf041a707ed04d68040f9Doris Liu            // Duration T should be long enough so that at the end of the fling,
2213d70f0fb68064e170fb2cf041a707ed04d68040f9Doris Liu            // image moves at 1 pixel/s for about P = 50ms = 0.05s
2214d70f0fb68064e170fb2cf041a707ed04d68040f9Doris Liu            // i.e. V(T - P) = 1
2215d70f0fb68064e170fb2cf041a707ed04d68040f9Doris Liu            // V(T - P) = V0 * (1 - (T -P) /T) ^ (factor - 1) = 1
2216d70f0fb68064e170fb2cf041a707ed04d68040f9Doris Liu            // T = P * V0 ^ (1 / (factor -1))
2217d70f0fb68064e170fb2cf041a707ed04d68040f9Doris Liu
2218d70f0fb68064e170fb2cf041a707ed04d68040f9Doris Liu            final float velocity = Math.max(Math.abs(velocityX), Math.abs(velocityY));
2219d70f0fb68064e170fb2cf041a707ed04d68040f9Doris Liu            // Dynamically calculate duration
2220d70f0fb68064e170fb2cf041a707ed04d68040f9Doris Liu            final float duration = (float) (FLING_COASTING_DURATION_S
2221280fd3edae1dc35ac1015bcc9532477169fad3aaSascha Haeberling                    * Math.pow(velocity, (1f / (factor - 1f))));
2222d70f0fb68064e170fb2cf041a707ed04d68040f9Doris Liu
22231f9db2dfe59c33228103c54523281501ef52c9baAngus Kong            final float translationX = current.getTranslationX() * mScale;
22241f9db2dfe59c33228103c54523281501ef52c9baAngus Kong            final float translationY = current.getTranslationY() * mScale;
2225d70f0fb68064e170fb2cf041a707ed04d68040f9Doris Liu
2226d70f0fb68064e170fb2cf041a707ed04d68040f9Doris Liu            final ValueAnimator decelerationX = ValueAnimator.ofFloat(translationX,
2227d70f0fb68064e170fb2cf041a707ed04d68040f9Doris Liu                    translationX + duration / factor * velocityX);
2228d70f0fb68064e170fb2cf041a707ed04d68040f9Doris Liu            final ValueAnimator decelerationY = ValueAnimator.ofFloat(translationY,
2229d70f0fb68064e170fb2cf041a707ed04d68040f9Doris Liu                    translationY + duration / factor * velocityY);
2230d70f0fb68064e170fb2cf041a707ed04d68040f9Doris Liu
2231d70f0fb68064e170fb2cf041a707ed04d68040f9Doris Liu            decelerationY.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
2232d70f0fb68064e170fb2cf041a707ed04d68040f9Doris Liu                @Override
2233d70f0fb68064e170fb2cf041a707ed04d68040f9Doris Liu                public void onAnimationUpdate(ValueAnimator animation) {
2234d70f0fb68064e170fb2cf041a707ed04d68040f9Doris Liu                    float transX = (Float) decelerationX.getAnimatedValue();
2235d70f0fb68064e170fb2cf041a707ed04d68040f9Doris Liu                    float transY = (Float) decelerationY.getAnimatedValue();
2236d70f0fb68064e170fb2cf041a707ed04d68040f9Doris Liu
2237d70f0fb68064e170fb2cf041a707ed04d68040f9Doris Liu                    current.updateTransform(transX, transY, mScale,
2238d70f0fb68064e170fb2cf041a707ed04d68040f9Doris Liu                            mScale, mDrawArea.width(), mDrawArea.height());
2239d70f0fb68064e170fb2cf041a707ed04d68040f9Doris Liu                }
2240d70f0fb68064e170fb2cf041a707ed04d68040f9Doris Liu            });
2241d70f0fb68064e170fb2cf041a707ed04d68040f9Doris Liu
2242d70f0fb68064e170fb2cf041a707ed04d68040f9Doris Liu            mFlingAnimator = new AnimatorSet();
2243d70f0fb68064e170fb2cf041a707ed04d68040f9Doris Liu            mFlingAnimator.play(decelerationX).with(decelerationY);
2244d70f0fb68064e170fb2cf041a707ed04d68040f9Doris Liu            mFlingAnimator.setDuration((int) (duration * 1000));
2245d70f0fb68064e170fb2cf041a707ed04d68040f9Doris Liu            mFlingAnimator.setInterpolator(new TimeInterpolator() {
2246d70f0fb68064e170fb2cf041a707ed04d68040f9Doris Liu                @Override
2247d70f0fb68064e170fb2cf041a707ed04d68040f9Doris Liu                public float getInterpolation(float input) {
2248280fd3edae1dc35ac1015bcc9532477169fad3aaSascha Haeberling                    return (float) (1.0f - Math.pow((1.0f - input), factor));
2249d70f0fb68064e170fb2cf041a707ed04d68040f9Doris Liu                }
2250d70f0fb68064e170fb2cf041a707ed04d68040f9Doris Liu            });
2251d70f0fb68064e170fb2cf041a707ed04d68040f9Doris Liu            mFlingAnimator.addListener(new Animator.AnimatorListener() {
2252d70f0fb68064e170fb2cf041a707ed04d68040f9Doris Liu                private boolean mCancelled = false;
2253280fd3edae1dc35ac1015bcc9532477169fad3aaSascha Haeberling
2254d70f0fb68064e170fb2cf041a707ed04d68040f9Doris Liu                @Override
2255d70f0fb68064e170fb2cf041a707ed04d68040f9Doris Liu                public void onAnimationStart(Animator animation) {
2256d70f0fb68064e170fb2cf041a707ed04d68040f9Doris Liu
2257d70f0fb68064e170fb2cf041a707ed04d68040f9Doris Liu                }
2258d70f0fb68064e170fb2cf041a707ed04d68040f9Doris Liu
2259d70f0fb68064e170fb2cf041a707ed04d68040f9Doris Liu                @Override
2260d70f0fb68064e170fb2cf041a707ed04d68040f9Doris Liu                public void onAnimationEnd(Animator animation) {
2261d70f0fb68064e170fb2cf041a707ed04d68040f9Doris Liu                    if (!mCancelled) {
2262d70f0fb68064e170fb2cf041a707ed04d68040f9Doris Liu                        loadZoomedImage();
2263d70f0fb68064e170fb2cf041a707ed04d68040f9Doris Liu                    }
2264d70f0fb68064e170fb2cf041a707ed04d68040f9Doris Liu                    mFlingAnimator = null;
2265d70f0fb68064e170fb2cf041a707ed04d68040f9Doris Liu                }
2266d70f0fb68064e170fb2cf041a707ed04d68040f9Doris Liu
2267d70f0fb68064e170fb2cf041a707ed04d68040f9Doris Liu                @Override
2268d70f0fb68064e170fb2cf041a707ed04d68040f9Doris Liu                public void onAnimationCancel(Animator animation) {
2269d70f0fb68064e170fb2cf041a707ed04d68040f9Doris Liu                    mCancelled = true;
2270d70f0fb68064e170fb2cf041a707ed04d68040f9Doris Liu                }
2271d70f0fb68064e170fb2cf041a707ed04d68040f9Doris Liu
2272d70f0fb68064e170fb2cf041a707ed04d68040f9Doris Liu                @Override
2273d70f0fb68064e170fb2cf041a707ed04d68040f9Doris Liu                public void onAnimationRepeat(Animator animation) {
2274d70f0fb68064e170fb2cf041a707ed04d68040f9Doris Liu
2275d70f0fb68064e170fb2cf041a707ed04d68040f9Doris Liu                }
2276d70f0fb68064e170fb2cf041a707ed04d68040f9Doris Liu            });
2277d70f0fb68064e170fb2cf041a707ed04d68040f9Doris Liu            mFlingAnimator.start();
2278d70f0fb68064e170fb2cf041a707ed04d68040f9Doris Liu        }
2279d70f0fb68064e170fb2cf041a707ed04d68040f9Doris Liu
2280d70f0fb68064e170fb2cf041a707ed04d68040f9Doris Liu        @Override
22817d2388d96522975307cf5c40f7ac911ad963c44dAngus Kong        public boolean stopScrolling(boolean forced) {
22827d2388d96522975307cf5c40f7ac911ad963c44dAngus Kong            if (!isScrolling()) {
22837d2388d96522975307cf5c40f7ac911ad963c44dAngus Kong                return true;
22847d2388d96522975307cf5c40f7ac911ad963c44dAngus Kong            } else if (!mCanStopScroll && !forced) {
2285f1f5186f7d3fd8bf35009d9e65a43914c664d82bSascha Haeberling                return false;
22863d0b691b8f7a844093e8de9267ffa2ce16d0c5a9Angus Kong            }
2287b6a8810b7a5ac0df1e19efd26628c01bcb32b97bPaul Rohde
22888ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde            mScrollGesture.forceFinished(true);
228949b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong            return true;
229049b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong        }
229149b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong
22926d23f4c77fde7ddf70e0edd48f46cc22b7f7320eAngus Kong        private void stopScale() {
229349b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong            mScaleAnimator.cancel();
229449b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong        }
229549b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong
22966d23f4c77fde7ddf70e0edd48f46cc22b7f7320eAngus Kong        @Override
2297734598c39e6a9d809982dae2907dc7d10e0b0e9fAngus Kong        public void scrollToPosition(int position, int duration, boolean interruptible) {
22988ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde            if (mViewItems[BUFFER_CENTER] == null) {
2299f1f5186f7d3fd8bf35009d9e65a43914c664d82bSascha Haeberling                return;
23003d0b691b8f7a844093e8de9267ffa2ce16d0c5a9Angus Kong            }
230149b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong            mCanStopScroll = interruptible;
23028ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde            mScrollGesture.startScroll(mCenterX, position - mCenterX, duration);
230349b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong        }
230449b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong
2305734598c39e6a9d809982dae2907dc7d10e0b0e9fAngus Kong        @Override
2306734598c39e6a9d809982dae2907dc7d10e0b0e9fAngus Kong        public boolean goToNextItem() {
23078ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde            return goToItem(BUFFER_CENTER + 1);
23088099a371048e45b9161ac63e4d6bd9644fcad5b5Alan Newberger        }
23098099a371048e45b9161ac63e4d6bd9644fcad5b5Alan Newberger
23108099a371048e45b9161ac63e4d6bd9644fcad5b5Alan Newberger        @Override
23118099a371048e45b9161ac63e4d6bd9644fcad5b5Alan Newberger        public boolean goToPreviousItem() {
23128ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde            return goToItem(BUFFER_CENTER - 1);
23138099a371048e45b9161ac63e4d6bd9644fcad5b5Alan Newberger        }
23148099a371048e45b9161ac63e4d6bd9644fcad5b5Alan Newberger
23158099a371048e45b9161ac63e4d6bd9644fcad5b5Alan Newberger        private boolean goToItem(int itemIndex) {
23168ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde            final ViewItem nextItem = mViewItems[itemIndex];
2317734598c39e6a9d809982dae2907dc7d10e0b0e9fAngus Kong            if (nextItem == null) {
2318734598c39e6a9d809982dae2907dc7d10e0b0e9fAngus Kong                return false;
2319734598c39e6a9d809982dae2907dc7d10e0b0e9fAngus Kong            }
2320b21215a1a59812592380f31891becedbdec7468bAngus Kong            stopScrolling(true);
2321734598c39e6a9d809982dae2907dc7d10e0b0e9fAngus Kong            scrollToPosition(nextItem.getCenterX(), GEOMETRY_ADJUST_TIME_MS * 2, false);
2322b21215a1a59812592380f31891becedbdec7468bAngus Kong
2323734598c39e6a9d809982dae2907dc7d10e0b0e9fAngus Kong            return true;
2324734598c39e6a9d809982dae2907dc7d10e0b0e9fAngus Kong        }
2325734598c39e6a9d809982dae2907dc7d10e0b0e9fAngus Kong
23266d23f4c77fde7ddf70e0edd48f46cc22b7f7320eAngus Kong        private void scaleTo(float scale, int duration) {
23278ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde            if (mViewItems[BUFFER_CENTER] == null) {
23283d0b691b8f7a844093e8de9267ffa2ce16d0c5a9Angus Kong                return;
23293d0b691b8f7a844093e8de9267ffa2ce16d0c5a9Angus Kong            }
23306d23f4c77fde7ddf70e0edd48f46cc22b7f7320eAngus Kong            stopScale();
233149b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong            mScaleAnimator.setDuration(duration);
233249b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong            mScaleAnimator.setFloatValues(mScale, scale);
233349b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong            mScaleAnimator.start();
233449b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong        }
233549b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong
23366d23f4c77fde7ddf70e0edd48f46cc22b7f7320eAngus Kong        @Override
2337faaee012acc80ad369cb03df9c196e48140f1e7bAngus Kong        public void goToFilmstrip() {
23388ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde            if (mViewItems[BUFFER_CENTER] == null) {
2339ec2fb473c094f8dfd1cc0cf5205d470e412d3919Angus Kong                return;
2340ec2fb473c094f8dfd1cc0cf5205d470e412d3919Angus Kong            }
2341faaee012acc80ad369cb03df9c196e48140f1e7bAngus Kong            if (mScale == FILM_STRIP_SCALE) {
2342faaee012acc80ad369cb03df9c196e48140f1e7bAngus Kong                return;
2343faaee012acc80ad369cb03df9c196e48140f1e7bAngus Kong            }
2344734598c39e6a9d809982dae2907dc7d10e0b0e9fAngus Kong            scaleTo(FILM_STRIP_SCALE, GEOMETRY_ADJUST_TIME_MS);
234517e669d695ef2a38d28d5ba2512937836293e9caAngus Kong
23468ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde            final ViewItem currItem = mViewItems[BUFFER_CENTER];
23478ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde            final ViewItem nextItem = mViewItems[BUFFER_CENTER + 1];
234817e669d695ef2a38d28d5ba2512937836293e9caAngus Kong
2349faaee012acc80ad369cb03df9c196e48140f1e7bAngus Kong            if (mScale == FILM_STRIP_SCALE) {
2350faaee012acc80ad369cb03df9c196e48140f1e7bAngus Kong                onLeaveFilmstrip();
2351a52f04c1ff32b2f529c21b49861a62ad34359e28Doris Liu            }
23526d23f4c77fde7ddf70e0edd48f46cc22b7f7320eAngus Kong        }
23536d23f4c77fde7ddf70e0edd48f46cc22b7f7320eAngus Kong
23546d23f4c77fde7ddf70e0edd48f46cc22b7f7320eAngus Kong        @Override
2355734598c39e6a9d809982dae2907dc7d10e0b0e9fAngus Kong        public void goToFullScreen() {
23568de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu            if (inFullScreen()) {
23578de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu                return;
23588de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu            }
2359893ca79bdea899fe3f3ed700c22235cc5b460527Spike Sprague
2360faaee012acc80ad369cb03df9c196e48140f1e7bAngus Kong            scaleTo(FULL_SCREEN_SCALE, GEOMETRY_ADJUST_TIME_MS);
2361a6a6a730074e9fedd56c41cd8fa8f1212cfac108Angus Kong        }
2362a6a6a730074e9fedd56c41cd8fa8f1212cfac108Angus Kong
2363d70f0fb68064e170fb2cf041a707ed04d68040f9Doris Liu        private void cancelFlingAnimation() {
2364d70f0fb68064e170fb2cf041a707ed04d68040f9Doris Liu            // Cancels flinging for zoomed images
2365d70f0fb68064e170fb2cf041a707ed04d68040f9Doris Liu            if (isFlingAnimationRunning()) {
2366d70f0fb68064e170fb2cf041a707ed04d68040f9Doris Liu                mFlingAnimator.cancel();
2367d70f0fb68064e170fb2cf041a707ed04d68040f9Doris Liu            }
2368d70f0fb68064e170fb2cf041a707ed04d68040f9Doris Liu        }
2369d70f0fb68064e170fb2cf041a707ed04d68040f9Doris Liu
2370d70f0fb68064e170fb2cf041a707ed04d68040f9Doris Liu        private void cancelZoomAnimation() {
2371d70f0fb68064e170fb2cf041a707ed04d68040f9Doris Liu            if (isZoomAnimationRunning()) {
2372d70f0fb68064e170fb2cf041a707ed04d68040f9Doris Liu                mZoomAnimator.cancel();
2373d70f0fb68064e170fb2cf041a707ed04d68040f9Doris Liu            }
2374d70f0fb68064e170fb2cf041a707ed04d68040f9Doris Liu        }
2375d70f0fb68064e170fb2cf041a707ed04d68040f9Doris Liu
23768de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu        private void setSurroundingViewsVisible(boolean visible) {
23778de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu            // Hide everything on the left
2378280fd3edae1dc35ac1015bcc9532477169fad3aaSascha Haeberling            // TODO: Need to find a better way to toggle the visibility of views
2379280fd3edae1dc35ac1015bcc9532477169fad3aaSascha Haeberling            // around the current view.
23808ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde            for (int i = 0; i < BUFFER_CENTER; i++) {
23818ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde                if (mViewItems[i] != null) {
23828ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde                    mViewItems[i].setVisibility(visible ? VISIBLE : INVISIBLE);
23838de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu                }
23848de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu            }
23858de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu        }
23868de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu
2387571a8c389798bd3f01429f6be34cd3e606ab34dfAngus Kong        private Uri getCurrentUri() {
23888ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde            ViewItem curr = mViewItems[BUFFER_CENTER];
23898de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu            if (curr == null) {
23908de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu                return Uri.EMPTY;
23918de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu            }
23928ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde            return mDataAdapter.getFilmstripItemAt(curr.getAdapterIndex()).getData().getUri();
23938de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu        }
23948de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu
23958de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu        /**
23968de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu         * Here we only support up to 1:1 image zoom (i.e. a 100% view of the
23978de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu         * actual pixels). The max scale that we can apply on the view should
23988de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu         * make the view same size as the image, in pixels.
23998de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu         */
24003179f6a49516833761be5155c4e1244ac0aa17feDoris Liu        private float getCurrentDataMaxScale(boolean allowOverScale) {
24018ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde            ViewItem curr = mViewItems[BUFFER_CENTER];
24023f192ccf03d0d25521b96304c4464b4bad28150aSascha Haeberling            if (curr == null) {
24033f192ccf03d0d25521b96304c4464b4bad28150aSascha Haeberling                return FULL_SCREEN_SCALE;
24043f192ccf03d0d25521b96304c4464b4bad28150aSascha Haeberling            }
24058ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde            FilmstripItem imageData = mDataAdapter.getFilmstripItemAt(curr.getAdapterIndex());
24068ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde            if (imageData == null || !imageData.getAttributes().canZoomInPlace()) {
24078de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu                return FULL_SCREEN_SCALE;
24088de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu            }
240951cafa0a35546a42c573357aa7a031a79cf9ba1bAlan Newberger            float imageWidth = imageData.getDimensions().getWidth();
241051cafa0a35546a42c573357aa7a031a79cf9ba1bAlan Newberger            if (imageData.getOrientation() == 90
241151cafa0a35546a42c573357aa7a031a79cf9ba1bAlan Newberger                    || imageData.getOrientation() == 270) {
241251cafa0a35546a42c573357aa7a031a79cf9ba1bAlan Newberger                imageWidth = imageData.getDimensions().getHeight();
241306578b59bf15c1ac35dbbffd979f40479b1cbda9ztenghui            }
24143179f6a49516833761be5155c4e1244ac0aa17feDoris Liu            float scale = imageWidth / curr.getWidth();
24153179f6a49516833761be5155c4e1244ac0aa17feDoris Liu            if (allowOverScale) {
24163179f6a49516833761be5155c4e1244ac0aa17feDoris Liu                // In addition to the scale we apply to the view for 100% view
24173179f6a49516833761be5155c4e1244ac0aa17feDoris Liu                // (i.e. each pixel on screen corresponds to a pixel in image)
24183179f6a49516833761be5155c4e1244ac0aa17feDoris Liu                // we allow scaling beyond that for better detail viewing.
24193179f6a49516833761be5155c4e1244ac0aa17feDoris Liu                scale *= mOverScaleFactor;
24203179f6a49516833761be5155c4e1244ac0aa17feDoris Liu            }
24213179f6a49516833761be5155c4e1244ac0aa17feDoris Liu            return scale;
24228de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu        }
24238de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu
24248de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu        private void loadZoomedImage() {
24254567160a43fa5787012bf0b618c216cf18ac0457Angus Kong            if (!inZoomView()) {
2426f1f5186f7d3fd8bf35009d9e65a43914c664d82bSascha Haeberling                return;
2427f1f5186f7d3fd8bf35009d9e65a43914c664d82bSascha Haeberling            }
24288ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde            ViewItem curr = mViewItems[BUFFER_CENTER];
242987fc5e1568f737e03327770cb3dee03c4b939c59Doris Liu            if (curr == null) {
243087fc5e1568f737e03327770cb3dee03c4b939c59Doris Liu                return;
243187fc5e1568f737e03327770cb3dee03c4b939c59Doris Liu            }
24328ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde            FilmstripItem imageData = mDataAdapter.getFilmstripItemAt(curr.getAdapterIndex());
24338ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde            if (!imageData.getAttributes().canZoomInPlace()) {
24348de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu                return;
24358de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu            }
2436571a8c389798bd3f01429f6be34cd3e606ab34dfAngus Kong            Uri uri = getCurrentUri();
24378de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu            RectF viewRect = curr.getViewRect();
24388de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu            if (uri == null || uri == Uri.EMPTY) {
24398de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu                return;
24408de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu            }
244151cafa0a35546a42c573357aa7a031a79cf9ba1bAlan Newberger            int orientation = imageData.getOrientation();
244287fc5e1568f737e03327770cb3dee03c4b939c59Doris Liu            mZoomView.loadBitmap(uri, orientation, viewRect);
24438de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu        }
24448de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu
24458de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu        private void cancelLoadingZoomedImage() {
24468de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu            mZoomView.cancelPartialDecodingTask();
24476d23f4c77fde7ddf70e0edd48f46cc22b7f7320eAngus Kong        }
24486d23f4c77fde7ddf70e0edd48f46cc22b7f7320eAngus Kong
24496d23f4c77fde7ddf70e0edd48f46cc22b7f7320eAngus Kong        @Override
24503f969c1735e2636bf22dfe44104d0e99924cca97Alan Newberger        public void goToFirstItem() {
24518ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde            if (mViewItems[BUFFER_CENTER] == null) {
2452faaee012acc80ad369cb03df9c196e48140f1e7bAngus Kong                return;
2453faaee012acc80ad369cb03df9c196e48140f1e7bAngus Kong            }
245403b75411538c60021799ccc7acc8ed4798253b6dDoris Liu            resetZoomView();
24558ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde            // TODO: animate to camera if it is still in the mViewItems buffer
24563f969c1735e2636bf22dfe44104d0e99924cca97Alan Newberger            // versus a full reload which will perform an immediate transition
24573f969c1735e2636bf22dfe44104d0e99924cca97Alan Newberger            reload();
245849b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong        }
245949b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong
24604567160a43fa5787012bf0b618c216cf18ac0457Angus Kong        public boolean inZoomView() {
24614567160a43fa5787012bf0b618c216cf18ac0457Angus Kong            return FilmstripView.this.inZoomView();
246249b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong        }
246349b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong
2464d70f0fb68064e170fb2cf041a707ed04d68040f9Doris Liu        public boolean isFlingAnimationRunning() {
2465d70f0fb68064e170fb2cf041a707ed04d68040f9Doris Liu            return mFlingAnimator != null && mFlingAnimator.isRunning();
2466d70f0fb68064e170fb2cf041a707ed04d68040f9Doris Liu        }
2467d70f0fb68064e170fb2cf041a707ed04d68040f9Doris Liu
2468b21215a1a59812592380f31891becedbdec7468bAngus Kong        public boolean isZoomAnimationRunning() {
2469b21215a1a59812592380f31891becedbdec7468bAngus Kong            return mZoomAnimator != null && mZoomAnimator.isRunning();
247049b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong        }
247186f1d1f3664c909d1d949951056c7bb1e9627324Alan Newberger
247286f1d1f3664c909d1d949951056c7bb1e9627324Alan Newberger        @Override
247386f1d1f3664c909d1d949951056c7bb1e9627324Alan Newberger        public boolean isVisible(FilmstripItem data) {
247486f1d1f3664c909d1d949951056c7bb1e9627324Alan Newberger            for (ViewItem viewItem : mViewItems) {
247586f1d1f3664c909d1d949951056c7bb1e9627324Alan Newberger                if (data != null && viewItem != null && viewItem.getVisibility() == VISIBLE
247686f1d1f3664c909d1d949951056c7bb1e9627324Alan Newberger                        && data.equals(viewItem.mData)) {
247786f1d1f3664c909d1d949951056c7bb1e9627324Alan Newberger                    return true;
247886f1d1f3664c909d1d949951056c7bb1e9627324Alan Newberger                }
247986f1d1f3664c909d1d949951056c7bb1e9627324Alan Newberger            }
248086f1d1f3664c909d1d949951056c7bb1e9627324Alan Newberger            return false;
248186f1d1f3664c909d1d949951056c7bb1e9627324Alan Newberger        }
2482b21215a1a59812592380f31891becedbdec7468bAngus Kong    }
248349b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong
2484ec2fb473c094f8dfd1cc0cf5205d470e412d3919Angus Kong    private boolean isCurrentItemCentered() {
24858ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde        return mViewItems[BUFFER_CENTER].getCenterX() == mCenterX;
2486ec2fb473c094f8dfd1cc0cf5205d470e412d3919Angus Kong    }
2487ec2fb473c094f8dfd1cc0cf5205d470e412d3919Angus Kong
24888ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde    private static class FilmstripScrollGesture {
2489b21215a1a59812592380f31891becedbdec7468bAngus Kong        public interface Listener {
2490b21215a1a59812592380f31891becedbdec7468bAngus Kong            public void onScrollUpdate(int currX, int currY);
2491280fd3edae1dc35ac1015bcc9532477169fad3aaSascha Haeberling
2492b21215a1a59812592380f31891becedbdec7468bAngus Kong            public void onScrollEnd();
2493b21215a1a59812592380f31891becedbdec7468bAngus Kong        }
2494b21215a1a59812592380f31891becedbdec7468bAngus Kong
2495280fd3edae1dc35ac1015bcc9532477169fad3aaSascha Haeberling        private final Handler mHandler;
2496280fd3edae1dc35ac1015bcc9532477169fad3aaSascha Haeberling        private final Listener mListener;
24971f36cf1c97faf2b7824bbecd62942456847f0e13Angus Kong
24981f36cf1c97faf2b7824bbecd62942456847f0e13Angus Kong        private final Scroller mScroller;
24991f36cf1c97faf2b7824bbecd62942456847f0e13Angus Kong
25001f36cf1c97faf2b7824bbecd62942456847f0e13Angus Kong        private final ValueAnimator mXScrollAnimator;
2501280fd3edae1dc35ac1015bcc9532477169fad3aaSascha Haeberling        private final Runnable mScrollChecker = new Runnable() {
2502b21215a1a59812592380f31891becedbdec7468bAngus Kong            @Override
2503b21215a1a59812592380f31891becedbdec7468bAngus Kong            public void run() {
25041f36cf1c97faf2b7824bbecd62942456847f0e13Angus Kong                boolean newPosition = mScroller.computeScrollOffset();
2505b21215a1a59812592380f31891becedbdec7468bAngus Kong                if (!newPosition) {
2506c14b6ba434575b7d795a8cd681821f86a39bc371Paul Rohde                    Log.d(TAG, "[fling] onScrollEnd from computeScrollOffset");
2507b21215a1a59812592380f31891becedbdec7468bAngus Kong                    mListener.onScrollEnd();
2508b21215a1a59812592380f31891becedbdec7468bAngus Kong                    return;
2509af24d28f8938a05d2e3c40155e39cbc7ff4d64b7Angus Kong                }
25101f36cf1c97faf2b7824bbecd62942456847f0e13Angus Kong                mListener.onScrollUpdate(mScroller.getCurrX(), mScroller.getCurrY());
2511b21215a1a59812592380f31891becedbdec7468bAngus Kong                mHandler.removeCallbacks(this);
2512b21215a1a59812592380f31891becedbdec7468bAngus Kong                mHandler.post(this);
251349b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong            }
2514b21215a1a59812592380f31891becedbdec7468bAngus Kong        };
2515b21215a1a59812592380f31891becedbdec7468bAngus Kong
2516280fd3edae1dc35ac1015bcc9532477169fad3aaSascha Haeberling        private final ValueAnimator.AnimatorUpdateListener mXScrollAnimatorUpdateListener =
25171f36cf1c97faf2b7824bbecd62942456847f0e13Angus Kong                new ValueAnimator.AnimatorUpdateListener() {
25181f36cf1c97faf2b7824bbecd62942456847f0e13Angus Kong                    @Override
25191f36cf1c97faf2b7824bbecd62942456847f0e13Angus Kong                    public void onAnimationUpdate(ValueAnimator animation) {
25201f36cf1c97faf2b7824bbecd62942456847f0e13Angus Kong                        mListener.onScrollUpdate((Integer) animation.getAnimatedValue(), 0);
25211f36cf1c97faf2b7824bbecd62942456847f0e13Angus Kong                    }
25221f36cf1c97faf2b7824bbecd62942456847f0e13Angus Kong                };
25231f36cf1c97faf2b7824bbecd62942456847f0e13Angus Kong
2524280fd3edae1dc35ac1015bcc9532477169fad3aaSascha Haeberling        private final Animator.AnimatorListener mXScrollAnimatorListener =
25251f36cf1c97faf2b7824bbecd62942456847f0e13Angus Kong                new Animator.AnimatorListener() {
25261f36cf1c97faf2b7824bbecd62942456847f0e13Angus Kong                    @Override
25271f36cf1c97faf2b7824bbecd62942456847f0e13Angus Kong                    public void onAnimationCancel(Animator animation) {
2528c14b6ba434575b7d795a8cd681821f86a39bc371Paul Rohde                        Log.d(TAG, "[fling] mXScrollAnimatorListener.onAnimationCancel");
25291f36cf1c97faf2b7824bbecd62942456847f0e13Angus Kong                        // Do nothing.
25301f36cf1c97faf2b7824bbecd62942456847f0e13Angus Kong                    }
25311f36cf1c97faf2b7824bbecd62942456847f0e13Angus Kong
25321f36cf1c97faf2b7824bbecd62942456847f0e13Angus Kong                    @Override
25331f36cf1c97faf2b7824bbecd62942456847f0e13Angus Kong                    public void onAnimationEnd(Animator animation) {
2534c14b6ba434575b7d795a8cd681821f86a39bc371Paul Rohde                        Log.d(TAG, "[fling] onScrollEnd from mXScrollAnimatorListener.onAnimationEnd");
25351f36cf1c97faf2b7824bbecd62942456847f0e13Angus Kong                        mListener.onScrollEnd();
25361f36cf1c97faf2b7824bbecd62942456847f0e13Angus Kong                    }
25371f36cf1c97faf2b7824bbecd62942456847f0e13Angus Kong
25381f36cf1c97faf2b7824bbecd62942456847f0e13Angus Kong                    @Override
25391f36cf1c97faf2b7824bbecd62942456847f0e13Angus Kong                    public void onAnimationRepeat(Animator animation) {
2540c14b6ba434575b7d795a8cd681821f86a39bc371Paul Rohde                        Log.d(TAG, "[fling] mXScrollAnimatorListener.onAnimationRepeat");
25411f36cf1c97faf2b7824bbecd62942456847f0e13Angus Kong                        // Do nothing.
25421f36cf1c97faf2b7824bbecd62942456847f0e13Angus Kong                    }
25431f36cf1c97faf2b7824bbecd62942456847f0e13Angus Kong
25441f36cf1c97faf2b7824bbecd62942456847f0e13Angus Kong                    @Override
25451f36cf1c97faf2b7824bbecd62942456847f0e13Angus Kong                    public void onAnimationStart(Animator animation) {
2546c14b6ba434575b7d795a8cd681821f86a39bc371Paul Rohde                        Log.d(TAG, "[fling] mXScrollAnimatorListener.onAnimationStart");
25471f36cf1c97faf2b7824bbecd62942456847f0e13Angus Kong                        // Do nothing.
25481f36cf1c97faf2b7824bbecd62942456847f0e13Angus Kong                    }
25491f36cf1c97faf2b7824bbecd62942456847f0e13Angus Kong                };
25501f36cf1c97faf2b7824bbecd62942456847f0e13Angus Kong
25518ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde        public FilmstripScrollGesture(Context ctx, Handler handler, Listener listener,
25528ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde              TimeInterpolator interpolator) {
2553b21215a1a59812592380f31891becedbdec7468bAngus Kong            mHandler = handler;
2554b21215a1a59812592380f31891becedbdec7468bAngus Kong            mListener = listener;
25551f36cf1c97faf2b7824bbecd62942456847f0e13Angus Kong            mScroller = new Scroller(ctx);
25561f36cf1c97faf2b7824bbecd62942456847f0e13Angus Kong            mXScrollAnimator = new ValueAnimator();
25571f36cf1c97faf2b7824bbecd62942456847f0e13Angus Kong            mXScrollAnimator.addUpdateListener(mXScrollAnimatorUpdateListener);
25581f36cf1c97faf2b7824bbecd62942456847f0e13Angus Kong            mXScrollAnimator.addListener(mXScrollAnimatorListener);
25591f36cf1c97faf2b7824bbecd62942456847f0e13Angus Kong            mXScrollAnimator.setInterpolator(interpolator);
256049b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong        }
256149b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong
2562b21215a1a59812592380f31891becedbdec7468bAngus Kong        public void fling(
2563b21215a1a59812592380f31891becedbdec7468bAngus Kong                int startX, int startY,
2564b21215a1a59812592380f31891becedbdec7468bAngus Kong                int velocityX, int velocityY,
2565b21215a1a59812592380f31891becedbdec7468bAngus Kong                int minX, int maxX,
2566b21215a1a59812592380f31891becedbdec7468bAngus Kong                int minY, int maxY) {
25671f36cf1c97faf2b7824bbecd62942456847f0e13Angus Kong            mScroller.fling(startX, startY, velocityX, velocityY, minX, maxX, minY, maxY);
2568b21215a1a59812592380f31891becedbdec7468bAngus Kong            runChecker();
256949b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong        }
257049b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong
2571b21215a1a59812592380f31891becedbdec7468bAngus Kong        public void startScroll(int startX, int startY, int dx, int dy) {
25721f36cf1c97faf2b7824bbecd62942456847f0e13Angus Kong            mScroller.startScroll(startX, startY, dx, dy);
2573b21215a1a59812592380f31891becedbdec7468bAngus Kong            runChecker();
257449b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong        }
25758de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu
25761f36cf1c97faf2b7824bbecd62942456847f0e13Angus Kong        /** Only starts and updates scroll in x-axis. */
25778ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde        public void startScroll(int startX, int dx, int duration) {
25781f36cf1c97faf2b7824bbecd62942456847f0e13Angus Kong            mXScrollAnimator.cancel();
25791f36cf1c97faf2b7824bbecd62942456847f0e13Angus Kong            mXScrollAnimator.setDuration(duration);
25801f36cf1c97faf2b7824bbecd62942456847f0e13Angus Kong            mXScrollAnimator.setIntValues(startX, startX + dx);
25811f36cf1c97faf2b7824bbecd62942456847f0e13Angus Kong            mXScrollAnimator.start();
25821f36cf1c97faf2b7824bbecd62942456847f0e13Angus Kong        }
25831f36cf1c97faf2b7824bbecd62942456847f0e13Angus Kong
25841f36cf1c97faf2b7824bbecd62942456847f0e13Angus Kong        public boolean isFinished() {
25851f36cf1c97faf2b7824bbecd62942456847f0e13Angus Kong            return (mScroller.isFinished() && !mXScrollAnimator.isRunning());
25861f36cf1c97faf2b7824bbecd62942456847f0e13Angus Kong        }
25871f36cf1c97faf2b7824bbecd62942456847f0e13Angus Kong
25881f36cf1c97faf2b7824bbecd62942456847f0e13Angus Kong        public void forceFinished(boolean finished) {
25891f36cf1c97faf2b7824bbecd62942456847f0e13Angus Kong            mScroller.forceFinished(finished);
25901f36cf1c97faf2b7824bbecd62942456847f0e13Angus Kong            if (finished) {
25911f36cf1c97faf2b7824bbecd62942456847f0e13Angus Kong                mXScrollAnimator.cancel();
25921f36cf1c97faf2b7824bbecd62942456847f0e13Angus Kong            }
25938de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu        }
2594121022cb590955e37f1264f77190ce4711159976Doris Liu
2595b21215a1a59812592380f31891becedbdec7468bAngus Kong        private void runChecker() {
2596b21215a1a59812592380f31891becedbdec7468bAngus Kong            if (mHandler == null || mListener == null) {
2597b21215a1a59812592380f31891becedbdec7468bAngus Kong                return;
2598b21215a1a59812592380f31891becedbdec7468bAngus Kong            }
2599b21215a1a59812592380f31891becedbdec7468bAngus Kong            mHandler.removeCallbacks(mScrollChecker);
2600b21215a1a59812592380f31891becedbdec7468bAngus Kong            mHandler.post(mScrollChecker);
2601121022cb590955e37f1264f77190ce4711159976Doris Liu        }
260249b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong    }
260349b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong
26048ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde    private class FilmstripGestures implements FilmstripGestureRecognizer.Listener {
260569f0964a91a035c222d1765768dbb8d175c3b0f7Angus Kong
260669f0964a91a035c222d1765768dbb8d175c3b0f7Angus Kong        private static final int SCROLL_DIR_NONE = 0;
260769f0964a91a035c222d1765768dbb8d175c3b0f7Angus Kong        private static final int SCROLL_DIR_VERTICAL = 1;
260869f0964a91a035c222d1765768dbb8d175c3b0f7Angus Kong        private static final int SCROLL_DIR_HORIZONTAL = 2;
260949b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong        // Indicating the current trend of scaling is up (>1) or down (<1).
261049b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong        private float mScaleTrend;
26118de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu        private float mMaxScale;
261269f0964a91a035c222d1765768dbb8d175c3b0f7Angus Kong        private int mScrollingDirection = SCROLL_DIR_NONE;
2613bcd1152e8214b1fb4a17963ffe8c292681c81688Sam Judd        private long mLastDownTime;
2614bcd1152e8214b1fb4a17963ffe8c292681c81688Sam Judd        private float mLastDownY;
261549b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong
2616e7263f7ea45cf31d3948984ba8514e03ef86627ePaul Rohde        private ViewItem mCurrentlyScalingItem;
2617e7263f7ea45cf31d3948984ba8514e03ef86627ePaul Rohde
261849b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong        @Override
261949b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong        public boolean onSingleTapUp(float x, float y) {
26208ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde            ViewItem centerItem = mViewItems[BUFFER_CENTER];
2621628481532e1e58a0a26330d238e850aec5657bceAngus Kong            if (inFilmstrip()) {
26228969a67c926608737f3e49f2d2240298372e8416Angus Kong                if (centerItem != null && centerItem.areaContains(x, y)) {
2623734598c39e6a9d809982dae2907dc7d10e0b0e9fAngus Kong                    mController.goToFullScreen();
2624a6a6a730074e9fedd56c41cd8fa8f1212cfac108Angus Kong                    return true;
2625a6a6a730074e9fedd56c41cd8fa8f1212cfac108Angus Kong                }
2626a6a6a730074e9fedd56c41cd8fa8f1212cfac108Angus Kong            } else if (inFullScreen()) {
26274567160a43fa5787012bf0b618c216cf18ac0457Angus Kong                if (mFullScreenUIHidden) {
26284567160a43fa5787012bf0b618c216cf18ac0457Angus Kong                    onLeaveFullScreenUiHidden();
26294567160a43fa5787012bf0b618c216cf18ac0457Angus Kong                    onEnterFullScreen();
26304567160a43fa5787012bf0b618c216cf18ac0457Angus Kong                } else {
26314567160a43fa5787012bf0b618c216cf18ac0457Angus Kong                    onLeaveFullScreen();
26324567160a43fa5787012bf0b618c216cf18ac0457Angus Kong                    onEnterFullScreenUiHidden();
26334567160a43fa5787012bf0b618c216cf18ac0457Angus Kong                }
2634a6a6a730074e9fedd56c41cd8fa8f1212cfac108Angus Kong                return true;
2635a6a6a730074e9fedd56c41cd8fa8f1212cfac108Angus Kong            }
263649b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong            return false;
26377e1c5fe26bc861d979b85014a511ba35bc1f4110Angus Kong        }
26387e1c5fe26bc861d979b85014a511ba35bc1f4110Angus Kong
26397e1c5fe26bc861d979b85014a511ba35bc1f4110Angus Kong        @Override
26407e1c5fe26bc861d979b85014a511ba35bc1f4110Angus Kong        public boolean onDoubleTap(float x, float y) {
26418ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde            ViewItem current = mViewItems[BUFFER_CENTER];
26424567160a43fa5787012bf0b618c216cf18ac0457Angus Kong            if (current == null) {
26434567160a43fa5787012bf0b618c216cf18ac0457Angus Kong                return false;
26444567160a43fa5787012bf0b618c216cf18ac0457Angus Kong            }
26454567160a43fa5787012bf0b618c216cf18ac0457Angus Kong            if (inFilmstrip()) {
26465326f0265206ccd8a2f3424bb983da674b20a31eDoris Liu                mController.goToFullScreen();
26475326f0265206ccd8a2f3424bb983da674b20a31eDoris Liu                return true;
264886f1d1f3664c909d1d949951056c7bb1e9627324Alan Newberger            } else if (mScale < FULL_SCREEN_SCALE) {
2649121022cb590955e37f1264f77190ce4711159976Doris Liu                return false;
2650121022cb590955e37f1264f77190ce4711159976Doris Liu            }
26517d2388d96522975307cf5c40f7ac911ad963c44dAngus Kong            if (!mController.stopScrolling(false)) {
26527d2388d96522975307cf5c40f7ac911ad963c44dAngus Kong                return false;
2653e1d5ba4fc5470043e8645984e3018b712f5dfcffDoris Liu            }
26544567160a43fa5787012bf0b618c216cf18ac0457Angus Kong            if (inFullScreen()) {
26554567160a43fa5787012bf0b618c216cf18ac0457Angus Kong                mController.zoomAt(current, x, y);
265677d9f023e76816e5da7bd067ad46cc0e9c98623fPaul Rohde                renderFullRes(BUFFER_CENTER);
26574567160a43fa5787012bf0b618c216cf18ac0457Angus Kong                return true;
26584567160a43fa5787012bf0b618c216cf18ac0457Angus Kong            } else if (mScale > FULL_SCREEN_SCALE) {
26594567160a43fa5787012bf0b618c216cf18ac0457Angus Kong                // In zoom view.
26604567160a43fa5787012bf0b618c216cf18ac0457Angus Kong                mController.zoomAt(current, x, y);
26614567160a43fa5787012bf0b618c216cf18ac0457Angus Kong            }
26624567160a43fa5787012bf0b618c216cf18ac0457Angus Kong            return false;
266349b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong        }
266449b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong
266549b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong        @Override
266649b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong        public boolean onDown(float x, float y) {
2667bcd1152e8214b1fb4a17963ffe8c292681c81688Sam Judd            mLastDownTime = SystemClock.uptimeMillis();
2668bcd1152e8214b1fb4a17963ffe8c292681c81688Sam Judd            mLastDownY = y;
2669d70f0fb68064e170fb2cf041a707ed04d68040f9Doris Liu            mController.cancelFlingAnimation();
26707d2388d96522975307cf5c40f7ac911ad963c44dAngus Kong            if (!mController.stopScrolling(false)) {
26717d2388d96522975307cf5c40f7ac911ad963c44dAngus Kong                return false;
2672a6a6a730074e9fedd56c41cd8fa8f1212cfac108Angus Kong            }
2673b0288ec84e06b83923180a04e1b546724fcfd258Doris Liu
267449b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong            return true;
267549b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong        }
267649b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong
267749b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong        @Override
267887f9a62f473a3c57505e2877b0a46ab3cb5e62acAngus Kong        public boolean onUp(float x, float y) {
26798ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde            ViewItem currItem = mViewItems[BUFFER_CENTER];
26807d2388d96522975307cf5c40f7ac911ad963c44dAngus Kong            if (currItem == null) {
26817d2388d96522975307cf5c40f7ac911ad963c44dAngus Kong                return false;
26827d2388d96522975307cf5c40f7ac911ad963c44dAngus Kong            }
2683d70f0fb68064e170fb2cf041a707ed04d68040f9Doris Liu            if (mController.isZoomAnimationRunning() || mController.isFlingAnimationRunning()) {
2684121022cb590955e37f1264f77190ce4711159976Doris Liu                return false;
2685121022cb590955e37f1264f77190ce4711159976Doris Liu            }
26864567160a43fa5787012bf0b618c216cf18ac0457Angus Kong            if (inZoomView()) {
26878de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu                mController.loadZoomedImage();
26888de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu                return true;
26898de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu            }
2690bcd1152e8214b1fb4a17963ffe8c292681c81688Sam Judd            float promoteHeight = getHeight() * PROMOTE_HEIGHT_RATIO;
2691bcd1152e8214b1fb4a17963ffe8c292681c81688Sam Judd            float velocityPromoteHeight = getHeight() * VELOCITY_PROMOTE_HEIGHT_RATIO;
269247721fa8f2dc13cb565ae5629a417dc4ed298e81Angus Kong            mIsUserScrolling = false;
269369f0964a91a035c222d1765768dbb8d175c3b0f7Angus Kong            mScrollingDirection = SCROLL_DIR_NONE;
26947d2388d96522975307cf5c40f7ac911ad963c44dAngus Kong            // Finds items promoted/demoted.
2695bcd1152e8214b1fb4a17963ffe8c292681c81688Sam Judd            float speedY = Math.abs(y - mLastDownY)
2696bcd1152e8214b1fb4a17963ffe8c292681c81688Sam Judd                    / (SystemClock.uptimeMillis() - mLastDownTime);
269749b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong            for (int i = 0; i < BUFFER_SIZE; i++) {
26988ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde                if (mViewItems[i] == null) {
2699f1f5186f7d3fd8bf35009d9e65a43914c664d82bSascha Haeberling                    continue;
2700f1f5186f7d3fd8bf35009d9e65a43914c664d82bSascha Haeberling                }
27018ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde                float transY = mViewItems[i].getTranslationY();
2702f1f5186f7d3fd8bf35009d9e65a43914c664d82bSascha Haeberling                if (transY == 0) {
2703f1f5186f7d3fd8bf35009d9e65a43914c664d82bSascha Haeberling                    continue;
2704f1f5186f7d3fd8bf35009d9e65a43914c664d82bSascha Haeberling                }
27058ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde                int index = mViewItems[i].getAdapterIndex();
270687f9a62f473a3c57505e2877b0a46ab3cb5e62acAngus Kong
27078ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde                if (mDataAdapter.getFilmstripItemAt(index).getAttributes().canSwipeAway()
2708bcd1152e8214b1fb4a17963ffe8c292681c81688Sam Judd                        && ((transY > promoteHeight)
2709bcd1152e8214b1fb4a17963ffe8c292681c81688Sam Judd                            || (transY > velocityPromoteHeight && speedY > PROMOTE_VELOCITY))) {
27108ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde                    demoteData(index);
27118ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde                } else if (mDataAdapter.getFilmstripItemAt(index).getAttributes().canSwipeAway()
2712bcd1152e8214b1fb4a17963ffe8c292681c81688Sam Judd                        && (transY < -promoteHeight
2713bcd1152e8214b1fb4a17963ffe8c292681c81688Sam Judd                            || (transY < -velocityPromoteHeight && speedY > PROMOTE_VELOCITY))) {
27148ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde                    promoteData(index);
271587f9a62f473a3c57505e2877b0a46ab3cb5e62acAngus Kong                } else {
27169f02c87cfb5039a72f516ea5edc0378c45ac5779Angus Kong                    // put the view back.
27178ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde                    slideViewBack(mViewItems[i]);
271887f9a62f473a3c57505e2877b0a46ab3cb5e62acAngus Kong                }
271987f9a62f473a3c57505e2877b0a46ab3cb5e62acAngus Kong            }
27207d2388d96522975307cf5c40f7ac911ad963c44dAngus Kong
27211f9db2dfe59c33228103c54523281501ef52c9baAngus Kong            // The data might be changed. Re-check.
27228ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde            currItem = mViewItems[BUFFER_CENTER];
27231f9db2dfe59c33228103c54523281501ef52c9baAngus Kong            if (currItem == null) {
27241f9db2dfe59c33228103c54523281501ef52c9baAngus Kong                return true;
27251f9db2dfe59c33228103c54523281501ef52c9baAngus Kong            }
27261f9db2dfe59c33228103c54523281501ef52c9baAngus Kong
27278ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde            int currId = currItem.getAdapterIndex();
272886f1d1f3664c909d1d949951056c7bb1e9627324Alan Newberger            if (mAdapterIndexUserIsScrollingOver == 0 && currId != 0) {
272986f1d1f3664c909d1d949951056c7bb1e9627324Alan Newberger                // Special case to go to filmstrip when the user scroll away
273086f1d1f3664c909d1d949951056c7bb1e9627324Alan Newberger                // from the camera preview and the current one is not the
273186f1d1f3664c909d1d949951056c7bb1e9627324Alan Newberger                // preview anymore.
2732faaee012acc80ad369cb03df9c196e48140f1e7bAngus Kong                mController.goToFilmstrip();
273386f1d1f3664c909d1d949951056c7bb1e9627324Alan Newberger                mAdapterIndexUserIsScrollingOver = currId;
27347d2388d96522975307cf5c40f7ac911ad963c44dAngus Kong            }
273586f1d1f3664c909d1d949951056c7bb1e9627324Alan Newberger            scrollCurrentItemToCenter();
273687f9a62f473a3c57505e2877b0a46ab3cb5e62acAngus Kong            return false;
273787f9a62f473a3c57505e2877b0a46ab3cb5e62acAngus Kong        }
273887f9a62f473a3c57505e2877b0a46ab3cb5e62acAngus Kong
273987f9a62f473a3c57505e2877b0a46ab3cb5e62acAngus Kong        @Override
274026795a9258c0815ca2a92d2c660438066f001022Angus Kong        public void onLongPress(float x, float y) {
27418ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde            final int index = getCurrentItemAdapterIndex();
27428ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde            if (index == -1) {
274326795a9258c0815ca2a92d2c660438066f001022Angus Kong                return;
274426795a9258c0815ca2a92d2c660438066f001022Angus Kong            }
27458ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde            mListener.onFocusedDataLongPressed(index);
274626795a9258c0815ca2a92d2c660438066f001022Angus Kong        }
274726795a9258c0815ca2a92d2c660438066f001022Angus Kong
274826795a9258c0815ca2a92d2c660438066f001022Angus Kong        @Override
274987f9a62f473a3c57505e2877b0a46ab3cb5e62acAngus Kong        public boolean onScroll(float x, float y, float dx, float dy) {
27508ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde            final ViewItem currItem = mViewItems[BUFFER_CENTER];
27517be0e2b8439d525c70cb062c6616d10c4fc004aaztenghui            if (currItem == null) {
27527be0e2b8439d525c70cb062c6616d10c4fc004aaztenghui                return false;
27537be0e2b8439d525c70cb062c6616d10c4fc004aaztenghui            }
2754b6a8810b7a5ac0df1e19efd26628c01bcb32b97bPaul Rohde
2755b0288ec84e06b83923180a04e1b546724fcfd258Doris Liu            hideZoomView();
27568de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu            // When image is zoomed in to be bigger than the screen
27574567160a43fa5787012bf0b618c216cf18ac0457Angus Kong            if (inZoomView()) {
27588ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde                ViewItem curr = mViewItems[BUFFER_CENTER];
27591f9db2dfe59c33228103c54523281501ef52c9baAngus Kong                float transX = curr.getTranslationX() * mScale - dx;
27601f9db2dfe59c33228103c54523281501ef52c9baAngus Kong                float transY = curr.getTranslationY() * mScale - dy;
27618de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu                curr.updateTransform(transX, transY, mScale, mScale, mDrawArea.width(),
27628de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu                        mDrawArea.height());
27638de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu                return true;
27648de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu            }
2765a6a6a730074e9fedd56c41cd8fa8f1212cfac108Angus Kong            int deltaX = (int) (dx / mScale);
27667d2388d96522975307cf5c40f7ac911ad963c44dAngus Kong            // Forces the current scrolling to stop.
27677d2388d96522975307cf5c40f7ac911ad963c44dAngus Kong            mController.stopScrolling(true);
27687d2388d96522975307cf5c40f7ac911ad963c44dAngus Kong            if (!mIsUserScrolling) {
27697d2388d96522975307cf5c40f7ac911ad963c44dAngus Kong                mIsUserScrolling = true;
27708ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde                mAdapterIndexUserIsScrollingOver =
27718ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde                      mViewItems[BUFFER_CENTER].getAdapterIndex();
27727d2388d96522975307cf5c40f7ac911ad963c44dAngus Kong            }
2773628481532e1e58a0a26330d238e850aec5657bceAngus Kong            if (inFilmstrip()) {
277469f0964a91a035c222d1765768dbb8d175c3b0f7Angus Kong                // Disambiguate horizontal/vertical first.
277569f0964a91a035c222d1765768dbb8d175c3b0f7Angus Kong                if (mScrollingDirection == SCROLL_DIR_NONE) {
277669f0964a91a035c222d1765768dbb8d175c3b0f7Angus Kong                    mScrollingDirection = (Math.abs(dx) > Math.abs(dy)) ? SCROLL_DIR_HORIZONTAL :
277769f0964a91a035c222d1765768dbb8d175c3b0f7Angus Kong                            SCROLL_DIR_VERTICAL;
277869f0964a91a035c222d1765768dbb8d175c3b0f7Angus Kong                }
277969f0964a91a035c222d1765768dbb8d175c3b0f7Angus Kong                if (mScrollingDirection == SCROLL_DIR_HORIZONTAL) {
27808ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde                    if (mCenterX == currItem.getCenterX() && currItem.getAdapterIndex() == 0 &&
27818ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde                          dx < 0) {
278269f0964a91a035c222d1765768dbb8d175c3b0f7Angus Kong                        // Already at the beginning, don't process the swipe.
278369f0964a91a035c222d1765768dbb8d175c3b0f7Angus Kong                        mIsUserScrolling = false;
278469f0964a91a035c222d1765768dbb8d175c3b0f7Angus Kong                        mScrollingDirection = SCROLL_DIR_NONE;
278569f0964a91a035c222d1765768dbb8d175c3b0f7Angus Kong                        return false;
278669f0964a91a035c222d1765768dbb8d175c3b0f7Angus Kong                    }
2787a6a6a730074e9fedd56c41cd8fa8f1212cfac108Angus Kong                    mController.scroll(deltaX);
2788a6a6a730074e9fedd56c41cd8fa8f1212cfac108Angus Kong                } else {
2789a6a6a730074e9fedd56c41cd8fa8f1212cfac108Angus Kong                    // Vertical part. Promote or demote.
2790a6a6a730074e9fedd56c41cd8fa8f1212cfac108Angus Kong                    int hit = 0;
2791a6a6a730074e9fedd56c41cd8fa8f1212cfac108Angus Kong                    Rect hitRect = new Rect();
2792a6a6a730074e9fedd56c41cd8fa8f1212cfac108Angus Kong                    for (; hit < BUFFER_SIZE; hit++) {
27938ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde                        if (mViewItems[hit] == null) {
2794f1f5186f7d3fd8bf35009d9e65a43914c664d82bSascha Haeberling                            continue;
2795f1f5186f7d3fd8bf35009d9e65a43914c664d82bSascha Haeberling                        }
27968ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde                        mViewItems[hit].getHitRect(hitRect);
2797f1f5186f7d3fd8bf35009d9e65a43914c664d82bSascha Haeberling                        if (hitRect.contains((int) x, (int) y)) {
2798f1f5186f7d3fd8bf35009d9e65a43914c664d82bSascha Haeberling                            break;
2799f1f5186f7d3fd8bf35009d9e65a43914c664d82bSascha Haeberling                        }
2800f1f5186f7d3fd8bf35009d9e65a43914c664d82bSascha Haeberling                    }
2801f1f5186f7d3fd8bf35009d9e65a43914c664d82bSascha Haeberling                    if (hit == BUFFER_SIZE) {
2802166e36fb6e04d40a1bef0459ee6b96c9c736039bAngus Kong                        // Hit none.
2803166e36fb6e04d40a1bef0459ee6b96c9c736039bAngus Kong                        return true;
2804a6a6a730074e9fedd56c41cd8fa8f1212cfac108Angus Kong                    }
280587f9a62f473a3c57505e2877b0a46ab3cb5e62acAngus Kong
28068ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde                    FilmstripItem data = mDataAdapter.getFilmstripItemAt(
28078ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde                          mViewItems[hit].getAdapterIndex());
28088ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde                    float transY = mViewItems[hit].getTranslationY() - dy / mScale;
28098ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde                    if (!data.getAttributes().canSwipeAway() &&
2810c02b13af4c44fe23d4a563d3c2df1ca50e44affcAngus Kong                            transY > 0f) {
2811a6a6a730074e9fedd56c41cd8fa8f1212cfac108Angus Kong                        transY = 0f;
2812a6a6a730074e9fedd56c41cd8fa8f1212cfac108Angus Kong                    }
28138ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde                    if (!data.getAttributes().canSwipeAway() &&
2814c02b13af4c44fe23d4a563d3c2df1ca50e44affcAngus Kong                            transY < 0f) {
2815a6a6a730074e9fedd56c41cd8fa8f1212cfac108Angus Kong                        transY = 0f;
2816a6a6a730074e9fedd56c41cd8fa8f1212cfac108Angus Kong                    }
28178ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde                    mViewItems[hit].setTranslationY(transY);
281887f9a62f473a3c57505e2877b0a46ab3cb5e62acAngus Kong                }
2819a6a6a730074e9fedd56c41cd8fa8f1212cfac108Angus Kong            } else if (inFullScreen()) {
28208ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde                if (mViewItems[BUFFER_CENTER] == null || (deltaX < 0 && mCenterX <=
28218ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde                        currItem.getCenterX() && currItem.getAdapterIndex() == 0)) {
2822166e36fb6e04d40a1bef0459ee6b96c9c736039bAngus Kong                    return false;
2823166e36fb6e04d40a1bef0459ee6b96c9c736039bAngus Kong                }
2824025136bb4639d84b2a5a7b95367fabcc92b86415Angus Kong                // Multiplied by 1.2 to make it more easy to swipe.
2825025136bb4639d84b2a5a7b95367fabcc92b86415Angus Kong                mController.scroll((int) (deltaX * 1.2));
282649b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong            }
28277d2388d96522975307cf5c40f7ac911ad963c44dAngus Kong            invalidate();
2828a6a6a730074e9fedd56c41cd8fa8f1212cfac108Angus Kong
282949b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong            return true;
283049b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong        }
283149b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong
283249b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong        @Override
28339652134e6e1b312e348ed3565343741239907f11Alan Newberger        public boolean onMouseScroll(float hscroll, float vscroll) {
28349652134e6e1b312e348ed3565343741239907f11Alan Newberger            final float scroll;
28359652134e6e1b312e348ed3565343741239907f11Alan Newberger
28369652134e6e1b312e348ed3565343741239907f11Alan Newberger            hscroll *= MOUSE_SCROLL_FACTOR;
28379652134e6e1b312e348ed3565343741239907f11Alan Newberger            vscroll *= MOUSE_SCROLL_FACTOR;
28389652134e6e1b312e348ed3565343741239907f11Alan Newberger
28399652134e6e1b312e348ed3565343741239907f11Alan Newberger            if (vscroll != 0f) {
28409652134e6e1b312e348ed3565343741239907f11Alan Newberger                scroll = vscroll;
28419652134e6e1b312e348ed3565343741239907f11Alan Newberger            } else {
28429652134e6e1b312e348ed3565343741239907f11Alan Newberger                scroll = hscroll;
28439652134e6e1b312e348ed3565343741239907f11Alan Newberger            }
28449652134e6e1b312e348ed3565343741239907f11Alan Newberger
28459652134e6e1b312e348ed3565343741239907f11Alan Newberger            if (inFullScreen()) {
28469652134e6e1b312e348ed3565343741239907f11Alan Newberger                onFling(-scroll, 0f);
28479652134e6e1b312e348ed3565343741239907f11Alan Newberger            } else if (inZoomView()) {
28489652134e6e1b312e348ed3565343741239907f11Alan Newberger                onScroll(0f, 0f, hscroll, vscroll);
28499652134e6e1b312e348ed3565343741239907f11Alan Newberger            } else {
28509652134e6e1b312e348ed3565343741239907f11Alan Newberger                onScroll(0f, 0f, scroll, 0f);
28519652134e6e1b312e348ed3565343741239907f11Alan Newberger            }
28529652134e6e1b312e348ed3565343741239907f11Alan Newberger
28539652134e6e1b312e348ed3565343741239907f11Alan Newberger            return true;
28549652134e6e1b312e348ed3565343741239907f11Alan Newberger        }
28559652134e6e1b312e348ed3565343741239907f11Alan Newberger
28569652134e6e1b312e348ed3565343741239907f11Alan Newberger        @Override
285749b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong        public boolean onFling(float velocityX, float velocityY) {
28588ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde            final ViewItem currItem = mViewItems[BUFFER_CENTER];
285917e669d695ef2a38d28d5ba2512937836293e9caAngus Kong            if (currItem == null) {
286017e669d695ef2a38d28d5ba2512937836293e9caAngus Kong                return false;
286117e669d695ef2a38d28d5ba2512937836293e9caAngus Kong            }
2862b6a8810b7a5ac0df1e19efd26628c01bcb32b97bPaul Rohde
28634567160a43fa5787012bf0b618c216cf18ac0457Angus Kong            if (inZoomView()) {
2864d70f0fb68064e170fb2cf041a707ed04d68040f9Doris Liu                // Fling within the zoomed image
2865d70f0fb68064e170fb2cf041a707ed04d68040f9Doris Liu                mController.flingInsideZoomView(velocityX, velocityY);
2866d70f0fb68064e170fb2cf041a707ed04d68040f9Doris Liu                return true;
2867d70f0fb68064e170fb2cf041a707ed04d68040f9Doris Liu            }
2868025136bb4639d84b2a5a7b95367fabcc92b86415Angus Kong            if (Math.abs(velocityX) < Math.abs(velocityY)) {
2869a6a6a730074e9fedd56c41cd8fa8f1212cfac108Angus Kong                // ignore vertical fling.
2870025136bb4639d84b2a5a7b95367fabcc92b86415Angus Kong                return true;
2871025136bb4639d84b2a5a7b95367fabcc92b86415Angus Kong            }
2872025136bb4639d84b2a5a7b95367fabcc92b86415Angus Kong
2873280fd3edae1dc35ac1015bcc9532477169fad3aaSascha Haeberling            // In full-screen, fling of a velocity above a threshold should go
2874280fd3edae1dc35ac1015bcc9532477169fad3aaSascha Haeberling            // to the next/prev photos
287517e669d695ef2a38d28d5ba2512937836293e9caAngus Kong            if (mScale == FULL_SCREEN_SCALE) {
287617e669d695ef2a38d28d5ba2512937836293e9caAngus Kong                int currItemCenterX = currItem.getCenterX();
28777d2388d96522975307cf5c40f7ac911ad963c44dAngus Kong
2878280fd3edae1dc35ac1015bcc9532477169fad3aaSascha Haeberling                if (velocityX > 0) { // left
287917e669d695ef2a38d28d5ba2512937836293e9caAngus Kong                    if (mCenterX > currItemCenterX) {
2880280fd3edae1dc35ac1015bcc9532477169fad3aaSascha Haeberling                        // The visually previous item is actually the current
2881280fd3edae1dc35ac1015bcc9532477169fad3aaSascha Haeberling                        // item.
288217e669d695ef2a38d28d5ba2512937836293e9caAngus Kong                        mController.scrollToPosition(
288317e669d695ef2a38d28d5ba2512937836293e9caAngus Kong                                currItemCenterX, GEOMETRY_ADJUST_TIME_MS, true);
288417e669d695ef2a38d28d5ba2512937836293e9caAngus Kong                        return true;
288517e669d695ef2a38d28d5ba2512937836293e9caAngus Kong                    }
28868ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde                    ViewItem prevItem = mViewItems[BUFFER_CENTER - 1];
288717e669d695ef2a38d28d5ba2512937836293e9caAngus Kong                    if (prevItem == null) {
288817e669d695ef2a38d28d5ba2512937836293e9caAngus Kong                        return false;
288917e669d695ef2a38d28d5ba2512937836293e9caAngus Kong                    }
289017e669d695ef2a38d28d5ba2512937836293e9caAngus Kong                    mController.scrollToPosition(
289117e669d695ef2a38d28d5ba2512937836293e9caAngus Kong                            prevItem.getCenterX(), GEOMETRY_ADJUST_TIME_MS, true);
2892280fd3edae1dc35ac1015bcc9532477169fad3aaSascha Haeberling                } else { // right
28937d2388d96522975307cf5c40f7ac911ad963c44dAngus Kong                    if (mController.stopScrolling(false)) {
28947d2388d96522975307cf5c40f7ac911ad963c44dAngus Kong                        if (mCenterX < currItemCenterX) {
2895280fd3edae1dc35ac1015bcc9532477169fad3aaSascha Haeberling                            // The visually next item is actually the current
2896280fd3edae1dc35ac1015bcc9532477169fad3aaSascha Haeberling                            // item.
28977d2388d96522975307cf5c40f7ac911ad963c44dAngus Kong                            mController.scrollToPosition(
28987d2388d96522975307cf5c40f7ac911ad963c44dAngus Kong                                    currItemCenterX, GEOMETRY_ADJUST_TIME_MS, true);
28997d2388d96522975307cf5c40f7ac911ad963c44dAngus Kong                            return true;
29007d2388d96522975307cf5c40f7ac911ad963c44dAngus Kong                        }
29018ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde                        final ViewItem nextItem = mViewItems[BUFFER_CENTER + 1];
29027d2388d96522975307cf5c40f7ac911ad963c44dAngus Kong                        if (nextItem == null) {
29037d2388d96522975307cf5c40f7ac911ad963c44dAngus Kong                            return false;
29047d2388d96522975307cf5c40f7ac911ad963c44dAngus Kong                        }
290517e669d695ef2a38d28d5ba2512937836293e9caAngus Kong                        mController.scrollToPosition(
29067d2388d96522975307cf5c40f7ac911ad963c44dAngus Kong                                nextItem.getCenterX(), GEOMETRY_ADJUST_TIME_MS, true);
290717e669d695ef2a38d28d5ba2512937836293e9caAngus Kong                    }
290817e669d695ef2a38d28d5ba2512937836293e9caAngus Kong                }
290917e669d695ef2a38d28d5ba2512937836293e9caAngus Kong            }
291017e669d695ef2a38d28d5ba2512937836293e9caAngus Kong
2911b6a8810b7a5ac0df1e19efd26628c01bcb32b97bPaul Rohde
291217e669d695ef2a38d28d5ba2512937836293e9caAngus Kong            if (mScale == FILM_STRIP_SCALE) {
291317e669d695ef2a38d28d5ba2512937836293e9caAngus Kong                mController.fling(velocityX);
291449b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong            }
291549b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong            return true;
291649b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong        }
291749b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong
291849b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong        @Override
291949b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong        public boolean onScaleBegin(float focusX, float focusY) {
2920b0288ec84e06b83923180a04e1b546724fcfd258Doris Liu            hideZoomView();
2921e7263f7ea45cf31d3948984ba8514e03ef86627ePaul Rohde
2922e7263f7ea45cf31d3948984ba8514e03ef86627ePaul Rohde            // This ensures that the item currently being manipulated
2923e7263f7ea45cf31d3948984ba8514e03ef86627ePaul Rohde            // is locked at full opacity.
2924e7263f7ea45cf31d3948984ba8514e03ef86627ePaul Rohde            mCurrentlyScalingItem = mViewItems[BUFFER_CENTER];
2925e7263f7ea45cf31d3948984ba8514e03ef86627ePaul Rohde            if (mCurrentlyScalingItem != null) {
2926e7263f7ea45cf31d3948984ba8514e03ef86627ePaul Rohde                mCurrentlyScalingItem.lockAtFullOpacity();
2927e7263f7ea45cf31d3948984ba8514e03ef86627ePaul Rohde            }
2928e7263f7ea45cf31d3948984ba8514e03ef86627ePaul Rohde
292949b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong            mScaleTrend = 1f;
293079dcb12bbbcb9b3650ace5290897e26451218fe1Doris Liu            // If the image is smaller than screen size, we should allow to zoom
293179dcb12bbbcb9b3650ace5290897e26451218fe1Doris Liu            // in to full screen size
29323179f6a49516833761be5155c4e1244ac0aa17feDoris Liu            mMaxScale = Math.max(mController.getCurrentDataMaxScale(true), FULL_SCREEN_SCALE);
293349b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong            return true;
293449b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong        }
293549b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong
293649b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong        @Override
293749b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong        public boolean onScale(float focusX, float focusY, float scale) {
293887f9a62f473a3c57505e2877b0a46ab3cb5e62acAngus Kong            mScaleTrend = mScaleTrend * 0.3f + scale * 0.7f;
2939121022cb590955e37f1264f77190ce4711159976Doris Liu            float newScale = mScale * scale;
2940121022cb590955e37f1264f77190ce4711159976Doris Liu            if (mScale < FULL_SCREEN_SCALE && newScale < FULL_SCREEN_SCALE) {
2941faaee012acc80ad369cb03df9c196e48140f1e7bAngus Kong                if (newScale <= FILM_STRIP_SCALE) {
2942faaee012acc80ad369cb03df9c196e48140f1e7bAngus Kong                    newScale = FILM_STRIP_SCALE;
2943faaee012acc80ad369cb03df9c196e48140f1e7bAngus Kong                }
2944280fd3edae1dc35ac1015bcc9532477169fad3aaSascha Haeberling                // Scaled view is smaller than or equal to screen size both
2945280fd3edae1dc35ac1015bcc9532477169fad3aaSascha Haeberling                // before and after scaling
2946faaee012acc80ad369cb03df9c196e48140f1e7bAngus Kong                if (mScale != newScale) {
2947faaee012acc80ad369cb03df9c196e48140f1e7bAngus Kong                    if (mScale == FILM_STRIP_SCALE) {
2948faaee012acc80ad369cb03df9c196e48140f1e7bAngus Kong                        onLeaveFilmstrip();
2949faaee012acc80ad369cb03df9c196e48140f1e7bAngus Kong                    }
2950faaee012acc80ad369cb03df9c196e48140f1e7bAngus Kong                    if (newScale == FILM_STRIP_SCALE) {
2951faaee012acc80ad369cb03df9c196e48140f1e7bAngus Kong                        onEnterFilmstrip();
2952faaee012acc80ad369cb03df9c196e48140f1e7bAngus Kong                    }
29538de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu                }
2954faaee012acc80ad369cb03df9c196e48140f1e7bAngus Kong                mScale = newScale;
29557d2388d96522975307cf5c40f7ac911ad963c44dAngus Kong                invalidate();
2956121022cb590955e37f1264f77190ce4711159976Doris Liu            } else if (mScale < FULL_SCREEN_SCALE && newScale >= FULL_SCREEN_SCALE) {
2957280fd3edae1dc35ac1015bcc9532477169fad3aaSascha Haeberling                // Going from smaller than screen size to bigger than or equal
2958280fd3edae1dc35ac1015bcc9532477169fad3aaSascha Haeberling                // to screen size
2959faaee012acc80ad369cb03df9c196e48140f1e7bAngus Kong                if (mScale == FILM_STRIP_SCALE) {
2960faaee012acc80ad369cb03df9c196e48140f1e7bAngus Kong                    onLeaveFilmstrip();
2961faaee012acc80ad369cb03df9c196e48140f1e7bAngus Kong                }
29624ff5a1ab360577c6a7ec953f9197778c84c9d334Angus Kong                mScale = FULL_SCREEN_SCALE;
2963faaee012acc80ad369cb03df9c196e48140f1e7bAngus Kong                onEnterFullScreen();
29648de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu                mController.setSurroundingViewsVisible(false);
29654567160a43fa5787012bf0b618c216cf18ac0457Angus Kong                invalidate();
2966121022cb590955e37f1264f77190ce4711159976Doris Liu            } else if (mScale >= FULL_SCREEN_SCALE && newScale < FULL_SCREEN_SCALE) {
2967280fd3edae1dc35ac1015bcc9532477169fad3aaSascha Haeberling                // Going from bigger than or equal to screen size to smaller
2968280fd3edae1dc35ac1015bcc9532477169fad3aaSascha Haeberling                // than screen size
29694567160a43fa5787012bf0b618c216cf18ac0457Angus Kong                if (inFullScreen()) {
29704567160a43fa5787012bf0b618c216cf18ac0457Angus Kong                    if (mFullScreenUIHidden) {
29714567160a43fa5787012bf0b618c216cf18ac0457Angus Kong                        onLeaveFullScreenUiHidden();
29724567160a43fa5787012bf0b618c216cf18ac0457Angus Kong                    } else {
29734567160a43fa5787012bf0b618c216cf18ac0457Angus Kong                        onLeaveFullScreen();
29744567160a43fa5787012bf0b618c216cf18ac0457Angus Kong                    }
29754567160a43fa5787012bf0b618c216cf18ac0457Angus Kong                } else {
29764567160a43fa5787012bf0b618c216cf18ac0457Angus Kong                    onLeaveZoomView();
2977faaee012acc80ad369cb03df9c196e48140f1e7bAngus Kong                }
2978121022cb590955e37f1264f77190ce4711159976Doris Liu                mScale = newScale;
297977d9f023e76816e5da7bd067ad46cc0e9c98623fPaul Rohde                renderThumbnail(BUFFER_CENTER);
29804567160a43fa5787012bf0b618c216cf18ac0457Angus Kong                onEnterFilmstrip();
29817d2388d96522975307cf5c40f7ac911ad963c44dAngus Kong                invalidate();
29828de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu            } else {
29838de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu                // Scaled view bigger than or equal to screen size both before
29848de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu                // and after scaling
29854567160a43fa5787012bf0b618c216cf18ac0457Angus Kong                if (!inZoomView()) {
29868de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu                    mController.setSurroundingViewsVisible(false);
29878de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu                }
29888ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde                ViewItem curr = mViewItems[BUFFER_CENTER];
29898de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu                // Make sure the image is not overly scaled
2990121022cb590955e37f1264f77190ce4711159976Doris Liu                newScale = Math.min(newScale, mMaxScale);
299187fc5e1568f737e03327770cb3dee03c4b939c59Doris Liu                if (newScale == mScale) {
299287fc5e1568f737e03327770cb3dee03c4b939c59Doris Liu                    return true;
299387fc5e1568f737e03327770cb3dee03c4b939c59Doris Liu                }
2994121022cb590955e37f1264f77190ce4711159976Doris Liu                float postScale = newScale / mScale;
2995121022cb590955e37f1264f77190ce4711159976Doris Liu                curr.postScale(focusX, focusY, postScale, mDrawArea.width(), mDrawArea.height());
2996121022cb590955e37f1264f77190ce4711159976Doris Liu                mScale = newScale;
2997faaee012acc80ad369cb03df9c196e48140f1e7bAngus Kong                if (mScale == FULL_SCREEN_SCALE) {
2998faaee012acc80ad369cb03df9c196e48140f1e7bAngus Kong                    onEnterFullScreen();
29994567160a43fa5787012bf0b618c216cf18ac0457Angus Kong                } else {
30004567160a43fa5787012bf0b618c216cf18ac0457Angus Kong                    onEnterZoomView();
3001faaee012acc80ad369cb03df9c196e48140f1e7bAngus Kong                }
300277d9f023e76816e5da7bd067ad46cc0e9c98623fPaul Rohde                renderFullRes(BUFFER_CENTER);
300387f9a62f473a3c57505e2877b0a46ab3cb5e62acAngus Kong            }
300449b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong            return true;
300549b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong        }
300649b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong
300749b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong        @Override
300849b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong        public void onScaleEnd() {
3009e7263f7ea45cf31d3948984ba8514e03ef86627ePaul Rohde            // Once the item is no longer under direct manipulation, unlock
3010e7263f7ea45cf31d3948984ba8514e03ef86627ePaul Rohde            // the opacity so it can be set by other parts of the layout code.
3011e7263f7ea45cf31d3948984ba8514e03ef86627ePaul Rohde            if (mCurrentlyScalingItem != null) {
3012e7263f7ea45cf31d3948984ba8514e03ef86627ePaul Rohde                mCurrentlyScalingItem.unlockOpacity();
3013e7263f7ea45cf31d3948984ba8514e03ef86627ePaul Rohde            }
3014e7263f7ea45cf31d3948984ba8514e03ef86627ePaul Rohde
3015c8924b2ec3788ca6d0f0af5ca749e5fe7be125ceAndy Huibers            zoomAtIndexChanged();
3016121022cb590955e37f1264f77190ce4711159976Doris Liu            if (mScale > FULL_SCREEN_SCALE + TOLERANCE) {
30178de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu                return;
30188de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu            }
30198de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu            mController.setSurroundingViewsVisible(true);
3020121022cb590955e37f1264f77190ce4711159976Doris Liu            if (mScale <= FILM_STRIP_SCALE + TOLERANCE) {
3021faaee012acc80ad369cb03df9c196e48140f1e7bAngus Kong                mController.goToFilmstrip();
3022121022cb590955e37f1264f77190ce4711159976Doris Liu            } else if (mScaleTrend > 1f || mScale > FULL_SCREEN_SCALE - TOLERANCE) {
30234567160a43fa5787012bf0b618c216cf18ac0457Angus Kong                if (inZoomView()) {
30248de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu                    mScale = FULL_SCREEN_SCALE;
3025e1d5ba4fc5470043e8645984e3018b712f5dfcffDoris Liu                    resetZoomView();
30268de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu                }
3027734598c39e6a9d809982dae2907dc7d10e0b0e9fAngus Kong                mController.goToFullScreen();
302849b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong            } else {
3029faaee012acc80ad369cb03df9c196e48140f1e7bAngus Kong                mController.goToFilmstrip();
303049b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong            }
3031a6a6a730074e9fedd56c41cd8fa8f1212cfac108Angus Kong            mScaleTrend = 1f;
303249b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong        }
303349b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong    }
303449b9ba2ba89760f297ecc7d6d94d68fb4b836be1Angus Kong}
3035