11c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu/*
21c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu * Copyright (C) 2013 The Android Open Source Project
31c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu *
41c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu * Licensed under the Apache License, Version 2.0 (the "License");
51c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu * you may not use this file except in compliance with the License.
61c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu * You may obtain a copy of the License at
71c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu *
81c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu *      http://www.apache.org/licenses/LICENSE-2.0
91c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu *
101c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu * Unless required by applicable law or agreed to in writing, software
111c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu * distributed under the License is distributed on an "AS IS" BASIS,
121c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
131c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu * See the License for the specific language governing permissions and
141c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu * limitations under the License.
151c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu */
161c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu
171c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liupackage com.android.camera.ui;
181c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu
191c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liuimport android.animation.Animator;
200496fcaa51f88f6c753975473b971941e4090cfaSpike Spragueimport android.animation.AnimatorListenerAdapter;
211c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liuimport android.animation.AnimatorSet;
221c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liuimport android.animation.ObjectAnimator;
231c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liuimport android.animation.TimeInterpolator;
2470576b611fddc929abbfb466fa44c396a3a793dbDoris Liuimport android.animation.ValueAnimator;
251c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liuimport android.content.Context;
26213a4a086b54904cee543adf60b16fc1a61efe38Doris Liuimport android.graphics.Bitmap;
27b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liuimport android.graphics.Canvas;
28b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liuimport android.graphics.Paint;
290496fcaa51f88f6c753975473b971941e4090cfaSpike Spragueimport android.graphics.Point;
30b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liuimport android.graphics.PorterDuff;
31b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liuimport android.graphics.PorterDuffXfermode;
32213a4a086b54904cee543adf60b16fc1a61efe38Doris Liuimport android.graphics.RectF;
33cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liuimport android.os.SystemClock;
341c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liuimport android.util.AttributeSet;
350496fcaa51f88f6c753975473b971941e4090cfaSpike Spragueimport android.util.SparseBooleanArray;
361c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liuimport android.view.GestureDetector;
371c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liuimport android.view.LayoutInflater;
381c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liuimport android.view.MotionEvent;
39213a4a086b54904cee543adf60b16fc1a61efe38Doris Liuimport android.view.View;
40213a4a086b54904cee543adf60b16fc1a61efe38Doris Liuimport android.widget.FrameLayout;
411c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liuimport android.widget.LinearLayout;
421c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu
4328a2950939614a0c1c83787960ce1a218fb69a5eDoris Liuimport com.android.camera.CaptureLayoutHelper;
44213a4a086b54904cee543adf60b16fc1a61efe38Doris Liuimport com.android.camera.app.CameraAppUI;
455596b4c902dcb685928b43678f428746ca5ffd08Angus Kongimport com.android.camera.debug.Log;
468be316c7a8caf962cf3fcf5e49d332fb2718319fPaul Rohdeimport com.android.camera.util.AndroidServices;
47bd1b8f910d6f2c94daac304f4bd1a098939474c3Doris Liuimport com.android.camera.util.CameraUtil;
481c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liuimport com.android.camera.util.Gusterpolator;
495f4f329d7d1780c6ea052bdaa64680e86bf74181Alan Newbergerimport com.android.camera.stats.UsageStatistics;
50b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liuimport com.android.camera.widget.AnimationEffects;
5126aa92bf5942bfde16621c6833c14ba0e1ea8b5eDoris Liuimport com.android.camera.widget.SettingsCling;
521c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liuimport com.android.camera2.R;
5310c5816e1ff3d30e4c4996643ba05d63d522b81aAndy Huibersimport com.google.common.logging.eventprotos;
541c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu
551c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liuimport java.util.ArrayList;
56cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liuimport java.util.LinkedList;
57a20074f0efeaa62a29b05838110d04a1b7271418Doris Liuimport java.util.List;
581c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu
5970576b611fddc929abbfb466fa44c396a3a793dbDoris Liu/**
6070576b611fddc929abbfb466fa44c396a3a793dbDoris Liu * ModeListView class displays all camera modes and settings in the form
6170576b611fddc929abbfb466fa44c396a3a793dbDoris Liu * of a list. A swipe to the right will bring up this list. Then tapping on
6270576b611fddc929abbfb466fa44c396a3a793dbDoris Liu * any of the items in the list will take the user to that corresponding mode
6370576b611fddc929abbfb466fa44c396a3a793dbDoris Liu * with an animation. To dismiss this list, simply swipe left or select a mode.
6470576b611fddc929abbfb466fa44c396a3a793dbDoris Liu */
65213a4a086b54904cee543adf60b16fc1a61efe38Doris Liupublic class ModeListView extends FrameLayout
66b908d933736a4ec042aad065b7ebf2059bbcac97Doris Liu        implements ModeSelectorItem.VisibleWidthChangedListener,
67b908d933736a4ec042aad065b7ebf2059bbcac97Doris Liu        PreviewStatusListener.PreviewAreaChangedListener {
68601921e746832bdef40503c606a95b4469f1bedbSascha Haeberling
695596b4c902dcb685928b43678f428746ca5ffd08Angus Kong    private static final Log.Tag TAG = new Log.Tag("ModeListView");
7070576b611fddc929abbfb466fa44c396a3a793dbDoris Liu
7170576b611fddc929abbfb466fa44c396a3a793dbDoris Liu    // Animation Durations
7270576b611fddc929abbfb466fa44c396a3a793dbDoris Liu    private static final int DEFAULT_DURATION_MS = 200;
73b00d64307ae77541f8576593a29214d649fb94e9Doris Liu    private static final int FLY_IN_DURATION_MS = 0;
741c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu    private static final int HOLD_DURATION_MS = 0;
751c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu    private static final int FLY_OUT_DURATION_MS = 850;
761c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu    private static final int START_DELAY_MS = 100;
771c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu    private static final int TOTAL_DURATION_MS = FLY_IN_DURATION_MS + HOLD_DURATION_MS
781c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu            + FLY_OUT_DURATION_MS;
79b00d64307ae77541f8576593a29214d649fb94e9Doris Liu    private static final int HIDE_SHIMMY_DELAY_MS = 1000;
8069cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu    // Assumption for time since last scroll when no data point for last scroll.
8169cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu    private static final int SCROLL_INTERVAL_MS = 50;
8269cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu    // Last 20% percent of the drawer opening should be slow to ensure soft landing.
8369cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu    private static final float SLOW_ZONE_PERCENTAGE = 0.2f;
841c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu
8570576b611fddc929abbfb466fa44c396a3a793dbDoris Liu    private static final int NO_ITEM_SELECTED = -1;
861c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu
87cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu    // Scrolling delay between non-focused item and focused item
88213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu    private static final int DELAY_MS = 30;
896c5a5375524efe7e5160f92edcaad121c7fbdb07Doris Liu    // If the fling velocity exceeds this threshold, snap to full screen at a constant
906c5a5375524efe7e5160f92edcaad121c7fbdb07Doris Liu    // speed. Unit: pixel/ms.
916c5a5375524efe7e5160f92edcaad121c7fbdb07Doris Liu    private static final float VELOCITY_THRESHOLD = 2f;
92cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu
93213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu    /**
94213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu     * A factor to change the UI responsiveness on a scroll.
95213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu     * e.g. A scroll factor of 0.5 means UI will move half as fast as the finger.
96213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu     */
97213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu    private static final float SCROLL_FACTOR = 0.5f;
985ac2bfbb23f6277947f284531237ea85c81a06e4Seth Raphael    // 60% opaque black background.
993020ba5dc23723bab7503324d82dd5ae3830008cSeth Raphael    private static final int BACKGROUND_TRANSPARENTCY = (int) (0.6f * 255);
100213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu    private static final int PREVIEW_DOWN_SAMPLE_FACTOR = 4;
101213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu    // Threshold, below which snap back will happen.
102213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu    private static final float SNAP_BACK_THRESHOLD_RATIO = 0.33f;
103213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu
10470576b611fddc929abbfb466fa44c396a3a793dbDoris Liu    private final GestureDetector mGestureDetector;
10526e901ed46054fb6cc2cb4c09bef3a779a1cbf78Alan Newberger    private final CurrentStateManager mCurrentStateManager = new CurrentStateManager();
10628a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu    private final int mSettingsButtonMargin;
10769cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu    private long mLastScrollTime;
10870576b611fddc929abbfb466fa44c396a3a793dbDoris Liu    private int mListBackgroundColor;
10970576b611fddc929abbfb466fa44c396a3a793dbDoris Liu    private LinearLayout mListView;
11028a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu    private View mSettingsButton;
11170576b611fddc929abbfb466fa44c396a3a793dbDoris Liu    private int mTotalModes;
11270576b611fddc929abbfb466fa44c396a3a793dbDoris Liu    private ModeSelectorItem[] mModeSelectorItems;
11370576b611fddc929abbfb466fa44c396a3a793dbDoris Liu    private AnimatorSet mAnimatorSet;
1141c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu    private int mFocusItem = NO_ITEM_SELECTED;
1152c559c4bd3c65e0cb0bca06f30e3bc979038e6f7Doris Liu    private ModeListOpenListener mModeListOpenListener;
1168c1a922df154182b53f00c38198db7da0b4feb20Sascha Haeberling    private ModeListVisibilityChangedListener mVisibilityChangedListener;
117213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu    private CameraAppUI.CameraModuleScreenShotProvider mScreenShotProvider = null;
118213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu    private int[] mInputPixels;
119213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu    private int[] mOutputPixels;
120a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu    private float mModeListOpenFactor = 1f;
12170576b611fddc929abbfb466fa44c396a3a793dbDoris Liu
12269cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu    private View mChildViewTouched = null;
12369cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu    private MotionEvent mLastChildTouchEvent = null;
12469cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu    private int mVisibleWidth = 0;
12569cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu
12670576b611fddc929abbfb466fa44c396a3a793dbDoris Liu    // Width and height of this view. They get updated in onLayout()
12770576b611fddc929abbfb466fa44c396a3a793dbDoris Liu    // Unit for width and height are pixels.
1281c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu    private int mWidth;
1291c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu    private int mHeight;
1301c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu    private float mScrollTrendX = 0f;
1311c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu    private float mScrollTrendY = 0f;
1322c559c4bd3c65e0cb0bca06f30e3bc979038e6f7Doris Liu    private ModeSwitchListener mModeSwitchListener = null;
133bd1b8f910d6f2c94daac304f4bd1a098939474c3Doris Liu    private ArrayList<Integer> mSupportedModes;
134cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu    private final LinkedList<TimeBasedPosition> mPositionHistory
135cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu            = new LinkedList<TimeBasedPosition>();
136cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu    private long mCurrentTime;
1376c5a5375524efe7e5160f92edcaad121c7fbdb07Doris Liu    private float mVelocityX; // Unit: pixel/ms.
138a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu    private long mLastDownTime = 0;
13928a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu    private CaptureLayoutHelper mCaptureLayoutHelper = null;
14026aa92bf5942bfde16621c6833c14ba0e1ea8b5eDoris Liu    private SettingsCling mSettingsCling = null;
141a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
14226e901ed46054fb6cc2cb4c09bef3a779a1cbf78Alan Newberger    private class CurrentStateManager {
14326e901ed46054fb6cc2cb4c09bef3a779a1cbf78Alan Newberger        private ModeListState mCurrentState;
14426e901ed46054fb6cc2cb4c09bef3a779a1cbf78Alan Newberger
14526e901ed46054fb6cc2cb4c09bef3a779a1cbf78Alan Newberger        ModeListState getCurrentState() {
14626e901ed46054fb6cc2cb4c09bef3a779a1cbf78Alan Newberger            return mCurrentState;
14726e901ed46054fb6cc2cb4c09bef3a779a1cbf78Alan Newberger        }
14826e901ed46054fb6cc2cb4c09bef3a779a1cbf78Alan Newberger
14926e901ed46054fb6cc2cb4c09bef3a779a1cbf78Alan Newberger        void setCurrentState(ModeListState state) {
15026e901ed46054fb6cc2cb4c09bef3a779a1cbf78Alan Newberger            mCurrentState = state;
15126e901ed46054fb6cc2cb4c09bef3a779a1cbf78Alan Newberger            state.onCurrentState();
15226e901ed46054fb6cc2cb4c09bef3a779a1cbf78Alan Newberger        }
15326e901ed46054fb6cc2cb4c09bef3a779a1cbf78Alan Newberger    }
15426e901ed46054fb6cc2cb4c09bef3a779a1cbf78Alan Newberger
155a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu    /**
156a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu     * ModeListState defines a set of functions through which the view could manage
157a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu     * or change the states. Sub-classes could selectively override these functions
158a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu     * accordingly to respect the specific requirements for each state. By overriding
159a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu     * these methods, state transition can also be achieved.
160a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu     */
161a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu    private abstract class ModeListState implements GestureDetector.OnGestureListener {
162a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        protected AnimationEffects mCurrentAnimationEffects = null;
163a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
164a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        /**
16526e901ed46054fb6cc2cb4c09bef3a779a1cbf78Alan Newberger         * Called by the state manager when this state instance becomes the current
16626e901ed46054fb6cc2cb4c09bef3a779a1cbf78Alan Newberger         * mode list state.
16726e901ed46054fb6cc2cb4c09bef3a779a1cbf78Alan Newberger         */
16826e901ed46054fb6cc2cb4c09bef3a779a1cbf78Alan Newberger        public void onCurrentState() {
16926e901ed46054fb6cc2cb4c09bef3a779a1cbf78Alan Newberger            // Do nothing.
17026aa92bf5942bfde16621c6833c14ba0e1ea8b5eDoris Liu            showSettingsClingIfEnabled(false);
17126e901ed46054fb6cc2cb4c09bef3a779a1cbf78Alan Newberger        }
17226e901ed46054fb6cc2cb4c09bef3a779a1cbf78Alan Newberger
17326e901ed46054fb6cc2cb4c09bef3a779a1cbf78Alan Newberger        /**
174a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu         * If supported, this should show the mode switcher and starts the accordion
175a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu         * animation with a delay. If the view does not currently have focus, (e.g.
176a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu         * There are popups on top of it.) start the delayed accordion animation
177a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu         * when it gains focus. Otherwise, start the animation with a delay right
178a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu         * away.
179a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu         */
180a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        public void showSwitcherHint() {
181a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            // Do nothing.
182a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        }
183a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
184a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        /**
185a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu         * Gets the currently running animation effects for the current state.
186a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu         */
187a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        public AnimationEffects getCurrentAnimationEffects() {
188a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            return mCurrentAnimationEffects;
189a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        }
190a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
191a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        /**
192a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu         * Returns true if the touch event should be handled, false otherwise.
193a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu         *
194a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu         * @param ev motion event to be handled
195a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu         * @return true if the event should be handled, false otherwise.
196a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu         */
197a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        public boolean shouldHandleTouchEvent(MotionEvent ev) {
198a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            return true;
199a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        }
200a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
201a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        /**
202a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu         * Handles touch event. This will be called if
203a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu         * {@link ModeListState#shouldHandleTouchEvent(android.view.MotionEvent)}
204a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu         * returns {@code true}
205a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu         *
206a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu         * @param ev touch event to be handled
207a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu         * @return always true
208a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu         */
209a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        public boolean onTouchEvent(MotionEvent ev) {
210a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            return true;
211a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        }
212a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
213a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        /**
214a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu         * Gets called when the window focus has changed.
215a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu         *
216a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu         * @param hasFocus whether current window has focus
217a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu         */
218a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        public void onWindowFocusChanged(boolean hasFocus) {
219a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            // Default to do nothing.
220a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        }
221a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
222a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        /**
223a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu         * Gets called when back key is pressed.
224a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu         *
225a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu         * @return true if handled, false otherwise.
226a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu         */
227a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        public boolean onBackPressed() {
228a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            return false;
229a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        }
230a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
231a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        /**
23215691af76927d932fcc1250b4ffaefd31a61cf81Erin Dahlgren         * Gets called when menu key is pressed.
23315691af76927d932fcc1250b4ffaefd31a61cf81Erin Dahlgren         *
23415691af76927d932fcc1250b4ffaefd31a61cf81Erin Dahlgren         * @return true if handled, false otherwise.
23515691af76927d932fcc1250b4ffaefd31a61cf81Erin Dahlgren         */
23615691af76927d932fcc1250b4ffaefd31a61cf81Erin Dahlgren        public boolean onMenuPressed() {
23715691af76927d932fcc1250b4ffaefd31a61cf81Erin Dahlgren            return false;
23815691af76927d932fcc1250b4ffaefd31a61cf81Erin Dahlgren        }
23915691af76927d932fcc1250b4ffaefd31a61cf81Erin Dahlgren
24015691af76927d932fcc1250b4ffaefd31a61cf81Erin Dahlgren        /**
241a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu         * Gets called when there is a {@link View#setVisibility(int)} call to
242a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu         * change the visibility of the mode drawer. Visibility change does not
243a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu         * always make sense, for example there can be an outside call to make
244a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu         * the mode drawer visible when it is in the fully hidden state. The logic
245a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu         * is that the mode drawer can only be made visible when user swipe it in.
246a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu         *
247a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu         * @param visibility the proposed visibility change
248a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu         * @return true if the visibility change is valid and therefore should be
249a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu         *         handled, false otherwise.
250a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu         */
251a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        public boolean shouldHandleVisibilityChange(int visibility) {
252a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            return true;
253a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        }
254a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
255a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        /**
256a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu         * If supported, this should start blurring the camera preview and
257a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu         * start the mode switch.
258a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu         *
259a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu         * @param selectedItem mode item that has been selected
260a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu         */
261a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        public void onItemSelected(ModeSelectorItem selectedItem) {
262a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            // Do nothing.
263a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        }
264a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
265a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        /**
266a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu         * This gets called when mode switch has finished and UI needs to
267a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu         * pinhole into the new mode through animation.
268a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu         */
269a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        public void startModeSelectionAnimation() {
270a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            // Do nothing.
271a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        }
272a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
273a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        /**
274a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu         * Hide the mode drawer and switch to fully hidden state.
275a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu         */
276a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        public void hide() {
277a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            // Do nothing.
278a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        }
279a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
2804c96d840aeff669011a9e8697cbac77fe8f4eda2Spike Sprague        /**
2814c96d840aeff669011a9e8697cbac77fe8f4eda2Spike Sprague         * Hide the mode drawer (with animation, if supported)
2824c96d840aeff669011a9e8697cbac77fe8f4eda2Spike Sprague         * and switch to fully hidden state.
2834c96d840aeff669011a9e8697cbac77fe8f4eda2Spike Sprague         * Default is to simply call {@link #hide()}.
2844c96d840aeff669011a9e8697cbac77fe8f4eda2Spike Sprague         */
2854c96d840aeff669011a9e8697cbac77fe8f4eda2Spike Sprague        public void hideAnimated() {
2864c96d840aeff669011a9e8697cbac77fe8f4eda2Spike Sprague            hide();
2874c96d840aeff669011a9e8697cbac77fe8f4eda2Spike Sprague        }
2884c96d840aeff669011a9e8697cbac77fe8f4eda2Spike Sprague
289a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        /***************GestureListener implementation*****************/
290a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        @Override
291a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        public boolean onDown(MotionEvent e) {
292a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            return false;
293a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        }
294a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
295a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        @Override
296a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        public void onShowPress(MotionEvent e) {
297a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            // Do nothing.
298a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        }
299a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
300a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        @Override
301a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        public boolean onSingleTapUp(MotionEvent e) {
302a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            return false;
303a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        }
304a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
305a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        @Override
306a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
307a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            return false;
308a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        }
309a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
310a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        @Override
311a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        public void onLongPress(MotionEvent e) {
312a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            // Do nothing.
313a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        }
314a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
315a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        @Override
316a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
317a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            return false;
318a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        }
319a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu    }
320a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
321a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu    /**
322a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu     * Fully hidden state. Transitioning to ScrollingState and ShimmyState are supported
323a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu     * in this state.
324a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu     */
325a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu    private class FullyHiddenState extends ModeListState {
32615691af76927d932fcc1250b4ffaefd31a61cf81Erin Dahlgren        private Animator mAnimator = null;
327a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        private boolean mShouldBeVisible = false;
32815691af76927d932fcc1250b4ffaefd31a61cf81Erin Dahlgren
329a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        public FullyHiddenState() {
330a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            reset();
331a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        }
332a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
333a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        @Override
334a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
335a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            mShouldBeVisible = true;
336a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            // Change visibility, and switch to scrolling state.
337a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            resetModeSelectors();
33826e901ed46054fb6cc2cb4c09bef3a779a1cbf78Alan Newberger            mCurrentStateManager.setCurrentState(new ScrollingState());
339a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            return true;
340a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        }
341a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
342a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        @Override
343a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        public void showSwitcherHint() {
344a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            mShouldBeVisible = true;
34526e901ed46054fb6cc2cb4c09bef3a779a1cbf78Alan Newberger            mCurrentStateManager.setCurrentState(new ShimmyState());
346a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        }
3476c5a5375524efe7e5160f92edcaad121c7fbdb07Doris Liu
3486c5a5375524efe7e5160f92edcaad121c7fbdb07Doris Liu        @Override
349a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        public boolean shouldHandleTouchEvent(MotionEvent ev) {
350a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            return true;
351a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        }
352a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
353a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        @Override
354a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        public boolean onTouchEvent(MotionEvent ev) {
355a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            if (ev.getActionMasked() == MotionEvent.ACTION_DOWN) {
356a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                mFocusItem = getFocusItem(ev.getX(), ev.getY());
357a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                setSwipeMode(true);
358a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            }
359a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            return true;
360a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        }
361a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
362a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        @Override
36315691af76927d932fcc1250b4ffaefd31a61cf81Erin Dahlgren        public boolean onMenuPressed() {
36415691af76927d932fcc1250b4ffaefd31a61cf81Erin Dahlgren            if (mAnimator != null) {
36515691af76927d932fcc1250b4ffaefd31a61cf81Erin Dahlgren                return false;
36615691af76927d932fcc1250b4ffaefd31a61cf81Erin Dahlgren            }
36715691af76927d932fcc1250b4ffaefd31a61cf81Erin Dahlgren            snapOpenAndShow();
36815691af76927d932fcc1250b4ffaefd31a61cf81Erin Dahlgren            return true;
36915691af76927d932fcc1250b4ffaefd31a61cf81Erin Dahlgren        }
37015691af76927d932fcc1250b4ffaefd31a61cf81Erin Dahlgren
37115691af76927d932fcc1250b4ffaefd31a61cf81Erin Dahlgren        @Override
372a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        public boolean shouldHandleVisibilityChange(int visibility) {
37315691af76927d932fcc1250b4ffaefd31a61cf81Erin Dahlgren            if (mAnimator != null) {
37415691af76927d932fcc1250b4ffaefd31a61cf81Erin Dahlgren                return false;
37515691af76927d932fcc1250b4ffaefd31a61cf81Erin Dahlgren            }
376a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            if (visibility == VISIBLE && !mShouldBeVisible) {
377a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                return false;
378a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            }
379a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            return true;
380a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        }
38115691af76927d932fcc1250b4ffaefd31a61cf81Erin Dahlgren        /**
38215691af76927d932fcc1250b4ffaefd31a61cf81Erin Dahlgren         * Snaps open the mode list and go to the fully shown state.
38315691af76927d932fcc1250b4ffaefd31a61cf81Erin Dahlgren         */
38415691af76927d932fcc1250b4ffaefd31a61cf81Erin Dahlgren        private void snapOpenAndShow() {
38515691af76927d932fcc1250b4ffaefd31a61cf81Erin Dahlgren            mShouldBeVisible = true;
38615691af76927d932fcc1250b4ffaefd31a61cf81Erin Dahlgren            setVisibility(VISIBLE);
387a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
38815691af76927d932fcc1250b4ffaefd31a61cf81Erin Dahlgren            mAnimator = snapToFullScreen();
38915691af76927d932fcc1250b4ffaefd31a61cf81Erin Dahlgren            if (mAnimator != null) {
39015691af76927d932fcc1250b4ffaefd31a61cf81Erin Dahlgren                mAnimator.addListener(new Animator.AnimatorListener() {
39115691af76927d932fcc1250b4ffaefd31a61cf81Erin Dahlgren                    @Override
39215691af76927d932fcc1250b4ffaefd31a61cf81Erin Dahlgren                    public void onAnimationStart(Animator animation) {
39315691af76927d932fcc1250b4ffaefd31a61cf81Erin Dahlgren
39415691af76927d932fcc1250b4ffaefd31a61cf81Erin Dahlgren                    }
39515691af76927d932fcc1250b4ffaefd31a61cf81Erin Dahlgren
39615691af76927d932fcc1250b4ffaefd31a61cf81Erin Dahlgren                    @Override
39715691af76927d932fcc1250b4ffaefd31a61cf81Erin Dahlgren                    public void onAnimationEnd(Animator animation) {
39815691af76927d932fcc1250b4ffaefd31a61cf81Erin Dahlgren                        mAnimator = null;
39926e901ed46054fb6cc2cb4c09bef3a779a1cbf78Alan Newberger                        mCurrentStateManager.setCurrentState(new FullyShownState());
40015691af76927d932fcc1250b4ffaefd31a61cf81Erin Dahlgren                    }
40115691af76927d932fcc1250b4ffaefd31a61cf81Erin Dahlgren
40215691af76927d932fcc1250b4ffaefd31a61cf81Erin Dahlgren                    @Override
40315691af76927d932fcc1250b4ffaefd31a61cf81Erin Dahlgren                    public void onAnimationCancel(Animator animation) {
40415691af76927d932fcc1250b4ffaefd31a61cf81Erin Dahlgren
40515691af76927d932fcc1250b4ffaefd31a61cf81Erin Dahlgren                    }
40615691af76927d932fcc1250b4ffaefd31a61cf81Erin Dahlgren
40715691af76927d932fcc1250b4ffaefd31a61cf81Erin Dahlgren                    @Override
40815691af76927d932fcc1250b4ffaefd31a61cf81Erin Dahlgren                    public void onAnimationRepeat(Animator animation) {
40915691af76927d932fcc1250b4ffaefd31a61cf81Erin Dahlgren
41015691af76927d932fcc1250b4ffaefd31a61cf81Erin Dahlgren                    }
41115691af76927d932fcc1250b4ffaefd31a61cf81Erin Dahlgren                });
41215691af76927d932fcc1250b4ffaefd31a61cf81Erin Dahlgren            } else {
41326e901ed46054fb6cc2cb4c09bef3a779a1cbf78Alan Newberger                mCurrentStateManager.setCurrentState(new FullyShownState());
41410c5816e1ff3d30e4c4996643ba05d63d522b81aAndy Huibers                UsageStatistics.instance().controlUsed(
41510c5816e1ff3d30e4c4996643ba05d63d522b81aAndy Huibers                        eventprotos.ControlEvent.ControlType.MENU_FULL_FROM_HIDDEN);
41615691af76927d932fcc1250b4ffaefd31a61cf81Erin Dahlgren            }
41715691af76927d932fcc1250b4ffaefd31a61cf81Erin Dahlgren        }
41826e901ed46054fb6cc2cb4c09bef3a779a1cbf78Alan Newberger
41926e901ed46054fb6cc2cb4c09bef3a779a1cbf78Alan Newberger        @Override
42026e901ed46054fb6cc2cb4c09bef3a779a1cbf78Alan Newberger        public void onCurrentState() {
42126aa92bf5942bfde16621c6833c14ba0e1ea8b5eDoris Liu            super.onCurrentState();
42226aa92bf5942bfde16621c6833c14ba0e1ea8b5eDoris Liu            announceForAccessibility(
42326aa92bf5942bfde16621c6833c14ba0e1ea8b5eDoris Liu                    getContext().getResources().getString(R.string.accessibility_mode_list_hidden));
42426e901ed46054fb6cc2cb4c09bef3a779a1cbf78Alan Newberger        }
425a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu    }
426a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
427a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu    /**
428a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu     * Fully shown state. This state represents when the mode list is entirely shown
429a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu     * on screen without any on-going animation. Transitions from this state could be
430a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu     * to ScrollingState, SelectedState, or FullyHiddenState.
431a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu     */
432a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu    private class FullyShownState extends ModeListState {
433a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        private Animator mAnimator = null;
434a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
435a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        @Override
436a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
437a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            // Go to scrolling state.
438a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            if (distanceX > 0) {
439a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                // Swipe out
440a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                cancelForwardingTouchEvent();
44126e901ed46054fb6cc2cb4c09bef3a779a1cbf78Alan Newberger                mCurrentStateManager.setCurrentState(new ScrollingState());
442a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            }
443a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            return true;
444a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        }
445a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
446a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        @Override
447a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        public boolean shouldHandleTouchEvent(MotionEvent ev) {
448a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            if (mAnimator != null && mAnimator.isRunning()) {
449a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                return false;
450a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            }
451a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            return true;
452a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        }
453a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
454a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        @Override
455a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        public boolean onTouchEvent(MotionEvent ev) {
456a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            if (ev.getActionMasked() == MotionEvent.ACTION_DOWN) {
457a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                mFocusItem = NO_ITEM_SELECTED;
458a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                setSwipeMode(false);
459a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                // If the down event happens inside the mode list, find out which
460a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                // mode item is being touched and forward all the subsequent touch
461a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                // events to that mode item for its pressed state and click handling.
462a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                if (isTouchInsideList(ev)) {
463a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                    mChildViewTouched = mModeSelectorItems[getFocusItem(ev.getX(), ev.getY())];
464a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                }
465a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            }
466a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            forwardTouchEventToChild(ev);
467a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            return true;
468a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        }
469a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
470a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
471a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        @Override
472a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        public boolean onSingleTapUp(MotionEvent ev) {
473a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            // If the tap is not inside the mode drawer area, snap back.
474a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            if(!isTouchInsideList(ev)) {
475a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                snapBackAndHide();
476a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                return false;
477a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            }
478a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            return true;
479a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        }
480a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
481a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        @Override
482a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        public boolean onBackPressed() {
483a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            snapBackAndHide();
484a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            return true;
485a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        }
486a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
487a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        @Override
48815691af76927d932fcc1250b4ffaefd31a61cf81Erin Dahlgren        public boolean onMenuPressed() {
48915691af76927d932fcc1250b4ffaefd31a61cf81Erin Dahlgren            snapBackAndHide();
49015691af76927d932fcc1250b4ffaefd31a61cf81Erin Dahlgren            return true;
49115691af76927d932fcc1250b4ffaefd31a61cf81Erin Dahlgren        }
49215691af76927d932fcc1250b4ffaefd31a61cf81Erin Dahlgren
49315691af76927d932fcc1250b4ffaefd31a61cf81Erin Dahlgren        @Override
494a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        public void onItemSelected(ModeSelectorItem selectedItem) {
49526e901ed46054fb6cc2cb4c09bef3a779a1cbf78Alan Newberger            mCurrentStateManager.setCurrentState(new SelectedState(selectedItem));
496a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        }
497a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
498a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        /**
499a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu         * Snaps back the mode list and go to the fully hidden state.
500a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu         */
501a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        private void snapBackAndHide() {
502a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            mAnimator = snapBack(true);
503a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            if (mAnimator != null) {
504a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                mAnimator.addListener(new Animator.AnimatorListener() {
505a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                    @Override
506a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                    public void onAnimationStart(Animator animation) {
507a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
508a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                    }
509a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
510a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                    @Override
511a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                    public void onAnimationEnd(Animator animation) {
512a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                        mAnimator = null;
51326e901ed46054fb6cc2cb4c09bef3a779a1cbf78Alan Newberger                        mCurrentStateManager.setCurrentState(new FullyHiddenState());
514a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                    }
515a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
516a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                    @Override
517a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                    public void onAnimationCancel(Animator animation) {
518a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
519a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                    }
520a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
521a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                    @Override
522a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                    public void onAnimationRepeat(Animator animation) {
523a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
524a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                    }
525a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                });
526a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            } else {
52726e901ed46054fb6cc2cb4c09bef3a779a1cbf78Alan Newberger                mCurrentStateManager.setCurrentState(new FullyHiddenState());
528a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            }
529a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        }
530a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
531a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        @Override
532a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        public void hide() {
533a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            if (mAnimator != null) {
534a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                mAnimator.cancel();
535a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            } else {
53626e901ed46054fb6cc2cb4c09bef3a779a1cbf78Alan Newberger                mCurrentStateManager.setCurrentState(new FullyHiddenState());
537a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            }
538a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        }
53926e901ed46054fb6cc2cb4c09bef3a779a1cbf78Alan Newberger
54026e901ed46054fb6cc2cb4c09bef3a779a1cbf78Alan Newberger        @Override
54126e901ed46054fb6cc2cb4c09bef3a779a1cbf78Alan Newberger        public void onCurrentState() {
54226aa92bf5942bfde16621c6833c14ba0e1ea8b5eDoris Liu            announceForAccessibility(
54326aa92bf5942bfde16621c6833c14ba0e1ea8b5eDoris Liu                    getContext().getResources().getString(R.string.accessibility_mode_list_shown));
54426aa92bf5942bfde16621c6833c14ba0e1ea8b5eDoris Liu            showSettingsClingIfEnabled(true);
54526e901ed46054fb6cc2cb4c09bef3a779a1cbf78Alan Newberger        }
546a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu    }
547a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
548a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu    /**
549a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu     * Shimmy state handles the specifics for shimmy animation, including
550a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu     * setting up to show mode drawer (without text) and hide it with shimmy animation.
551a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu     *
552a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu     * This state can be interrupted when scrolling or mode selection happened,
553a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu     * in which case the state will transition into ScrollingState, or SelectedState.
554a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu     * Otherwise, after shimmy finishes successfully, a transition to fully hidden
555a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu     * state will happen.
556a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu     */
557a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu    private class ShimmyState extends ModeListState {
558a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
559a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        private boolean mStartHidingShimmyWhenWindowGainsFocus = false;
560a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        private Animator mAnimator = null;
561a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        private final Runnable mHideShimmy = new Runnable() {
562a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            @Override
563a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            public void run() {
564a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                startHidingShimmy();
565a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            }
566a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        };
567a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
568a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        public ShimmyState() {
5696c5a5375524efe7e5160f92edcaad121c7fbdb07Doris Liu            setVisibility(VISIBLE);
570a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            mSettingsButton.setVisibility(INVISIBLE);
571a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            mModeListOpenFactor = 0f;
572a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            onModeListOpenRatioUpdate(0);
573a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            int maxVisibleWidth = mModeSelectorItems[0].getMaxVisibleWidth();
574a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            for (int i = 0; i < mModeSelectorItems.length; i++) {
575a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                mModeSelectorItems[i].setVisibleWidth(maxVisibleWidth);
576a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            }
577a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            if (hasWindowFocus()) {
578a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                hideShimmyWithDelay();
579a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            } else {
580a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                mStartHidingShimmyWhenWindowGainsFocus = true;
581a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            }
582a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        }
583a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
584a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        @Override
585a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
586a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            // Scroll happens during accordion animation.
587a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            cancelAnimation();
5881c5a87b4301c4211d3e3b85be5c863c50d4eab7bDoris Liu            cancelForwardingTouchEvent();
589a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            // Go to scrolling state
59026e901ed46054fb6cc2cb4c09bef3a779a1cbf78Alan Newberger            mCurrentStateManager.setCurrentState(new ScrollingState());
59110c5816e1ff3d30e4c4996643ba05d63d522b81aAndy Huibers            UsageStatistics.instance().controlUsed(
59210c5816e1ff3d30e4c4996643ba05d63d522b81aAndy Huibers                    eventprotos.ControlEvent.ControlType.MENU_SCROLL_FROM_SHIMMY);
593a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            return true;
594a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        }
595a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
596a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        @Override
597a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        public boolean shouldHandleTouchEvent(MotionEvent ev) {
598a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            if (MotionEvent.ACTION_DOWN == ev.getActionMasked()) {
599a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                if (isTouchInsideList(ev) &&
600a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                        ev.getX() <= mModeSelectorItems[0].getMaxVisibleWidth()) {
601a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                    mChildViewTouched = mModeSelectorItems[getFocusItem(ev.getX(), ev.getY())];
602a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                    return true;
603a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                }
604a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                // If shimmy is on-going, reject the first down event, so that it can be handled
605a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                // by the view underneath. If a swipe is detected, the same series of touch will
606a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                // re-enter this function, in which case we will consume the touch events.
607a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                if (mLastDownTime != ev.getDownTime()) {
608a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                    mLastDownTime = ev.getDownTime();
609a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                    return false;
610a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                }
611a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            }
612a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            return true;
6136c5a5375524efe7e5160f92edcaad121c7fbdb07Doris Liu        }
6146c5a5375524efe7e5160f92edcaad121c7fbdb07Doris Liu
6156c5a5375524efe7e5160f92edcaad121c7fbdb07Doris Liu        @Override
616a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        public boolean onTouchEvent(MotionEvent ev) {
617a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            if (MotionEvent.ACTION_DOWN == ev.getActionMasked()) {
618a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                if (ev.getActionMasked() == MotionEvent.ACTION_DOWN) {
619a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                    mFocusItem = getFocusItem(ev.getX(), ev.getY());
620a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                    setSwipeMode(true);
621a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                }
622a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            }
623a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            forwardTouchEventToChild(ev);
624a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            return true;
625a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        }
626a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
627a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        @Override
628a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        public void onItemSelected(ModeSelectorItem selectedItem) {
629a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            cancelAnimation();
63026e901ed46054fb6cc2cb4c09bef3a779a1cbf78Alan Newberger            mCurrentStateManager.setCurrentState(new SelectedState(selectedItem));
631a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        }
632a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
633a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        private void hideShimmyWithDelay() {
634a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            postDelayed(mHideShimmy, HIDE_SHIMMY_DELAY_MS);
635a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        }
636a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
637a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        @Override
638a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        public void onWindowFocusChanged(boolean hasFocus) {
639a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            if (mStartHidingShimmyWhenWindowGainsFocus && hasFocus) {
640a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                mStartHidingShimmyWhenWindowGainsFocus = false;
641a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                hideShimmyWithDelay();
642a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            }
643a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        }
644a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
645a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        /**
646a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu         * This starts the accordion animation, unless it's already running, in which
647a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu         * case the start animation call will be ignored.
648a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu         */
649a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        private void startHidingShimmy() {
650a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            if (mAnimator != null) {
65169cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu                return;
65269cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu            }
653a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            int maxVisibleWidth = mModeSelectorItems[0].getMaxVisibleWidth();
654a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            mAnimator = animateListToWidth(START_DELAY_MS * (-1), TOTAL_DURATION_MS,
655a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                    Gusterpolator.INSTANCE, maxVisibleWidth, 0);
656a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            mAnimator.addListener(new Animator.AnimatorListener() {
657a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                private boolean mSuccess = true;
658a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                @Override
659a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                public void onAnimationStart(Animator animation) {
660a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                    // Do nothing.
661a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                }
662a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
663a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                @Override
664a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                public void onAnimationEnd(Animator animation) {
665a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                    mAnimator = null;
666a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                    ShimmyState.this.onAnimationEnd(mSuccess);
667a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                }
668a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
669a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                @Override
670a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                public void onAnimationCancel(Animator animation) {
671a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                    mSuccess = false;
672a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                }
673a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
674a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                @Override
675a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                public void onAnimationRepeat(Animator animation) {
676a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                    // Do nothing.
677a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                }
678a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            });
679a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        }
680a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
681a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        /**
682a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu         * Cancels the pending/on-going animation.
683a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu         */
684a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        private void cancelAnimation() {
685a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            removeCallbacks(mHideShimmy);
686a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            if (mAnimator != null && mAnimator.isRunning()) {
687a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                mAnimator.cancel();
688a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            } else {
689a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                mAnimator = null;
690a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                onAnimationEnd(false);
691a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            }
692a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        }
693a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
694c064750b7a02c4e495a30d2ec8d25319a9e49e9cSpike Sprague        @Override
695c064750b7a02c4e495a30d2ec8d25319a9e49e9cSpike Sprague        public void onCurrentState() {
696c064750b7a02c4e495a30d2ec8d25319a9e49e9cSpike Sprague            super.onCurrentState();
697c064750b7a02c4e495a30d2ec8d25319a9e49e9cSpike Sprague            ModeListView.this.disableA11yOnModeSelectorItems();
698c064750b7a02c4e495a30d2ec8d25319a9e49e9cSpike Sprague        }
699a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        /**
700a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu         * Gets called when the animation finishes or gets canceled.
701a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu         *
702a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu         * @param success indicates whether the animation finishes successfully
703a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu         */
704a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        private void onAnimationEnd(boolean success) {
7057543914f2de33b1be25626afb31fa979b06dd582Paul Rohde            if (mSettingsButton.getLayerType() == View.LAYER_TYPE_HARDWARE) {
7061908beee07fe9792338dbf8e0c36a5f1e3e9401fPaul Rohde                Log.v(TAG, "Disabling hardware layer for the Settings Button. (onAnimationEnd)");
7071908beee07fe9792338dbf8e0c36a5f1e3e9401fPaul Rohde                mSettingsButton.setLayerType(View.LAYER_TYPE_NONE, null);
7081908beee07fe9792338dbf8e0c36a5f1e3e9401fPaul Rohde            }
7091908beee07fe9792338dbf8e0c36a5f1e3e9401fPaul Rohde
710a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            mSettingsButton.setVisibility(VISIBLE);
711a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            // If successfully finish hiding shimmy, then we should go back to
712a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            // fully hidden state.
713a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            if (success) {
714c064750b7a02c4e495a30d2ec8d25319a9e49e9cSpike Sprague                ModeListView.this.enableA11yOnModeSelectorItems();
715a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                mModeListOpenFactor = 1;
71626e901ed46054fb6cc2cb4c09bef3a779a1cbf78Alan Newberger                mCurrentStateManager.setCurrentState(new FullyHiddenState());
717a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                return;
7186c5a5375524efe7e5160f92edcaad121c7fbdb07Doris Liu            }
719a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
720a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            // If the animation was canceled before it's finished, animate the mode
721a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            // list open factor from 0 to 1 to ensure a smooth visual transition.
722a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            final ValueAnimator openFactorAnimator = ValueAnimator.ofFloat(mModeListOpenFactor, 1f);
723a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            openFactorAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
724a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                @Override
725a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                public void onAnimationUpdate(ValueAnimator animation) {
726a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                    mModeListOpenFactor = (Float) openFactorAnimator.getAnimatedValue();
727a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                    onVisibleWidthChanged(mVisibleWidth);
728a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                }
729a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            });
730a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            openFactorAnimator.addListener(new Animator.AnimatorListener() {
731a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                @Override
732a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                public void onAnimationStart(Animator animation) {
733a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                    // Do nothing.
734a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                }
735a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
736a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                @Override
737a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                public void onAnimationEnd(Animator animation) {
738a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                    mModeListOpenFactor = 1f;
739a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                }
740a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
741a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                @Override
742a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                public void onAnimationCancel(Animator animation) {
743a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                    // Do nothing.
744a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                }
745a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
746a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                @Override
747a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                public void onAnimationRepeat(Animator animation) {
748a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                    // Do nothing.
749a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                }
750a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            });
751a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            openFactorAnimator.start();
7526c5a5375524efe7e5160f92edcaad121c7fbdb07Doris Liu        }
7536c5a5375524efe7e5160f92edcaad121c7fbdb07Doris Liu
7546c5a5375524efe7e5160f92edcaad121c7fbdb07Doris Liu        @Override
755a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        public void hide() {
756a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            cancelAnimation();
75726e901ed46054fb6cc2cb4c09bef3a779a1cbf78Alan Newberger            mCurrentStateManager.setCurrentState(new FullyHiddenState());
75826e901ed46054fb6cc2cb4c09bef3a779a1cbf78Alan Newberger        }
75926e901ed46054fb6cc2cb4c09bef3a779a1cbf78Alan Newberger
7604c96d840aeff669011a9e8697cbac77fe8f4eda2Spike Sprague        @Override
7614c96d840aeff669011a9e8697cbac77fe8f4eda2Spike Sprague        public void hideAnimated() {
7624c96d840aeff669011a9e8697cbac77fe8f4eda2Spike Sprague            cancelAnimation();
7634c96d840aeff669011a9e8697cbac77fe8f4eda2Spike Sprague            animateListToWidth(0).addListener(new AnimatorListenerAdapter() {
7644c96d840aeff669011a9e8697cbac77fe8f4eda2Spike Sprague                @Override
7654c96d840aeff669011a9e8697cbac77fe8f4eda2Spike Sprague                public void onAnimationEnd(Animator animation) {
7664c96d840aeff669011a9e8697cbac77fe8f4eda2Spike Sprague                    mCurrentStateManager.setCurrentState(new FullyHiddenState());
7674c96d840aeff669011a9e8697cbac77fe8f4eda2Spike Sprague                }
7684c96d840aeff669011a9e8697cbac77fe8f4eda2Spike Sprague            });
7694c96d840aeff669011a9e8697cbac77fe8f4eda2Spike Sprague        }
770a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu    }
771a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
772a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu    /**
773a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu     * When the mode list is being scrolled, it will be in ScrollingState. From
774a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu     * this state, the mode list could transition to fully hidden, fully open
775a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu     * depending on which direction the scrolling goes.
776a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu     */
777a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu    private class ScrollingState extends ModeListState {
778a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        private Animator mAnimator = null;
779a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
780a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        public ScrollingState() {
781a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            setVisibility(VISIBLE);
7826c5a5375524efe7e5160f92edcaad121c7fbdb07Doris Liu        }
7836c5a5375524efe7e5160f92edcaad121c7fbdb07Doris Liu
7846c5a5375524efe7e5160f92edcaad121c7fbdb07Doris Liu        @Override
785a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
786a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            // Scroll based on the scrolling distance on the currently focused
787a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            // item.
788a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            scroll(mFocusItem, distanceX * SCROLL_FACTOR,
789a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                    distanceY * SCROLL_FACTOR);
790a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            return true;
791a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        }
7926c5a5375524efe7e5160f92edcaad121c7fbdb07Doris Liu
793a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        @Override
794a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        public boolean shouldHandleTouchEvent(MotionEvent ev) {
795a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            // If the snap back/to full screen animation is on going, ignore any
796a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            // touch.
797a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            if (mAnimator != null) {
798a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                return false;
799a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            }
800a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            return true;
8016c5a5375524efe7e5160f92edcaad121c7fbdb07Doris Liu        }
80269cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu
803a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        @Override
804a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        public boolean onTouchEvent(MotionEvent ev) {
805a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            if (ev.getActionMasked() == MotionEvent.ACTION_UP ||
806a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                    ev.getActionMasked() == MotionEvent.ACTION_CANCEL) {
807a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                final boolean shouldSnapBack = shouldSnapBack();
808a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                if (shouldSnapBack) {
809a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                    mAnimator = snapBack();
810a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                } else {
811a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                    mAnimator = snapToFullScreen();
812a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                }
813a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                mAnimator.addListener(new Animator.AnimatorListener() {
814a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                    @Override
815a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                    public void onAnimationStart(Animator animation) {
816a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
817a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                    }
818a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
819a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                    @Override
820a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                    public void onAnimationEnd(Animator animation) {
821a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                        mAnimator = null;
822a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                        mFocusItem = NO_ITEM_SELECTED;
823a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                        if (shouldSnapBack) {
82426e901ed46054fb6cc2cb4c09bef3a779a1cbf78Alan Newberger                            mCurrentStateManager.setCurrentState(new FullyHiddenState());
825a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                        } else {
82626e901ed46054fb6cc2cb4c09bef3a779a1cbf78Alan Newberger                            mCurrentStateManager.setCurrentState(new FullyShownState());
82710c5816e1ff3d30e4c4996643ba05d63d522b81aAndy Huibers                            UsageStatistics.instance().controlUsed(
82810c5816e1ff3d30e4c4996643ba05d63d522b81aAndy Huibers                                    eventprotos.ControlEvent.ControlType.MENU_FULL_FROM_SCROLL);
829a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                        }
830a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                    }
831a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
832a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                    @Override
833a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                    public void onAnimationCancel(Animator animation) {
834a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
835a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                    }
836a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
837a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                    @Override
838a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                    public void onAnimationRepeat(Animator animation) {
839a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
840a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                    }
841a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                });
842a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            }
843a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            return true;
844a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        }
845a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu    }
846a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
847a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu    /**
848a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu     * Mode list gets in this state when a mode item has been selected/clicked.
849a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu     * There will be an animation with the blurred preview fading in, a potential
850a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu     * pause to wait for the new mode to be ready, and then the new mode will
851a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu     * be revealed through a pinhole animation. After all the animations finish,
852a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu     * mode list will transition into fully hidden state.
853a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu     */
8543c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague    private class SelectedState extends ModeListState {
855a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        public SelectedState(ModeSelectorItem selectedItem) {
856a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            final int modeId = selectedItem.getModeId();
857a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            // Un-highlight all the modes.
858a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            for (int i = 0; i < mModeSelectorItems.length; i++) {
859a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                mModeSelectorItems[i].setSelected(false);
860a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            }
8610496fcaa51f88f6c753975473b971941e4090cfaSpike Sprague
862a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            PeepholeAnimationEffect effect = new PeepholeAnimationEffect();
863a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            effect.setSize(mWidth, mHeight);
864a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
865a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            // Calculate the position of the icon in the selected item, and
866a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            // start animation from that position.
867a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            int[] location = new int[2];
868a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            // Gets icon's center position in relative to the window.
869a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            selectedItem.getIconCenterLocationInWindow(location);
870a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            int iconX = location[0];
871a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            int iconY = location[1];
872a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            // Gets current view's top left position relative to the window.
873a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            getLocationInWindow(location);
874a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            // Calculate icon location relative to this view
875a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            iconX -= location[0];
876a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            iconY -= location[1];
877a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
878a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            effect.setAnimationStartingPosition(iconX, iconY);
8790496fcaa51f88f6c753975473b971941e4090cfaSpike Sprague            effect.setModeSpecificColor(selectedItem.getHighlightColor());
880a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            if (mScreenShotProvider != null) {
881a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                effect.setBackground(mScreenShotProvider
88228a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu                        .getPreviewFrame(PREVIEW_DOWN_SAMPLE_FACTOR),
88328a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu                        mCaptureLayoutHelper.getPreviewRect());
884a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                effect.setBackgroundOverlay(mScreenShotProvider.getPreviewOverlayAndControls());
885a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            }
886a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            mCurrentAnimationEffects = effect;
8870496fcaa51f88f6c753975473b971941e4090cfaSpike Sprague            effect.startFadeoutAnimation(null, selectedItem, iconX, iconY, modeId);
888a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            invalidate();
889a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        }
890a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
891a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        @Override
892a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        public boolean shouldHandleTouchEvent(MotionEvent ev) {
893a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            return false;
894a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        }
895a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
896a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        @Override
897a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        public void startModeSelectionAnimation() {
8980496fcaa51f88f6c753975473b971941e4090cfaSpike Sprague            mCurrentAnimationEffects.startAnimation(new AnimatorListenerAdapter() {
899a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                @Override
900a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                public void onAnimationEnd(Animator animation) {
901a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                    mCurrentAnimationEffects = null;
90226e901ed46054fb6cc2cb4c09bef3a779a1cbf78Alan Newberger                    mCurrentStateManager.setCurrentState(new FullyHiddenState());
903a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                }
904a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            });
90569cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu        }
906fa50f7d71a77dc730746c33fd17a570d42453759Doris Liu
907fa50f7d71a77dc730746c33fd17a570d42453759Doris Liu        @Override
908fa50f7d71a77dc730746c33fd17a570d42453759Doris Liu        public void hide() {
909fa50f7d71a77dc730746c33fd17a570d42453759Doris Liu            if (!mCurrentAnimationEffects.cancelAnimation()) {
910fa50f7d71a77dc730746c33fd17a570d42453759Doris Liu                mCurrentAnimationEffects = null;
911fa50f7d71a77dc730746c33fd17a570d42453759Doris Liu                mCurrentStateManager.setCurrentState(new FullyHiddenState());
912fa50f7d71a77dc730746c33fd17a570d42453759Doris Liu            }
913fa50f7d71a77dc730746c33fd17a570d42453759Doris Liu        }
91469cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu    }
9151c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu
9161c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu    public interface ModeSwitchListener {
917427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu        public void onModeButtonPressed(int modeIndex);
9181c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu        public void onModeSelected(int modeIndex);
919213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu        public int getCurrentModeIndex();
9209d264309a341c52601bdccdeff6647e0ce58c31fDoris Liu        public void onSettingsSelected();
9211c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu    }
9221c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu
9232c559c4bd3c65e0cb0bca06f30e3bc979038e6f7Doris Liu    public interface ModeListOpenListener {
9249d264309a341c52601bdccdeff6647e0ce58c31fDoris Liu        /**
9259d264309a341c52601bdccdeff6647e0ce58c31fDoris Liu         * Mode list will open to full screen after current animation.
9269d264309a341c52601bdccdeff6647e0ce58c31fDoris Liu         */
9272c559c4bd3c65e0cb0bca06f30e3bc979038e6f7Doris Liu        public void onOpenFullScreen();
9289d264309a341c52601bdccdeff6647e0ce58c31fDoris Liu
9299d264309a341c52601bdccdeff6647e0ce58c31fDoris Liu        /**
9309d264309a341c52601bdccdeff6647e0ce58c31fDoris Liu         * Updates the listener with the current progress of mode drawer opening.
9319d264309a341c52601bdccdeff6647e0ce58c31fDoris Liu         *
9329d264309a341c52601bdccdeff6647e0ce58c31fDoris Liu         * @param progress progress of the mode drawer opening, ranging [0f, 1f]
9339d264309a341c52601bdccdeff6647e0ce58c31fDoris Liu         *                 0 means mode drawer is fully closed, 1 indicates a fully
9349d264309a341c52601bdccdeff6647e0ce58c31fDoris Liu         *                 open mode drawer.
9359d264309a341c52601bdccdeff6647e0ce58c31fDoris Liu         */
9369d264309a341c52601bdccdeff6647e0ce58c31fDoris Liu        public void onModeListOpenProgress(float progress);
9379d264309a341c52601bdccdeff6647e0ce58c31fDoris Liu
9389d264309a341c52601bdccdeff6647e0ce58c31fDoris Liu        /**
9399d264309a341c52601bdccdeff6647e0ce58c31fDoris Liu         * Gets called when mode list is completely closed.
9409d264309a341c52601bdccdeff6647e0ce58c31fDoris Liu         */
9419d264309a341c52601bdccdeff6647e0ce58c31fDoris Liu        public void onModeListClosed();
9422c559c4bd3c65e0cb0bca06f30e3bc979038e6f7Doris Liu    }
9432c559c4bd3c65e0cb0bca06f30e3bc979038e6f7Doris Liu
9448c1a922df154182b53f00c38198db7da0b4feb20Sascha Haeberling    public static abstract class ModeListVisibilityChangedListener {
945b49ab283fe6fce242901a1a09fc2b148b27fe123Sascha Haeberling        private Boolean mCurrentVisibility = null;
9468c1a922df154182b53f00c38198db7da0b4feb20Sascha Haeberling
9478c1a922df154182b53f00c38198db7da0b4feb20Sascha Haeberling        /** Whether the mode list is (partially or fully) visible. */
9488c1a922df154182b53f00c38198db7da0b4feb20Sascha Haeberling        public abstract void onVisibilityChanged(boolean visible);
9498c1a922df154182b53f00c38198db7da0b4feb20Sascha Haeberling
9508c1a922df154182b53f00c38198db7da0b4feb20Sascha Haeberling        /**
9518c1a922df154182b53f00c38198db7da0b4feb20Sascha Haeberling         * Internal method to be called by the mode list whenever a visibility
9528c1a922df154182b53f00c38198db7da0b4feb20Sascha Haeberling         * even occurs.
9538c1a922df154182b53f00c38198db7da0b4feb20Sascha Haeberling         * <p>
9548c1a922df154182b53f00c38198db7da0b4feb20Sascha Haeberling         * Do not call {@link #onVisibilityChanged(boolean)} directly, as this
9558c1a922df154182b53f00c38198db7da0b4feb20Sascha Haeberling         * is only called when the visibility has actually changed and not on
9568c1a922df154182b53f00c38198db7da0b4feb20Sascha Haeberling         * each visibility event.
9578c1a922df154182b53f00c38198db7da0b4feb20Sascha Haeberling         *
9588c1a922df154182b53f00c38198db7da0b4feb20Sascha Haeberling         * @param visible whether the mode drawer is currently visible.
9598c1a922df154182b53f00c38198db7da0b4feb20Sascha Haeberling         */
9608c1a922df154182b53f00c38198db7da0b4feb20Sascha Haeberling        private void onVisibilityEvent(boolean visible) {
961b49ab283fe6fce242901a1a09fc2b148b27fe123Sascha Haeberling            if (mCurrentVisibility == null || mCurrentVisibility != visible) {
962b49ab283fe6fce242901a1a09fc2b148b27fe123Sascha Haeberling                mCurrentVisibility = visible;
9638c1a922df154182b53f00c38198db7da0b4feb20Sascha Haeberling                onVisibilityChanged(visible);
964b49ab283fe6fce242901a1a09fc2b148b27fe123Sascha Haeberling            }
9658c1a922df154182b53f00c38198db7da0b4feb20Sascha Haeberling        }
9668c1a922df154182b53f00c38198db7da0b4feb20Sascha Haeberling    }
9678c1a922df154182b53f00c38198db7da0b4feb20Sascha Haeberling
96870576b611fddc929abbfb466fa44c396a3a793dbDoris Liu    /**
969cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu     * This class aims to help store time and position in pairs.
970cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu     */
971cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu    private static class TimeBasedPosition {
972601921e746832bdef40503c606a95b4469f1bedbSascha Haeberling        private final float mPosition;
973601921e746832bdef40503c606a95b4469f1bedbSascha Haeberling        private final long mTimeStamp;
974cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu        public TimeBasedPosition(float position, long time) {
975cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu            mPosition = position;
976cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu            mTimeStamp = time;
977cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu        }
978cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu
979cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu        public float getPosition() {
980cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu            return mPosition;
981cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu        }
982cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu
983cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu        public long getTimeStamp() {
984cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu            return mTimeStamp;
985cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu        }
986cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu    }
987cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu
988cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu    /**
98970576b611fddc929abbfb466fa44c396a3a793dbDoris Liu     * This is a highly customized interpolator. The purpose of having this subclass
99070576b611fddc929abbfb466fa44c396a3a793dbDoris Liu     * is to encapsulate intricate animation timing, so that the actual animation
99170576b611fddc929abbfb466fa44c396a3a793dbDoris Liu     * implementation can be re-used with other interpolators to achieve different
99270576b611fddc929abbfb466fa44c396a3a793dbDoris Liu     * animation effects.
99370576b611fddc929abbfb466fa44c396a3a793dbDoris Liu     *
99470576b611fddc929abbfb466fa44c396a3a793dbDoris Liu     * The accordion animation consists of three stages:
99570576b611fddc929abbfb466fa44c396a3a793dbDoris Liu     * 1) Animate into the screen within a pre-specified fly in duration.
99670576b611fddc929abbfb466fa44c396a3a793dbDoris Liu     * 2) Hold in place for a certain amount of time (Optional).
99770576b611fddc929abbfb466fa44c396a3a793dbDoris Liu     * 3) Animate out of the screen within the given time.
99870576b611fddc929abbfb466fa44c396a3a793dbDoris Liu     *
99970576b611fddc929abbfb466fa44c396a3a793dbDoris Liu     * The accordion animator is initialized with 3 parameter: 1) initial position,
100070576b611fddc929abbfb466fa44c396a3a793dbDoris Liu     * 2) how far out the view should be before flying back out,  3) end position.
100170576b611fddc929abbfb466fa44c396a3a793dbDoris Liu     * The interpolation output should be [0f, 0.5f] during animation between 1)
100270576b611fddc929abbfb466fa44c396a3a793dbDoris Liu     * to 2), and [0.5f, 1f] for flying from 2) to 3).
100370576b611fddc929abbfb466fa44c396a3a793dbDoris Liu     */
1004601921e746832bdef40503c606a95b4469f1bedbSascha Haeberling    private final TimeInterpolator mAccordionInterpolator = new TimeInterpolator() {
10051c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu        @Override
10061c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu        public float getInterpolation(float input) {
10071c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu
10081c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu            float flyInDuration = (float) FLY_OUT_DURATION_MS / (float) TOTAL_DURATION_MS;
10091c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu            float holdDuration = (float) (FLY_OUT_DURATION_MS + HOLD_DURATION_MS)
10101c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu                    / (float) TOTAL_DURATION_MS;
10111c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu            if (input == 0) {
10121c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu                return 0;
101369cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu            } else if (input < flyInDuration) {
101470576b611fddc929abbfb466fa44c396a3a793dbDoris Liu                // Stage 1, project result to [0f, 0.5f]
10151c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu                input /= flyInDuration;
10161c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu                float result = Gusterpolator.INSTANCE.getInterpolation(input);
10171c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu                return result * 0.5f;
10181c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu            } else if (input < holdDuration) {
101970576b611fddc929abbfb466fa44c396a3a793dbDoris Liu                // Stage 2
10201c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu                return 0.5f;
10211c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu            } else {
102270576b611fddc929abbfb466fa44c396a3a793dbDoris Liu                // Stage 3, project result to [0.5f, 1f]
10231c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu                input -= holdDuration;
10241c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu                input /= (1 - holdDuration);
10251c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu                float result = Gusterpolator.INSTANCE.getInterpolation(input);
10261c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu                return 0.5f + result * 0.5f;
10271c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu            }
10281c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu        }
10291c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu    };
10301c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu
103170576b611fddc929abbfb466fa44c396a3a793dbDoris Liu    /**
103270576b611fddc929abbfb466fa44c396a3a793dbDoris Liu     * The listener that is used to notify when gestures occur.
103370576b611fddc929abbfb466fa44c396a3a793dbDoris Liu     * Here we only listen to a subset of gestures.
103470576b611fddc929abbfb466fa44c396a3a793dbDoris Liu     */
1035601921e746832bdef40503c606a95b4469f1bedbSascha Haeberling    private final GestureDetector.OnGestureListener mOnGestureListener
10361c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu            = new GestureDetector.SimpleOnGestureListener(){
103770576b611fddc929abbfb466fa44c396a3a793dbDoris Liu        @Override
10381c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu        public boolean onScroll(MotionEvent e1, MotionEvent e2,
10391c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu                                float distanceX, float distanceY) {
104026e901ed46054fb6cc2cb4c09bef3a779a1cbf78Alan Newberger            mCurrentStateManager.getCurrentState().onScroll(e1, e2, distanceX, distanceY);
104169cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu            mLastScrollTime = System.currentTimeMillis();
10421c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu            return true;
10431c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu        }
10441c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu
10451c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu        @Override
10461c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu        public boolean onSingleTapUp(MotionEvent ev) {
104726e901ed46054fb6cc2cb4c09bef3a779a1cbf78Alan Newberger            mCurrentStateManager.getCurrentState().onSingleTapUp(ev);
10481c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu            return true;
10491c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu        }
10506c5a5375524efe7e5160f92edcaad121c7fbdb07Doris Liu
10516c5a5375524efe7e5160f92edcaad121c7fbdb07Doris Liu        @Override
10526c5a5375524efe7e5160f92edcaad121c7fbdb07Doris Liu        public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
10536c5a5375524efe7e5160f92edcaad121c7fbdb07Doris Liu            // Cache velocity in the unit pixel/ms.
1054213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu            mVelocityX = velocityX / 1000f * SCROLL_FACTOR;
105526e901ed46054fb6cc2cb4c09bef3a779a1cbf78Alan Newberger            mCurrentStateManager.getCurrentState().onFling(e1, e2, velocityX, velocityY);
1056a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            return true;
1057a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        }
1058a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
1059a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        @Override
1060a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        public boolean onDown(MotionEvent ev) {
1061a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            mVelocityX = 0;
106226e901ed46054fb6cc2cb4c09bef3a779a1cbf78Alan Newberger            mCurrentStateManager.getCurrentState().onDown(ev);
10636c5a5375524efe7e5160f92edcaad121c7fbdb07Doris Liu            return true;
10646c5a5375524efe7e5160f92edcaad121c7fbdb07Doris Liu        }
10651c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu    };
10661c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu
10676ababe9379ecee4056a415085e28e7eb5e1249bfDoris Liu    /**
10686ababe9379ecee4056a415085e28e7eb5e1249bfDoris Liu     * Gets called when a mode item in the mode drawer is clicked.
10696ababe9379ecee4056a415085e28e7eb5e1249bfDoris Liu     *
10706ababe9379ecee4056a415085e28e7eb5e1249bfDoris Liu     * @param selectedItem the item being clicked
10716ababe9379ecee4056a415085e28e7eb5e1249bfDoris Liu     */
10726ababe9379ecee4056a415085e28e7eb5e1249bfDoris Liu    private void onItemSelected(ModeSelectorItem selectedItem) {
1073427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu        int modeId = selectedItem.getModeId();
1074427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu        mModeSwitchListener.onModeButtonPressed(modeId);
1075427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu
107626e901ed46054fb6cc2cb4c09bef3a779a1cbf78Alan Newberger        mCurrentStateManager.getCurrentState().onItemSelected(selectedItem);
10776ababe9379ecee4056a415085e28e7eb5e1249bfDoris Liu    }
10786ababe9379ecee4056a415085e28e7eb5e1249bfDoris Liu
10796ababe9379ecee4056a415085e28e7eb5e1249bfDoris Liu    /**
10806ababe9379ecee4056a415085e28e7eb5e1249bfDoris Liu     * Checks whether a touch event is inside of the bounds of the mode list.
10816ababe9379ecee4056a415085e28e7eb5e1249bfDoris Liu     *
10826ababe9379ecee4056a415085e28e7eb5e1249bfDoris Liu     * @param ev touch event to be checked
10836ababe9379ecee4056a415085e28e7eb5e1249bfDoris Liu     * @return whether the touch is inside the bounds of the mode list
10846ababe9379ecee4056a415085e28e7eb5e1249bfDoris Liu     */
10856ababe9379ecee4056a415085e28e7eb5e1249bfDoris Liu    private boolean isTouchInsideList(MotionEvent ev) {
10866ababe9379ecee4056a415085e28e7eb5e1249bfDoris Liu        // Ignore the tap if it happens outside of the mode list linear layout.
10876ababe9379ecee4056a415085e28e7eb5e1249bfDoris Liu        float x = ev.getX() - mListView.getX();
10886ababe9379ecee4056a415085e28e7eb5e1249bfDoris Liu        float y = ev.getY() - mListView.getY();
10896ababe9379ecee4056a415085e28e7eb5e1249bfDoris Liu        if (x < 0 || x > mListView.getWidth() || y < 0 || y > mListView.getHeight()) {
10906ababe9379ecee4056a415085e28e7eb5e1249bfDoris Liu            return false;
10916ababe9379ecee4056a415085e28e7eb5e1249bfDoris Liu        }
10926ababe9379ecee4056a415085e28e7eb5e1249bfDoris Liu        return true;
10936ababe9379ecee4056a415085e28e7eb5e1249bfDoris Liu    }
10946ababe9379ecee4056a415085e28e7eb5e1249bfDoris Liu
10951c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu    public ModeListView(Context context, AttributeSet attrs) {
10961c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu        super(context, attrs);
10971c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu        mGestureDetector = new GestureDetector(context, mOnGestureListener);
109870576b611fddc929abbfb466fa44c396a3a793dbDoris Liu        mListBackgroundColor = getResources().getColor(R.color.mode_list_background);
109928a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu        mSettingsButtonMargin = getResources().getDimensionPixelSize(
110028a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu                R.dimen.mode_list_settings_icon_margin);
1101e7e0c028d568c1a4c3e88ed75411b8b41c5aec69Doris Liu    }
1102e7e0c028d568c1a4c3e88ed75411b8b41c5aec69Doris Liu
1103c064750b7a02c4e495a30d2ec8d25319a9e49e9cSpike Sprague    private void disableA11yOnModeSelectorItems() {
1104c064750b7a02c4e495a30d2ec8d25319a9e49e9cSpike Sprague        for (View selectorItem : mModeSelectorItems) {
1105c064750b7a02c4e495a30d2ec8d25319a9e49e9cSpike Sprague            selectorItem.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
1106c064750b7a02c4e495a30d2ec8d25319a9e49e9cSpike Sprague        }
1107c064750b7a02c4e495a30d2ec8d25319a9e49e9cSpike Sprague    }
1108c064750b7a02c4e495a30d2ec8d25319a9e49e9cSpike Sprague
1109c064750b7a02c4e495a30d2ec8d25319a9e49e9cSpike Sprague    private void enableA11yOnModeSelectorItems() {
1110c064750b7a02c4e495a30d2ec8d25319a9e49e9cSpike Sprague        for (View selectorItem : mModeSelectorItems) {
1111c064750b7a02c4e495a30d2ec8d25319a9e49e9cSpike Sprague            selectorItem.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_AUTO);
1112c064750b7a02c4e495a30d2ec8d25319a9e49e9cSpike Sprague        }
1113c064750b7a02c4e495a30d2ec8d25319a9e49e9cSpike Sprague    }
1114c064750b7a02c4e495a30d2ec8d25319a9e49e9cSpike Sprague
111570576b611fddc929abbfb466fa44c396a3a793dbDoris Liu    /**
111670576b611fddc929abbfb466fa44c396a3a793dbDoris Liu     * Sets the alpha on the list background. This is called whenever the list
111770576b611fddc929abbfb466fa44c396a3a793dbDoris Liu     * is scrolling or animating, so that background can adjust its dimness.
111870576b611fddc929abbfb466fa44c396a3a793dbDoris Liu     *
111970576b611fddc929abbfb466fa44c396a3a793dbDoris Liu     * @param alpha new alpha to be applied on list background color
112070576b611fddc929abbfb466fa44c396a3a793dbDoris Liu     */
112170576b611fddc929abbfb466fa44c396a3a793dbDoris Liu    private void setBackgroundAlpha(int alpha) {
112270576b611fddc929abbfb466fa44c396a3a793dbDoris Liu        // Make sure alpha is valid.
112370576b611fddc929abbfb466fa44c396a3a793dbDoris Liu        alpha = alpha & 0xFF;
112470576b611fddc929abbfb466fa44c396a3a793dbDoris Liu        // Change alpha on the background color.
112570576b611fddc929abbfb466fa44c396a3a793dbDoris Liu        mListBackgroundColor = mListBackgroundColor & 0xFFFFFF;
112670576b611fddc929abbfb466fa44c396a3a793dbDoris Liu        mListBackgroundColor = mListBackgroundColor | (alpha << 24);
112770576b611fddc929abbfb466fa44c396a3a793dbDoris Liu        // Set new color to list background.
1128213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu        setBackgroundColor(mListBackgroundColor);
11291c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu    }
11301c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu
1131a20074f0efeaa62a29b05838110d04a1b7271418Doris Liu    /**
1132a20074f0efeaa62a29b05838110d04a1b7271418Doris Liu     * Initialize mode list with a list of indices of supported modes.
1133a20074f0efeaa62a29b05838110d04a1b7271418Doris Liu     *
1134a20074f0efeaa62a29b05838110d04a1b7271418Doris Liu     * @param modeIndexList a list of indices of supported modes
1135a20074f0efeaa62a29b05838110d04a1b7271418Doris Liu     */
1136a20074f0efeaa62a29b05838110d04a1b7271418Doris Liu    public void init(List<Integer> modeIndexList) {
1137bd1b8f910d6f2c94daac304f4bd1a098939474c3Doris Liu        int[] modeSequence = getResources()
1138bd1b8f910d6f2c94daac304f4bd1a098939474c3Doris Liu                .getIntArray(R.array.camera_modes_in_nav_drawer_if_supported);
1139bd1b8f910d6f2c94daac304f4bd1a098939474c3Doris Liu        int[] visibleModes = getResources()
1140bd1b8f910d6f2c94daac304f4bd1a098939474c3Doris Liu                .getIntArray(R.array.camera_modes_always_visible);
1141a20074f0efeaa62a29b05838110d04a1b7271418Doris Liu
1142a20074f0efeaa62a29b05838110d04a1b7271418Doris Liu        // Mark the supported modes in a boolean array to preserve the
1143a20074f0efeaa62a29b05838110d04a1b7271418Doris Liu        // sequence of the modes
11440496fcaa51f88f6c753975473b971941e4090cfaSpike Sprague        SparseBooleanArray modeIsSupported = new SparseBooleanArray();
1145a20074f0efeaa62a29b05838110d04a1b7271418Doris Liu        for (int i = 0; i < modeIndexList.size(); i++) {
1146a20074f0efeaa62a29b05838110d04a1b7271418Doris Liu            int mode = modeIndexList.get(i);
1147bd1b8f910d6f2c94daac304f4bd1a098939474c3Doris Liu            modeIsSupported.put(mode, true);
1148a20074f0efeaa62a29b05838110d04a1b7271418Doris Liu        }
1149bd1b8f910d6f2c94daac304f4bd1a098939474c3Doris Liu        for (int i = 0; i < visibleModes.length; i++) {
1150bd1b8f910d6f2c94daac304f4bd1a098939474c3Doris Liu            int mode = visibleModes[i];
1151bd1b8f910d6f2c94daac304f4bd1a098939474c3Doris Liu            modeIsSupported.put(mode, true);
1152bd1b8f910d6f2c94daac304f4bd1a098939474c3Doris Liu        }
1153bd1b8f910d6f2c94daac304f4bd1a098939474c3Doris Liu
1154a20074f0efeaa62a29b05838110d04a1b7271418Doris Liu        // Put the indices of supported modes into an array preserving their
1155a20074f0efeaa62a29b05838110d04a1b7271418Doris Liu        // display order.
1156bd1b8f910d6f2c94daac304f4bd1a098939474c3Doris Liu        mSupportedModes = new ArrayList<Integer>();
1157bd1b8f910d6f2c94daac304f4bd1a098939474c3Doris Liu        for (int i = 0; i < modeSequence.length; i++) {
1158bd1b8f910d6f2c94daac304f4bd1a098939474c3Doris Liu            int mode = modeSequence[i];
1159bd1b8f910d6f2c94daac304f4bd1a098939474c3Doris Liu            if (modeIsSupported.get(mode, false)) {
1160bd1b8f910d6f2c94daac304f4bd1a098939474c3Doris Liu                mSupportedModes.add(mode);
1161a20074f0efeaa62a29b05838110d04a1b7271418Doris Liu            }
1162a20074f0efeaa62a29b05838110d04a1b7271418Doris Liu        }
1163bd1b8f910d6f2c94daac304f4bd1a098939474c3Doris Liu        mTotalModes = mSupportedModes.size();
1164a20074f0efeaa62a29b05838110d04a1b7271418Doris Liu        initializeModeSelectorItems();
116528a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu        mSettingsButton = findViewById(R.id.settings_button);
11669d264309a341c52601bdccdeff6647e0ce58c31fDoris Liu        mSettingsButton.setOnClickListener(new OnClickListener() {
11679d264309a341c52601bdccdeff6647e0ce58c31fDoris Liu            @Override
11689d264309a341c52601bdccdeff6647e0ce58c31fDoris Liu            public void onClick(View v) {
11695842607d1b9ec2b5e751a8aaf43ff97cc3f85d22Doris Liu                // Post this callback to make sure current user interaction has
11705842607d1b9ec2b5e751a8aaf43ff97cc3f85d22Doris Liu                // been reflected in the UI. Specifically, the pressed state gets
11715842607d1b9ec2b5e751a8aaf43ff97cc3f85d22Doris Liu                // unset after click happens. In order to ensure the pressed state
11725842607d1b9ec2b5e751a8aaf43ff97cc3f85d22Doris Liu                // gets unset in UI before getting in the low frame rate settings
11735842607d1b9ec2b5e751a8aaf43ff97cc3f85d22Doris Liu                // activity launch stage, the settings selected callback is posted.
11745842607d1b9ec2b5e751a8aaf43ff97cc3f85d22Doris Liu                post(new Runnable() {
11755842607d1b9ec2b5e751a8aaf43ff97cc3f85d22Doris Liu                    @Override
11765842607d1b9ec2b5e751a8aaf43ff97cc3f85d22Doris Liu                    public void run() {
11775842607d1b9ec2b5e751a8aaf43ff97cc3f85d22Doris Liu                        mModeSwitchListener.onSettingsSelected();
11785842607d1b9ec2b5e751a8aaf43ff97cc3f85d22Doris Liu                    }
11795842607d1b9ec2b5e751a8aaf43ff97cc3f85d22Doris Liu                });
11809d264309a341c52601bdccdeff6647e0ce58c31fDoris Liu            }
11819d264309a341c52601bdccdeff6647e0ce58c31fDoris Liu        });
11828676e7802fb40f64a3b465c9b5e1a0a1ada9f699Doris Liu        // The mode list is initialized to be all the way closed.
11838676e7802fb40f64a3b465c9b5e1a0a1ada9f699Doris Liu        onModeListOpenRatioUpdate(0);
118426e901ed46054fb6cc2cb4c09bef3a779a1cbf78Alan Newberger        if (mCurrentStateManager.getCurrentState() == null) {
118526e901ed46054fb6cc2cb4c09bef3a779a1cbf78Alan Newberger            mCurrentStateManager.setCurrentState(new FullyHiddenState());
1186a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        }
1187a20074f0efeaa62a29b05838110d04a1b7271418Doris Liu    }
1188a20074f0efeaa62a29b05838110d04a1b7271418Doris Liu
1189213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu    /**
1190213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu     * Sets the screen shot provider for getting a preview frame and a bitmap
1191213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu     * of the controls and overlay.
1192213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu     */
1193213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu    public void setCameraModuleScreenShotProvider(
1194213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu            CameraAppUI.CameraModuleScreenShotProvider provider) {
1195213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu        mScreenShotProvider = provider;
1196213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu    }
1197213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu
1198a20074f0efeaa62a29b05838110d04a1b7271418Doris Liu    private void initializeModeSelectorItems() {
1199a20074f0efeaa62a29b05838110d04a1b7271418Doris Liu        mModeSelectorItems = new ModeSelectorItem[mTotalModes];
120070576b611fddc929abbfb466fa44c396a3a793dbDoris Liu        // Inflate the mode selector items and add them to a linear layout
12018be316c7a8caf962cf3fcf5e49d332fb2718319fPaul Rohde        LayoutInflater inflater = AndroidServices.instance().provideLayoutInflater();
12021c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu        mListView = (LinearLayout) findViewById(R.id.mode_list);
12031c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu        for (int i = 0; i < mTotalModes; i++) {
12046ababe9379ecee4056a415085e28e7eb5e1249bfDoris Liu            final ModeSelectorItem selectorItem =
12051c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu                    (ModeSelectorItem) inflater.inflate(R.layout.mode_selector, null);
12061c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu            mListView.addView(selectorItem);
1207e7e0c028d568c1a4c3e88ed75411b8b41c5aec69Doris Liu            // Sets the top padding of the top item to 0.
1208e7e0c028d568c1a4c3e88ed75411b8b41c5aec69Doris Liu            if (i == 0) {
1209e7e0c028d568c1a4c3e88ed75411b8b41c5aec69Doris Liu                selectorItem.setPadding(selectorItem.getPaddingLeft(), 0,
1210e7e0c028d568c1a4c3e88ed75411b8b41c5aec69Doris Liu                        selectorItem.getPaddingRight(), selectorItem.getPaddingBottom());
1211e7e0c028d568c1a4c3e88ed75411b8b41c5aec69Doris Liu            }
1212e7e0c028d568c1a4c3e88ed75411b8b41c5aec69Doris Liu            // Sets the bottom padding of the bottom item to 0.
1213e7e0c028d568c1a4c3e88ed75411b8b41c5aec69Doris Liu            if (i == mTotalModes - 1) {
1214e7e0c028d568c1a4c3e88ed75411b8b41c5aec69Doris Liu                selectorItem.setPadding(selectorItem.getPaddingLeft(), selectorItem.getPaddingTop(),
1215e7e0c028d568c1a4c3e88ed75411b8b41c5aec69Doris Liu                        selectorItem.getPaddingRight(), 0);
1216e7e0c028d568c1a4c3e88ed75411b8b41c5aec69Doris Liu            }
1217213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu
1218a20074f0efeaa62a29b05838110d04a1b7271418Doris Liu            int modeId = getModeIndex(i);
1219213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu            selectorItem.setHighlightColor(getResources()
1220bd1b8f910d6f2c94daac304f4bd1a098939474c3Doris Liu                    .getColor(CameraUtil.getCameraThemeColorId(modeId, getContext())));
12211c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu
12221c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu            // Set image
1223bd1b8f910d6f2c94daac304f4bd1a098939474c3Doris Liu            selectorItem.setImageResource(CameraUtil.getCameraModeIconResId(modeId, getContext()));
122470576b611fddc929abbfb466fa44c396a3a793dbDoris Liu
12251c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu            // Set text
1226bd1b8f910d6f2c94daac304f4bd1a098939474c3Doris Liu            selectorItem.setText(CameraUtil.getCameraModeText(modeId, getContext()));
122779718f6c6fb60aedd09edeedb2c16d1e2f9fe02dSpike Sprague
122879718f6c6fb60aedd09edeedb2c16d1e2f9fe02dSpike Sprague            // Set content description (for a11y)
122979718f6c6fb60aedd09edeedb2c16d1e2f9fe02dSpike Sprague            selectorItem.setContentDescription(CameraUtil
123079718f6c6fb60aedd09edeedb2c16d1e2f9fe02dSpike Sprague                    .getCameraModeContentDescription(modeId, getContext()));
12316ababe9379ecee4056a415085e28e7eb5e1249bfDoris Liu            selectorItem.setModeId(modeId);
12326ababe9379ecee4056a415085e28e7eb5e1249bfDoris Liu            selectorItem.setOnClickListener(new OnClickListener() {
12336ababe9379ecee4056a415085e28e7eb5e1249bfDoris Liu                @Override
12346ababe9379ecee4056a415085e28e7eb5e1249bfDoris Liu                public void onClick(View v) {
12356ababe9379ecee4056a415085e28e7eb5e1249bfDoris Liu                    onItemSelected(selectorItem);
12366ababe9379ecee4056a415085e28e7eb5e1249bfDoris Liu                }
12376ababe9379ecee4056a415085e28e7eb5e1249bfDoris Liu            });
123879718f6c6fb60aedd09edeedb2c16d1e2f9fe02dSpike Sprague
123970576b611fddc929abbfb466fa44c396a3a793dbDoris Liu            mModeSelectorItems[i] = selectorItem;
12401c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu        }
124169cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu        // During drawer opening/closing, we change the visible width of the mode
124269cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu        // items in sequence, so we listen to the last item's visible width change
124369cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu        // for a good timing to do corresponding UI adjustments.
124469cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu        mModeSelectorItems[mTotalModes - 1].setVisibleWidthChangedListener(this);
12451c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu        resetModeSelectors();
12461c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu    }
12471c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu
1248a20074f0efeaa62a29b05838110d04a1b7271418Doris Liu    /**
1249a20074f0efeaa62a29b05838110d04a1b7271418Doris Liu     * Maps between the UI mode selector index to the actual mode id.
1250a20074f0efeaa62a29b05838110d04a1b7271418Doris Liu     *
1251a20074f0efeaa62a29b05838110d04a1b7271418Doris Liu     * @param modeSelectorIndex the index of the UI item
1252a20074f0efeaa62a29b05838110d04a1b7271418Doris Liu     * @return the index of the corresponding camera mode
1253a20074f0efeaa62a29b05838110d04a1b7271418Doris Liu     */
1254a20074f0efeaa62a29b05838110d04a1b7271418Doris Liu    private int getModeIndex(int modeSelectorIndex) {
1255a20074f0efeaa62a29b05838110d04a1b7271418Doris Liu        if (modeSelectorIndex < mTotalModes && modeSelectorIndex >= 0) {
1256bd1b8f910d6f2c94daac304f4bd1a098939474c3Doris Liu            return mSupportedModes.get(modeSelectorIndex);
1257a20074f0efeaa62a29b05838110d04a1b7271418Doris Liu        }
12585596b4c902dcb685928b43678f428746ca5ffd08Angus Kong        Log.e(TAG, "Invalid mode selector index: " + modeSelectorIndex + ", total modes: " +
12595596b4c902dcb685928b43678f428746ca5ffd08Angus Kong                mTotalModes);
1260bd1b8f910d6f2c94daac304f4bd1a098939474c3Doris Liu        return getResources().getInteger(R.integer.camera_mode_photo);
1261a20074f0efeaa62a29b05838110d04a1b7271418Doris Liu    }
1262a20074f0efeaa62a29b05838110d04a1b7271418Doris Liu
126370576b611fddc929abbfb466fa44c396a3a793dbDoris Liu    /** Notify ModeSwitchListener, if any, of the mode change. */
12641c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu    private void onModeSelected(int modeIndex) {
12652c559c4bd3c65e0cb0bca06f30e3bc979038e6f7Doris Liu        if (mModeSwitchListener != null) {
12662c559c4bd3c65e0cb0bca06f30e3bc979038e6f7Doris Liu            mModeSwitchListener.onModeSelected(modeIndex);
12671c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu        }
12681c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu    }
12691c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu
12701c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu    /**
127170576b611fddc929abbfb466fa44c396a3a793dbDoris Liu     * Sets a listener that listens to receive mode switch event.
127270576b611fddc929abbfb466fa44c396a3a793dbDoris Liu     *
127370576b611fddc929abbfb466fa44c396a3a793dbDoris Liu     * @param listener a listener that gets notified when mode changes.
12741c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu     */
12751c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu    public void setModeSwitchListener(ModeSwitchListener listener) {
12762c559c4bd3c65e0cb0bca06f30e3bc979038e6f7Doris Liu        mModeSwitchListener = listener;
12772c559c4bd3c65e0cb0bca06f30e3bc979038e6f7Doris Liu    }
12782c559c4bd3c65e0cb0bca06f30e3bc979038e6f7Doris Liu
12792c559c4bd3c65e0cb0bca06f30e3bc979038e6f7Doris Liu    /**
12802c559c4bd3c65e0cb0bca06f30e3bc979038e6f7Doris Liu     * Sets a listener that gets notified when the mode list is open full screen.
12812c559c4bd3c65e0cb0bca06f30e3bc979038e6f7Doris Liu     *
12822c559c4bd3c65e0cb0bca06f30e3bc979038e6f7Doris Liu     * @param listener a listener that listens to mode list open events
12832c559c4bd3c65e0cb0bca06f30e3bc979038e6f7Doris Liu     */
12842c559c4bd3c65e0cb0bca06f30e3bc979038e6f7Doris Liu    public void setModeListOpenListener(ModeListOpenListener listener) {
12852c559c4bd3c65e0cb0bca06f30e3bc979038e6f7Doris Liu        mModeListOpenListener = listener;
12861c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu    }
12871c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu
12888c1a922df154182b53f00c38198db7da0b4feb20Sascha Haeberling    /**
12898c1a922df154182b53f00c38198db7da0b4feb20Sascha Haeberling     * Sets or replaces a listener that is called when the visibility of the
12908c1a922df154182b53f00c38198db7da0b4feb20Sascha Haeberling     * mode list changed.
12918c1a922df154182b53f00c38198db7da0b4feb20Sascha Haeberling     */
12928c1a922df154182b53f00c38198db7da0b4feb20Sascha Haeberling    public void setVisibilityChangedListener(ModeListVisibilityChangedListener listener) {
12938c1a922df154182b53f00c38198db7da0b4feb20Sascha Haeberling        mVisibilityChangedListener = listener;
12948c1a922df154182b53f00c38198db7da0b4feb20Sascha Haeberling    }
12958c1a922df154182b53f00c38198db7da0b4feb20Sascha Haeberling
12961c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu    @Override
12971c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu    public boolean onTouchEvent(MotionEvent ev) {
1298a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        // Reset touch forward recipient
1299a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        if (MotionEvent.ACTION_DOWN == ev.getActionMasked()) {
1300a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            mChildViewTouched = null;
1301b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liu        }
1302b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liu
130326e901ed46054fb6cc2cb4c09bef3a779a1cbf78Alan Newberger        if (!mCurrentStateManager.getCurrentState().shouldHandleTouchEvent(ev)) {
1304a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            return false;
130569cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu        }
13069eb517f4c4bcf00fbe435a56f89d3abcaaa4d881Doris Liu        getParent().requestDisallowInterceptTouchEvent(true);
13071c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu        super.onTouchEvent(ev);
1308908bace4c8ceb9ebf4644827350a0792cbd44331Seth Raphael
130970576b611fddc929abbfb466fa44c396a3a793dbDoris Liu        // Pass all touch events to gesture detector for gesture handling.
13101c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu        mGestureDetector.onTouchEvent(ev);
131126e901ed46054fb6cc2cb4c09bef3a779a1cbf78Alan Newberger        mCurrentStateManager.getCurrentState().onTouchEvent(ev);
13121c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu        return true;
13131c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu    }
13141c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu
13151c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu    /**
13166ababe9379ecee4056a415085e28e7eb5e1249bfDoris Liu     * Forward touch events to a recipient child view. Before feeding the motion
13176ababe9379ecee4056a415085e28e7eb5e1249bfDoris Liu     * event into the child view, the event needs to be converted in child view's
13186ababe9379ecee4056a415085e28e7eb5e1249bfDoris Liu     * coordinates.
13196ababe9379ecee4056a415085e28e7eb5e1249bfDoris Liu     */
13206ababe9379ecee4056a415085e28e7eb5e1249bfDoris Liu    private void forwardTouchEventToChild(MotionEvent ev) {
13216ababe9379ecee4056a415085e28e7eb5e1249bfDoris Liu        if (mChildViewTouched != null) {
13226ababe9379ecee4056a415085e28e7eb5e1249bfDoris Liu            float x = ev.getX() - mListView.getX();
13236ababe9379ecee4056a415085e28e7eb5e1249bfDoris Liu            float y = ev.getY() - mListView.getY();
13246ababe9379ecee4056a415085e28e7eb5e1249bfDoris Liu            x -= mChildViewTouched.getLeft();
13256ababe9379ecee4056a415085e28e7eb5e1249bfDoris Liu            y -= mChildViewTouched.getTop();
13266ababe9379ecee4056a415085e28e7eb5e1249bfDoris Liu
13276ababe9379ecee4056a415085e28e7eb5e1249bfDoris Liu            mLastChildTouchEvent = MotionEvent.obtain(ev);
13286ababe9379ecee4056a415085e28e7eb5e1249bfDoris Liu            mLastChildTouchEvent.setLocation(x, y);
13296ababe9379ecee4056a415085e28e7eb5e1249bfDoris Liu            mChildViewTouched.onTouchEvent(mLastChildTouchEvent);
13306ababe9379ecee4056a415085e28e7eb5e1249bfDoris Liu        }
13316ababe9379ecee4056a415085e28e7eb5e1249bfDoris Liu    }
13326ababe9379ecee4056a415085e28e7eb5e1249bfDoris Liu
13336ababe9379ecee4056a415085e28e7eb5e1249bfDoris Liu    /**
13341c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu     * Sets the swipe mode to indicate whether this is a swiping in
13351c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu     * or out, and therefore we can have different animations.
133670576b611fddc929abbfb466fa44c396a3a793dbDoris Liu     *
133770576b611fddc929abbfb466fa44c396a3a793dbDoris Liu     * @param swipeIn indicates whether the swipe should reveal/hide the list.
13381c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu     */
13391c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu    private void setSwipeMode(boolean swipeIn) {
134070576b611fddc929abbfb466fa44c396a3a793dbDoris Liu        for (int i = 0 ; i < mModeSelectorItems.length; i++) {
134170576b611fddc929abbfb466fa44c396a3a793dbDoris Liu            mModeSelectorItems[i].onSwipeModeChanged(swipeIn);
13421c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu        }
13431c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu    }
13441c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu
13451c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu    @Override
13461c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
13471c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu        super.onLayout(changed, left, top, right, bottom);
13481c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu        mWidth = right - left;
13491c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu        mHeight = bottom - top - getPaddingTop() - getPaddingBottom();
135028a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu
135128a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu        updateModeListLayout();
135228a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu
135326e901ed46054fb6cc2cb4c09bef3a779a1cbf78Alan Newberger        if (mCurrentStateManager.getCurrentState().getCurrentAnimationEffects() != null) {
135426e901ed46054fb6cc2cb4c09bef3a779a1cbf78Alan Newberger            mCurrentStateManager.getCurrentState().getCurrentAnimationEffects().setSize(
135526e901ed46054fb6cc2cb4c09bef3a779a1cbf78Alan Newberger                    mWidth, mHeight);
1356b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liu        }
13571c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu    }
13581c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu
135970576b611fddc929abbfb466fa44c396a3a793dbDoris Liu    /**
136028a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu     * Sets a capture layout helper to query layout rect from.
136170576b611fddc929abbfb466fa44c396a3a793dbDoris Liu     */
136228a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu    public void setCaptureLayoutHelper(CaptureLayoutHelper helper) {
136328a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu        mCaptureLayoutHelper = helper;
136428a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu    }
136528a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu
1366b908d933736a4ec042aad065b7ebf2059bbcac97Doris Liu    @Override
1367b908d933736a4ec042aad065b7ebf2059bbcac97Doris Liu    public void onPreviewAreaChanged(RectF previewArea) {
1368b908d933736a4ec042aad065b7ebf2059bbcac97Doris Liu        if (getVisibility() == View.VISIBLE && !hasWindowFocus()) {
1369b908d933736a4ec042aad065b7ebf2059bbcac97Doris Liu            // When the preview area has changed, to avoid visual disruption we
1370b908d933736a4ec042aad065b7ebf2059bbcac97Doris Liu            // only make corresponding UI changes when mode list does not have
1371b908d933736a4ec042aad065b7ebf2059bbcac97Doris Liu            // window focus.
1372b908d933736a4ec042aad065b7ebf2059bbcac97Doris Liu            updateModeListLayout();
1373b908d933736a4ec042aad065b7ebf2059bbcac97Doris Liu        }
1374b908d933736a4ec042aad065b7ebf2059bbcac97Doris Liu    }
1375b908d933736a4ec042aad065b7ebf2059bbcac97Doris Liu
137628a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu    private void updateModeListLayout() {
137728a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu        if (mCaptureLayoutHelper == null) {
137828a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu            Log.e(TAG, "Capture layout helper needs to be set first.");
137928a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu            return;
138028a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu        }
138128a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu        // Center mode drawer in the portion of camera preview that is not covered by
138228a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu        // bottom bar.
138328a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu        RectF uncoveredPreviewArea = mCaptureLayoutHelper.getUncoveredPreviewRect();
138428a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu        // Align left:
138528a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu        mListView.setTranslationX(uncoveredPreviewArea.left);
138628a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu        // Align center vertical:
138728a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu        mListView.setTranslationY(uncoveredPreviewArea.centerY()
138828a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu                - mListView.getMeasuredHeight() / 2);
138928a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu
139028a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu        updateSettingsButtonLayout(uncoveredPreviewArea);
139128a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu    }
139228a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu
139328a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu    private void updateSettingsButtonLayout(RectF uncoveredPreviewArea) {
139428a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu        if (mWidth > mHeight) {
139528a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu            // Align to the top right.
139628a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu            mSettingsButton.setTranslationX(uncoveredPreviewArea.right - mSettingsButtonMargin
139728a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu                    - mSettingsButton.getMeasuredWidth());
139828a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu            mSettingsButton.setTranslationY(uncoveredPreviewArea.top + mSettingsButtonMargin);
139928a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu        } else {
140028a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu            // Align to the bottom right.
140128a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu            mSettingsButton.setTranslationX(uncoveredPreviewArea.right - mSettingsButtonMargin
140228a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu                    - mSettingsButton.getMeasuredWidth());
140328a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu            mSettingsButton.setTranslationY(uncoveredPreviewArea.bottom - mSettingsButtonMargin
140428a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu                    - mSettingsButton.getMeasuredHeight());
140528a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu        }
140626aa92bf5942bfde16621c6833c14ba0e1ea8b5eDoris Liu        if (mSettingsCling != null) {
140726aa92bf5942bfde16621c6833c14ba0e1ea8b5eDoris Liu            mSettingsCling.updatePosition(mSettingsButton);
140826aa92bf5942bfde16621c6833c14ba0e1ea8b5eDoris Liu        }
14091c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu    }
14101c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu
1411b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liu    @Override
1412b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liu    public void draw(Canvas canvas) {
141326e901ed46054fb6cc2cb4c09bef3a779a1cbf78Alan Newberger        ModeListState currentState = mCurrentStateManager.getCurrentState();
1414bee2022d8dcde61d2b0c6cf9b709d8e34cdcb739Doris Liu        AnimationEffects currentEffects = currentState.getCurrentAnimationEffects();
1415bee2022d8dcde61d2b0c6cf9b709d8e34cdcb739Doris Liu        if (currentEffects != null) {
1416bee2022d8dcde61d2b0c6cf9b709d8e34cdcb739Doris Liu            currentEffects.drawBackground(canvas);
1417bee2022d8dcde61d2b0c6cf9b709d8e34cdcb739Doris Liu            if (currentEffects.shouldDrawSuper()) {
1418bee2022d8dcde61d2b0c6cf9b709d8e34cdcb739Doris Liu                super.draw(canvas);
1419bee2022d8dcde61d2b0c6cf9b709d8e34cdcb739Doris Liu            }
1420bee2022d8dcde61d2b0c6cf9b709d8e34cdcb739Doris Liu            currentEffects.drawForeground(canvas);
1421b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liu        } else {
1422b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liu            super.draw(canvas);
1423b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liu        }
1424b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liu    }
1425b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liu
142670576b611fddc929abbfb466fa44c396a3a793dbDoris Liu    /**
142726aa92bf5942bfde16621c6833c14ba0e1ea8b5eDoris Liu     * Sets whether a cling for settings button should be shown. If not, remove
142826aa92bf5942bfde16621c6833c14ba0e1ea8b5eDoris Liu     * the cling from view hierarchy if any. If a cling should be shown, inflate
142926aa92bf5942bfde16621c6833c14ba0e1ea8b5eDoris Liu     * the cling into this view group.
143026aa92bf5942bfde16621c6833c14ba0e1ea8b5eDoris Liu     *
143126aa92bf5942bfde16621c6833c14ba0e1ea8b5eDoris Liu     * @param show whether the cling needs to be shown.
143226aa92bf5942bfde16621c6833c14ba0e1ea8b5eDoris Liu     */
143326aa92bf5942bfde16621c6833c14ba0e1ea8b5eDoris Liu    public void setShouldShowSettingsCling(boolean show) {
143426aa92bf5942bfde16621c6833c14ba0e1ea8b5eDoris Liu        if (show) {
143526aa92bf5942bfde16621c6833c14ba0e1ea8b5eDoris Liu            if (mSettingsCling == null) {
143626aa92bf5942bfde16621c6833c14ba0e1ea8b5eDoris Liu                inflate(getContext(), R.layout.settings_cling, this);
143726aa92bf5942bfde16621c6833c14ba0e1ea8b5eDoris Liu                mSettingsCling = (SettingsCling) findViewById(R.id.settings_cling);
143826aa92bf5942bfde16621c6833c14ba0e1ea8b5eDoris Liu            }
143926aa92bf5942bfde16621c6833c14ba0e1ea8b5eDoris Liu        } else {
144026aa92bf5942bfde16621c6833c14ba0e1ea8b5eDoris Liu            if (mSettingsCling != null) {
144126aa92bf5942bfde16621c6833c14ba0e1ea8b5eDoris Liu                // Remove settings cling from view hierarchy.
144226aa92bf5942bfde16621c6833c14ba0e1ea8b5eDoris Liu                removeView(mSettingsCling);
144326aa92bf5942bfde16621c6833c14ba0e1ea8b5eDoris Liu                mSettingsCling = null;
144426aa92bf5942bfde16621c6833c14ba0e1ea8b5eDoris Liu            }
144526aa92bf5942bfde16621c6833c14ba0e1ea8b5eDoris Liu        }
144626aa92bf5942bfde16621c6833c14ba0e1ea8b5eDoris Liu    }
144726aa92bf5942bfde16621c6833c14ba0e1ea8b5eDoris Liu
144826aa92bf5942bfde16621c6833c14ba0e1ea8b5eDoris Liu    /**
144926aa92bf5942bfde16621c6833c14ba0e1ea8b5eDoris Liu     * Show or hide cling for settings button. The cling will only be shown if
145026aa92bf5942bfde16621c6833c14ba0e1ea8b5eDoris Liu     * settings button has never been clicked. Otherwise, cling will be null,
145126aa92bf5942bfde16621c6833c14ba0e1ea8b5eDoris Liu     * and will not show even if this method is called to show it.
145226aa92bf5942bfde16621c6833c14ba0e1ea8b5eDoris Liu     */
145326aa92bf5942bfde16621c6833c14ba0e1ea8b5eDoris Liu    private void showSettingsClingIfEnabled(boolean show) {
145426aa92bf5942bfde16621c6833c14ba0e1ea8b5eDoris Liu        if (mSettingsCling != null) {
145526aa92bf5942bfde16621c6833c14ba0e1ea8b5eDoris Liu            int visibility = show ? VISIBLE : INVISIBLE;
145626aa92bf5942bfde16621c6833c14ba0e1ea8b5eDoris Liu            mSettingsCling.setVisibility(visibility);
145726aa92bf5942bfde16621c6833c14ba0e1ea8b5eDoris Liu        }
145826aa92bf5942bfde16621c6833c14ba0e1ea8b5eDoris Liu    }
145926aa92bf5942bfde16621c6833c14ba0e1ea8b5eDoris Liu
146026aa92bf5942bfde16621c6833c14ba0e1ea8b5eDoris Liu    /**
1461b00d64307ae77541f8576593a29214d649fb94e9Doris Liu     * This shows the mode switcher and starts the accordion animation with a delay.
1462b00d64307ae77541f8576593a29214d649fb94e9Doris Liu     * If the view does not currently have focus, (e.g. There are popups on top of
1463b00d64307ae77541f8576593a29214d649fb94e9Doris Liu     * it.) start the delayed accordion animation when it gains focus. Otherwise,
1464b00d64307ae77541f8576593a29214d649fb94e9Doris Liu     * start the animation with a delay right away.
1465b7d3ce145dd7b1cf0ac51f53d5bc73dbbf35f6e6Doris Liu     */
1466b00d64307ae77541f8576593a29214d649fb94e9Doris Liu    public void showModeSwitcherHint() {
146726e901ed46054fb6cc2cb4c09bef3a779a1cbf78Alan Newberger        mCurrentStateManager.getCurrentState().showSwitcherHint();
1468b7d3ce145dd7b1cf0ac51f53d5bc73dbbf35f6e6Doris Liu    }
1469b7d3ce145dd7b1cf0ac51f53d5bc73dbbf35f6e6Doris Liu
1470b7d3ce145dd7b1cf0ac51f53d5bc73dbbf35f6e6Doris Liu    /**
14714c96d840aeff669011a9e8697cbac77fe8f4eda2Spike Sprague     * Hide the mode list immediately (provided the current state allows it).
14724c96d840aeff669011a9e8697cbac77fe8f4eda2Spike Sprague     */
14734c96d840aeff669011a9e8697cbac77fe8f4eda2Spike Sprague    public void hide() {
14744c96d840aeff669011a9e8697cbac77fe8f4eda2Spike Sprague        mCurrentStateManager.getCurrentState().hide();
14754c96d840aeff669011a9e8697cbac77fe8f4eda2Spike Sprague    }
14764c96d840aeff669011a9e8697cbac77fe8f4eda2Spike Sprague
14774c96d840aeff669011a9e8697cbac77fe8f4eda2Spike Sprague    /**
14784c96d840aeff669011a9e8697cbac77fe8f4eda2Spike Sprague     * Hide the mode list with an animation.
14794c96d840aeff669011a9e8697cbac77fe8f4eda2Spike Sprague     */
14804c96d840aeff669011a9e8697cbac77fe8f4eda2Spike Sprague    public void hideAnimated() {
14814c96d840aeff669011a9e8697cbac77fe8f4eda2Spike Sprague        mCurrentStateManager.getCurrentState().hideAnimated();
14824c96d840aeff669011a9e8697cbac77fe8f4eda2Spike Sprague    }
14834c96d840aeff669011a9e8697cbac77fe8f4eda2Spike Sprague
14844c96d840aeff669011a9e8697cbac77fe8f4eda2Spike Sprague    /**
148570576b611fddc929abbfb466fa44c396a3a793dbDoris Liu     * Resets the visible width of all the mode selectors to 0.
148670576b611fddc929abbfb466fa44c396a3a793dbDoris Liu     */
14871c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu    private void resetModeSelectors() {
148870576b611fddc929abbfb466fa44c396a3a793dbDoris Liu        for (int i = 0; i < mModeSelectorItems.length; i++) {
148970576b611fddc929abbfb466fa44c396a3a793dbDoris Liu            mModeSelectorItems[i].setVisibleWidth(0);
14901c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu        }
14911c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu    }
14921c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu
14931c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu    private boolean isRunningAccordionAnimation() {
14941c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu        return mAnimatorSet != null && mAnimatorSet.isRunning();
14951c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu    }
14961c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu
149770576b611fddc929abbfb466fa44c396a3a793dbDoris Liu    /**
149870576b611fddc929abbfb466fa44c396a3a793dbDoris Liu     * Calculate the mode selector item in the list that is at position (x, y).
1499213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu     * If the position is above the top item or below the bottom item, return
1500213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu     * the top item or bottom item respectively.
1501612321f39ca524de2efced1e826db9ec523bd6dcAngus Kong     *
150270576b611fddc929abbfb466fa44c396a3a793dbDoris Liu     * @param x horizontal position
150370576b611fddc929abbfb466fa44c396a3a793dbDoris Liu     * @param y vertical position
150470576b611fddc929abbfb466fa44c396a3a793dbDoris Liu     * @return index of the item that is at position (x, y)
150570576b611fddc929abbfb466fa44c396a3a793dbDoris Liu     */
15061c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu    private int getFocusItem(float x, float y) {
1507213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu        // Convert coordinates into child view's coordinates.
1508e7e0c028d568c1a4c3e88ed75411b8b41c5aec69Doris Liu        x -= mListView.getX();
1509e7e0c028d568c1a4c3e88ed75411b8b41c5aec69Doris Liu        y -= mListView.getY();
15101c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu
151170576b611fddc929abbfb466fa44c396a3a793dbDoris Liu        for (int i = 0; i < mModeSelectorItems.length; i++) {
1512213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu            if (y <= mModeSelectorItems[i].getBottom()) {
15131c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu                return i;
15141c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu            }
15151c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu        }
1516213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu        return mModeSelectorItems.length - 1;
1517213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu    }
1518213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu
1519213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu    @Override
1520b00d64307ae77541f8576593a29214d649fb94e9Doris Liu    public void onWindowFocusChanged(boolean hasFocus) {
1521b00d64307ae77541f8576593a29214d649fb94e9Doris Liu        super.onWindowFocusChanged(hasFocus);
152226e901ed46054fb6cc2cb4c09bef3a779a1cbf78Alan Newberger        mCurrentStateManager.getCurrentState().onWindowFocusChanged(hasFocus);
1523b00d64307ae77541f8576593a29214d649fb94e9Doris Liu    }
1524b00d64307ae77541f8576593a29214d649fb94e9Doris Liu
1525b00d64307ae77541f8576593a29214d649fb94e9Doris Liu    @Override
1526213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu    public void onVisibilityChanged(View v, int visibility) {
1527213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu        super.onVisibilityChanged(v, visibility);
1528213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu        if (visibility == VISIBLE) {
1529213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu            // Highlight current module
1530213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu            if (mModeSwitchListener != null) {
1531213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu                int modeId = mModeSwitchListener.getCurrentModeIndex();
1532213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu                int parentMode = CameraUtil.getCameraModeParentModeId(modeId, getContext());
1533213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu                // Find parent mode in the nav drawer.
1534213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu                for (int i = 0; i < mSupportedModes.size(); i++) {
1535213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu                    if (mSupportedModes.get(i) == parentMode) {
15366ababe9379ecee4056a415085e28e7eb5e1249bfDoris Liu                        mModeSelectorItems[i].setSelected(true);
1537213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu                    }
1538213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu                }
1539213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu            }
154028a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu            updateModeListLayout();
15419d264309a341c52601bdccdeff6647e0ce58c31fDoris Liu        } else {
15429d264309a341c52601bdccdeff6647e0ce58c31fDoris Liu            if (mModeSelectorItems != null) {
15439d264309a341c52601bdccdeff6647e0ce58c31fDoris Liu                // When becoming invisible/gone after initializing mode selector items.
15449d264309a341c52601bdccdeff6647e0ce58c31fDoris Liu                for (int i = 0; i < mModeSelectorItems.length; i++) {
15459d264309a341c52601bdccdeff6647e0ce58c31fDoris Liu                    mModeSelectorItems[i].setSelected(false);
15469d264309a341c52601bdccdeff6647e0ce58c31fDoris Liu                }
15479d264309a341c52601bdccdeff6647e0ce58c31fDoris Liu            }
15488c1a922df154182b53f00c38198db7da0b4feb20Sascha Haeberling            if (mModeListOpenListener != null) {
15499d264309a341c52601bdccdeff6647e0ce58c31fDoris Liu                mModeListOpenListener.onModeListClosed();
1550213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu            }
1551213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu        }
155238989540cb1488cb7b8062ff74b9dedcd76c047fSpike Sprague
15538c1a922df154182b53f00c38198db7da0b4feb20Sascha Haeberling        if (mVisibilityChangedListener != null) {
1554b49ab283fe6fce242901a1a09fc2b148b27fe123Sascha Haeberling            mVisibilityChangedListener.onVisibilityEvent(getVisibility() == VISIBLE);
15558c1a922df154182b53f00c38198db7da0b4feb20Sascha Haeberling        }
1556213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu    }
1557213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu
1558a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu    @Override
1559a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu    public void setVisibility(int visibility) {
156026e901ed46054fb6cc2cb4c09bef3a779a1cbf78Alan Newberger        ModeListState currentState = mCurrentStateManager.getCurrentState();
156126e901ed46054fb6cc2cb4c09bef3a779a1cbf78Alan Newberger        if (currentState != null && !currentState.shouldHandleVisibilityChange(visibility)) {
1562a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            return;
1563a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        }
1564a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        super.setVisibility(visibility);
1565a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu    }
1566a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
15671c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu    private void scroll(int itemId, float deltaX, float deltaY) {
156870576b611fddc929abbfb466fa44c396a3a793dbDoris Liu        // Scrolling trend on X and Y axis, to track the trend by biasing
156970576b611fddc929abbfb466fa44c396a3a793dbDoris Liu        // towards latest touch events.
15701c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu        mScrollTrendX = mScrollTrendX * 0.3f + deltaX * 0.7f;
15711c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu        mScrollTrendY = mScrollTrendY * 0.3f + deltaY * 0.7f;
15721c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu
157370576b611fddc929abbfb466fa44c396a3a793dbDoris Liu        // TODO: Change how the curve is calculated below when UX finalize their design.
1574cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu        mCurrentTime = SystemClock.uptimeMillis();
15751c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu        float longestWidth;
157670576b611fddc929abbfb466fa44c396a3a793dbDoris Liu        if (itemId != NO_ITEM_SELECTED) {
157769cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu            longestWidth = mModeSelectorItems[itemId].getVisibleWidth();
15781c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu        } else {
157969cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu            longestWidth = mModeSelectorItems[0].getVisibleWidth();
15801c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu        }
158169cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu        float newPosition = longestWidth - deltaX;
158269cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu        int maxVisibleWidth = mModeSelectorItems[0].getMaxVisibleWidth();
158369cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu        newPosition = Math.min(newPosition, getMaxMovementBasedOnPosition((int) longestWidth,
158469cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu                maxVisibleWidth));
158569cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu        newPosition = Math.max(newPosition, 0);
158669cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu        insertNewPosition(newPosition, mCurrentTime);
15871c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu
158870576b611fddc929abbfb466fa44c396a3a793dbDoris Liu        for (int i = 0; i < mModeSelectorItems.length; i++) {
1589d2710487e724660cbb890ebf5eb887b4a93281c1Alan Newberger            mModeSelectorItems[i].setVisibleWidth((int) newPosition);
15901c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu        }
15911c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu    }
15921c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu
1593cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu    /**
1594cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu     * Insert new position and time stamp into the history position list, and
1595cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu     * remove stale position items.
1596cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu     *
1597cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu     * @param position latest position of the focus item
1598cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu     * @param time  current time in milliseconds
1599cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu     */
1600cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu    private void insertNewPosition(float position, long time) {
1601cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu        // TODO: Consider re-using stale position objects rather than
1602cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu        // always creating new position objects.
1603cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu        mPositionHistory.add(new TimeBasedPosition(position, time));
1604cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu
1605cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu        // Positions that are from too long ago will not be of any use for
1606cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu        // future position interpolation. So we need to remove those positions
1607cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu        // from the list.
1608cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu        long timeCutoff = time - (mTotalModes - 1) * DELAY_MS;
1609cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu        while (mPositionHistory.size() > 0) {
1610cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu            // Remove all the position items that are prior to the cutoff time.
1611cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu            TimeBasedPosition historyPosition = mPositionHistory.getFirst();
1612cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu            if (historyPosition.getTimeStamp() < timeCutoff) {
1613cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu                mPositionHistory.removeFirst();
1614cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu            } else {
1615cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu                break;
1616cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu            }
1617cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu        }
1618cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu    }
1619cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu
1620cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu    /**
1621cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu     * Gets the interpolated position at the specified time. This involves going
1622cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu     * through the recorded positions until a {@link TimeBasedPosition} is found
1623cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu     * such that the position the recorded before the given time, and the
1624cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu     * {@link TimeBasedPosition} after that is recorded no earlier than the given
1625cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu     * time. These two positions are then interpolated to get the position at the
1626cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu     * specified time.
1627cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu     */
162869cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu    private float getPosition(long time, float currentPosition) {
1629cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu        int i;
1630cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu        for (i = 0; i < mPositionHistory.size(); i++) {
1631cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu            TimeBasedPosition historyPosition = mPositionHistory.get(i);
1632cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu            if (historyPosition.getTimeStamp() > time) {
1633cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu                // Found the winner. Now interpolate between position i and position i - 1
1634cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu                if (i == 0) {
163569cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu                    // Slowly approaching to the destination if there isn't enough data points
163669cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu                    float weight = 0.2f;
163769cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu                    return historyPosition.getPosition() * weight + (1f - weight) * currentPosition;
1638cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu                } else {
1639cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu                    TimeBasedPosition prevTimeBasedPosition = mPositionHistory.get(i - 1);
1640cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu                    // Start interpolation
1641cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu                    float fraction = (float) (time - prevTimeBasedPosition.getTimeStamp()) /
1642cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu                            (float) (historyPosition.getTimeStamp() - prevTimeBasedPosition.getTimeStamp());
1643cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu                    float position = fraction * (historyPosition.getPosition()
1644cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu                            - prevTimeBasedPosition.getPosition()) + prevTimeBasedPosition.getPosition();
1645cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu                    return position;
1646cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu                }
1647cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu            }
1648cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu        }
1649cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu        // It should never get here.
1650cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu        Log.e(TAG, "Invalid time input for getPosition(). time: " + time);
1651cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu        if (mPositionHistory.size() == 0) {
1652cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu            Log.e(TAG, "TimeBasedPosition history size is 0");
1653cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu        } else {
1654cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu            Log.e(TAG, "First position recorded at " + mPositionHistory.getFirst().getTimeStamp()
1655cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu            + " , last position recorded at " + mPositionHistory.getLast().getTimeStamp());
1656cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu        }
1657cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu        assert (i < mPositionHistory.size());
1658cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu        return i;
1659cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu    }
1660cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu
16611c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu    private void reset() {
16621c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu        resetModeSelectors();
16631c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu        mScrollTrendX = 0f;
16641c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu        mScrollTrendY = 0f;
16651c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu        setVisibility(INVISIBLE);
16661c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu    }
16671c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu
16681c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu    /**
166970576b611fddc929abbfb466fa44c396a3a793dbDoris Liu     * When visible width of list is changed, the background of the list needs
167070576b611fddc929abbfb466fa44c396a3a793dbDoris Liu     * to darken/lighten correspondingly.
167170576b611fddc929abbfb466fa44c396a3a793dbDoris Liu     */
16720496fcaa51f88f6c753975473b971941e4090cfaSpike Sprague    @Override
167369cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu    public void onVisibleWidthChanged(int visibleWidth) {
167469cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu        mVisibleWidth = visibleWidth;
167569cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu
1676213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu        // When the longest mode item is entirely shown (across the screen), the
167770576b611fddc929abbfb466fa44c396a3a793dbDoris Liu        // background should be 50% transparent.
1678213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu        int maxVisibleWidth = mModeSelectorItems[0].getMaxVisibleWidth();
167969cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu        visibleWidth = Math.min(maxVisibleWidth, visibleWidth);
168069cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu        if (visibleWidth != maxVisibleWidth) {
16816ababe9379ecee4056a415085e28e7eb5e1249bfDoris Liu            // No longer full screen.
16826ababe9379ecee4056a415085e28e7eb5e1249bfDoris Liu            cancelForwardingTouchEvent();
16836ababe9379ecee4056a415085e28e7eb5e1249bfDoris Liu        }
168469cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu        float openRatio = (float) visibleWidth / maxVisibleWidth;
1685a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        onModeListOpenRatioUpdate(openRatio * mModeListOpenFactor);
168669cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu    }
168769cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu
168869cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu    /**
168969cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu     * Gets called when UI elements such as background and gear icon need to adjust
169069cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu     * their appearance based on the percentage of the mode list opening.
169169cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu     *
169269cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu     * @param openRatio percentage of the mode list opening, ranging [0f, 1f]
169369cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu     */
169469cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu    private void onModeListOpenRatioUpdate(float openRatio) {
169569cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu        for (int i = 0; i < mModeSelectorItems.length; i++) {
169669cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu            mModeSelectorItems[i].setTextAlpha(openRatio);
169769cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu        }
16989d264309a341c52601bdccdeff6647e0ce58c31fDoris Liu        setBackgroundAlpha((int) (BACKGROUND_TRANSPARENTCY * openRatio));
16999d264309a341c52601bdccdeff6647e0ce58c31fDoris Liu        if (mModeListOpenListener != null) {
17009d264309a341c52601bdccdeff6647e0ce58c31fDoris Liu            mModeListOpenListener.onModeListOpenProgress(openRatio);
17019d264309a341c52601bdccdeff6647e0ce58c31fDoris Liu        }
17029d264309a341c52601bdccdeff6647e0ce58c31fDoris Liu        if (mSettingsButton != null) {
17031908beee07fe9792338dbf8e0c36a5f1e3e9401fPaul Rohde            // Disable the hardware layer when the ratio reaches 0.0 or 1.0.
17041908beee07fe9792338dbf8e0c36a5f1e3e9401fPaul Rohde            if (openRatio >= 1.0f || openRatio <= 0.0f) {
17057543914f2de33b1be25626afb31fa979b06dd582Paul Rohde                if (mSettingsButton.getLayerType() == View.LAYER_TYPE_HARDWARE) {
17061908beee07fe9792338dbf8e0c36a5f1e3e9401fPaul Rohde                    Log.v(TAG, "Disabling hardware layer for the Settings Button. (via alpha)");
17071908beee07fe9792338dbf8e0c36a5f1e3e9401fPaul Rohde                    mSettingsButton.setLayerType(View.LAYER_TYPE_NONE, null);
17081908beee07fe9792338dbf8e0c36a5f1e3e9401fPaul Rohde                }
17091908beee07fe9792338dbf8e0c36a5f1e3e9401fPaul Rohde            } else {
17107543914f2de33b1be25626afb31fa979b06dd582Paul Rohde                if (mSettingsButton.getLayerType() != View.LAYER_TYPE_HARDWARE) {
17111908beee07fe9792338dbf8e0c36a5f1e3e9401fPaul Rohde                    Log.v(TAG, "Enabling hardware layer for the Settings Button.");
17121908beee07fe9792338dbf8e0c36a5f1e3e9401fPaul Rohde                    mSettingsButton.setLayerType(View.LAYER_TYPE_HARDWARE, null);
17131908beee07fe9792338dbf8e0c36a5f1e3e9401fPaul Rohde                }
17141908beee07fe9792338dbf8e0c36a5f1e3e9401fPaul Rohde            }
17151908beee07fe9792338dbf8e0c36a5f1e3e9401fPaul Rohde
17169d264309a341c52601bdccdeff6647e0ce58c31fDoris Liu            mSettingsButton.setAlpha(openRatio);
17179d264309a341c52601bdccdeff6647e0ce58c31fDoris Liu        }
171870576b611fddc929abbfb466fa44c396a3a793dbDoris Liu    }
171970576b611fddc929abbfb466fa44c396a3a793dbDoris Liu
17206ababe9379ecee4056a415085e28e7eb5e1249bfDoris Liu    /**
17216ababe9379ecee4056a415085e28e7eb5e1249bfDoris Liu     * Cancels the touch event forwarding by sending a cancel event to the recipient
17226ababe9379ecee4056a415085e28e7eb5e1249bfDoris Liu     * view and resetting the touch forward recipient to ensure no more events
17236ababe9379ecee4056a415085e28e7eb5e1249bfDoris Liu     * can be forwarded in the current series of the touch events.
17246ababe9379ecee4056a415085e28e7eb5e1249bfDoris Liu     */
17256ababe9379ecee4056a415085e28e7eb5e1249bfDoris Liu    private void cancelForwardingTouchEvent() {
17266ababe9379ecee4056a415085e28e7eb5e1249bfDoris Liu        if (mChildViewTouched != null) {
17276ababe9379ecee4056a415085e28e7eb5e1249bfDoris Liu            mLastChildTouchEvent.setAction(MotionEvent.ACTION_CANCEL);
17286ababe9379ecee4056a415085e28e7eb5e1249bfDoris Liu            mChildViewTouched.onTouchEvent(mLastChildTouchEvent);
17296ababe9379ecee4056a415085e28e7eb5e1249bfDoris Liu            mChildViewTouched = null;
17306ababe9379ecee4056a415085e28e7eb5e1249bfDoris Liu        }
17316ababe9379ecee4056a415085e28e7eb5e1249bfDoris Liu    }
17326ababe9379ecee4056a415085e28e7eb5e1249bfDoris Liu
1733b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liu    @Override
1734b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liu    public void onWindowVisibilityChanged(int visibility) {
1735b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liu        super.onWindowVisibilityChanged(visibility);
1736b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liu        if (visibility != VISIBLE) {
173726e901ed46054fb6cc2cb4c09bef3a779a1cbf78Alan Newberger            mCurrentStateManager.getCurrentState().hide();
1738b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liu        }
1739b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liu    }
1740b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liu
174170576b611fddc929abbfb466fa44c396a3a793dbDoris Liu    /**
174215691af76927d932fcc1250b4ffaefd31a61cf81Erin Dahlgren     * Defines how the list view should respond to a menu button pressed
174315691af76927d932fcc1250b4ffaefd31a61cf81Erin Dahlgren     * event.
174415691af76927d932fcc1250b4ffaefd31a61cf81Erin Dahlgren     */
174515691af76927d932fcc1250b4ffaefd31a61cf81Erin Dahlgren    public boolean onMenuPressed() {
174626e901ed46054fb6cc2cb4c09bef3a779a1cbf78Alan Newberger        return mCurrentStateManager.getCurrentState().onMenuPressed();
174715691af76927d932fcc1250b4ffaefd31a61cf81Erin Dahlgren    }
174815691af76927d932fcc1250b4ffaefd31a61cf81Erin Dahlgren
174915691af76927d932fcc1250b4ffaefd31a61cf81Erin Dahlgren    /**
17501c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu     * The list view should either snap back or snap to full screen after a gesture.
17511c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu     * This function is called when an up or cancel event is received, and then based
17521c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu     * on the current position of the list and the gesture we can decide which way
17531c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu     * to snap.
17541c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu     */
17551c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu    private void snap() {
1756a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        if (shouldSnapBack()) {
1757a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            snapBack();
1758a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        } else {
1759a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            snapToFullScreen();
1760a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        }
1761a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu    }
1762a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
1763a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu    private boolean shouldSnapBack() {
1764a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        int itemId = Math.max(0, mFocusItem);
1765a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        if (Math.abs(mVelocityX) > VELOCITY_THRESHOLD) {
1766a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            // Fling to open / close
1767a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            return mVelocityX < 0;
1768a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        } else if (mModeSelectorItems[itemId].getVisibleWidth()
1769a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                < mModeSelectorItems[itemId].getMaxVisibleWidth() * SNAP_BACK_THRESHOLD_RATIO) {
1770a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            return true;
1771a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        } else if (Math.abs(mScrollTrendX) > Math.abs(mScrollTrendY) && mScrollTrendX > 0) {
1772a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            return true;
1773a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        } else {
1774a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            return false;
17751c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu        }
17761c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu    }
17771c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu
1778b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liu    /**
1779b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liu     * Snaps back out of the screen.
1780b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liu     *
1781b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liu     * @param withAnimation whether snapping back should be animated
1782b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liu     */
1783a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu    public Animator snapBack(boolean withAnimation) {
1784b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liu        if (withAnimation) {
1785213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu            if (mVelocityX > -VELOCITY_THRESHOLD * SCROLL_FACTOR) {
1786a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                return animateListToWidth(0);
1787213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu            } else {
1788a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                return animateListToWidthAtVelocity(mVelocityX, 0);
1789213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu            }
1790b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liu        } else {
1791b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liu            setVisibility(INVISIBLE);
1792b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liu            resetModeSelectors();
1793a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            return null;
1794b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liu        }
1795b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liu    }
1796b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liu
1797b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liu    /**
1798b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liu     * Snaps the mode list back out with animation.
1799b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liu     */
1800a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu    private Animator snapBack() {
1801a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        return snapBack(true);
18021c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu    }
18031c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu
1804a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu    private Animator snapToFullScreen() {
1805a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        Animator animator;
1806213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu        int focusItem = mFocusItem == NO_ITEM_SELECTED ? 0 : mFocusItem;
1807213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu        int fullWidth = mModeSelectorItems[focusItem].getMaxVisibleWidth();
180869cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu        if (mVelocityX <= VELOCITY_THRESHOLD) {
1809a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            animator = animateListToWidth(fullWidth);
18106c5a5375524efe7e5160f92edcaad121c7fbdb07Doris Liu        } else {
18116c5a5375524efe7e5160f92edcaad121c7fbdb07Doris Liu            // If the fling velocity exceeds this threshold, snap to full screen
18126c5a5375524efe7e5160f92edcaad121c7fbdb07Doris Liu            // at a constant speed.
1813a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            animator = animateListToWidthAtVelocity(VELOCITY_THRESHOLD, fullWidth);
18146c5a5375524efe7e5160f92edcaad121c7fbdb07Doris Liu        }
18152c559c4bd3c65e0cb0bca06f30e3bc979038e6f7Doris Liu        if (mModeListOpenListener != null) {
18162c559c4bd3c65e0cb0bca06f30e3bc979038e6f7Doris Liu            mModeListOpenListener.onOpenFullScreen();
18172c559c4bd3c65e0cb0bca06f30e3bc979038e6f7Doris Liu        }
1818a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        return animator;
18191c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu    }
18201c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu
182170576b611fddc929abbfb466fa44c396a3a793dbDoris Liu    /**
182270576b611fddc929abbfb466fa44c396a3a793dbDoris Liu     * Overloaded function to provide a simple way to start animation. Animation
182370576b611fddc929abbfb466fa44c396a3a793dbDoris Liu     * will use default duration, and a value of <code>null</code> for interpolator
182470576b611fddc929abbfb466fa44c396a3a793dbDoris Liu     * means linear interpolation will be used.
182570576b611fddc929abbfb466fa44c396a3a793dbDoris Liu     *
182670576b611fddc929abbfb466fa44c396a3a793dbDoris Liu     * @param width a set of values that the animation will animate between over time
182770576b611fddc929abbfb466fa44c396a3a793dbDoris Liu     */
1828a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu    private Animator animateListToWidth(int... width) {
1829a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        return animateListToWidth(0, DEFAULT_DURATION_MS, null, width);
18301c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu    }
18311c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu
183270576b611fddc929abbfb466fa44c396a3a793dbDoris Liu    /**
183370576b611fddc929abbfb466fa44c396a3a793dbDoris Liu     * Animate the mode list between the given set of visible width.
183470576b611fddc929abbfb466fa44c396a3a793dbDoris Liu     *
1835b00d64307ae77541f8576593a29214d649fb94e9Doris Liu     * @param delay start delay between consecutive mode item. If delay < 0, the
1836b00d64307ae77541f8576593a29214d649fb94e9Doris Liu     *              leader in the animation will be the bottom item.
183770576b611fddc929abbfb466fa44c396a3a793dbDoris Liu     * @param duration duration for the animation of each mode item
183870576b611fddc929abbfb466fa44c396a3a793dbDoris Liu     * @param interpolator interpolator to be used by the animation
183970576b611fddc929abbfb466fa44c396a3a793dbDoris Liu     * @param width a set of values that the animation will animate between over time
184070576b611fddc929abbfb466fa44c396a3a793dbDoris Liu     */
184169cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu    private Animator animateListToWidth(int delay, int duration,
18421c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu                                    TimeInterpolator interpolator, int... width) {
18431c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu        if (mAnimatorSet != null && mAnimatorSet.isRunning()) {
18441c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu            mAnimatorSet.end();
18451c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu        }
18461c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu
18471c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu        ArrayList<Animator> animators = new ArrayList<Animator>();
1848b00d64307ae77541f8576593a29214d649fb94e9Doris Liu        boolean animateModeItemsInOrder = true;
1849b00d64307ae77541f8576593a29214d649fb94e9Doris Liu        if (delay < 0) {
1850b00d64307ae77541f8576593a29214d649fb94e9Doris Liu            animateModeItemsInOrder = false;
1851b00d64307ae77541f8576593a29214d649fb94e9Doris Liu            delay *= -1;
1852b00d64307ae77541f8576593a29214d649fb94e9Doris Liu        }
18531c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu        for (int i = 0; i < mTotalModes; i++) {
1854b00d64307ae77541f8576593a29214d649fb94e9Doris Liu            ObjectAnimator animator;
1855b00d64307ae77541f8576593a29214d649fb94e9Doris Liu            if (animateModeItemsInOrder) {
1856b00d64307ae77541f8576593a29214d649fb94e9Doris Liu                animator = ObjectAnimator.ofInt(mModeSelectorItems[i],
185770576b611fddc929abbfb466fa44c396a3a793dbDoris Liu                    "visibleWidth", width);
1858b00d64307ae77541f8576593a29214d649fb94e9Doris Liu            } else {
1859b00d64307ae77541f8576593a29214d649fb94e9Doris Liu                animator = ObjectAnimator.ofInt(mModeSelectorItems[mTotalModes - 1 -i],
1860b00d64307ae77541f8576593a29214d649fb94e9Doris Liu                        "visibleWidth", width);
1861b00d64307ae77541f8576593a29214d649fb94e9Doris Liu            }
18621c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu            animator.setDuration(duration);
18631c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu            animators.add(animator);
18641c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu        }
18651c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu
18661c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu        mAnimatorSet = new AnimatorSet();
18671c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu        mAnimatorSet.playTogether(animators);
18681c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu        mAnimatorSet.setInterpolator(interpolator);
18696c5a5375524efe7e5160f92edcaad121c7fbdb07Doris Liu        mAnimatorSet.start();
187069cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu
187169cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu        return mAnimatorSet;
18726c5a5375524efe7e5160f92edcaad121c7fbdb07Doris Liu    }
18731c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu
18746c5a5375524efe7e5160f92edcaad121c7fbdb07Doris Liu    /**
18756c5a5375524efe7e5160f92edcaad121c7fbdb07Doris Liu     * Animate the mode list to the given width at a constant velocity.
18766c5a5375524efe7e5160f92edcaad121c7fbdb07Doris Liu     *
18776c5a5375524efe7e5160f92edcaad121c7fbdb07Doris Liu     * @param velocity the velocity that animation will be at
18786c5a5375524efe7e5160f92edcaad121c7fbdb07Doris Liu     * @param width final width of the list
18796c5a5375524efe7e5160f92edcaad121c7fbdb07Doris Liu     */
1880a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu    private Animator animateListToWidthAtVelocity(float velocity, int width) {
18816c5a5375524efe7e5160f92edcaad121c7fbdb07Doris Liu        if (mAnimatorSet != null && mAnimatorSet.isRunning()) {
18826c5a5375524efe7e5160f92edcaad121c7fbdb07Doris Liu            mAnimatorSet.end();
18836c5a5375524efe7e5160f92edcaad121c7fbdb07Doris Liu        }
18841c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu
18856c5a5375524efe7e5160f92edcaad121c7fbdb07Doris Liu        ArrayList<Animator> animators = new ArrayList<Animator>();
18866c5a5375524efe7e5160f92edcaad121c7fbdb07Doris Liu        for (int i = 0; i < mTotalModes; i++) {
18876c5a5375524efe7e5160f92edcaad121c7fbdb07Doris Liu            ObjectAnimator animator = ObjectAnimator.ofInt(mModeSelectorItems[i],
18886c5a5375524efe7e5160f92edcaad121c7fbdb07Doris Liu                    "visibleWidth", width);
18898c1a922df154182b53f00c38198db7da0b4feb20Sascha Haeberling            int duration = (int) (width / velocity);
18906c5a5375524efe7e5160f92edcaad121c7fbdb07Doris Liu            animator.setDuration(duration);
18916c5a5375524efe7e5160f92edcaad121c7fbdb07Doris Liu            animators.add(animator);
18926c5a5375524efe7e5160f92edcaad121c7fbdb07Doris Liu        }
18931c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu
18946c5a5375524efe7e5160f92edcaad121c7fbdb07Doris Liu        mAnimatorSet = new AnimatorSet();
18956c5a5375524efe7e5160f92edcaad121c7fbdb07Doris Liu        mAnimatorSet.playTogether(animators);
18966c5a5375524efe7e5160f92edcaad121c7fbdb07Doris Liu        mAnimatorSet.setInterpolator(null);
18971c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu        mAnimatorSet.start();
1898a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
1899a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        return mAnimatorSet;
19001c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu    }
1901f55f3c461c5a6ae6b61fa75562ca01683aa93f9aDoris Liu
1902f55f3c461c5a6ae6b61fa75562ca01683aa93f9aDoris Liu    /**
1903b9b7240d62dd2ad245f2c0e09fd1056a28687978Doris Liu     * Called when the back key is pressed.
1904b9b7240d62dd2ad245f2c0e09fd1056a28687978Doris Liu     *
1905b9b7240d62dd2ad245f2c0e09fd1056a28687978Doris Liu     * @return Whether the UI responded to the key event.
1906b9b7240d62dd2ad245f2c0e09fd1056a28687978Doris Liu     */
1907b9b7240d62dd2ad245f2c0e09fd1056a28687978Doris Liu    public boolean onBackPressed() {
190826e901ed46054fb6cc2cb4c09bef3a779a1cbf78Alan Newberger        return mCurrentStateManager.getCurrentState().onBackPressed();
1909b9b7240d62dd2ad245f2c0e09fd1056a28687978Doris Liu    }
1910b9b7240d62dd2ad245f2c0e09fd1056a28687978Doris Liu
1911b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liu    public void startModeSelectionAnimation() {
191226e901ed46054fb6cc2cb4c09bef3a779a1cbf78Alan Newberger        mCurrentStateManager.getCurrentState().startModeSelectionAnimation();
1913b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liu    }
1914b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liu
191569cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu    public float getMaxMovementBasedOnPosition(int lastVisibleWidth, int maxWidth) {
191669cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu        int timeElapsed = (int) (System.currentTimeMillis() - mLastScrollTime);
191769cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu        if (timeElapsed > SCROLL_INTERVAL_MS) {
191869cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu            timeElapsed = SCROLL_INTERVAL_MS;
191969cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu        }
192069cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu        float position;
192169cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu        int slowZone = (int) (maxWidth * SLOW_ZONE_PERCENTAGE);
192269cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu        if (lastVisibleWidth < (maxWidth - slowZone)) {
19230496fcaa51f88f6c753975473b971941e4090cfaSpike Sprague            position = VELOCITY_THRESHOLD * timeElapsed + lastVisibleWidth;
192469cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu        } else {
192569cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu            float percentageIntoSlowZone = (lastVisibleWidth - (maxWidth - slowZone)) / slowZone;
192669cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu            float velocity = (1 - percentageIntoSlowZone) * VELOCITY_THRESHOLD;
19270496fcaa51f88f6c753975473b971941e4090cfaSpike Sprague            position = velocity * timeElapsed + lastVisibleWidth;
192869cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu        }
192969cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu        position = Math.min(maxWidth, position);
193069cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu        return position;
193169cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu    }
193269cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu
1933a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu    private class PeepholeAnimationEffect extends AnimationEffects {
1934b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liu
1935b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liu        private final static int UNSET = -1;
19367dae6bb5ec44da5d7b51a4b98c45821ee5e2860eSpike Sprague        private final static int PEEP_HOLE_ANIMATION_DURATION_MS = 500;
1937213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu
1938213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu        private final Paint mMaskPaint = new Paint();
1939213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu        private final RectF mBackgroundDrawArea = new RectF();
1940b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liu
1941b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liu        private int mPeepHoleCenterX = UNSET;
1942b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liu        private int mPeepHoleCenterY = UNSET;
1943b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liu        private float mRadius = 0f;
1944b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liu        private ValueAnimator mPeepHoleAnimator;
19453c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague        private ValueAnimator mFadeOutAlphaAnimator;
19463c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague        private ValueAnimator mRevealAlphaAnimator;
1947213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu        private Bitmap mBackground;
1948213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu        private Bitmap mBackgroundOverlay;
1949b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liu
19500496fcaa51f88f6c753975473b971941e4090cfaSpike Sprague        private Paint mCirclePaint = new Paint();
19510496fcaa51f88f6c753975473b971941e4090cfaSpike Sprague        private Paint mCoverPaint = new Paint();
19520496fcaa51f88f6c753975473b971941e4090cfaSpike Sprague
19530496fcaa51f88f6c753975473b971941e4090cfaSpike Sprague        private TouchCircleDrawable mCircleDrawable;
19540496fcaa51f88f6c753975473b971941e4090cfaSpike Sprague
1955b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liu        public PeepholeAnimationEffect() {
1956b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liu            mMaskPaint.setAlpha(0);
1957b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liu            mMaskPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
19580496fcaa51f88f6c753975473b971941e4090cfaSpike Sprague
19590496fcaa51f88f6c753975473b971941e4090cfaSpike Sprague            mCirclePaint.setColor(0);
19600496fcaa51f88f6c753975473b971941e4090cfaSpike Sprague            mCirclePaint.setAlpha(0);
19610496fcaa51f88f6c753975473b971941e4090cfaSpike Sprague
19620496fcaa51f88f6c753975473b971941e4090cfaSpike Sprague            mCoverPaint.setColor(0);
19630496fcaa51f88f6c753975473b971941e4090cfaSpike Sprague            mCoverPaint.setAlpha(0);
19643c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague
19653c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague            setupAnimators();
19663c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague        }
19673c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague
19683c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague        private void setupAnimators() {
19693c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague            mFadeOutAlphaAnimator = ValueAnimator.ofInt(0, 255);
19703c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague            mFadeOutAlphaAnimator.setDuration(100);
19713c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague            mFadeOutAlphaAnimator.setInterpolator(Gusterpolator.INSTANCE);
19723c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague            mFadeOutAlphaAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
19733c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague                @Override
19743c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague                public void onAnimationUpdate(ValueAnimator animation) {
19753c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague                    mCoverPaint.setAlpha((Integer) animation.getAnimatedValue());
19763c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague                    invalidate();
19773c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague                }
19783c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague            });
19793c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague            mFadeOutAlphaAnimator.addListener(new AnimatorListenerAdapter() {
19803c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague                @Override
19813c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague                public void onAnimationStart(Animator animation) {
19823c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague                    // Sets a HW layer on the view for the animation.
19833c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague                    setLayerType(LAYER_TYPE_HARDWARE, null);
19843c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague                }
19853c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague
19863c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague                @Override
19873c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague                public void onAnimationEnd(Animator animation) {
19883c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague                    // Sets the layer type back to NONE as a workaround for b/12594617.
19893c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague                    setLayerType(LAYER_TYPE_NONE, null);
19903c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague                }
19913c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague            });
19923c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague
19933c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague            /////////////////
19943c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague
19953c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague            mRevealAlphaAnimator = ValueAnimator.ofInt(255, 0);
19963c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague            mRevealAlphaAnimator.setDuration(PEEP_HOLE_ANIMATION_DURATION_MS);
19973c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague            mRevealAlphaAnimator.setInterpolator(Gusterpolator.INSTANCE);
19983c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague            mRevealAlphaAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
19993c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague                @Override
20003c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague                public void onAnimationUpdate(ValueAnimator animation) {
20013c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague                    int alpha = (Integer) animation.getAnimatedValue();
20023c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague                    mCirclePaint.setAlpha(alpha);
20033c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague                    mCoverPaint.setAlpha(alpha);
20043c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague                }
20053c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague            });
20063c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague            mRevealAlphaAnimator.addListener(new AnimatorListenerAdapter() {
20073c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague                @Override
20083c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague                public void onAnimationStart(Animator animation) {
20093c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague                    // Sets a HW layer on the view for the animation.
20103c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague                    setLayerType(LAYER_TYPE_HARDWARE, null);
20113c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague                }
20123c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague
20133c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague                @Override
20143c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague                public void onAnimationEnd(Animator animation) {
20153c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague                    // Sets the layer type back to NONE as a workaround for b/12594617.
20163c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague                    setLayerType(LAYER_TYPE_NONE, null);
20173c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague                }
20183c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague            });
20193c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague
20203c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague            ////////////////
20213c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague
20223c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague            int horizontalDistanceToFarEdge = Math.max(mPeepHoleCenterX, mWidth - mPeepHoleCenterX);
20233c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague            int verticalDistanceToFarEdge = Math.max(mPeepHoleCenterY, mHeight - mPeepHoleCenterY);
20243c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague            int endRadius = (int) (Math.sqrt(horizontalDistanceToFarEdge * horizontalDistanceToFarEdge
20253c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague                    + verticalDistanceToFarEdge * verticalDistanceToFarEdge));
20263c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague            int startRadius = getResources().getDimensionPixelSize(
20273c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague                    R.dimen.mode_selector_icon_block_width) / 2;
20283c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague
20293c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague            mPeepHoleAnimator = ValueAnimator.ofFloat(startRadius, endRadius);
20303c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague            mPeepHoleAnimator.setDuration(PEEP_HOLE_ANIMATION_DURATION_MS);
20313c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague            mPeepHoleAnimator.setInterpolator(Gusterpolator.INSTANCE);
20323c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague            mPeepHoleAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
20333c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague                @Override
20343c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague                public void onAnimationUpdate(ValueAnimator animation) {
20353c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague                    // Modify mask by enlarging the hole
20363c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague                    mRadius = (Float) mPeepHoleAnimator.getAnimatedValue();
20373c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague                    invalidate();
20383c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague                }
20393c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague            });
20403c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague            mPeepHoleAnimator.addListener(new AnimatorListenerAdapter() {
20413c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague                @Override
20423c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague                public void onAnimationStart(Animator animation) {
20433c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague                    // Sets a HW layer on the view for the animation.
20443c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague                    setLayerType(LAYER_TYPE_HARDWARE, null);
20453c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague                }
20463c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague
20473c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague                @Override
20483c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague                public void onAnimationEnd(Animator animation) {
20493c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague                    // Sets the layer type back to NONE as a workaround for b/12594617.
20503c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague                    setLayerType(LAYER_TYPE_NONE, null);
20513c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague                }
20523c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague            });
20533c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague
20543c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague            ////////////////
20553c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague            int size = getContext().getResources()
20563c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague                    .getDimensionPixelSize(R.dimen.mode_selector_icon_block_width);
20573c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague            mCircleDrawable = new TouchCircleDrawable(getContext().getResources());
20583c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague            mCircleDrawable.setSize(size, size);
20593c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague            mCircleDrawable.setUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
20603c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague                @Override
20613c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague                public void onAnimationUpdate(ValueAnimator animation) {
20623c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague                    invalidate();
20633c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague                }
20643c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague            });
2065b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liu        }
2066b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liu
2067601921e746832bdef40503c606a95b4469f1bedbSascha Haeberling        @Override
2068b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liu        public void setSize(int width, int height) {
2069b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liu            mWidth = width;
2070b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liu            mHeight = height;
2071b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liu        }
2072b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liu
2073601921e746832bdef40503c606a95b4469f1bedbSascha Haeberling        @Override
207469cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu        public boolean onTouchEvent(MotionEvent event) {
207569cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu            return true;
207669cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu        }
207769cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu
20783c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague        @Override
20793c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague        public void drawForeground(Canvas canvas) {
20803c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague            // Draw the circle in clear mode
20813c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague            if (mPeepHoleAnimator != null) {
20823c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague                // Draw a transparent circle using clear mode
20833c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague                canvas.drawCircle(mPeepHoleCenterX, mPeepHoleCenterY, mRadius, mMaskPaint);
20843c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague                canvas.drawCircle(mPeepHoleCenterX, mPeepHoleCenterY, mRadius, mCirclePaint);
20853c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague            }
20863c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague        }
20873c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague
2088b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liu        public void setAnimationStartingPosition(int x, int y) {
2089b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liu            mPeepHoleCenterX = x;
2090b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liu            mPeepHoleCenterY = y;
2091b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liu        }
2092b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liu
20930496fcaa51f88f6c753975473b971941e4090cfaSpike Sprague        public void setModeSpecificColor(int color) {
20940496fcaa51f88f6c753975473b971941e4090cfaSpike Sprague            mCirclePaint.setColor(color & 0x00ffffff);
20950496fcaa51f88f6c753975473b971941e4090cfaSpike Sprague        }
20960496fcaa51f88f6c753975473b971941e4090cfaSpike Sprague
2097213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu        /**
2098213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu         * Sets the bitmap to be drawn in the background and the drawArea to draw
20990496fcaa51f88f6c753975473b971941e4090cfaSpike Sprague         * the bitmap.
2100213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu         *
2101213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu         * @param background image to be drawn in the background
2102213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu         * @param drawArea area to draw the background image
2103213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu         */
2104213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu        public void setBackground(Bitmap background, RectF drawArea) {
2105213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu            mBackground = background;
2106213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu            mBackgroundDrawArea.set(drawArea);
2107213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu        }
2108213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu
2109213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu        /**
2110213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu         * Sets the overlay image to be drawn on top of the background.
2111213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu         */
2112213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu        public void setBackgroundOverlay(Bitmap overlay) {
2113213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu            mBackgroundOverlay = overlay;
2114213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu        }
2115213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu
2116213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu        @Override
2117213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu        public void drawBackground(Canvas canvas) {
2118213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu            if (mBackground != null && mBackgroundOverlay != null) {
2119213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu                canvas.drawBitmap(mBackground, null, mBackgroundDrawArea, null);
21200496fcaa51f88f6c753975473b971941e4090cfaSpike Sprague                canvas.drawPaint(mCoverPaint);
2121213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu                canvas.drawBitmap(mBackgroundOverlay, 0, 0, null);
21220496fcaa51f88f6c753975473b971941e4090cfaSpike Sprague
21230496fcaa51f88f6c753975473b971941e4090cfaSpike Sprague                if (mCircleDrawable != null) {
21240496fcaa51f88f6c753975473b971941e4090cfaSpike Sprague                    mCircleDrawable.draw(canvas);
21250496fcaa51f88f6c753975473b971941e4090cfaSpike Sprague                }
21260496fcaa51f88f6c753975473b971941e4090cfaSpike Sprague            }
21270496fcaa51f88f6c753975473b971941e4090cfaSpike Sprague        }
21280496fcaa51f88f6c753975473b971941e4090cfaSpike Sprague
21290496fcaa51f88f6c753975473b971941e4090cfaSpike Sprague        @Override
2130bee2022d8dcde61d2b0c6cf9b709d8e34cdcb739Doris Liu        public boolean shouldDrawSuper() {
2131bee2022d8dcde61d2b0c6cf9b709d8e34cdcb739Doris Liu            // No need to draw super when mBackgroundOverlay is being drawn, as
2132bee2022d8dcde61d2b0c6cf9b709d8e34cdcb739Doris Liu            // background overlay already contains what's drawn in super.
2133bee2022d8dcde61d2b0c6cf9b709d8e34cdcb739Doris Liu            return (mBackground == null || mBackgroundOverlay == null);
2134bee2022d8dcde61d2b0c6cf9b709d8e34cdcb739Doris Liu        }
2135bee2022d8dcde61d2b0c6cf9b709d8e34cdcb739Doris Liu
21360496fcaa51f88f6c753975473b971941e4090cfaSpike Sprague        public void startFadeoutAnimation(Animator.AnimatorListener listener,
21370496fcaa51f88f6c753975473b971941e4090cfaSpike Sprague                final ModeSelectorItem selectedItem,
21380496fcaa51f88f6c753975473b971941e4090cfaSpike Sprague                int x, int y, final int modeId) {
21390496fcaa51f88f6c753975473b971941e4090cfaSpike Sprague            mCoverPaint.setColor(0);
21400496fcaa51f88f6c753975473b971941e4090cfaSpike Sprague            mCoverPaint.setAlpha(0);
21410496fcaa51f88f6c753975473b971941e4090cfaSpike Sprague
21420496fcaa51f88f6c753975473b971941e4090cfaSpike Sprague            mCircleDrawable.setIconDrawable(
21430496fcaa51f88f6c753975473b971941e4090cfaSpike Sprague                    selectedItem.getIcon().getIconDrawableClone(),
21440496fcaa51f88f6c753975473b971941e4090cfaSpike Sprague                    selectedItem.getIcon().getIconDrawableSize());
21450496fcaa51f88f6c753975473b971941e4090cfaSpike Sprague            mCircleDrawable.setCenter(new Point(x, y));
21460496fcaa51f88f6c753975473b971941e4090cfaSpike Sprague            mCircleDrawable.setColor(selectedItem.getHighlightColor());
21470496fcaa51f88f6c753975473b971941e4090cfaSpike Sprague            mCircleDrawable.setAnimatorListener(new AnimatorListenerAdapter() {
21480496fcaa51f88f6c753975473b971941e4090cfaSpike Sprague                @Override
21490496fcaa51f88f6c753975473b971941e4090cfaSpike Sprague                public void onAnimationEnd(Animator animation) {
21500496fcaa51f88f6c753975473b971941e4090cfaSpike Sprague                    // Post mode selection runnable to the end of the message queue
21510496fcaa51f88f6c753975473b971941e4090cfaSpike Sprague                    // so that current UI changes can finish before mode initialization
21520496fcaa51f88f6c753975473b971941e4090cfaSpike Sprague                    // clogs up UI thread.
21530496fcaa51f88f6c753975473b971941e4090cfaSpike Sprague                    post(new Runnable() {
21540496fcaa51f88f6c753975473b971941e4090cfaSpike Sprague                        @Override
21550496fcaa51f88f6c753975473b971941e4090cfaSpike Sprague                        public void run() {
21560496fcaa51f88f6c753975473b971941e4090cfaSpike Sprague                            // Select the focused item.
21570496fcaa51f88f6c753975473b971941e4090cfaSpike Sprague                            selectedItem.setSelected(true);
21580496fcaa51f88f6c753975473b971941e4090cfaSpike Sprague                            onModeSelected(modeId);
21590496fcaa51f88f6c753975473b971941e4090cfaSpike Sprague                        }
21600496fcaa51f88f6c753975473b971941e4090cfaSpike Sprague                    });
21610496fcaa51f88f6c753975473b971941e4090cfaSpike Sprague                }
21620496fcaa51f88f6c753975473b971941e4090cfaSpike Sprague            });
21633c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague
21643c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague            // add fade out animator to a set, so we can freely add
21653c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague            // the listener without having to worry about listener dupes
21663c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague            AnimatorSet s = new AnimatorSet();
21673c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague            s.play(mFadeOutAlphaAnimator);
21683c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague            if (listener != null) {
21693c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague                s.addListener(listener);
21703c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague            }
21710496fcaa51f88f6c753975473b971941e4090cfaSpike Sprague            mCircleDrawable.animate();
21723c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague            s.start();
21730496fcaa51f88f6c753975473b971941e4090cfaSpike Sprague        }
21740496fcaa51f88f6c753975473b971941e4090cfaSpike Sprague
2175bee2022d8dcde61d2b0c6cf9b709d8e34cdcb739Doris Liu        @Override
2176a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        public void startAnimation(Animator.AnimatorListener listener) {
2177b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liu            if (mPeepHoleAnimator != null && mPeepHoleAnimator.isRunning()) {
2178b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liu                return;
2179b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liu            }
2180b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liu            if (mPeepHoleCenterY == UNSET || mPeepHoleCenterX == UNSET) {
2181b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liu                mPeepHoleCenterX = mWidth / 2;
2182b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liu                mPeepHoleCenterY = mHeight / 2;
2183b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liu            }
2184b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liu
21850496fcaa51f88f6c753975473b971941e4090cfaSpike Sprague            mCirclePaint.setAlpha(255);
21860496fcaa51f88f6c753975473b971941e4090cfaSpike Sprague            mCoverPaint.setAlpha(255);
2187759122e8b3071b79fa2a388f00403af4e21be68cDoris Liu
21883c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague            // add peephole and reveal animators to a set, so we can
21893c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague            // freely add the listener without having to worry about
21903c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague            // listener dupes
21913c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague            AnimatorSet s = new AnimatorSet();
21923c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague            s.play(mPeepHoleAnimator).with(mRevealAlphaAnimator);
21933c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague            if (listener != null) {
21943c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague                s.addListener(listener);
21953c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague            }
21963c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague            s.start();
2197b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liu        }
2198b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liu
219969cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu        @Override
220069cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu        public void endAnimation() {
2201b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liu        }
2202213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu
2203fa50f7d71a77dc730746c33fd17a570d42453759Doris Liu        @Override
2204fa50f7d71a77dc730746c33fd17a570d42453759Doris Liu        public boolean cancelAnimation() {
2205fa50f7d71a77dc730746c33fd17a570d42453759Doris Liu            if (mPeepHoleAnimator == null || !mPeepHoleAnimator.isRunning()) {
2206fa50f7d71a77dc730746c33fd17a570d42453759Doris Liu                return false;
2207fa50f7d71a77dc730746c33fd17a570d42453759Doris Liu            } else {
2208fa50f7d71a77dc730746c33fd17a570d42453759Doris Liu                mPeepHoleAnimator.cancel();
2209fa50f7d71a77dc730746c33fd17a570d42453759Doris Liu                return true;
2210fa50f7d71a77dc730746c33fd17a570d42453759Doris Liu            }
2211fa50f7d71a77dc730746c33fd17a570d42453759Doris Liu        }
2212b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liu    }
22131c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu}
2214