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;
46bd1b8f910d6f2c94daac304f4bd1a098939474c3Doris Liuimport com.android.camera.util.CameraUtil;
471c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liuimport com.android.camera.util.Gusterpolator;
4810c5816e1ff3d30e4c4996643ba05d63d522b81aAndy Huibersimport com.android.camera.util.UsageStatistics;
49b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liuimport com.android.camera.widget.AnimationEffects;
5026aa92bf5942bfde16621c6833c14ba0e1ea8b5eDoris Liuimport com.android.camera.widget.SettingsCling;
511c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liuimport com.android.camera2.R;
5210c5816e1ff3d30e4c4996643ba05d63d522b81aAndy Huibersimport com.google.common.logging.eventprotos;
531c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu
541c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liuimport java.util.ArrayList;
55cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liuimport java.util.LinkedList;
56a20074f0efeaa62a29b05838110d04a1b7271418Doris Liuimport java.util.List;
571c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu
5870576b611fddc929abbfb466fa44c396a3a793dbDoris Liu/**
5970576b611fddc929abbfb466fa44c396a3a793dbDoris Liu * ModeListView class displays all camera modes and settings in the form
6070576b611fddc929abbfb466fa44c396a3a793dbDoris Liu * of a list. A swipe to the right will bring up this list. Then tapping on
6170576b611fddc929abbfb466fa44c396a3a793dbDoris Liu * any of the items in the list will take the user to that corresponding mode
6270576b611fddc929abbfb466fa44c396a3a793dbDoris Liu * with an animation. To dismiss this list, simply swipe left or select a mode.
6370576b611fddc929abbfb466fa44c396a3a793dbDoris Liu */
64213a4a086b54904cee543adf60b16fc1a61efe38Doris Liupublic class ModeListView extends FrameLayout
65b908d933736a4ec042aad065b7ebf2059bbcac97Doris Liu        implements ModeSelectorItem.VisibleWidthChangedListener,
66b908d933736a4ec042aad065b7ebf2059bbcac97Doris Liu        PreviewStatusListener.PreviewAreaChangedListener {
67601921e746832bdef40503c606a95b4469f1bedbSascha Haeberling
685596b4c902dcb685928b43678f428746ca5ffd08Angus Kong    private static final Log.Tag TAG = new Log.Tag("ModeListView");
6970576b611fddc929abbfb466fa44c396a3a793dbDoris Liu
7070576b611fddc929abbfb466fa44c396a3a793dbDoris Liu    // Animation Durations
7170576b611fddc929abbfb466fa44c396a3a793dbDoris Liu    private static final int DEFAULT_DURATION_MS = 200;
72b00d64307ae77541f8576593a29214d649fb94e9Doris Liu    private static final int FLY_IN_DURATION_MS = 0;
731c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu    private static final int HOLD_DURATION_MS = 0;
741c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu    private static final int FLY_OUT_DURATION_MS = 850;
751c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu    private static final int START_DELAY_MS = 100;
761c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu    private static final int TOTAL_DURATION_MS = FLY_IN_DURATION_MS + HOLD_DURATION_MS
771c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu            + FLY_OUT_DURATION_MS;
78b00d64307ae77541f8576593a29214d649fb94e9Doris Liu    private static final int HIDE_SHIMMY_DELAY_MS = 1000;
7969cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu    // Assumption for time since last scroll when no data point for last scroll.
8069cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu    private static final int SCROLL_INTERVAL_MS = 50;
8169cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu    // Last 20% percent of the drawer opening should be slow to ensure soft landing.
8269cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu    private static final float SLOW_ZONE_PERCENTAGE = 0.2f;
831c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu
8470576b611fddc929abbfb466fa44c396a3a793dbDoris Liu    private static final int NO_ITEM_SELECTED = -1;
851c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu
86cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu    // Scrolling delay between non-focused item and focused item
87213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu    private static final int DELAY_MS = 30;
886c5a5375524efe7e5160f92edcaad121c7fbdb07Doris Liu    // If the fling velocity exceeds this threshold, snap to full screen at a constant
896c5a5375524efe7e5160f92edcaad121c7fbdb07Doris Liu    // speed. Unit: pixel/ms.
906c5a5375524efe7e5160f92edcaad121c7fbdb07Doris Liu    private static final float VELOCITY_THRESHOLD = 2f;
91cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu
92213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu    /**
93213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu     * A factor to change the UI responsiveness on a scroll.
94213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu     * e.g. A scroll factor of 0.5 means UI will move half as fast as the finger.
95213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu     */
96213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu    private static final float SCROLL_FACTOR = 0.5f;
975ac2bfbb23f6277947f284531237ea85c81a06e4Seth Raphael    // 60% opaque black background.
983020ba5dc23723bab7503324d82dd5ae3830008cSeth Raphael    private static final int BACKGROUND_TRANSPARENTCY = (int) (0.6f * 255);
99213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu    private static final int PREVIEW_DOWN_SAMPLE_FACTOR = 4;
100213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu    // Threshold, below which snap back will happen.
101213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu    private static final float SNAP_BACK_THRESHOLD_RATIO = 0.33f;
102213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu
10370576b611fddc929abbfb466fa44c396a3a793dbDoris Liu    private final GestureDetector mGestureDetector;
10426e901ed46054fb6cc2cb4c09bef3a779a1cbf78Alan Newberger    private final CurrentStateManager mCurrentStateManager = new CurrentStateManager();
10528a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu    private final int mSettingsButtonMargin;
10669cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu    private long mLastScrollTime;
10770576b611fddc929abbfb466fa44c396a3a793dbDoris Liu    private int mListBackgroundColor;
10870576b611fddc929abbfb466fa44c396a3a793dbDoris Liu    private LinearLayout mListView;
10928a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu    private View mSettingsButton;
11070576b611fddc929abbfb466fa44c396a3a793dbDoris Liu    private int mTotalModes;
11170576b611fddc929abbfb466fa44c396a3a793dbDoris Liu    private ModeSelectorItem[] mModeSelectorItems;
11270576b611fddc929abbfb466fa44c396a3a793dbDoris Liu    private AnimatorSet mAnimatorSet;
1131c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu    private int mFocusItem = NO_ITEM_SELECTED;
1142c559c4bd3c65e0cb0bca06f30e3bc979038e6f7Doris Liu    private ModeListOpenListener mModeListOpenListener;
1158c1a922df154182b53f00c38198db7da0b4feb20Sascha Haeberling    private ModeListVisibilityChangedListener mVisibilityChangedListener;
116213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu    private CameraAppUI.CameraModuleScreenShotProvider mScreenShotProvider = null;
117213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu    private int[] mInputPixels;
118213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu    private int[] mOutputPixels;
119a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu    private float mModeListOpenFactor = 1f;
12070576b611fddc929abbfb466fa44c396a3a793dbDoris Liu
12169cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu    private View mChildViewTouched = null;
12269cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu    private MotionEvent mLastChildTouchEvent = null;
12369cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu    private int mVisibleWidth = 0;
12469cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu
12570576b611fddc929abbfb466fa44c396a3a793dbDoris Liu    // Width and height of this view. They get updated in onLayout()
12670576b611fddc929abbfb466fa44c396a3a793dbDoris Liu    // Unit for width and height are pixels.
1271c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu    private int mWidth;
1281c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu    private int mHeight;
1291c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu    private float mScrollTrendX = 0f;
1301c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu    private float mScrollTrendY = 0f;
1312c559c4bd3c65e0cb0bca06f30e3bc979038e6f7Doris Liu    private ModeSwitchListener mModeSwitchListener = null;
132bd1b8f910d6f2c94daac304f4bd1a098939474c3Doris Liu    private ArrayList<Integer> mSupportedModes;
133cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu    private final LinkedList<TimeBasedPosition> mPositionHistory
134cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu            = new LinkedList<TimeBasedPosition>();
135cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu    private long mCurrentTime;
1366c5a5375524efe7e5160f92edcaad121c7fbdb07Doris Liu    private float mVelocityX; // Unit: pixel/ms.
137a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu    private long mLastDownTime = 0;
13828a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu    private CaptureLayoutHelper mCaptureLayoutHelper = null;
13926aa92bf5942bfde16621c6833c14ba0e1ea8b5eDoris Liu    private SettingsCling mSettingsCling = null;
140a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
14126e901ed46054fb6cc2cb4c09bef3a779a1cbf78Alan Newberger    private class CurrentStateManager {
14226e901ed46054fb6cc2cb4c09bef3a779a1cbf78Alan Newberger        private ModeListState mCurrentState;
14326e901ed46054fb6cc2cb4c09bef3a779a1cbf78Alan Newberger
14426e901ed46054fb6cc2cb4c09bef3a779a1cbf78Alan Newberger        ModeListState getCurrentState() {
14526e901ed46054fb6cc2cb4c09bef3a779a1cbf78Alan Newberger            return mCurrentState;
14626e901ed46054fb6cc2cb4c09bef3a779a1cbf78Alan Newberger        }
14726e901ed46054fb6cc2cb4c09bef3a779a1cbf78Alan Newberger
14826e901ed46054fb6cc2cb4c09bef3a779a1cbf78Alan Newberger        void setCurrentState(ModeListState state) {
14926e901ed46054fb6cc2cb4c09bef3a779a1cbf78Alan Newberger            mCurrentState = state;
15026e901ed46054fb6cc2cb4c09bef3a779a1cbf78Alan Newberger            state.onCurrentState();
15126e901ed46054fb6cc2cb4c09bef3a779a1cbf78Alan Newberger        }
15226e901ed46054fb6cc2cb4c09bef3a779a1cbf78Alan Newberger    }
15326e901ed46054fb6cc2cb4c09bef3a779a1cbf78Alan Newberger
154a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu    /**
155a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu     * ModeListState defines a set of functions through which the view could manage
156a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu     * or change the states. Sub-classes could selectively override these functions
157a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu     * accordingly to respect the specific requirements for each state. By overriding
158a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu     * these methods, state transition can also be achieved.
159a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu     */
160a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu    private abstract class ModeListState implements GestureDetector.OnGestureListener {
161a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        protected AnimationEffects mCurrentAnimationEffects = null;
162a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
163a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        /**
16426e901ed46054fb6cc2cb4c09bef3a779a1cbf78Alan Newberger         * Called by the state manager when this state instance becomes the current
16526e901ed46054fb6cc2cb4c09bef3a779a1cbf78Alan Newberger         * mode list state.
16626e901ed46054fb6cc2cb4c09bef3a779a1cbf78Alan Newberger         */
16726e901ed46054fb6cc2cb4c09bef3a779a1cbf78Alan Newberger        public void onCurrentState() {
16826e901ed46054fb6cc2cb4c09bef3a779a1cbf78Alan Newberger            // Do nothing.
16926aa92bf5942bfde16621c6833c14ba0e1ea8b5eDoris Liu            showSettingsClingIfEnabled(false);
17026e901ed46054fb6cc2cb4c09bef3a779a1cbf78Alan Newberger        }
17126e901ed46054fb6cc2cb4c09bef3a779a1cbf78Alan Newberger
17226e901ed46054fb6cc2cb4c09bef3a779a1cbf78Alan Newberger        /**
173a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu         * If supported, this should show the mode switcher and starts the accordion
174a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu         * animation with a delay. If the view does not currently have focus, (e.g.
175a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu         * There are popups on top of it.) start the delayed accordion animation
176a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu         * when it gains focus. Otherwise, start the animation with a delay right
177a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu         * away.
178a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu         */
179a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        public void showSwitcherHint() {
180a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            // Do nothing.
181a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        }
182a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
183a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        /**
184a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu         * Gets the currently running animation effects for the current state.
185a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu         */
186a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        public AnimationEffects getCurrentAnimationEffects() {
187a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            return mCurrentAnimationEffects;
188a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        }
189a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
190a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        /**
191a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu         * Returns true if the touch event should be handled, false otherwise.
192a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu         *
193a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu         * @param ev motion event to be handled
194a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu         * @return true if the event should be handled, false otherwise.
195a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu         */
196a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        public boolean shouldHandleTouchEvent(MotionEvent ev) {
197a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            return true;
198a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        }
199a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
200a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        /**
201a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu         * Handles touch event. This will be called if
202a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu         * {@link ModeListState#shouldHandleTouchEvent(android.view.MotionEvent)}
203a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu         * returns {@code true}
204a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu         *
205a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu         * @param ev touch event to be handled
206a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu         * @return always true
207a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu         */
208a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        public boolean onTouchEvent(MotionEvent ev) {
209a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            return true;
210a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        }
211a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
212a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        /**
213a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu         * Gets called when the window focus has changed.
214a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu         *
215a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu         * @param hasFocus whether current window has focus
216a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu         */
217a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        public void onWindowFocusChanged(boolean hasFocus) {
218a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            // Default to do nothing.
219a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        }
220a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
221a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        /**
222a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu         * Gets called when back key is pressed.
223a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu         *
224a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu         * @return true if handled, false otherwise.
225a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu         */
226a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        public boolean onBackPressed() {
227a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            return false;
228a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        }
229a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
230a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        /**
23115691af76927d932fcc1250b4ffaefd31a61cf81Erin Dahlgren         * Gets called when menu key is pressed.
23215691af76927d932fcc1250b4ffaefd31a61cf81Erin Dahlgren         *
23315691af76927d932fcc1250b4ffaefd31a61cf81Erin Dahlgren         * @return true if handled, false otherwise.
23415691af76927d932fcc1250b4ffaefd31a61cf81Erin Dahlgren         */
23515691af76927d932fcc1250b4ffaefd31a61cf81Erin Dahlgren        public boolean onMenuPressed() {
23615691af76927d932fcc1250b4ffaefd31a61cf81Erin Dahlgren            return false;
23715691af76927d932fcc1250b4ffaefd31a61cf81Erin Dahlgren        }
23815691af76927d932fcc1250b4ffaefd31a61cf81Erin Dahlgren
23915691af76927d932fcc1250b4ffaefd31a61cf81Erin Dahlgren        /**
240a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu         * Gets called when there is a {@link View#setVisibility(int)} call to
241a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu         * change the visibility of the mode drawer. Visibility change does not
242a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu         * always make sense, for example there can be an outside call to make
243a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu         * the mode drawer visible when it is in the fully hidden state. The logic
244a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu         * is that the mode drawer can only be made visible when user swipe it in.
245a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu         *
246a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu         * @param visibility the proposed visibility change
247a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu         * @return true if the visibility change is valid and therefore should be
248a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu         *         handled, false otherwise.
249a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu         */
250a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        public boolean shouldHandleVisibilityChange(int visibility) {
251a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            return true;
252a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        }
253a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
254a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        /**
255a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu         * If supported, this should start blurring the camera preview and
256a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu         * start the mode switch.
257a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu         *
258a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu         * @param selectedItem mode item that has been selected
259a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu         */
260a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        public void onItemSelected(ModeSelectorItem selectedItem) {
261a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            // Do nothing.
262a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        }
263a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
264a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        /**
265a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu         * This gets called when mode switch has finished and UI needs to
266a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu         * pinhole into the new mode through animation.
267a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu         */
268a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        public void startModeSelectionAnimation() {
269a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            // Do nothing.
270a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        }
271a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
272a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        /**
273a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu         * Hide the mode drawer and switch to fully hidden state.
274a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu         */
275a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        public void hide() {
276a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            // Do nothing.
277a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        }
278a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
279a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        /***************GestureListener implementation*****************/
280a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        @Override
281a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        public boolean onDown(MotionEvent e) {
282a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            return false;
283a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        }
284a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
285a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        @Override
286a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        public void onShowPress(MotionEvent e) {
287a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            // Do nothing.
288a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        }
289a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
290a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        @Override
291a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        public boolean onSingleTapUp(MotionEvent e) {
292a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            return false;
293a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        }
294a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
295a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        @Override
296a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
297a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            return false;
298a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        }
299a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
300a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        @Override
301a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        public void onLongPress(MotionEvent e) {
302a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            // Do nothing.
303a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        }
304a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
305a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        @Override
306a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
307a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            return false;
308a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        }
309a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu    }
310a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
311a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu    /**
312a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu     * Fully hidden state. Transitioning to ScrollingState and ShimmyState are supported
313a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu     * in this state.
314a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu     */
315a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu    private class FullyHiddenState extends ModeListState {
31615691af76927d932fcc1250b4ffaefd31a61cf81Erin Dahlgren        private Animator mAnimator = null;
317a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        private boolean mShouldBeVisible = false;
31815691af76927d932fcc1250b4ffaefd31a61cf81Erin Dahlgren
319a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        public FullyHiddenState() {
320a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            reset();
321a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        }
322a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
323a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        @Override
324a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
325a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            mShouldBeVisible = true;
326a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            // Change visibility, and switch to scrolling state.
327a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            resetModeSelectors();
32826e901ed46054fb6cc2cb4c09bef3a779a1cbf78Alan Newberger            mCurrentStateManager.setCurrentState(new ScrollingState());
329a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            return true;
330a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        }
331a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
332a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        @Override
333a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        public void showSwitcherHint() {
334a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            mShouldBeVisible = true;
33526e901ed46054fb6cc2cb4c09bef3a779a1cbf78Alan Newberger            mCurrentStateManager.setCurrentState(new ShimmyState());
336a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        }
3376c5a5375524efe7e5160f92edcaad121c7fbdb07Doris Liu
3386c5a5375524efe7e5160f92edcaad121c7fbdb07Doris Liu        @Override
339a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        public boolean shouldHandleTouchEvent(MotionEvent ev) {
340a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            return true;
341a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        }
342a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
343a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        @Override
344a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        public boolean onTouchEvent(MotionEvent ev) {
345a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            if (ev.getActionMasked() == MotionEvent.ACTION_DOWN) {
346a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                mFocusItem = getFocusItem(ev.getX(), ev.getY());
347a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                setSwipeMode(true);
348a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            }
349a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            return true;
350a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        }
351a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
352a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        @Override
35315691af76927d932fcc1250b4ffaefd31a61cf81Erin Dahlgren        public boolean onMenuPressed() {
35415691af76927d932fcc1250b4ffaefd31a61cf81Erin Dahlgren            if (mAnimator != null) {
35515691af76927d932fcc1250b4ffaefd31a61cf81Erin Dahlgren                return false;
35615691af76927d932fcc1250b4ffaefd31a61cf81Erin Dahlgren            }
35715691af76927d932fcc1250b4ffaefd31a61cf81Erin Dahlgren            snapOpenAndShow();
35815691af76927d932fcc1250b4ffaefd31a61cf81Erin Dahlgren            return true;
35915691af76927d932fcc1250b4ffaefd31a61cf81Erin Dahlgren        }
36015691af76927d932fcc1250b4ffaefd31a61cf81Erin Dahlgren
36115691af76927d932fcc1250b4ffaefd31a61cf81Erin Dahlgren        @Override
362a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        public boolean shouldHandleVisibilityChange(int visibility) {
36315691af76927d932fcc1250b4ffaefd31a61cf81Erin Dahlgren            if (mAnimator != null) {
36415691af76927d932fcc1250b4ffaefd31a61cf81Erin Dahlgren                return false;
36515691af76927d932fcc1250b4ffaefd31a61cf81Erin Dahlgren            }
366a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            if (visibility == VISIBLE && !mShouldBeVisible) {
367a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                return false;
368a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            }
369a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            return true;
370a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        }
37115691af76927d932fcc1250b4ffaefd31a61cf81Erin Dahlgren        /**
37215691af76927d932fcc1250b4ffaefd31a61cf81Erin Dahlgren         * Snaps open the mode list and go to the fully shown state.
37315691af76927d932fcc1250b4ffaefd31a61cf81Erin Dahlgren         */
37415691af76927d932fcc1250b4ffaefd31a61cf81Erin Dahlgren        private void snapOpenAndShow() {
37515691af76927d932fcc1250b4ffaefd31a61cf81Erin Dahlgren            mShouldBeVisible = true;
37615691af76927d932fcc1250b4ffaefd31a61cf81Erin Dahlgren            setVisibility(VISIBLE);
377a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
37815691af76927d932fcc1250b4ffaefd31a61cf81Erin Dahlgren            mAnimator = snapToFullScreen();
37915691af76927d932fcc1250b4ffaefd31a61cf81Erin Dahlgren            if (mAnimator != null) {
38015691af76927d932fcc1250b4ffaefd31a61cf81Erin Dahlgren                mAnimator.addListener(new Animator.AnimatorListener() {
38115691af76927d932fcc1250b4ffaefd31a61cf81Erin Dahlgren                    @Override
38215691af76927d932fcc1250b4ffaefd31a61cf81Erin Dahlgren                    public void onAnimationStart(Animator animation) {
38315691af76927d932fcc1250b4ffaefd31a61cf81Erin Dahlgren
38415691af76927d932fcc1250b4ffaefd31a61cf81Erin Dahlgren                    }
38515691af76927d932fcc1250b4ffaefd31a61cf81Erin Dahlgren
38615691af76927d932fcc1250b4ffaefd31a61cf81Erin Dahlgren                    @Override
38715691af76927d932fcc1250b4ffaefd31a61cf81Erin Dahlgren                    public void onAnimationEnd(Animator animation) {
38815691af76927d932fcc1250b4ffaefd31a61cf81Erin Dahlgren                        mAnimator = null;
38926e901ed46054fb6cc2cb4c09bef3a779a1cbf78Alan Newberger                        mCurrentStateManager.setCurrentState(new FullyShownState());
39015691af76927d932fcc1250b4ffaefd31a61cf81Erin Dahlgren                    }
39115691af76927d932fcc1250b4ffaefd31a61cf81Erin Dahlgren
39215691af76927d932fcc1250b4ffaefd31a61cf81Erin Dahlgren                    @Override
39315691af76927d932fcc1250b4ffaefd31a61cf81Erin Dahlgren                    public void onAnimationCancel(Animator animation) {
39415691af76927d932fcc1250b4ffaefd31a61cf81Erin Dahlgren
39515691af76927d932fcc1250b4ffaefd31a61cf81Erin Dahlgren                    }
39615691af76927d932fcc1250b4ffaefd31a61cf81Erin Dahlgren
39715691af76927d932fcc1250b4ffaefd31a61cf81Erin Dahlgren                    @Override
39815691af76927d932fcc1250b4ffaefd31a61cf81Erin Dahlgren                    public void onAnimationRepeat(Animator animation) {
39915691af76927d932fcc1250b4ffaefd31a61cf81Erin Dahlgren
40015691af76927d932fcc1250b4ffaefd31a61cf81Erin Dahlgren                    }
40115691af76927d932fcc1250b4ffaefd31a61cf81Erin Dahlgren                });
40215691af76927d932fcc1250b4ffaefd31a61cf81Erin Dahlgren            } else {
40326e901ed46054fb6cc2cb4c09bef3a779a1cbf78Alan Newberger                mCurrentStateManager.setCurrentState(new FullyShownState());
40410c5816e1ff3d30e4c4996643ba05d63d522b81aAndy Huibers                UsageStatistics.instance().controlUsed(
40510c5816e1ff3d30e4c4996643ba05d63d522b81aAndy Huibers                        eventprotos.ControlEvent.ControlType.MENU_FULL_FROM_HIDDEN);
40615691af76927d932fcc1250b4ffaefd31a61cf81Erin Dahlgren            }
40715691af76927d932fcc1250b4ffaefd31a61cf81Erin Dahlgren        }
40826e901ed46054fb6cc2cb4c09bef3a779a1cbf78Alan Newberger
40926e901ed46054fb6cc2cb4c09bef3a779a1cbf78Alan Newberger        @Override
41026e901ed46054fb6cc2cb4c09bef3a779a1cbf78Alan Newberger        public void onCurrentState() {
41126aa92bf5942bfde16621c6833c14ba0e1ea8b5eDoris Liu            super.onCurrentState();
41226aa92bf5942bfde16621c6833c14ba0e1ea8b5eDoris Liu            announceForAccessibility(
41326aa92bf5942bfde16621c6833c14ba0e1ea8b5eDoris Liu                    getContext().getResources().getString(R.string.accessibility_mode_list_hidden));
41426e901ed46054fb6cc2cb4c09bef3a779a1cbf78Alan Newberger        }
415a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu    }
416a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
417a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu    /**
418a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu     * Fully shown state. This state represents when the mode list is entirely shown
419a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu     * on screen without any on-going animation. Transitions from this state could be
420a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu     * to ScrollingState, SelectedState, or FullyHiddenState.
421a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu     */
422a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu    private class FullyShownState extends ModeListState {
423a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        private Animator mAnimator = null;
424a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
425a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        @Override
426a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
427a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            // Go to scrolling state.
428a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            if (distanceX > 0) {
429a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                // Swipe out
430a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                cancelForwardingTouchEvent();
43126e901ed46054fb6cc2cb4c09bef3a779a1cbf78Alan Newberger                mCurrentStateManager.setCurrentState(new ScrollingState());
432a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            }
433a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            return true;
434a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        }
435a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
436a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        @Override
437a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        public boolean shouldHandleTouchEvent(MotionEvent ev) {
438a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            if (mAnimator != null && mAnimator.isRunning()) {
439a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                return false;
440a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            }
441a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            return true;
442a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        }
443a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
444a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        @Override
445a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        public boolean onTouchEvent(MotionEvent ev) {
446a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            if (ev.getActionMasked() == MotionEvent.ACTION_DOWN) {
447a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                mFocusItem = NO_ITEM_SELECTED;
448a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                setSwipeMode(false);
449a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                // If the down event happens inside the mode list, find out which
450a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                // mode item is being touched and forward all the subsequent touch
451a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                // events to that mode item for its pressed state and click handling.
452a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                if (isTouchInsideList(ev)) {
453a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                    mChildViewTouched = mModeSelectorItems[getFocusItem(ev.getX(), ev.getY())];
454a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                }
455a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            }
456a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            forwardTouchEventToChild(ev);
457a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            return true;
458a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        }
459a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
460a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
461a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        @Override
462a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        public boolean onSingleTapUp(MotionEvent ev) {
463a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            // If the tap is not inside the mode drawer area, snap back.
464a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            if(!isTouchInsideList(ev)) {
465a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                snapBackAndHide();
466a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                return false;
467a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            }
468a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            return true;
469a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        }
470a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
471a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        @Override
472a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        public boolean onBackPressed() {
473a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            snapBackAndHide();
474a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            return true;
475a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        }
476a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
477a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        @Override
47815691af76927d932fcc1250b4ffaefd31a61cf81Erin Dahlgren        public boolean onMenuPressed() {
47915691af76927d932fcc1250b4ffaefd31a61cf81Erin Dahlgren            snapBackAndHide();
48015691af76927d932fcc1250b4ffaefd31a61cf81Erin Dahlgren            return true;
48115691af76927d932fcc1250b4ffaefd31a61cf81Erin Dahlgren        }
48215691af76927d932fcc1250b4ffaefd31a61cf81Erin Dahlgren
48315691af76927d932fcc1250b4ffaefd31a61cf81Erin Dahlgren        @Override
484a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        public void onItemSelected(ModeSelectorItem selectedItem) {
48526e901ed46054fb6cc2cb4c09bef3a779a1cbf78Alan Newberger            mCurrentStateManager.setCurrentState(new SelectedState(selectedItem));
486a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        }
487a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
488a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        /**
489a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu         * Snaps back the mode list and go to the fully hidden state.
490a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu         */
491a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        private void snapBackAndHide() {
492a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            mAnimator = snapBack(true);
493a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            if (mAnimator != null) {
494a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                mAnimator.addListener(new Animator.AnimatorListener() {
495a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                    @Override
496a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                    public void onAnimationStart(Animator animation) {
497a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
498a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                    }
499a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
500a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                    @Override
501a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                    public void onAnimationEnd(Animator animation) {
502a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                        mAnimator = null;
50326e901ed46054fb6cc2cb4c09bef3a779a1cbf78Alan Newberger                        mCurrentStateManager.setCurrentState(new FullyHiddenState());
504a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                    }
505a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
506a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                    @Override
507a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                    public void onAnimationCancel(Animator animation) {
508a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
509a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                    }
510a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
511a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                    @Override
512a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                    public void onAnimationRepeat(Animator animation) {
513a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
514a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                    }
515a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                });
516a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            } else {
51726e901ed46054fb6cc2cb4c09bef3a779a1cbf78Alan Newberger                mCurrentStateManager.setCurrentState(new FullyHiddenState());
518a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            }
519a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        }
520a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
521a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        @Override
522a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        public void hide() {
523a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            if (mAnimator != null) {
524a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                mAnimator.cancel();
525a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            } else {
52626e901ed46054fb6cc2cb4c09bef3a779a1cbf78Alan Newberger                mCurrentStateManager.setCurrentState(new FullyHiddenState());
527a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            }
528a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        }
52926e901ed46054fb6cc2cb4c09bef3a779a1cbf78Alan Newberger
53026e901ed46054fb6cc2cb4c09bef3a779a1cbf78Alan Newberger        @Override
53126e901ed46054fb6cc2cb4c09bef3a779a1cbf78Alan Newberger        public void onCurrentState() {
53226aa92bf5942bfde16621c6833c14ba0e1ea8b5eDoris Liu            announceForAccessibility(
53326aa92bf5942bfde16621c6833c14ba0e1ea8b5eDoris Liu                    getContext().getResources().getString(R.string.accessibility_mode_list_shown));
53426aa92bf5942bfde16621c6833c14ba0e1ea8b5eDoris Liu            showSettingsClingIfEnabled(true);
53526e901ed46054fb6cc2cb4c09bef3a779a1cbf78Alan Newberger        }
536a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu    }
537a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
538a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu    /**
539a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu     * Shimmy state handles the specifics for shimmy animation, including
540a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu     * setting up to show mode drawer (without text) and hide it with shimmy animation.
541a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu     *
542a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu     * This state can be interrupted when scrolling or mode selection happened,
543a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu     * in which case the state will transition into ScrollingState, or SelectedState.
544a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu     * Otherwise, after shimmy finishes successfully, a transition to fully hidden
545a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu     * state will happen.
546a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu     */
547a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu    private class ShimmyState extends ModeListState {
548a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
549a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        private boolean mStartHidingShimmyWhenWindowGainsFocus = false;
550a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        private Animator mAnimator = null;
551a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        private final Runnable mHideShimmy = new Runnable() {
552a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            @Override
553a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            public void run() {
554a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                startHidingShimmy();
555a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            }
556a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        };
557a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
558a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        public ShimmyState() {
5596c5a5375524efe7e5160f92edcaad121c7fbdb07Doris Liu            setVisibility(VISIBLE);
560a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            mSettingsButton.setVisibility(INVISIBLE);
561a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            mModeListOpenFactor = 0f;
562a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            onModeListOpenRatioUpdate(0);
563a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            int maxVisibleWidth = mModeSelectorItems[0].getMaxVisibleWidth();
564a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            for (int i = 0; i < mModeSelectorItems.length; i++) {
565a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                mModeSelectorItems[i].setVisibleWidth(maxVisibleWidth);
566a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            }
567a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            if (hasWindowFocus()) {
568a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                hideShimmyWithDelay();
569a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            } else {
570a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                mStartHidingShimmyWhenWindowGainsFocus = true;
571a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            }
572a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        }
573a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
574a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        @Override
575a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
576a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            // Scroll happens during accordion animation.
577a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            cancelAnimation();
5781c5a87b4301c4211d3e3b85be5c863c50d4eab7bDoris Liu            cancelForwardingTouchEvent();
579a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            // Go to scrolling state
58026e901ed46054fb6cc2cb4c09bef3a779a1cbf78Alan Newberger            mCurrentStateManager.setCurrentState(new ScrollingState());
58110c5816e1ff3d30e4c4996643ba05d63d522b81aAndy Huibers            UsageStatistics.instance().controlUsed(
58210c5816e1ff3d30e4c4996643ba05d63d522b81aAndy Huibers                    eventprotos.ControlEvent.ControlType.MENU_SCROLL_FROM_SHIMMY);
583a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            return true;
584a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        }
585a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
586a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        @Override
587a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        public boolean shouldHandleTouchEvent(MotionEvent ev) {
588a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            if (MotionEvent.ACTION_DOWN == ev.getActionMasked()) {
589a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                if (isTouchInsideList(ev) &&
590a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                        ev.getX() <= mModeSelectorItems[0].getMaxVisibleWidth()) {
591a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                    mChildViewTouched = mModeSelectorItems[getFocusItem(ev.getX(), ev.getY())];
592a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                    return true;
593a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                }
594a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                // If shimmy is on-going, reject the first down event, so that it can be handled
595a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                // by the view underneath. If a swipe is detected, the same series of touch will
596a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                // re-enter this function, in which case we will consume the touch events.
597a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                if (mLastDownTime != ev.getDownTime()) {
598a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                    mLastDownTime = ev.getDownTime();
599a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                    return false;
600a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                }
601a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            }
602a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            return true;
6036c5a5375524efe7e5160f92edcaad121c7fbdb07Doris Liu        }
6046c5a5375524efe7e5160f92edcaad121c7fbdb07Doris Liu
6056c5a5375524efe7e5160f92edcaad121c7fbdb07Doris Liu        @Override
606a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        public boolean onTouchEvent(MotionEvent ev) {
607a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            if (MotionEvent.ACTION_DOWN == ev.getActionMasked()) {
608a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                if (ev.getActionMasked() == MotionEvent.ACTION_DOWN) {
609a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                    mFocusItem = getFocusItem(ev.getX(), ev.getY());
610a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                    setSwipeMode(true);
611a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                }
612a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            }
613a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            forwardTouchEventToChild(ev);
614a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            return true;
615a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        }
616a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
617a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        @Override
618a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        public void onItemSelected(ModeSelectorItem selectedItem) {
619a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            cancelAnimation();
62026e901ed46054fb6cc2cb4c09bef3a779a1cbf78Alan Newberger            mCurrentStateManager.setCurrentState(new SelectedState(selectedItem));
621a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        }
622a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
623a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        private void hideShimmyWithDelay() {
624a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            postDelayed(mHideShimmy, HIDE_SHIMMY_DELAY_MS);
625a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        }
626a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
627a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        @Override
628a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        public void onWindowFocusChanged(boolean hasFocus) {
629a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            if (mStartHidingShimmyWhenWindowGainsFocus && hasFocus) {
630a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                mStartHidingShimmyWhenWindowGainsFocus = false;
631a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                hideShimmyWithDelay();
632a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            }
633a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        }
634a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
635a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        /**
636a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu         * This starts the accordion animation, unless it's already running, in which
637a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu         * case the start animation call will be ignored.
638a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu         */
639a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        private void startHidingShimmy() {
640a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            if (mAnimator != null) {
64169cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu                return;
64269cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu            }
643a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            int maxVisibleWidth = mModeSelectorItems[0].getMaxVisibleWidth();
644a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            mAnimator = animateListToWidth(START_DELAY_MS * (-1), TOTAL_DURATION_MS,
645a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                    Gusterpolator.INSTANCE, maxVisibleWidth, 0);
646a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            mAnimator.addListener(new Animator.AnimatorListener() {
647a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                private boolean mSuccess = true;
648a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                @Override
649a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                public void onAnimationStart(Animator animation) {
650a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                    // Do nothing.
651a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                }
652a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
653a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                @Override
654a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                public void onAnimationEnd(Animator animation) {
655a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                    mAnimator = null;
656a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                    ShimmyState.this.onAnimationEnd(mSuccess);
657a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                }
658a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
659a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                @Override
660a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                public void onAnimationCancel(Animator animation) {
661a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                    mSuccess = false;
662a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                }
663a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
664a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                @Override
665a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                public void onAnimationRepeat(Animator animation) {
666a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                    // Do nothing.
667a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                }
668a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            });
669a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        }
670a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
671a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        /**
672a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu         * Cancels the pending/on-going animation.
673a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu         */
674a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        private void cancelAnimation() {
675a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            removeCallbacks(mHideShimmy);
676a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            if (mAnimator != null && mAnimator.isRunning()) {
677a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                mAnimator.cancel();
678a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            } else {
679a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                mAnimator = null;
680a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                onAnimationEnd(false);
681a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            }
682a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        }
683a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
684c064750b7a02c4e495a30d2ec8d25319a9e49e9cSpike Sprague        @Override
685c064750b7a02c4e495a30d2ec8d25319a9e49e9cSpike Sprague        public void onCurrentState() {
686c064750b7a02c4e495a30d2ec8d25319a9e49e9cSpike Sprague            super.onCurrentState();
687c064750b7a02c4e495a30d2ec8d25319a9e49e9cSpike Sprague            ModeListView.this.disableA11yOnModeSelectorItems();
688c064750b7a02c4e495a30d2ec8d25319a9e49e9cSpike Sprague        }
689a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        /**
690a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu         * Gets called when the animation finishes or gets canceled.
691a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu         *
692a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu         * @param success indicates whether the animation finishes successfully
693a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu         */
694a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        private void onAnimationEnd(boolean success) {
695a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            mSettingsButton.setVisibility(VISIBLE);
696a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            // If successfully finish hiding shimmy, then we should go back to
697a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            // fully hidden state.
698a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            if (success) {
699c064750b7a02c4e495a30d2ec8d25319a9e49e9cSpike Sprague                ModeListView.this.enableA11yOnModeSelectorItems();
700a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                mModeListOpenFactor = 1;
70126e901ed46054fb6cc2cb4c09bef3a779a1cbf78Alan Newberger                mCurrentStateManager.setCurrentState(new FullyHiddenState());
702a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                return;
7036c5a5375524efe7e5160f92edcaad121c7fbdb07Doris Liu            }
704a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
705a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            // If the animation was canceled before it's finished, animate the mode
706a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            // list open factor from 0 to 1 to ensure a smooth visual transition.
707a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            final ValueAnimator openFactorAnimator = ValueAnimator.ofFloat(mModeListOpenFactor, 1f);
708a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            openFactorAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
709a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                @Override
710a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                public void onAnimationUpdate(ValueAnimator animation) {
711a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                    mModeListOpenFactor = (Float) openFactorAnimator.getAnimatedValue();
712a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                    onVisibleWidthChanged(mVisibleWidth);
713a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                }
714a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            });
715a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            openFactorAnimator.addListener(new Animator.AnimatorListener() {
716a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                @Override
717a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                public void onAnimationStart(Animator animation) {
718a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                    // Do nothing.
719a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                }
720a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
721a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                @Override
722a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                public void onAnimationEnd(Animator animation) {
723a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                    mModeListOpenFactor = 1f;
724a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                }
725a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
726a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                @Override
727a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                public void onAnimationCancel(Animator animation) {
728a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                    // Do nothing.
729a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                }
730a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
731a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                @Override
732a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                public void onAnimationRepeat(Animator animation) {
733a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                    // Do nothing.
734a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                }
735a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            });
736a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            openFactorAnimator.start();
7376c5a5375524efe7e5160f92edcaad121c7fbdb07Doris Liu        }
7386c5a5375524efe7e5160f92edcaad121c7fbdb07Doris Liu
7396c5a5375524efe7e5160f92edcaad121c7fbdb07Doris Liu        @Override
740a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        public void hide() {
741a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            cancelAnimation();
74226e901ed46054fb6cc2cb4c09bef3a779a1cbf78Alan Newberger            mCurrentStateManager.setCurrentState(new FullyHiddenState());
74326e901ed46054fb6cc2cb4c09bef3a779a1cbf78Alan Newberger        }
74426e901ed46054fb6cc2cb4c09bef3a779a1cbf78Alan Newberger
745a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu    }
746a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
747a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu    /**
748a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu     * When the mode list is being scrolled, it will be in ScrollingState. From
749a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu     * this state, the mode list could transition to fully hidden, fully open
750a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu     * depending on which direction the scrolling goes.
751a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu     */
752a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu    private class ScrollingState extends ModeListState {
753a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        private Animator mAnimator = null;
754a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
755a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        public ScrollingState() {
756a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            setVisibility(VISIBLE);
7576c5a5375524efe7e5160f92edcaad121c7fbdb07Doris Liu        }
7586c5a5375524efe7e5160f92edcaad121c7fbdb07Doris Liu
7596c5a5375524efe7e5160f92edcaad121c7fbdb07Doris Liu        @Override
760a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
761a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            // Scroll based on the scrolling distance on the currently focused
762a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            // item.
763a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            scroll(mFocusItem, distanceX * SCROLL_FACTOR,
764a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                    distanceY * SCROLL_FACTOR);
765a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            return true;
766a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        }
7676c5a5375524efe7e5160f92edcaad121c7fbdb07Doris Liu
768a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        @Override
769a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        public boolean shouldHandleTouchEvent(MotionEvent ev) {
770a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            // If the snap back/to full screen animation is on going, ignore any
771a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            // touch.
772a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            if (mAnimator != null) {
773a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                return false;
774a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            }
775a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            return true;
7766c5a5375524efe7e5160f92edcaad121c7fbdb07Doris Liu        }
77769cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu
778a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        @Override
779a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        public boolean onTouchEvent(MotionEvent ev) {
780a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            if (ev.getActionMasked() == MotionEvent.ACTION_UP ||
781a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                    ev.getActionMasked() == MotionEvent.ACTION_CANCEL) {
782a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                final boolean shouldSnapBack = shouldSnapBack();
783a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                if (shouldSnapBack) {
784a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                    mAnimator = snapBack();
785a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                } else {
786a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                    mAnimator = snapToFullScreen();
787a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                }
788a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                mAnimator.addListener(new Animator.AnimatorListener() {
789a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                    @Override
790a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                    public void onAnimationStart(Animator animation) {
791a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
792a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                    }
793a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
794a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                    @Override
795a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                    public void onAnimationEnd(Animator animation) {
796a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                        mAnimator = null;
797a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                        mFocusItem = NO_ITEM_SELECTED;
798a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                        if (shouldSnapBack) {
79926e901ed46054fb6cc2cb4c09bef3a779a1cbf78Alan Newberger                            mCurrentStateManager.setCurrentState(new FullyHiddenState());
800a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                        } else {
80126e901ed46054fb6cc2cb4c09bef3a779a1cbf78Alan Newberger                            mCurrentStateManager.setCurrentState(new FullyShownState());
80210c5816e1ff3d30e4c4996643ba05d63d522b81aAndy Huibers                            UsageStatistics.instance().controlUsed(
80310c5816e1ff3d30e4c4996643ba05d63d522b81aAndy Huibers                                    eventprotos.ControlEvent.ControlType.MENU_FULL_FROM_SCROLL);
804a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                        }
805a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                    }
806a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
807a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                    @Override
808a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                    public void onAnimationCancel(Animator animation) {
809a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
810a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                    }
811a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
812a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                    @Override
813a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                    public void onAnimationRepeat(Animator animation) {
814a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
815a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                    }
816a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                });
817a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            }
818a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            return true;
819a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        }
820a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu    }
821a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
822a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu    /**
823a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu     * Mode list gets in this state when a mode item has been selected/clicked.
824a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu     * There will be an animation with the blurred preview fading in, a potential
825a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu     * pause to wait for the new mode to be ready, and then the new mode will
826a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu     * be revealed through a pinhole animation. After all the animations finish,
827a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu     * mode list will transition into fully hidden state.
828a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu     */
8293c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague    private class SelectedState extends ModeListState {
830a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        public SelectedState(ModeSelectorItem selectedItem) {
831a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            final int modeId = selectedItem.getModeId();
832a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            // Un-highlight all the modes.
833a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            for (int i = 0; i < mModeSelectorItems.length; i++) {
834a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                mModeSelectorItems[i].setSelected(false);
835a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            }
8360496fcaa51f88f6c753975473b971941e4090cfaSpike Sprague
837a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            PeepholeAnimationEffect effect = new PeepholeAnimationEffect();
838a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            effect.setSize(mWidth, mHeight);
839a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
840a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            // Calculate the position of the icon in the selected item, and
841a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            // start animation from that position.
842a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            int[] location = new int[2];
843a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            // Gets icon's center position in relative to the window.
844a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            selectedItem.getIconCenterLocationInWindow(location);
845a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            int iconX = location[0];
846a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            int iconY = location[1];
847a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            // Gets current view's top left position relative to the window.
848a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            getLocationInWindow(location);
849a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            // Calculate icon location relative to this view
850a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            iconX -= location[0];
851a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            iconY -= location[1];
852a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
853a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            effect.setAnimationStartingPosition(iconX, iconY);
8540496fcaa51f88f6c753975473b971941e4090cfaSpike Sprague            effect.setModeSpecificColor(selectedItem.getHighlightColor());
855a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            if (mScreenShotProvider != null) {
856a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                effect.setBackground(mScreenShotProvider
85728a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu                        .getPreviewFrame(PREVIEW_DOWN_SAMPLE_FACTOR),
85828a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu                        mCaptureLayoutHelper.getPreviewRect());
859a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                effect.setBackgroundOverlay(mScreenShotProvider.getPreviewOverlayAndControls());
860a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            }
861a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            mCurrentAnimationEffects = effect;
8620496fcaa51f88f6c753975473b971941e4090cfaSpike Sprague            effect.startFadeoutAnimation(null, selectedItem, iconX, iconY, modeId);
863a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            invalidate();
864a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        }
865a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
866a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        @Override
867a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        public boolean shouldHandleTouchEvent(MotionEvent ev) {
868a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            return false;
869a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        }
870a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
871a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        @Override
872a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        public void startModeSelectionAnimation() {
8730496fcaa51f88f6c753975473b971941e4090cfaSpike Sprague            mCurrentAnimationEffects.startAnimation(new AnimatorListenerAdapter() {
874a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                @Override
875a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                public void onAnimationEnd(Animator animation) {
876a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                    mCurrentAnimationEffects = null;
87726e901ed46054fb6cc2cb4c09bef3a779a1cbf78Alan Newberger                    mCurrentStateManager.setCurrentState(new FullyHiddenState());
878a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                }
879a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            });
88069cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu        }
881fa50f7d71a77dc730746c33fd17a570d42453759Doris Liu
882fa50f7d71a77dc730746c33fd17a570d42453759Doris Liu        @Override
883fa50f7d71a77dc730746c33fd17a570d42453759Doris Liu        public void hide() {
884fa50f7d71a77dc730746c33fd17a570d42453759Doris Liu            if (!mCurrentAnimationEffects.cancelAnimation()) {
885fa50f7d71a77dc730746c33fd17a570d42453759Doris Liu                mCurrentAnimationEffects = null;
886fa50f7d71a77dc730746c33fd17a570d42453759Doris Liu                mCurrentStateManager.setCurrentState(new FullyHiddenState());
887fa50f7d71a77dc730746c33fd17a570d42453759Doris Liu            }
888fa50f7d71a77dc730746c33fd17a570d42453759Doris Liu        }
88969cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu    }
8901c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu
8911c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu    public interface ModeSwitchListener {
8921c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu        public void onModeSelected(int modeIndex);
893213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu        public int getCurrentModeIndex();
8949d264309a341c52601bdccdeff6647e0ce58c31fDoris Liu        public void onSettingsSelected();
8951c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu    }
8961c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu
8972c559c4bd3c65e0cb0bca06f30e3bc979038e6f7Doris Liu    public interface ModeListOpenListener {
8989d264309a341c52601bdccdeff6647e0ce58c31fDoris Liu        /**
8999d264309a341c52601bdccdeff6647e0ce58c31fDoris Liu         * Mode list will open to full screen after current animation.
9009d264309a341c52601bdccdeff6647e0ce58c31fDoris Liu         */
9012c559c4bd3c65e0cb0bca06f30e3bc979038e6f7Doris Liu        public void onOpenFullScreen();
9029d264309a341c52601bdccdeff6647e0ce58c31fDoris Liu
9039d264309a341c52601bdccdeff6647e0ce58c31fDoris Liu        /**
9049d264309a341c52601bdccdeff6647e0ce58c31fDoris Liu         * Updates the listener with the current progress of mode drawer opening.
9059d264309a341c52601bdccdeff6647e0ce58c31fDoris Liu         *
9069d264309a341c52601bdccdeff6647e0ce58c31fDoris Liu         * @param progress progress of the mode drawer opening, ranging [0f, 1f]
9079d264309a341c52601bdccdeff6647e0ce58c31fDoris Liu         *                 0 means mode drawer is fully closed, 1 indicates a fully
9089d264309a341c52601bdccdeff6647e0ce58c31fDoris Liu         *                 open mode drawer.
9099d264309a341c52601bdccdeff6647e0ce58c31fDoris Liu         */
9109d264309a341c52601bdccdeff6647e0ce58c31fDoris Liu        public void onModeListOpenProgress(float progress);
9119d264309a341c52601bdccdeff6647e0ce58c31fDoris Liu
9129d264309a341c52601bdccdeff6647e0ce58c31fDoris Liu        /**
9139d264309a341c52601bdccdeff6647e0ce58c31fDoris Liu         * Gets called when mode list is completely closed.
9149d264309a341c52601bdccdeff6647e0ce58c31fDoris Liu         */
9159d264309a341c52601bdccdeff6647e0ce58c31fDoris Liu        public void onModeListClosed();
9162c559c4bd3c65e0cb0bca06f30e3bc979038e6f7Doris Liu    }
9172c559c4bd3c65e0cb0bca06f30e3bc979038e6f7Doris Liu
9188c1a922df154182b53f00c38198db7da0b4feb20Sascha Haeberling    public static abstract class ModeListVisibilityChangedListener {
919b49ab283fe6fce242901a1a09fc2b148b27fe123Sascha Haeberling        private Boolean mCurrentVisibility = null;
9208c1a922df154182b53f00c38198db7da0b4feb20Sascha Haeberling
9218c1a922df154182b53f00c38198db7da0b4feb20Sascha Haeberling        /** Whether the mode list is (partially or fully) visible. */
9228c1a922df154182b53f00c38198db7da0b4feb20Sascha Haeberling        public abstract void onVisibilityChanged(boolean visible);
9238c1a922df154182b53f00c38198db7da0b4feb20Sascha Haeberling
9248c1a922df154182b53f00c38198db7da0b4feb20Sascha Haeberling        /**
9258c1a922df154182b53f00c38198db7da0b4feb20Sascha Haeberling         * Internal method to be called by the mode list whenever a visibility
9268c1a922df154182b53f00c38198db7da0b4feb20Sascha Haeberling         * even occurs.
9278c1a922df154182b53f00c38198db7da0b4feb20Sascha Haeberling         * <p>
9288c1a922df154182b53f00c38198db7da0b4feb20Sascha Haeberling         * Do not call {@link #onVisibilityChanged(boolean)} directly, as this
9298c1a922df154182b53f00c38198db7da0b4feb20Sascha Haeberling         * is only called when the visibility has actually changed and not on
9308c1a922df154182b53f00c38198db7da0b4feb20Sascha Haeberling         * each visibility event.
9318c1a922df154182b53f00c38198db7da0b4feb20Sascha Haeberling         *
9328c1a922df154182b53f00c38198db7da0b4feb20Sascha Haeberling         * @param visible whether the mode drawer is currently visible.
9338c1a922df154182b53f00c38198db7da0b4feb20Sascha Haeberling         */
9348c1a922df154182b53f00c38198db7da0b4feb20Sascha Haeberling        private void onVisibilityEvent(boolean visible) {
935b49ab283fe6fce242901a1a09fc2b148b27fe123Sascha Haeberling            if (mCurrentVisibility == null || mCurrentVisibility != visible) {
936b49ab283fe6fce242901a1a09fc2b148b27fe123Sascha Haeberling                mCurrentVisibility = visible;
9378c1a922df154182b53f00c38198db7da0b4feb20Sascha Haeberling                onVisibilityChanged(visible);
938b49ab283fe6fce242901a1a09fc2b148b27fe123Sascha Haeberling            }
9398c1a922df154182b53f00c38198db7da0b4feb20Sascha Haeberling        }
9408c1a922df154182b53f00c38198db7da0b4feb20Sascha Haeberling    }
9418c1a922df154182b53f00c38198db7da0b4feb20Sascha Haeberling
94270576b611fddc929abbfb466fa44c396a3a793dbDoris Liu    /**
943cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu     * This class aims to help store time and position in pairs.
944cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu     */
945cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu    private static class TimeBasedPosition {
946601921e746832bdef40503c606a95b4469f1bedbSascha Haeberling        private final float mPosition;
947601921e746832bdef40503c606a95b4469f1bedbSascha Haeberling        private final long mTimeStamp;
948cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu        public TimeBasedPosition(float position, long time) {
949cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu            mPosition = position;
950cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu            mTimeStamp = time;
951cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu        }
952cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu
953cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu        public float getPosition() {
954cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu            return mPosition;
955cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu        }
956cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu
957cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu        public long getTimeStamp() {
958cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu            return mTimeStamp;
959cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu        }
960cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu    }
961cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu
962cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu    /**
96370576b611fddc929abbfb466fa44c396a3a793dbDoris Liu     * This is a highly customized interpolator. The purpose of having this subclass
96470576b611fddc929abbfb466fa44c396a3a793dbDoris Liu     * is to encapsulate intricate animation timing, so that the actual animation
96570576b611fddc929abbfb466fa44c396a3a793dbDoris Liu     * implementation can be re-used with other interpolators to achieve different
96670576b611fddc929abbfb466fa44c396a3a793dbDoris Liu     * animation effects.
96770576b611fddc929abbfb466fa44c396a3a793dbDoris Liu     *
96870576b611fddc929abbfb466fa44c396a3a793dbDoris Liu     * The accordion animation consists of three stages:
96970576b611fddc929abbfb466fa44c396a3a793dbDoris Liu     * 1) Animate into the screen within a pre-specified fly in duration.
97070576b611fddc929abbfb466fa44c396a3a793dbDoris Liu     * 2) Hold in place for a certain amount of time (Optional).
97170576b611fddc929abbfb466fa44c396a3a793dbDoris Liu     * 3) Animate out of the screen within the given time.
97270576b611fddc929abbfb466fa44c396a3a793dbDoris Liu     *
97370576b611fddc929abbfb466fa44c396a3a793dbDoris Liu     * The accordion animator is initialized with 3 parameter: 1) initial position,
97470576b611fddc929abbfb466fa44c396a3a793dbDoris Liu     * 2) how far out the view should be before flying back out,  3) end position.
97570576b611fddc929abbfb466fa44c396a3a793dbDoris Liu     * The interpolation output should be [0f, 0.5f] during animation between 1)
97670576b611fddc929abbfb466fa44c396a3a793dbDoris Liu     * to 2), and [0.5f, 1f] for flying from 2) to 3).
97770576b611fddc929abbfb466fa44c396a3a793dbDoris Liu     */
978601921e746832bdef40503c606a95b4469f1bedbSascha Haeberling    private final TimeInterpolator mAccordionInterpolator = new TimeInterpolator() {
9791c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu        @Override
9801c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu        public float getInterpolation(float input) {
9811c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu
9821c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu            float flyInDuration = (float) FLY_OUT_DURATION_MS / (float) TOTAL_DURATION_MS;
9831c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu            float holdDuration = (float) (FLY_OUT_DURATION_MS + HOLD_DURATION_MS)
9841c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu                    / (float) TOTAL_DURATION_MS;
9851c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu            if (input == 0) {
9861c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu                return 0;
98769cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu            } else if (input < flyInDuration) {
98870576b611fddc929abbfb466fa44c396a3a793dbDoris Liu                // Stage 1, project result to [0f, 0.5f]
9891c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu                input /= flyInDuration;
9901c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu                float result = Gusterpolator.INSTANCE.getInterpolation(input);
9911c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu                return result * 0.5f;
9921c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu            } else if (input < holdDuration) {
99370576b611fddc929abbfb466fa44c396a3a793dbDoris Liu                // Stage 2
9941c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu                return 0.5f;
9951c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu            } else {
99670576b611fddc929abbfb466fa44c396a3a793dbDoris Liu                // Stage 3, project result to [0.5f, 1f]
9971c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu                input -= holdDuration;
9981c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu                input /= (1 - holdDuration);
9991c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu                float result = Gusterpolator.INSTANCE.getInterpolation(input);
10001c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu                return 0.5f + result * 0.5f;
10011c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu            }
10021c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu        }
10031c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu    };
10041c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu
100570576b611fddc929abbfb466fa44c396a3a793dbDoris Liu    /**
100670576b611fddc929abbfb466fa44c396a3a793dbDoris Liu     * The listener that is used to notify when gestures occur.
100770576b611fddc929abbfb466fa44c396a3a793dbDoris Liu     * Here we only listen to a subset of gestures.
100870576b611fddc929abbfb466fa44c396a3a793dbDoris Liu     */
1009601921e746832bdef40503c606a95b4469f1bedbSascha Haeberling    private final GestureDetector.OnGestureListener mOnGestureListener
10101c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu            = new GestureDetector.SimpleOnGestureListener(){
101170576b611fddc929abbfb466fa44c396a3a793dbDoris Liu        @Override
10121c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu        public boolean onScroll(MotionEvent e1, MotionEvent e2,
10131c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu                                float distanceX, float distanceY) {
101426e901ed46054fb6cc2cb4c09bef3a779a1cbf78Alan Newberger            mCurrentStateManager.getCurrentState().onScroll(e1, e2, distanceX, distanceY);
101569cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu            mLastScrollTime = System.currentTimeMillis();
10161c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu            return true;
10171c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu        }
10181c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu
10191c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu        @Override
10201c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu        public boolean onSingleTapUp(MotionEvent ev) {
102126e901ed46054fb6cc2cb4c09bef3a779a1cbf78Alan Newberger            mCurrentStateManager.getCurrentState().onSingleTapUp(ev);
10221c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu            return true;
10231c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu        }
10246c5a5375524efe7e5160f92edcaad121c7fbdb07Doris Liu
10256c5a5375524efe7e5160f92edcaad121c7fbdb07Doris Liu        @Override
10266c5a5375524efe7e5160f92edcaad121c7fbdb07Doris Liu        public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
10276c5a5375524efe7e5160f92edcaad121c7fbdb07Doris Liu            // Cache velocity in the unit pixel/ms.
1028213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu            mVelocityX = velocityX / 1000f * SCROLL_FACTOR;
102926e901ed46054fb6cc2cb4c09bef3a779a1cbf78Alan Newberger            mCurrentStateManager.getCurrentState().onFling(e1, e2, velocityX, velocityY);
1030a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            return true;
1031a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        }
1032a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
1033a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        @Override
1034a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        public boolean onDown(MotionEvent ev) {
1035a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            mVelocityX = 0;
103626e901ed46054fb6cc2cb4c09bef3a779a1cbf78Alan Newberger            mCurrentStateManager.getCurrentState().onDown(ev);
10376c5a5375524efe7e5160f92edcaad121c7fbdb07Doris Liu            return true;
10386c5a5375524efe7e5160f92edcaad121c7fbdb07Doris Liu        }
10391c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu    };
10401c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu
10416ababe9379ecee4056a415085e28e7eb5e1249bfDoris Liu    /**
10426ababe9379ecee4056a415085e28e7eb5e1249bfDoris Liu     * Gets called when a mode item in the mode drawer is clicked.
10436ababe9379ecee4056a415085e28e7eb5e1249bfDoris Liu     *
10446ababe9379ecee4056a415085e28e7eb5e1249bfDoris Liu     * @param selectedItem the item being clicked
10456ababe9379ecee4056a415085e28e7eb5e1249bfDoris Liu     */
10466ababe9379ecee4056a415085e28e7eb5e1249bfDoris Liu    private void onItemSelected(ModeSelectorItem selectedItem) {
104726e901ed46054fb6cc2cb4c09bef3a779a1cbf78Alan Newberger        mCurrentStateManager.getCurrentState().onItemSelected(selectedItem);
10486ababe9379ecee4056a415085e28e7eb5e1249bfDoris Liu    }
10496ababe9379ecee4056a415085e28e7eb5e1249bfDoris Liu
10506ababe9379ecee4056a415085e28e7eb5e1249bfDoris Liu    /**
10516ababe9379ecee4056a415085e28e7eb5e1249bfDoris Liu     * Checks whether a touch event is inside of the bounds of the mode list.
10526ababe9379ecee4056a415085e28e7eb5e1249bfDoris Liu     *
10536ababe9379ecee4056a415085e28e7eb5e1249bfDoris Liu     * @param ev touch event to be checked
10546ababe9379ecee4056a415085e28e7eb5e1249bfDoris Liu     * @return whether the touch is inside the bounds of the mode list
10556ababe9379ecee4056a415085e28e7eb5e1249bfDoris Liu     */
10566ababe9379ecee4056a415085e28e7eb5e1249bfDoris Liu    private boolean isTouchInsideList(MotionEvent ev) {
10576ababe9379ecee4056a415085e28e7eb5e1249bfDoris Liu        // Ignore the tap if it happens outside of the mode list linear layout.
10586ababe9379ecee4056a415085e28e7eb5e1249bfDoris Liu        float x = ev.getX() - mListView.getX();
10596ababe9379ecee4056a415085e28e7eb5e1249bfDoris Liu        float y = ev.getY() - mListView.getY();
10606ababe9379ecee4056a415085e28e7eb5e1249bfDoris Liu        if (x < 0 || x > mListView.getWidth() || y < 0 || y > mListView.getHeight()) {
10616ababe9379ecee4056a415085e28e7eb5e1249bfDoris Liu            return false;
10626ababe9379ecee4056a415085e28e7eb5e1249bfDoris Liu        }
10636ababe9379ecee4056a415085e28e7eb5e1249bfDoris Liu        return true;
10646ababe9379ecee4056a415085e28e7eb5e1249bfDoris Liu    }
10656ababe9379ecee4056a415085e28e7eb5e1249bfDoris Liu
10661c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu    public ModeListView(Context context, AttributeSet attrs) {
10671c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu        super(context, attrs);
10681c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu        mGestureDetector = new GestureDetector(context, mOnGestureListener);
106970576b611fddc929abbfb466fa44c396a3a793dbDoris Liu        mListBackgroundColor = getResources().getColor(R.color.mode_list_background);
107028a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu        mSettingsButtonMargin = getResources().getDimensionPixelSize(
107128a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu                R.dimen.mode_list_settings_icon_margin);
1072e7e0c028d568c1a4c3e88ed75411b8b41c5aec69Doris Liu    }
1073e7e0c028d568c1a4c3e88ed75411b8b41c5aec69Doris Liu
1074c064750b7a02c4e495a30d2ec8d25319a9e49e9cSpike Sprague    private void disableA11yOnModeSelectorItems() {
1075c064750b7a02c4e495a30d2ec8d25319a9e49e9cSpike Sprague        for (View selectorItem : mModeSelectorItems) {
1076c064750b7a02c4e495a30d2ec8d25319a9e49e9cSpike Sprague            selectorItem.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
1077c064750b7a02c4e495a30d2ec8d25319a9e49e9cSpike Sprague        }
1078c064750b7a02c4e495a30d2ec8d25319a9e49e9cSpike Sprague    }
1079c064750b7a02c4e495a30d2ec8d25319a9e49e9cSpike Sprague
1080c064750b7a02c4e495a30d2ec8d25319a9e49e9cSpike Sprague    private void enableA11yOnModeSelectorItems() {
1081c064750b7a02c4e495a30d2ec8d25319a9e49e9cSpike Sprague        for (View selectorItem : mModeSelectorItems) {
1082c064750b7a02c4e495a30d2ec8d25319a9e49e9cSpike Sprague            selectorItem.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_AUTO);
1083c064750b7a02c4e495a30d2ec8d25319a9e49e9cSpike Sprague        }
1084c064750b7a02c4e495a30d2ec8d25319a9e49e9cSpike Sprague    }
1085c064750b7a02c4e495a30d2ec8d25319a9e49e9cSpike Sprague
108670576b611fddc929abbfb466fa44c396a3a793dbDoris Liu    /**
108770576b611fddc929abbfb466fa44c396a3a793dbDoris Liu     * Sets the alpha on the list background. This is called whenever the list
108870576b611fddc929abbfb466fa44c396a3a793dbDoris Liu     * is scrolling or animating, so that background can adjust its dimness.
108970576b611fddc929abbfb466fa44c396a3a793dbDoris Liu     *
109070576b611fddc929abbfb466fa44c396a3a793dbDoris Liu     * @param alpha new alpha to be applied on list background color
109170576b611fddc929abbfb466fa44c396a3a793dbDoris Liu     */
109270576b611fddc929abbfb466fa44c396a3a793dbDoris Liu    private void setBackgroundAlpha(int alpha) {
109370576b611fddc929abbfb466fa44c396a3a793dbDoris Liu        // Make sure alpha is valid.
109470576b611fddc929abbfb466fa44c396a3a793dbDoris Liu        alpha = alpha & 0xFF;
109570576b611fddc929abbfb466fa44c396a3a793dbDoris Liu        // Change alpha on the background color.
109670576b611fddc929abbfb466fa44c396a3a793dbDoris Liu        mListBackgroundColor = mListBackgroundColor & 0xFFFFFF;
109770576b611fddc929abbfb466fa44c396a3a793dbDoris Liu        mListBackgroundColor = mListBackgroundColor | (alpha << 24);
109870576b611fddc929abbfb466fa44c396a3a793dbDoris Liu        // Set new color to list background.
1099213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu        setBackgroundColor(mListBackgroundColor);
11001c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu    }
11011c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu
1102a20074f0efeaa62a29b05838110d04a1b7271418Doris Liu    /**
1103a20074f0efeaa62a29b05838110d04a1b7271418Doris Liu     * Initialize mode list with a list of indices of supported modes.
1104a20074f0efeaa62a29b05838110d04a1b7271418Doris Liu     *
1105a20074f0efeaa62a29b05838110d04a1b7271418Doris Liu     * @param modeIndexList a list of indices of supported modes
1106a20074f0efeaa62a29b05838110d04a1b7271418Doris Liu     */
1107a20074f0efeaa62a29b05838110d04a1b7271418Doris Liu    public void init(List<Integer> modeIndexList) {
1108bd1b8f910d6f2c94daac304f4bd1a098939474c3Doris Liu        int[] modeSequence = getResources()
1109bd1b8f910d6f2c94daac304f4bd1a098939474c3Doris Liu                .getIntArray(R.array.camera_modes_in_nav_drawer_if_supported);
1110bd1b8f910d6f2c94daac304f4bd1a098939474c3Doris Liu        int[] visibleModes = getResources()
1111bd1b8f910d6f2c94daac304f4bd1a098939474c3Doris Liu                .getIntArray(R.array.camera_modes_always_visible);
1112a20074f0efeaa62a29b05838110d04a1b7271418Doris Liu
1113a20074f0efeaa62a29b05838110d04a1b7271418Doris Liu        // Mark the supported modes in a boolean array to preserve the
1114a20074f0efeaa62a29b05838110d04a1b7271418Doris Liu        // sequence of the modes
11150496fcaa51f88f6c753975473b971941e4090cfaSpike Sprague        SparseBooleanArray modeIsSupported = new SparseBooleanArray();
1116a20074f0efeaa62a29b05838110d04a1b7271418Doris Liu        for (int i = 0; i < modeIndexList.size(); i++) {
1117a20074f0efeaa62a29b05838110d04a1b7271418Doris Liu            int mode = modeIndexList.get(i);
1118bd1b8f910d6f2c94daac304f4bd1a098939474c3Doris Liu            modeIsSupported.put(mode, true);
1119a20074f0efeaa62a29b05838110d04a1b7271418Doris Liu        }
1120bd1b8f910d6f2c94daac304f4bd1a098939474c3Doris Liu        for (int i = 0; i < visibleModes.length; i++) {
1121bd1b8f910d6f2c94daac304f4bd1a098939474c3Doris Liu            int mode = visibleModes[i];
1122bd1b8f910d6f2c94daac304f4bd1a098939474c3Doris Liu            modeIsSupported.put(mode, true);
1123bd1b8f910d6f2c94daac304f4bd1a098939474c3Doris Liu        }
1124bd1b8f910d6f2c94daac304f4bd1a098939474c3Doris Liu
1125a20074f0efeaa62a29b05838110d04a1b7271418Doris Liu        // Put the indices of supported modes into an array preserving their
1126a20074f0efeaa62a29b05838110d04a1b7271418Doris Liu        // display order.
1127bd1b8f910d6f2c94daac304f4bd1a098939474c3Doris Liu        mSupportedModes = new ArrayList<Integer>();
1128bd1b8f910d6f2c94daac304f4bd1a098939474c3Doris Liu        for (int i = 0; i < modeSequence.length; i++) {
1129bd1b8f910d6f2c94daac304f4bd1a098939474c3Doris Liu            int mode = modeSequence[i];
1130bd1b8f910d6f2c94daac304f4bd1a098939474c3Doris Liu            if (modeIsSupported.get(mode, false)) {
1131bd1b8f910d6f2c94daac304f4bd1a098939474c3Doris Liu                mSupportedModes.add(mode);
1132a20074f0efeaa62a29b05838110d04a1b7271418Doris Liu            }
1133a20074f0efeaa62a29b05838110d04a1b7271418Doris Liu        }
1134bd1b8f910d6f2c94daac304f4bd1a098939474c3Doris Liu        mTotalModes = mSupportedModes.size();
1135a20074f0efeaa62a29b05838110d04a1b7271418Doris Liu        initializeModeSelectorItems();
113628a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu        mSettingsButton = findViewById(R.id.settings_button);
11379d264309a341c52601bdccdeff6647e0ce58c31fDoris Liu        mSettingsButton.setOnClickListener(new OnClickListener() {
11389d264309a341c52601bdccdeff6647e0ce58c31fDoris Liu            @Override
11399d264309a341c52601bdccdeff6647e0ce58c31fDoris Liu            public void onClick(View v) {
11405842607d1b9ec2b5e751a8aaf43ff97cc3f85d22Doris Liu                // Post this callback to make sure current user interaction has
11415842607d1b9ec2b5e751a8aaf43ff97cc3f85d22Doris Liu                // been reflected in the UI. Specifically, the pressed state gets
11425842607d1b9ec2b5e751a8aaf43ff97cc3f85d22Doris Liu                // unset after click happens. In order to ensure the pressed state
11435842607d1b9ec2b5e751a8aaf43ff97cc3f85d22Doris Liu                // gets unset in UI before getting in the low frame rate settings
11445842607d1b9ec2b5e751a8aaf43ff97cc3f85d22Doris Liu                // activity launch stage, the settings selected callback is posted.
11455842607d1b9ec2b5e751a8aaf43ff97cc3f85d22Doris Liu                post(new Runnable() {
11465842607d1b9ec2b5e751a8aaf43ff97cc3f85d22Doris Liu                    @Override
11475842607d1b9ec2b5e751a8aaf43ff97cc3f85d22Doris Liu                    public void run() {
11485842607d1b9ec2b5e751a8aaf43ff97cc3f85d22Doris Liu                        mModeSwitchListener.onSettingsSelected();
11495842607d1b9ec2b5e751a8aaf43ff97cc3f85d22Doris Liu                    }
11505842607d1b9ec2b5e751a8aaf43ff97cc3f85d22Doris Liu                });
11519d264309a341c52601bdccdeff6647e0ce58c31fDoris Liu            }
11529d264309a341c52601bdccdeff6647e0ce58c31fDoris Liu        });
11538676e7802fb40f64a3b465c9b5e1a0a1ada9f699Doris Liu        // The mode list is initialized to be all the way closed.
11548676e7802fb40f64a3b465c9b5e1a0a1ada9f699Doris Liu        onModeListOpenRatioUpdate(0);
115526e901ed46054fb6cc2cb4c09bef3a779a1cbf78Alan Newberger        if (mCurrentStateManager.getCurrentState() == null) {
115626e901ed46054fb6cc2cb4c09bef3a779a1cbf78Alan Newberger            mCurrentStateManager.setCurrentState(new FullyHiddenState());
1157a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        }
1158a20074f0efeaa62a29b05838110d04a1b7271418Doris Liu    }
1159a20074f0efeaa62a29b05838110d04a1b7271418Doris Liu
1160213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu    /**
1161213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu     * Sets the screen shot provider for getting a preview frame and a bitmap
1162213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu     * of the controls and overlay.
1163213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu     */
1164213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu    public void setCameraModuleScreenShotProvider(
1165213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu            CameraAppUI.CameraModuleScreenShotProvider provider) {
1166213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu        mScreenShotProvider = provider;
1167213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu    }
1168213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu
1169a20074f0efeaa62a29b05838110d04a1b7271418Doris Liu    private void initializeModeSelectorItems() {
1170a20074f0efeaa62a29b05838110d04a1b7271418Doris Liu        mModeSelectorItems = new ModeSelectorItem[mTotalModes];
117170576b611fddc929abbfb466fa44c396a3a793dbDoris Liu        // Inflate the mode selector items and add them to a linear layout
11721c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu        LayoutInflater inflater = (LayoutInflater) getContext()
11731c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu                .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
11741c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu        mListView = (LinearLayout) findViewById(R.id.mode_list);
11751c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu        for (int i = 0; i < mTotalModes; i++) {
11766ababe9379ecee4056a415085e28e7eb5e1249bfDoris Liu            final ModeSelectorItem selectorItem =
11771c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu                    (ModeSelectorItem) inflater.inflate(R.layout.mode_selector, null);
11781c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu            mListView.addView(selectorItem);
1179e7e0c028d568c1a4c3e88ed75411b8b41c5aec69Doris Liu            // Sets the top padding of the top item to 0.
1180e7e0c028d568c1a4c3e88ed75411b8b41c5aec69Doris Liu            if (i == 0) {
1181e7e0c028d568c1a4c3e88ed75411b8b41c5aec69Doris Liu                selectorItem.setPadding(selectorItem.getPaddingLeft(), 0,
1182e7e0c028d568c1a4c3e88ed75411b8b41c5aec69Doris Liu                        selectorItem.getPaddingRight(), selectorItem.getPaddingBottom());
1183e7e0c028d568c1a4c3e88ed75411b8b41c5aec69Doris Liu            }
1184e7e0c028d568c1a4c3e88ed75411b8b41c5aec69Doris Liu            // Sets the bottom padding of the bottom item to 0.
1185e7e0c028d568c1a4c3e88ed75411b8b41c5aec69Doris Liu            if (i == mTotalModes - 1) {
1186e7e0c028d568c1a4c3e88ed75411b8b41c5aec69Doris Liu                selectorItem.setPadding(selectorItem.getPaddingLeft(), selectorItem.getPaddingTop(),
1187e7e0c028d568c1a4c3e88ed75411b8b41c5aec69Doris Liu                        selectorItem.getPaddingRight(), 0);
1188e7e0c028d568c1a4c3e88ed75411b8b41c5aec69Doris Liu            }
1189213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu
1190a20074f0efeaa62a29b05838110d04a1b7271418Doris Liu            int modeId = getModeIndex(i);
1191213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu            selectorItem.setHighlightColor(getResources()
1192bd1b8f910d6f2c94daac304f4bd1a098939474c3Doris Liu                    .getColor(CameraUtil.getCameraThemeColorId(modeId, getContext())));
11931c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu
11941c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu            // Set image
1195bd1b8f910d6f2c94daac304f4bd1a098939474c3Doris Liu            selectorItem.setImageResource(CameraUtil.getCameraModeIconResId(modeId, getContext()));
119670576b611fddc929abbfb466fa44c396a3a793dbDoris Liu
11971c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu            // Set text
1198bd1b8f910d6f2c94daac304f4bd1a098939474c3Doris Liu            selectorItem.setText(CameraUtil.getCameraModeText(modeId, getContext()));
119979718f6c6fb60aedd09edeedb2c16d1e2f9fe02dSpike Sprague
120079718f6c6fb60aedd09edeedb2c16d1e2f9fe02dSpike Sprague            // Set content description (for a11y)
120179718f6c6fb60aedd09edeedb2c16d1e2f9fe02dSpike Sprague            selectorItem.setContentDescription(CameraUtil
120279718f6c6fb60aedd09edeedb2c16d1e2f9fe02dSpike Sprague                    .getCameraModeContentDescription(modeId, getContext()));
12036ababe9379ecee4056a415085e28e7eb5e1249bfDoris Liu            selectorItem.setModeId(modeId);
12046ababe9379ecee4056a415085e28e7eb5e1249bfDoris Liu            selectorItem.setOnClickListener(new OnClickListener() {
12056ababe9379ecee4056a415085e28e7eb5e1249bfDoris Liu                @Override
12066ababe9379ecee4056a415085e28e7eb5e1249bfDoris Liu                public void onClick(View v) {
12076ababe9379ecee4056a415085e28e7eb5e1249bfDoris Liu                    onItemSelected(selectorItem);
12086ababe9379ecee4056a415085e28e7eb5e1249bfDoris Liu                }
12096ababe9379ecee4056a415085e28e7eb5e1249bfDoris Liu            });
121079718f6c6fb60aedd09edeedb2c16d1e2f9fe02dSpike Sprague
121170576b611fddc929abbfb466fa44c396a3a793dbDoris Liu            mModeSelectorItems[i] = selectorItem;
12121c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu        }
121369cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu        // During drawer opening/closing, we change the visible width of the mode
121469cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu        // items in sequence, so we listen to the last item's visible width change
121569cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu        // for a good timing to do corresponding UI adjustments.
121669cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu        mModeSelectorItems[mTotalModes - 1].setVisibleWidthChangedListener(this);
12171c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu        resetModeSelectors();
12181c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu    }
12191c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu
1220a20074f0efeaa62a29b05838110d04a1b7271418Doris Liu    /**
1221a20074f0efeaa62a29b05838110d04a1b7271418Doris Liu     * Maps between the UI mode selector index to the actual mode id.
1222a20074f0efeaa62a29b05838110d04a1b7271418Doris Liu     *
1223a20074f0efeaa62a29b05838110d04a1b7271418Doris Liu     * @param modeSelectorIndex the index of the UI item
1224a20074f0efeaa62a29b05838110d04a1b7271418Doris Liu     * @return the index of the corresponding camera mode
1225a20074f0efeaa62a29b05838110d04a1b7271418Doris Liu     */
1226a20074f0efeaa62a29b05838110d04a1b7271418Doris Liu    private int getModeIndex(int modeSelectorIndex) {
1227a20074f0efeaa62a29b05838110d04a1b7271418Doris Liu        if (modeSelectorIndex < mTotalModes && modeSelectorIndex >= 0) {
1228bd1b8f910d6f2c94daac304f4bd1a098939474c3Doris Liu            return mSupportedModes.get(modeSelectorIndex);
1229a20074f0efeaa62a29b05838110d04a1b7271418Doris Liu        }
12305596b4c902dcb685928b43678f428746ca5ffd08Angus Kong        Log.e(TAG, "Invalid mode selector index: " + modeSelectorIndex + ", total modes: " +
12315596b4c902dcb685928b43678f428746ca5ffd08Angus Kong                mTotalModes);
1232bd1b8f910d6f2c94daac304f4bd1a098939474c3Doris Liu        return getResources().getInteger(R.integer.camera_mode_photo);
1233a20074f0efeaa62a29b05838110d04a1b7271418Doris Liu    }
1234a20074f0efeaa62a29b05838110d04a1b7271418Doris Liu
123570576b611fddc929abbfb466fa44c396a3a793dbDoris Liu    /** Notify ModeSwitchListener, if any, of the mode change. */
12361c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu    private void onModeSelected(int modeIndex) {
12372c559c4bd3c65e0cb0bca06f30e3bc979038e6f7Doris Liu        if (mModeSwitchListener != null) {
12382c559c4bd3c65e0cb0bca06f30e3bc979038e6f7Doris Liu            mModeSwitchListener.onModeSelected(modeIndex);
12391c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu        }
12401c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu    }
12411c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu
12421c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu    /**
124370576b611fddc929abbfb466fa44c396a3a793dbDoris Liu     * Sets a listener that listens to receive mode switch event.
124470576b611fddc929abbfb466fa44c396a3a793dbDoris Liu     *
124570576b611fddc929abbfb466fa44c396a3a793dbDoris Liu     * @param listener a listener that gets notified when mode changes.
12461c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu     */
12471c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu    public void setModeSwitchListener(ModeSwitchListener listener) {
12482c559c4bd3c65e0cb0bca06f30e3bc979038e6f7Doris Liu        mModeSwitchListener = listener;
12492c559c4bd3c65e0cb0bca06f30e3bc979038e6f7Doris Liu    }
12502c559c4bd3c65e0cb0bca06f30e3bc979038e6f7Doris Liu
12512c559c4bd3c65e0cb0bca06f30e3bc979038e6f7Doris Liu    /**
12522c559c4bd3c65e0cb0bca06f30e3bc979038e6f7Doris Liu     * Sets a listener that gets notified when the mode list is open full screen.
12532c559c4bd3c65e0cb0bca06f30e3bc979038e6f7Doris Liu     *
12542c559c4bd3c65e0cb0bca06f30e3bc979038e6f7Doris Liu     * @param listener a listener that listens to mode list open events
12552c559c4bd3c65e0cb0bca06f30e3bc979038e6f7Doris Liu     */
12562c559c4bd3c65e0cb0bca06f30e3bc979038e6f7Doris Liu    public void setModeListOpenListener(ModeListOpenListener listener) {
12572c559c4bd3c65e0cb0bca06f30e3bc979038e6f7Doris Liu        mModeListOpenListener = listener;
12581c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu    }
12591c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu
12608c1a922df154182b53f00c38198db7da0b4feb20Sascha Haeberling    /**
12618c1a922df154182b53f00c38198db7da0b4feb20Sascha Haeberling     * Sets or replaces a listener that is called when the visibility of the
12628c1a922df154182b53f00c38198db7da0b4feb20Sascha Haeberling     * mode list changed.
12638c1a922df154182b53f00c38198db7da0b4feb20Sascha Haeberling     */
12648c1a922df154182b53f00c38198db7da0b4feb20Sascha Haeberling    public void setVisibilityChangedListener(ModeListVisibilityChangedListener listener) {
12658c1a922df154182b53f00c38198db7da0b4feb20Sascha Haeberling        mVisibilityChangedListener = listener;
12668c1a922df154182b53f00c38198db7da0b4feb20Sascha Haeberling    }
12678c1a922df154182b53f00c38198db7da0b4feb20Sascha Haeberling
12681c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu    @Override
12691c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu    public boolean onTouchEvent(MotionEvent ev) {
1270a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        // Reset touch forward recipient
1271a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        if (MotionEvent.ACTION_DOWN == ev.getActionMasked()) {
1272a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            mChildViewTouched = null;
1273b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liu        }
1274b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liu
127526e901ed46054fb6cc2cb4c09bef3a779a1cbf78Alan Newberger        if (!mCurrentStateManager.getCurrentState().shouldHandleTouchEvent(ev)) {
1276a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            return false;
127769cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu        }
12789eb517f4c4bcf00fbe435a56f89d3abcaaa4d881Doris Liu        getParent().requestDisallowInterceptTouchEvent(true);
12791c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu        super.onTouchEvent(ev);
1280908bace4c8ceb9ebf4644827350a0792cbd44331Seth Raphael
128170576b611fddc929abbfb466fa44c396a3a793dbDoris Liu        // Pass all touch events to gesture detector for gesture handling.
12821c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu        mGestureDetector.onTouchEvent(ev);
128326e901ed46054fb6cc2cb4c09bef3a779a1cbf78Alan Newberger        mCurrentStateManager.getCurrentState().onTouchEvent(ev);
12841c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu        return true;
12851c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu    }
12861c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu
12871c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu    /**
12886ababe9379ecee4056a415085e28e7eb5e1249bfDoris Liu     * Forward touch events to a recipient child view. Before feeding the motion
12896ababe9379ecee4056a415085e28e7eb5e1249bfDoris Liu     * event into the child view, the event needs to be converted in child view's
12906ababe9379ecee4056a415085e28e7eb5e1249bfDoris Liu     * coordinates.
12916ababe9379ecee4056a415085e28e7eb5e1249bfDoris Liu     */
12926ababe9379ecee4056a415085e28e7eb5e1249bfDoris Liu    private void forwardTouchEventToChild(MotionEvent ev) {
12936ababe9379ecee4056a415085e28e7eb5e1249bfDoris Liu        if (mChildViewTouched != null) {
12946ababe9379ecee4056a415085e28e7eb5e1249bfDoris Liu            float x = ev.getX() - mListView.getX();
12956ababe9379ecee4056a415085e28e7eb5e1249bfDoris Liu            float y = ev.getY() - mListView.getY();
12966ababe9379ecee4056a415085e28e7eb5e1249bfDoris Liu            x -= mChildViewTouched.getLeft();
12976ababe9379ecee4056a415085e28e7eb5e1249bfDoris Liu            y -= mChildViewTouched.getTop();
12986ababe9379ecee4056a415085e28e7eb5e1249bfDoris Liu
12996ababe9379ecee4056a415085e28e7eb5e1249bfDoris Liu            mLastChildTouchEvent = MotionEvent.obtain(ev);
13006ababe9379ecee4056a415085e28e7eb5e1249bfDoris Liu            mLastChildTouchEvent.setLocation(x, y);
13016ababe9379ecee4056a415085e28e7eb5e1249bfDoris Liu            mChildViewTouched.onTouchEvent(mLastChildTouchEvent);
13026ababe9379ecee4056a415085e28e7eb5e1249bfDoris Liu        }
13036ababe9379ecee4056a415085e28e7eb5e1249bfDoris Liu    }
13046ababe9379ecee4056a415085e28e7eb5e1249bfDoris Liu
13056ababe9379ecee4056a415085e28e7eb5e1249bfDoris Liu    /**
13061c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu     * Sets the swipe mode to indicate whether this is a swiping in
13071c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu     * or out, and therefore we can have different animations.
130870576b611fddc929abbfb466fa44c396a3a793dbDoris Liu     *
130970576b611fddc929abbfb466fa44c396a3a793dbDoris Liu     * @param swipeIn indicates whether the swipe should reveal/hide the list.
13101c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu     */
13111c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu    private void setSwipeMode(boolean swipeIn) {
131270576b611fddc929abbfb466fa44c396a3a793dbDoris Liu        for (int i = 0 ; i < mModeSelectorItems.length; i++) {
131370576b611fddc929abbfb466fa44c396a3a793dbDoris Liu            mModeSelectorItems[i].onSwipeModeChanged(swipeIn);
13141c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu        }
13151c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu    }
13161c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu
13171c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu    @Override
13181c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
13191c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu        super.onLayout(changed, left, top, right, bottom);
13201c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu        mWidth = right - left;
13211c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu        mHeight = bottom - top - getPaddingTop() - getPaddingBottom();
132228a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu
132328a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu        updateModeListLayout();
132428a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu
132526e901ed46054fb6cc2cb4c09bef3a779a1cbf78Alan Newberger        if (mCurrentStateManager.getCurrentState().getCurrentAnimationEffects() != null) {
132626e901ed46054fb6cc2cb4c09bef3a779a1cbf78Alan Newberger            mCurrentStateManager.getCurrentState().getCurrentAnimationEffects().setSize(
132726e901ed46054fb6cc2cb4c09bef3a779a1cbf78Alan Newberger                    mWidth, mHeight);
1328b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liu        }
13291c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu    }
13301c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu
133170576b611fddc929abbfb466fa44c396a3a793dbDoris Liu    /**
133228a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu     * Sets a capture layout helper to query layout rect from.
133370576b611fddc929abbfb466fa44c396a3a793dbDoris Liu     */
133428a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu    public void setCaptureLayoutHelper(CaptureLayoutHelper helper) {
133528a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu        mCaptureLayoutHelper = helper;
133628a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu    }
133728a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu
1338b908d933736a4ec042aad065b7ebf2059bbcac97Doris Liu    @Override
1339b908d933736a4ec042aad065b7ebf2059bbcac97Doris Liu    public void onPreviewAreaChanged(RectF previewArea) {
1340b908d933736a4ec042aad065b7ebf2059bbcac97Doris Liu        if (getVisibility() == View.VISIBLE && !hasWindowFocus()) {
1341b908d933736a4ec042aad065b7ebf2059bbcac97Doris Liu            // When the preview area has changed, to avoid visual disruption we
1342b908d933736a4ec042aad065b7ebf2059bbcac97Doris Liu            // only make corresponding UI changes when mode list does not have
1343b908d933736a4ec042aad065b7ebf2059bbcac97Doris Liu            // window focus.
1344b908d933736a4ec042aad065b7ebf2059bbcac97Doris Liu            updateModeListLayout();
1345b908d933736a4ec042aad065b7ebf2059bbcac97Doris Liu        }
1346b908d933736a4ec042aad065b7ebf2059bbcac97Doris Liu    }
1347b908d933736a4ec042aad065b7ebf2059bbcac97Doris Liu
134828a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu    private void updateModeListLayout() {
134928a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu        if (mCaptureLayoutHelper == null) {
135028a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu            Log.e(TAG, "Capture layout helper needs to be set first.");
135128a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu            return;
135228a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu        }
135328a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu        // Center mode drawer in the portion of camera preview that is not covered by
135428a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu        // bottom bar.
135528a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu        RectF uncoveredPreviewArea = mCaptureLayoutHelper.getUncoveredPreviewRect();
135628a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu        // Align left:
135728a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu        mListView.setTranslationX(uncoveredPreviewArea.left);
135828a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu        // Align center vertical:
135928a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu        mListView.setTranslationY(uncoveredPreviewArea.centerY()
136028a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu                - mListView.getMeasuredHeight() / 2);
136128a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu
136228a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu        updateSettingsButtonLayout(uncoveredPreviewArea);
136328a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu    }
136428a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu
136528a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu    private void updateSettingsButtonLayout(RectF uncoveredPreviewArea) {
136628a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu        if (mWidth > mHeight) {
136728a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu            // Align to the top right.
136828a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu            mSettingsButton.setTranslationX(uncoveredPreviewArea.right - mSettingsButtonMargin
136928a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu                    - mSettingsButton.getMeasuredWidth());
137028a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu            mSettingsButton.setTranslationY(uncoveredPreviewArea.top + mSettingsButtonMargin);
137128a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu        } else {
137228a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu            // Align to the bottom right.
137328a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu            mSettingsButton.setTranslationX(uncoveredPreviewArea.right - mSettingsButtonMargin
137428a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu                    - mSettingsButton.getMeasuredWidth());
137528a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu            mSettingsButton.setTranslationY(uncoveredPreviewArea.bottom - mSettingsButtonMargin
137628a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu                    - mSettingsButton.getMeasuredHeight());
137728a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu        }
137826aa92bf5942bfde16621c6833c14ba0e1ea8b5eDoris Liu        if (mSettingsCling != null) {
137926aa92bf5942bfde16621c6833c14ba0e1ea8b5eDoris Liu            mSettingsCling.updatePosition(mSettingsButton);
138026aa92bf5942bfde16621c6833c14ba0e1ea8b5eDoris Liu        }
13811c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu    }
13821c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu
1383b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liu    @Override
1384b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liu    public void draw(Canvas canvas) {
138526e901ed46054fb6cc2cb4c09bef3a779a1cbf78Alan Newberger        ModeListState currentState = mCurrentStateManager.getCurrentState();
1386bee2022d8dcde61d2b0c6cf9b709d8e34cdcb739Doris Liu        AnimationEffects currentEffects = currentState.getCurrentAnimationEffects();
1387bee2022d8dcde61d2b0c6cf9b709d8e34cdcb739Doris Liu        if (currentEffects != null) {
1388bee2022d8dcde61d2b0c6cf9b709d8e34cdcb739Doris Liu            currentEffects.drawBackground(canvas);
1389bee2022d8dcde61d2b0c6cf9b709d8e34cdcb739Doris Liu            if (currentEffects.shouldDrawSuper()) {
1390bee2022d8dcde61d2b0c6cf9b709d8e34cdcb739Doris Liu                super.draw(canvas);
1391bee2022d8dcde61d2b0c6cf9b709d8e34cdcb739Doris Liu            }
1392bee2022d8dcde61d2b0c6cf9b709d8e34cdcb739Doris Liu            currentEffects.drawForeground(canvas);
1393b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liu        } else {
1394b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liu            super.draw(canvas);
1395b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liu        }
1396b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liu    }
1397b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liu
139870576b611fddc929abbfb466fa44c396a3a793dbDoris Liu    /**
139926aa92bf5942bfde16621c6833c14ba0e1ea8b5eDoris Liu     * Sets whether a cling for settings button should be shown. If not, remove
140026aa92bf5942bfde16621c6833c14ba0e1ea8b5eDoris Liu     * the cling from view hierarchy if any. If a cling should be shown, inflate
140126aa92bf5942bfde16621c6833c14ba0e1ea8b5eDoris Liu     * the cling into this view group.
140226aa92bf5942bfde16621c6833c14ba0e1ea8b5eDoris Liu     *
140326aa92bf5942bfde16621c6833c14ba0e1ea8b5eDoris Liu     * @param show whether the cling needs to be shown.
140426aa92bf5942bfde16621c6833c14ba0e1ea8b5eDoris Liu     */
140526aa92bf5942bfde16621c6833c14ba0e1ea8b5eDoris Liu    public void setShouldShowSettingsCling(boolean show) {
140626aa92bf5942bfde16621c6833c14ba0e1ea8b5eDoris Liu        if (show) {
140726aa92bf5942bfde16621c6833c14ba0e1ea8b5eDoris Liu            if (mSettingsCling == null) {
140826aa92bf5942bfde16621c6833c14ba0e1ea8b5eDoris Liu                inflate(getContext(), R.layout.settings_cling, this);
140926aa92bf5942bfde16621c6833c14ba0e1ea8b5eDoris Liu                mSettingsCling = (SettingsCling) findViewById(R.id.settings_cling);
141026aa92bf5942bfde16621c6833c14ba0e1ea8b5eDoris Liu            }
141126aa92bf5942bfde16621c6833c14ba0e1ea8b5eDoris Liu        } else {
141226aa92bf5942bfde16621c6833c14ba0e1ea8b5eDoris Liu            if (mSettingsCling != null) {
141326aa92bf5942bfde16621c6833c14ba0e1ea8b5eDoris Liu                // Remove settings cling from view hierarchy.
141426aa92bf5942bfde16621c6833c14ba0e1ea8b5eDoris Liu                removeView(mSettingsCling);
141526aa92bf5942bfde16621c6833c14ba0e1ea8b5eDoris Liu                mSettingsCling = null;
141626aa92bf5942bfde16621c6833c14ba0e1ea8b5eDoris Liu            }
141726aa92bf5942bfde16621c6833c14ba0e1ea8b5eDoris Liu        }
141826aa92bf5942bfde16621c6833c14ba0e1ea8b5eDoris Liu    }
141926aa92bf5942bfde16621c6833c14ba0e1ea8b5eDoris Liu
142026aa92bf5942bfde16621c6833c14ba0e1ea8b5eDoris Liu    /**
142126aa92bf5942bfde16621c6833c14ba0e1ea8b5eDoris Liu     * Show or hide cling for settings button. The cling will only be shown if
142226aa92bf5942bfde16621c6833c14ba0e1ea8b5eDoris Liu     * settings button has never been clicked. Otherwise, cling will be null,
142326aa92bf5942bfde16621c6833c14ba0e1ea8b5eDoris Liu     * and will not show even if this method is called to show it.
142426aa92bf5942bfde16621c6833c14ba0e1ea8b5eDoris Liu     */
142526aa92bf5942bfde16621c6833c14ba0e1ea8b5eDoris Liu    private void showSettingsClingIfEnabled(boolean show) {
142626aa92bf5942bfde16621c6833c14ba0e1ea8b5eDoris Liu        if (mSettingsCling != null) {
142726aa92bf5942bfde16621c6833c14ba0e1ea8b5eDoris Liu            int visibility = show ? VISIBLE : INVISIBLE;
142826aa92bf5942bfde16621c6833c14ba0e1ea8b5eDoris Liu            mSettingsCling.setVisibility(visibility);
142926aa92bf5942bfde16621c6833c14ba0e1ea8b5eDoris Liu        }
143026aa92bf5942bfde16621c6833c14ba0e1ea8b5eDoris Liu    }
143126aa92bf5942bfde16621c6833c14ba0e1ea8b5eDoris Liu
143226aa92bf5942bfde16621c6833c14ba0e1ea8b5eDoris Liu    /**
1433b00d64307ae77541f8576593a29214d649fb94e9Doris Liu     * This shows the mode switcher and starts the accordion animation with a delay.
1434b00d64307ae77541f8576593a29214d649fb94e9Doris Liu     * If the view does not currently have focus, (e.g. There are popups on top of
1435b00d64307ae77541f8576593a29214d649fb94e9Doris Liu     * it.) start the delayed accordion animation when it gains focus. Otherwise,
1436b00d64307ae77541f8576593a29214d649fb94e9Doris Liu     * start the animation with a delay right away.
1437b7d3ce145dd7b1cf0ac51f53d5bc73dbbf35f6e6Doris Liu     */
1438b00d64307ae77541f8576593a29214d649fb94e9Doris Liu    public void showModeSwitcherHint() {
143926e901ed46054fb6cc2cb4c09bef3a779a1cbf78Alan Newberger        mCurrentStateManager.getCurrentState().showSwitcherHint();
1440b7d3ce145dd7b1cf0ac51f53d5bc73dbbf35f6e6Doris Liu    }
1441b7d3ce145dd7b1cf0ac51f53d5bc73dbbf35f6e6Doris Liu
1442b7d3ce145dd7b1cf0ac51f53d5bc73dbbf35f6e6Doris Liu    /**
144370576b611fddc929abbfb466fa44c396a3a793dbDoris Liu     * Resets the visible width of all the mode selectors to 0.
144470576b611fddc929abbfb466fa44c396a3a793dbDoris Liu     */
14451c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu    private void resetModeSelectors() {
144670576b611fddc929abbfb466fa44c396a3a793dbDoris Liu        for (int i = 0; i < mModeSelectorItems.length; i++) {
144770576b611fddc929abbfb466fa44c396a3a793dbDoris Liu            mModeSelectorItems[i].setVisibleWidth(0);
14481c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu        }
14491c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu    }
14501c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu
14511c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu    private boolean isRunningAccordionAnimation() {
14521c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu        return mAnimatorSet != null && mAnimatorSet.isRunning();
14531c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu    }
14541c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu
145570576b611fddc929abbfb466fa44c396a3a793dbDoris Liu    /**
145670576b611fddc929abbfb466fa44c396a3a793dbDoris Liu     * Calculate the mode selector item in the list that is at position (x, y).
1457213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu     * If the position is above the top item or below the bottom item, return
1458213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu     * the top item or bottom item respectively.
1459612321f39ca524de2efced1e826db9ec523bd6dcAngus Kong     *
146070576b611fddc929abbfb466fa44c396a3a793dbDoris Liu     * @param x horizontal position
146170576b611fddc929abbfb466fa44c396a3a793dbDoris Liu     * @param y vertical position
146270576b611fddc929abbfb466fa44c396a3a793dbDoris Liu     * @return index of the item that is at position (x, y)
146370576b611fddc929abbfb466fa44c396a3a793dbDoris Liu     */
14641c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu    private int getFocusItem(float x, float y) {
1465213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu        // Convert coordinates into child view's coordinates.
1466e7e0c028d568c1a4c3e88ed75411b8b41c5aec69Doris Liu        x -= mListView.getX();
1467e7e0c028d568c1a4c3e88ed75411b8b41c5aec69Doris Liu        y -= mListView.getY();
14681c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu
146970576b611fddc929abbfb466fa44c396a3a793dbDoris Liu        for (int i = 0; i < mModeSelectorItems.length; i++) {
1470213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu            if (y <= mModeSelectorItems[i].getBottom()) {
14711c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu                return i;
14721c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu            }
14731c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu        }
1474213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu        return mModeSelectorItems.length - 1;
1475213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu    }
1476213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu
1477213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu    @Override
1478b00d64307ae77541f8576593a29214d649fb94e9Doris Liu    public void onWindowFocusChanged(boolean hasFocus) {
1479b00d64307ae77541f8576593a29214d649fb94e9Doris Liu        super.onWindowFocusChanged(hasFocus);
148026e901ed46054fb6cc2cb4c09bef3a779a1cbf78Alan Newberger        mCurrentStateManager.getCurrentState().onWindowFocusChanged(hasFocus);
1481b00d64307ae77541f8576593a29214d649fb94e9Doris Liu    }
1482b00d64307ae77541f8576593a29214d649fb94e9Doris Liu
1483b00d64307ae77541f8576593a29214d649fb94e9Doris Liu    @Override
1484213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu    public void onVisibilityChanged(View v, int visibility) {
1485213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu        super.onVisibilityChanged(v, visibility);
1486213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu        if (visibility == VISIBLE) {
1487213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu            // Highlight current module
1488213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu            if (mModeSwitchListener != null) {
1489213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu                int modeId = mModeSwitchListener.getCurrentModeIndex();
1490213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu                int parentMode = CameraUtil.getCameraModeParentModeId(modeId, getContext());
1491213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu                // Find parent mode in the nav drawer.
1492213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu                for (int i = 0; i < mSupportedModes.size(); i++) {
1493213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu                    if (mSupportedModes.get(i) == parentMode) {
14946ababe9379ecee4056a415085e28e7eb5e1249bfDoris Liu                        mModeSelectorItems[i].setSelected(true);
1495213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu                    }
1496213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu                }
1497213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu            }
149828a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu            updateModeListLayout();
14999d264309a341c52601bdccdeff6647e0ce58c31fDoris Liu        } else {
15009d264309a341c52601bdccdeff6647e0ce58c31fDoris Liu            if (mModeSelectorItems != null) {
15019d264309a341c52601bdccdeff6647e0ce58c31fDoris Liu                // When becoming invisible/gone after initializing mode selector items.
15029d264309a341c52601bdccdeff6647e0ce58c31fDoris Liu                for (int i = 0; i < mModeSelectorItems.length; i++) {
15039d264309a341c52601bdccdeff6647e0ce58c31fDoris Liu                    mModeSelectorItems[i].setSelected(false);
15049d264309a341c52601bdccdeff6647e0ce58c31fDoris Liu                }
15059d264309a341c52601bdccdeff6647e0ce58c31fDoris Liu            }
15068c1a922df154182b53f00c38198db7da0b4feb20Sascha Haeberling            if (mModeListOpenListener != null) {
15079d264309a341c52601bdccdeff6647e0ce58c31fDoris Liu                mModeListOpenListener.onModeListClosed();
1508213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu            }
1509213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu        }
151038989540cb1488cb7b8062ff74b9dedcd76c047fSpike Sprague
15118c1a922df154182b53f00c38198db7da0b4feb20Sascha Haeberling        if (mVisibilityChangedListener != null) {
1512b49ab283fe6fce242901a1a09fc2b148b27fe123Sascha Haeberling            mVisibilityChangedListener.onVisibilityEvent(getVisibility() == VISIBLE);
15138c1a922df154182b53f00c38198db7da0b4feb20Sascha Haeberling        }
1514213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu    }
1515213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu
1516a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu    @Override
1517a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu    public void setVisibility(int visibility) {
151826e901ed46054fb6cc2cb4c09bef3a779a1cbf78Alan Newberger        ModeListState currentState = mCurrentStateManager.getCurrentState();
151926e901ed46054fb6cc2cb4c09bef3a779a1cbf78Alan Newberger        if (currentState != null && !currentState.shouldHandleVisibilityChange(visibility)) {
1520a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            return;
1521a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        }
1522a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        super.setVisibility(visibility);
1523a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu    }
1524a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
15251c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu    private void scroll(int itemId, float deltaX, float deltaY) {
152670576b611fddc929abbfb466fa44c396a3a793dbDoris Liu        // Scrolling trend on X and Y axis, to track the trend by biasing
152770576b611fddc929abbfb466fa44c396a3a793dbDoris Liu        // towards latest touch events.
15281c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu        mScrollTrendX = mScrollTrendX * 0.3f + deltaX * 0.7f;
15291c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu        mScrollTrendY = mScrollTrendY * 0.3f + deltaY * 0.7f;
15301c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu
153170576b611fddc929abbfb466fa44c396a3a793dbDoris Liu        // TODO: Change how the curve is calculated below when UX finalize their design.
1532cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu        mCurrentTime = SystemClock.uptimeMillis();
15331c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu        float longestWidth;
153470576b611fddc929abbfb466fa44c396a3a793dbDoris Liu        if (itemId != NO_ITEM_SELECTED) {
153569cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu            longestWidth = mModeSelectorItems[itemId].getVisibleWidth();
15361c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu        } else {
153769cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu            longestWidth = mModeSelectorItems[0].getVisibleWidth();
15381c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu        }
153969cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu        float newPosition = longestWidth - deltaX;
154069cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu        int maxVisibleWidth = mModeSelectorItems[0].getMaxVisibleWidth();
154169cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu        newPosition = Math.min(newPosition, getMaxMovementBasedOnPosition((int) longestWidth,
154269cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu                maxVisibleWidth));
154369cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu        newPosition = Math.max(newPosition, 0);
154469cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu        insertNewPosition(newPosition, mCurrentTime);
15451c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu
154670576b611fddc929abbfb466fa44c396a3a793dbDoris Liu        for (int i = 0; i < mModeSelectorItems.length; i++) {
1547cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu            mModeSelectorItems[i].setVisibleWidth(calculateVisibleWidthForItem(i,
154869cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu                    (int) newPosition));
15491c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu        }
15501c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu    }
15511c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu
1552cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu    /**
1553cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu     * Calculate the width of a specified item based on its position relative to
1554cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu     * the item with longest width.
1555cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu     */
1556cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu    private int calculateVisibleWidthForItem(int itemId, int longestWidth) {
1557cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu        if (itemId == mFocusItem || mFocusItem == NO_ITEM_SELECTED) {
1558cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu            return longestWidth;
1559cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu        }
1560cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu
1561cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu        int delay = Math.abs(itemId - mFocusItem) * DELAY_MS;
156269cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu        return (int) getPosition(mCurrentTime - delay,
156369cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu                mModeSelectorItems[itemId].getVisibleWidth());
1564cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu    }
1565cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu
1566cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu    /**
1567cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu     * Insert new position and time stamp into the history position list, and
1568cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu     * remove stale position items.
1569cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu     *
1570cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu     * @param position latest position of the focus item
1571cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu     * @param time  current time in milliseconds
1572cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu     */
1573cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu    private void insertNewPosition(float position, long time) {
1574cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu        // TODO: Consider re-using stale position objects rather than
1575cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu        // always creating new position objects.
1576cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu        mPositionHistory.add(new TimeBasedPosition(position, time));
1577cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu
1578cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu        // Positions that are from too long ago will not be of any use for
1579cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu        // future position interpolation. So we need to remove those positions
1580cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu        // from the list.
1581cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu        long timeCutoff = time - (mTotalModes - 1) * DELAY_MS;
1582cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu        while (mPositionHistory.size() > 0) {
1583cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu            // Remove all the position items that are prior to the cutoff time.
1584cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu            TimeBasedPosition historyPosition = mPositionHistory.getFirst();
1585cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu            if (historyPosition.getTimeStamp() < timeCutoff) {
1586cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu                mPositionHistory.removeFirst();
1587cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu            } else {
1588cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu                break;
1589cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu            }
1590cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu        }
1591cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu    }
1592cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu
1593cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu    /**
1594cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu     * Gets the interpolated position at the specified time. This involves going
1595cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu     * through the recorded positions until a {@link TimeBasedPosition} is found
1596cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu     * such that the position the recorded before the given time, and the
1597cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu     * {@link TimeBasedPosition} after that is recorded no earlier than the given
1598cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu     * time. These two positions are then interpolated to get the position at the
1599cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu     * specified time.
1600cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu     */
160169cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu    private float getPosition(long time, float currentPosition) {
1602cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu        int i;
1603cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu        for (i = 0; i < mPositionHistory.size(); i++) {
1604cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu            TimeBasedPosition historyPosition = mPositionHistory.get(i);
1605cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu            if (historyPosition.getTimeStamp() > time) {
1606cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu                // Found the winner. Now interpolate between position i and position i - 1
1607cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu                if (i == 0) {
160869cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu                    // Slowly approaching to the destination if there isn't enough data points
160969cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu                    float weight = 0.2f;
161069cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu                    return historyPosition.getPosition() * weight + (1f - weight) * currentPosition;
1611cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu                } else {
1612cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu                    TimeBasedPosition prevTimeBasedPosition = mPositionHistory.get(i - 1);
1613cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu                    // Start interpolation
1614cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu                    float fraction = (float) (time - prevTimeBasedPosition.getTimeStamp()) /
1615cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu                            (float) (historyPosition.getTimeStamp() - prevTimeBasedPosition.getTimeStamp());
1616cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu                    float position = fraction * (historyPosition.getPosition()
1617cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu                            - prevTimeBasedPosition.getPosition()) + prevTimeBasedPosition.getPosition();
1618cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu                    return position;
1619cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu                }
1620cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu            }
1621cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu        }
1622cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu        // It should never get here.
1623cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu        Log.e(TAG, "Invalid time input for getPosition(). time: " + time);
1624cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu        if (mPositionHistory.size() == 0) {
1625cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu            Log.e(TAG, "TimeBasedPosition history size is 0");
1626cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu        } else {
1627cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu            Log.e(TAG, "First position recorded at " + mPositionHistory.getFirst().getTimeStamp()
1628cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu            + " , last position recorded at " + mPositionHistory.getLast().getTimeStamp());
1629cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu        }
1630cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu        assert (i < mPositionHistory.size());
1631cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu        return i;
1632cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu    }
1633cfaf7b0226e5d556ce12b34793f9bd0901652c62Doris Liu
16341c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu    private void reset() {
16351c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu        resetModeSelectors();
16361c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu        mScrollTrendX = 0f;
16371c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu        mScrollTrendY = 0f;
16381c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu        setVisibility(INVISIBLE);
16391c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu    }
16401c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu
16411c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu    /**
164270576b611fddc929abbfb466fa44c396a3a793dbDoris Liu     * When visible width of list is changed, the background of the list needs
164370576b611fddc929abbfb466fa44c396a3a793dbDoris Liu     * to darken/lighten correspondingly.
164470576b611fddc929abbfb466fa44c396a3a793dbDoris Liu     */
16450496fcaa51f88f6c753975473b971941e4090cfaSpike Sprague    @Override
164669cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu    public void onVisibleWidthChanged(int visibleWidth) {
164769cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu        mVisibleWidth = visibleWidth;
164869cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu
1649213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu        // When the longest mode item is entirely shown (across the screen), the
165070576b611fddc929abbfb466fa44c396a3a793dbDoris Liu        // background should be 50% transparent.
1651213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu        int maxVisibleWidth = mModeSelectorItems[0].getMaxVisibleWidth();
165269cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu        visibleWidth = Math.min(maxVisibleWidth, visibleWidth);
165369cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu        if (visibleWidth != maxVisibleWidth) {
16546ababe9379ecee4056a415085e28e7eb5e1249bfDoris Liu            // No longer full screen.
16556ababe9379ecee4056a415085e28e7eb5e1249bfDoris Liu            cancelForwardingTouchEvent();
16566ababe9379ecee4056a415085e28e7eb5e1249bfDoris Liu        }
165769cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu        float openRatio = (float) visibleWidth / maxVisibleWidth;
1658a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        onModeListOpenRatioUpdate(openRatio * mModeListOpenFactor);
165969cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu    }
166069cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu
166169cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu    /**
166269cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu     * Gets called when UI elements such as background and gear icon need to adjust
166369cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu     * their appearance based on the percentage of the mode list opening.
166469cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu     *
166569cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu     * @param openRatio percentage of the mode list opening, ranging [0f, 1f]
166669cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu     */
166769cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu    private void onModeListOpenRatioUpdate(float openRatio) {
166869cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu        for (int i = 0; i < mModeSelectorItems.length; i++) {
166969cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu            mModeSelectorItems[i].setTextAlpha(openRatio);
167069cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu        }
16719d264309a341c52601bdccdeff6647e0ce58c31fDoris Liu        setBackgroundAlpha((int) (BACKGROUND_TRANSPARENTCY * openRatio));
16729d264309a341c52601bdccdeff6647e0ce58c31fDoris Liu        if (mModeListOpenListener != null) {
16739d264309a341c52601bdccdeff6647e0ce58c31fDoris Liu            mModeListOpenListener.onModeListOpenProgress(openRatio);
16749d264309a341c52601bdccdeff6647e0ce58c31fDoris Liu        }
16759d264309a341c52601bdccdeff6647e0ce58c31fDoris Liu        if (mSettingsButton != null) {
16769d264309a341c52601bdccdeff6647e0ce58c31fDoris Liu            mSettingsButton.setAlpha(openRatio);
16779d264309a341c52601bdccdeff6647e0ce58c31fDoris Liu        }
167870576b611fddc929abbfb466fa44c396a3a793dbDoris Liu    }
167970576b611fddc929abbfb466fa44c396a3a793dbDoris Liu
16806ababe9379ecee4056a415085e28e7eb5e1249bfDoris Liu    /**
16816ababe9379ecee4056a415085e28e7eb5e1249bfDoris Liu     * Cancels the touch event forwarding by sending a cancel event to the recipient
16826ababe9379ecee4056a415085e28e7eb5e1249bfDoris Liu     * view and resetting the touch forward recipient to ensure no more events
16836ababe9379ecee4056a415085e28e7eb5e1249bfDoris Liu     * can be forwarded in the current series of the touch events.
16846ababe9379ecee4056a415085e28e7eb5e1249bfDoris Liu     */
16856ababe9379ecee4056a415085e28e7eb5e1249bfDoris Liu    private void cancelForwardingTouchEvent() {
16866ababe9379ecee4056a415085e28e7eb5e1249bfDoris Liu        if (mChildViewTouched != null) {
16876ababe9379ecee4056a415085e28e7eb5e1249bfDoris Liu            mLastChildTouchEvent.setAction(MotionEvent.ACTION_CANCEL);
16886ababe9379ecee4056a415085e28e7eb5e1249bfDoris Liu            mChildViewTouched.onTouchEvent(mLastChildTouchEvent);
16896ababe9379ecee4056a415085e28e7eb5e1249bfDoris Liu            mChildViewTouched = null;
16906ababe9379ecee4056a415085e28e7eb5e1249bfDoris Liu        }
16916ababe9379ecee4056a415085e28e7eb5e1249bfDoris Liu    }
16926ababe9379ecee4056a415085e28e7eb5e1249bfDoris Liu
1693b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liu    @Override
1694b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liu    public void onWindowVisibilityChanged(int visibility) {
1695b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liu        super.onWindowVisibilityChanged(visibility);
1696b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liu        if (visibility != VISIBLE) {
169726e901ed46054fb6cc2cb4c09bef3a779a1cbf78Alan Newberger            mCurrentStateManager.getCurrentState().hide();
1698b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liu        }
1699b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liu    }
1700b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liu
170170576b611fddc929abbfb466fa44c396a3a793dbDoris Liu    /**
170215691af76927d932fcc1250b4ffaefd31a61cf81Erin Dahlgren     * Defines how the list view should respond to a menu button pressed
170315691af76927d932fcc1250b4ffaefd31a61cf81Erin Dahlgren     * event.
170415691af76927d932fcc1250b4ffaefd31a61cf81Erin Dahlgren     */
170515691af76927d932fcc1250b4ffaefd31a61cf81Erin Dahlgren    public boolean onMenuPressed() {
170626e901ed46054fb6cc2cb4c09bef3a779a1cbf78Alan Newberger        return mCurrentStateManager.getCurrentState().onMenuPressed();
170715691af76927d932fcc1250b4ffaefd31a61cf81Erin Dahlgren    }
170815691af76927d932fcc1250b4ffaefd31a61cf81Erin Dahlgren
170915691af76927d932fcc1250b4ffaefd31a61cf81Erin Dahlgren    /**
17101c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu     * The list view should either snap back or snap to full screen after a gesture.
17111c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu     * This function is called when an up or cancel event is received, and then based
17121c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu     * on the current position of the list and the gesture we can decide which way
17131c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu     * to snap.
17141c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu     */
17151c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu    private void snap() {
1716a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        if (shouldSnapBack()) {
1717a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            snapBack();
1718a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        } else {
1719a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            snapToFullScreen();
1720a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        }
1721a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu    }
1722a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
1723a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu    private boolean shouldSnapBack() {
1724a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        int itemId = Math.max(0, mFocusItem);
1725a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        if (Math.abs(mVelocityX) > VELOCITY_THRESHOLD) {
1726a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            // Fling to open / close
1727a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            return mVelocityX < 0;
1728a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        } else if (mModeSelectorItems[itemId].getVisibleWidth()
1729a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                < mModeSelectorItems[itemId].getMaxVisibleWidth() * SNAP_BACK_THRESHOLD_RATIO) {
1730a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            return true;
1731a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        } else if (Math.abs(mScrollTrendX) > Math.abs(mScrollTrendY) && mScrollTrendX > 0) {
1732a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            return true;
1733a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        } else {
1734a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            return false;
17351c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu        }
17361c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu    }
17371c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu
1738b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liu    /**
1739b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liu     * Snaps back out of the screen.
1740b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liu     *
1741b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liu     * @param withAnimation whether snapping back should be animated
1742b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liu     */
1743a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu    public Animator snapBack(boolean withAnimation) {
1744b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liu        if (withAnimation) {
1745213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu            if (mVelocityX > -VELOCITY_THRESHOLD * SCROLL_FACTOR) {
1746a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                return animateListToWidth(0);
1747213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu            } else {
1748a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu                return animateListToWidthAtVelocity(mVelocityX, 0);
1749213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu            }
1750b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liu        } else {
1751b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liu            setVisibility(INVISIBLE);
1752b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liu            resetModeSelectors();
1753a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            return null;
1754b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liu        }
1755b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liu    }
1756b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liu
1757b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liu    /**
1758b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liu     * Snaps the mode list back out with animation.
1759b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liu     */
1760a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu    private Animator snapBack() {
1761a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        return snapBack(true);
17621c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu    }
17631c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu
1764a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu    private Animator snapToFullScreen() {
1765a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        Animator animator;
1766213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu        int focusItem = mFocusItem == NO_ITEM_SELECTED ? 0 : mFocusItem;
1767213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu        int fullWidth = mModeSelectorItems[focusItem].getMaxVisibleWidth();
176869cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu        if (mVelocityX <= VELOCITY_THRESHOLD) {
1769a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            animator = animateListToWidth(fullWidth);
17706c5a5375524efe7e5160f92edcaad121c7fbdb07Doris Liu        } else {
17716c5a5375524efe7e5160f92edcaad121c7fbdb07Doris Liu            // If the fling velocity exceeds this threshold, snap to full screen
17726c5a5375524efe7e5160f92edcaad121c7fbdb07Doris Liu            // at a constant speed.
1773a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu            animator = animateListToWidthAtVelocity(VELOCITY_THRESHOLD, fullWidth);
17746c5a5375524efe7e5160f92edcaad121c7fbdb07Doris Liu        }
17752c559c4bd3c65e0cb0bca06f30e3bc979038e6f7Doris Liu        if (mModeListOpenListener != null) {
17762c559c4bd3c65e0cb0bca06f30e3bc979038e6f7Doris Liu            mModeListOpenListener.onOpenFullScreen();
17772c559c4bd3c65e0cb0bca06f30e3bc979038e6f7Doris Liu        }
1778a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        return animator;
17791c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu    }
17801c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu
178170576b611fddc929abbfb466fa44c396a3a793dbDoris Liu    /**
178270576b611fddc929abbfb466fa44c396a3a793dbDoris Liu     * Overloaded function to provide a simple way to start animation. Animation
178370576b611fddc929abbfb466fa44c396a3a793dbDoris Liu     * will use default duration, and a value of <code>null</code> for interpolator
178470576b611fddc929abbfb466fa44c396a3a793dbDoris Liu     * means linear interpolation will be used.
178570576b611fddc929abbfb466fa44c396a3a793dbDoris Liu     *
178670576b611fddc929abbfb466fa44c396a3a793dbDoris Liu     * @param width a set of values that the animation will animate between over time
178770576b611fddc929abbfb466fa44c396a3a793dbDoris Liu     */
1788a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu    private Animator animateListToWidth(int... width) {
1789a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        return animateListToWidth(0, DEFAULT_DURATION_MS, null, width);
17901c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu    }
17911c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu
179270576b611fddc929abbfb466fa44c396a3a793dbDoris Liu    /**
179370576b611fddc929abbfb466fa44c396a3a793dbDoris Liu     * Animate the mode list between the given set of visible width.
179470576b611fddc929abbfb466fa44c396a3a793dbDoris Liu     *
1795b00d64307ae77541f8576593a29214d649fb94e9Doris Liu     * @param delay start delay between consecutive mode item. If delay < 0, the
1796b00d64307ae77541f8576593a29214d649fb94e9Doris Liu     *              leader in the animation will be the bottom item.
179770576b611fddc929abbfb466fa44c396a3a793dbDoris Liu     * @param duration duration for the animation of each mode item
179870576b611fddc929abbfb466fa44c396a3a793dbDoris Liu     * @param interpolator interpolator to be used by the animation
179970576b611fddc929abbfb466fa44c396a3a793dbDoris Liu     * @param width a set of values that the animation will animate between over time
180070576b611fddc929abbfb466fa44c396a3a793dbDoris Liu     */
180169cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu    private Animator animateListToWidth(int delay, int duration,
18021c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu                                    TimeInterpolator interpolator, int... width) {
18031c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu        if (mAnimatorSet != null && mAnimatorSet.isRunning()) {
18041c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu            mAnimatorSet.end();
18051c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu        }
18061c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu
18071c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu        ArrayList<Animator> animators = new ArrayList<Animator>();
1808b00d64307ae77541f8576593a29214d649fb94e9Doris Liu        boolean animateModeItemsInOrder = true;
1809b00d64307ae77541f8576593a29214d649fb94e9Doris Liu        if (delay < 0) {
1810b00d64307ae77541f8576593a29214d649fb94e9Doris Liu            animateModeItemsInOrder = false;
1811b00d64307ae77541f8576593a29214d649fb94e9Doris Liu            delay *= -1;
1812b00d64307ae77541f8576593a29214d649fb94e9Doris Liu        }
18131c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu        for (int i = 0; i < mTotalModes; i++) {
1814b00d64307ae77541f8576593a29214d649fb94e9Doris Liu            ObjectAnimator animator;
1815b00d64307ae77541f8576593a29214d649fb94e9Doris Liu            if (animateModeItemsInOrder) {
1816b00d64307ae77541f8576593a29214d649fb94e9Doris Liu                animator = ObjectAnimator.ofInt(mModeSelectorItems[i],
181770576b611fddc929abbfb466fa44c396a3a793dbDoris Liu                    "visibleWidth", width);
1818b00d64307ae77541f8576593a29214d649fb94e9Doris Liu            } else {
1819b00d64307ae77541f8576593a29214d649fb94e9Doris Liu                animator = ObjectAnimator.ofInt(mModeSelectorItems[mTotalModes - 1 -i],
1820b00d64307ae77541f8576593a29214d649fb94e9Doris Liu                        "visibleWidth", width);
1821b00d64307ae77541f8576593a29214d649fb94e9Doris Liu            }
18221c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu            animator.setDuration(duration);
18231c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu            animator.setStartDelay(i * delay);
18241c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu            animators.add(animator);
18251c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu        }
18261c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu
18271c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu        mAnimatorSet = new AnimatorSet();
18281c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu        mAnimatorSet.playTogether(animators);
18291c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu        mAnimatorSet.setInterpolator(interpolator);
18306c5a5375524efe7e5160f92edcaad121c7fbdb07Doris Liu        mAnimatorSet.start();
183169cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu
183269cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu        return mAnimatorSet;
18336c5a5375524efe7e5160f92edcaad121c7fbdb07Doris Liu    }
18341c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu
18356c5a5375524efe7e5160f92edcaad121c7fbdb07Doris Liu    /**
18366c5a5375524efe7e5160f92edcaad121c7fbdb07Doris Liu     * Animate the mode list to the given width at a constant velocity.
18376c5a5375524efe7e5160f92edcaad121c7fbdb07Doris Liu     *
18386c5a5375524efe7e5160f92edcaad121c7fbdb07Doris Liu     * @param velocity the velocity that animation will be at
18396c5a5375524efe7e5160f92edcaad121c7fbdb07Doris Liu     * @param width final width of the list
18406c5a5375524efe7e5160f92edcaad121c7fbdb07Doris Liu     */
1841a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu    private Animator animateListToWidthAtVelocity(float velocity, int width) {
18426c5a5375524efe7e5160f92edcaad121c7fbdb07Doris Liu        if (mAnimatorSet != null && mAnimatorSet.isRunning()) {
18436c5a5375524efe7e5160f92edcaad121c7fbdb07Doris Liu            mAnimatorSet.end();
18446c5a5375524efe7e5160f92edcaad121c7fbdb07Doris Liu        }
18451c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu
18466c5a5375524efe7e5160f92edcaad121c7fbdb07Doris Liu        ArrayList<Animator> animators = new ArrayList<Animator>();
18476c5a5375524efe7e5160f92edcaad121c7fbdb07Doris Liu        int focusItem = mFocusItem == NO_ITEM_SELECTED ? 0 : mFocusItem;
18486c5a5375524efe7e5160f92edcaad121c7fbdb07Doris Liu        for (int i = 0; i < mTotalModes; i++) {
18496c5a5375524efe7e5160f92edcaad121c7fbdb07Doris Liu            ObjectAnimator animator = ObjectAnimator.ofInt(mModeSelectorItems[i],
18506c5a5375524efe7e5160f92edcaad121c7fbdb07Doris Liu                    "visibleWidth", width);
18518c1a922df154182b53f00c38198db7da0b4feb20Sascha Haeberling            int duration = (int) (width / velocity);
18526c5a5375524efe7e5160f92edcaad121c7fbdb07Doris Liu            animator.setDuration(duration);
18536c5a5375524efe7e5160f92edcaad121c7fbdb07Doris Liu            animators.add(animator);
18546c5a5375524efe7e5160f92edcaad121c7fbdb07Doris Liu        }
18551c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu
18566c5a5375524efe7e5160f92edcaad121c7fbdb07Doris Liu        mAnimatorSet = new AnimatorSet();
18576c5a5375524efe7e5160f92edcaad121c7fbdb07Doris Liu        mAnimatorSet.playTogether(animators);
18586c5a5375524efe7e5160f92edcaad121c7fbdb07Doris Liu        mAnimatorSet.setInterpolator(null);
18591c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu        mAnimatorSet.start();
1860a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu
1861a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        return mAnimatorSet;
18621c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu    }
1863f55f3c461c5a6ae6b61fa75562ca01683aa93f9aDoris Liu
1864f55f3c461c5a6ae6b61fa75562ca01683aa93f9aDoris Liu    /**
1865b9b7240d62dd2ad245f2c0e09fd1056a28687978Doris Liu     * Called when the back key is pressed.
1866b9b7240d62dd2ad245f2c0e09fd1056a28687978Doris Liu     *
1867b9b7240d62dd2ad245f2c0e09fd1056a28687978Doris Liu     * @return Whether the UI responded to the key event.
1868b9b7240d62dd2ad245f2c0e09fd1056a28687978Doris Liu     */
1869b9b7240d62dd2ad245f2c0e09fd1056a28687978Doris Liu    public boolean onBackPressed() {
187026e901ed46054fb6cc2cb4c09bef3a779a1cbf78Alan Newberger        return mCurrentStateManager.getCurrentState().onBackPressed();
1871b9b7240d62dd2ad245f2c0e09fd1056a28687978Doris Liu    }
1872b9b7240d62dd2ad245f2c0e09fd1056a28687978Doris Liu
1873b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liu    public void startModeSelectionAnimation() {
187426e901ed46054fb6cc2cb4c09bef3a779a1cbf78Alan Newberger        mCurrentStateManager.getCurrentState().startModeSelectionAnimation();
1875b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liu    }
1876b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liu
187769cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu    public float getMaxMovementBasedOnPosition(int lastVisibleWidth, int maxWidth) {
187869cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu        int timeElapsed = (int) (System.currentTimeMillis() - mLastScrollTime);
187969cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu        if (timeElapsed > SCROLL_INTERVAL_MS) {
188069cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu            timeElapsed = SCROLL_INTERVAL_MS;
188169cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu        }
188269cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu        float position;
188369cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu        int slowZone = (int) (maxWidth * SLOW_ZONE_PERCENTAGE);
188469cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu        if (lastVisibleWidth < (maxWidth - slowZone)) {
18850496fcaa51f88f6c753975473b971941e4090cfaSpike Sprague            position = VELOCITY_THRESHOLD * timeElapsed + lastVisibleWidth;
188669cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu        } else {
188769cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu            float percentageIntoSlowZone = (lastVisibleWidth - (maxWidth - slowZone)) / slowZone;
188869cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu            float velocity = (1 - percentageIntoSlowZone) * VELOCITY_THRESHOLD;
18890496fcaa51f88f6c753975473b971941e4090cfaSpike Sprague            position = velocity * timeElapsed + lastVisibleWidth;
189069cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu        }
189169cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu        position = Math.min(maxWidth, position);
189269cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu        return position;
189369cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu    }
189469cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu
1895a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu    private class PeepholeAnimationEffect extends AnimationEffects {
1896b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liu
1897b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liu        private final static int UNSET = -1;
1898213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu        private final static int PEEP_HOLE_ANIMATION_DURATION_MS = 300;
1899213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu
1900213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu        private final Paint mMaskPaint = new Paint();
1901213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu        private final RectF mBackgroundDrawArea = new RectF();
1902b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liu
1903b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liu        private int mPeepHoleCenterX = UNSET;
1904b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liu        private int mPeepHoleCenterY = UNSET;
1905b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liu        private float mRadius = 0f;
1906b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liu        private ValueAnimator mPeepHoleAnimator;
19073c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague        private ValueAnimator mFadeOutAlphaAnimator;
19083c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague        private ValueAnimator mRevealAlphaAnimator;
1909213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu        private Bitmap mBackground;
1910213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu        private Bitmap mBackgroundOverlay;
1911b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liu
19120496fcaa51f88f6c753975473b971941e4090cfaSpike Sprague        private Paint mCirclePaint = new Paint();
19130496fcaa51f88f6c753975473b971941e4090cfaSpike Sprague        private Paint mCoverPaint = new Paint();
19140496fcaa51f88f6c753975473b971941e4090cfaSpike Sprague
19150496fcaa51f88f6c753975473b971941e4090cfaSpike Sprague        private TouchCircleDrawable mCircleDrawable;
19160496fcaa51f88f6c753975473b971941e4090cfaSpike Sprague
1917b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liu        public PeepholeAnimationEffect() {
1918b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liu            mMaskPaint.setAlpha(0);
1919b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liu            mMaskPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
19200496fcaa51f88f6c753975473b971941e4090cfaSpike Sprague
19210496fcaa51f88f6c753975473b971941e4090cfaSpike Sprague            mCirclePaint.setColor(0);
19220496fcaa51f88f6c753975473b971941e4090cfaSpike Sprague            mCirclePaint.setAlpha(0);
19230496fcaa51f88f6c753975473b971941e4090cfaSpike Sprague
19240496fcaa51f88f6c753975473b971941e4090cfaSpike Sprague            mCoverPaint.setColor(0);
19250496fcaa51f88f6c753975473b971941e4090cfaSpike Sprague            mCoverPaint.setAlpha(0);
19263c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague
19273c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague            setupAnimators();
19283c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague        }
19293c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague
19303c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague        private void setupAnimators() {
19313c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague            mFadeOutAlphaAnimator = ValueAnimator.ofInt(0, 255);
19323c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague            mFadeOutAlphaAnimator.setDuration(100);
19333c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague            mFadeOutAlphaAnimator.setInterpolator(Gusterpolator.INSTANCE);
19343c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague            mFadeOutAlphaAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
19353c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague                @Override
19363c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague                public void onAnimationUpdate(ValueAnimator animation) {
19373c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague                    mCoverPaint.setAlpha((Integer) animation.getAnimatedValue());
19383c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague                    invalidate();
19393c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague                }
19403c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague            });
19413c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague            mFadeOutAlphaAnimator.addListener(new AnimatorListenerAdapter() {
19423c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague                @Override
19433c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague                public void onAnimationStart(Animator animation) {
19443c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague                    // Sets a HW layer on the view for the animation.
19453c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague                    setLayerType(LAYER_TYPE_HARDWARE, null);
19463c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague                }
19473c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague
19483c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague                @Override
19493c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague                public void onAnimationEnd(Animator animation) {
19503c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague                    // Sets the layer type back to NONE as a workaround for b/12594617.
19513c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague                    setLayerType(LAYER_TYPE_NONE, null);
19523c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague                }
19533c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague            });
19543c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague
19553c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague            /////////////////
19563c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague
19573c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague            mRevealAlphaAnimator = ValueAnimator.ofInt(255, 0);
19583c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague            mRevealAlphaAnimator.setDuration(PEEP_HOLE_ANIMATION_DURATION_MS);
19593c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague            mRevealAlphaAnimator.setInterpolator(Gusterpolator.INSTANCE);
19603c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague            mRevealAlphaAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
19613c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague                @Override
19623c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague                public void onAnimationUpdate(ValueAnimator animation) {
19633c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague                    int alpha = (Integer) animation.getAnimatedValue();
19643c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague                    mCirclePaint.setAlpha(alpha);
19653c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague                    mCoverPaint.setAlpha(alpha);
19663c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague                }
19673c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague            });
19683c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague            mRevealAlphaAnimator.addListener(new AnimatorListenerAdapter() {
19693c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague                @Override
19703c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague                public void onAnimationStart(Animator animation) {
19713c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague                    // Sets a HW layer on the view for the animation.
19723c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague                    setLayerType(LAYER_TYPE_HARDWARE, null);
19733c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague                }
19743c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague
19753c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague                @Override
19763c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague                public void onAnimationEnd(Animator animation) {
19773c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague                    // Sets the layer type back to NONE as a workaround for b/12594617.
19783c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague                    setLayerType(LAYER_TYPE_NONE, null);
19793c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague                }
19803c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague            });
19813c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague
19823c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague            ////////////////
19833c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague
19843c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague            int horizontalDistanceToFarEdge = Math.max(mPeepHoleCenterX, mWidth - mPeepHoleCenterX);
19853c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague            int verticalDistanceToFarEdge = Math.max(mPeepHoleCenterY, mHeight - mPeepHoleCenterY);
19863c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague            int endRadius = (int) (Math.sqrt(horizontalDistanceToFarEdge * horizontalDistanceToFarEdge
19873c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague                    + verticalDistanceToFarEdge * verticalDistanceToFarEdge));
19883c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague            int startRadius = getResources().getDimensionPixelSize(
19893c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague                    R.dimen.mode_selector_icon_block_width) / 2;
19903c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague
19913c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague            mPeepHoleAnimator = ValueAnimator.ofFloat(startRadius, endRadius);
19923c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague            mPeepHoleAnimator.setDuration(PEEP_HOLE_ANIMATION_DURATION_MS);
19933c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague            mPeepHoleAnimator.setInterpolator(Gusterpolator.INSTANCE);
19943c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague            mPeepHoleAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
19953c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague                @Override
19963c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague                public void onAnimationUpdate(ValueAnimator animation) {
19973c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague                    // Modify mask by enlarging the hole
19983c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague                    mRadius = (Float) mPeepHoleAnimator.getAnimatedValue();
19993c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague                    invalidate();
20003c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague                }
20013c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague            });
20023c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague            mPeepHoleAnimator.addListener(new AnimatorListenerAdapter() {
20033c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague                @Override
20043c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague                public void onAnimationStart(Animator animation) {
20053c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague                    // Sets a HW layer on the view for the animation.
20063c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague                    setLayerType(LAYER_TYPE_HARDWARE, null);
20073c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague                }
20083c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague
20093c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague                @Override
20103c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague                public void onAnimationEnd(Animator animation) {
20113c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague                    // Sets the layer type back to NONE as a workaround for b/12594617.
20123c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague                    setLayerType(LAYER_TYPE_NONE, null);
20133c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague                }
20143c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague            });
20153c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague
20163c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague            ////////////////
20173c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague            int size = getContext().getResources()
20183c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague                    .getDimensionPixelSize(R.dimen.mode_selector_icon_block_width);
20193c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague            mCircleDrawable = new TouchCircleDrawable(getContext().getResources());
20203c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague            mCircleDrawable.setSize(size, size);
20213c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague            mCircleDrawable.setUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
20223c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague                @Override
20233c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague                public void onAnimationUpdate(ValueAnimator animation) {
20243c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague                    invalidate();
20253c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague                }
20263c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague            });
2027b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liu        }
2028b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liu
2029601921e746832bdef40503c606a95b4469f1bedbSascha Haeberling        @Override
2030b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liu        public void setSize(int width, int height) {
2031b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liu            mWidth = width;
2032b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liu            mHeight = height;
2033b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liu        }
2034b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liu
2035601921e746832bdef40503c606a95b4469f1bedbSascha Haeberling        @Override
203669cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu        public boolean onTouchEvent(MotionEvent event) {
203769cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu            return true;
203869cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu        }
203969cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu
20403c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague        @Override
20413c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague        public void drawForeground(Canvas canvas) {
20423c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague            // Draw the circle in clear mode
20433c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague            if (mPeepHoleAnimator != null) {
20443c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague                // Draw a transparent circle using clear mode
20453c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague                canvas.drawCircle(mPeepHoleCenterX, mPeepHoleCenterY, mRadius, mMaskPaint);
20463c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague                canvas.drawCircle(mPeepHoleCenterX, mPeepHoleCenterY, mRadius, mCirclePaint);
20473c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague            }
20483c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague        }
20493c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague
2050b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liu        public void setAnimationStartingPosition(int x, int y) {
2051b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liu            mPeepHoleCenterX = x;
2052b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liu            mPeepHoleCenterY = y;
2053b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liu        }
2054b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liu
20550496fcaa51f88f6c753975473b971941e4090cfaSpike Sprague        public void setModeSpecificColor(int color) {
20560496fcaa51f88f6c753975473b971941e4090cfaSpike Sprague            mCirclePaint.setColor(color & 0x00ffffff);
20570496fcaa51f88f6c753975473b971941e4090cfaSpike Sprague        }
20580496fcaa51f88f6c753975473b971941e4090cfaSpike Sprague
2059213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu        /**
2060213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu         * Sets the bitmap to be drawn in the background and the drawArea to draw
20610496fcaa51f88f6c753975473b971941e4090cfaSpike Sprague         * the bitmap.
2062213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu         *
2063213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu         * @param background image to be drawn in the background
2064213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu         * @param drawArea area to draw the background image
2065213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu         */
2066213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu        public void setBackground(Bitmap background, RectF drawArea) {
2067213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu            mBackground = background;
2068213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu            mBackgroundDrawArea.set(drawArea);
2069213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu        }
2070213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu
2071213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu        /**
2072213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu         * Sets the overlay image to be drawn on top of the background.
2073213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu         */
2074213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu        public void setBackgroundOverlay(Bitmap overlay) {
2075213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu            mBackgroundOverlay = overlay;
2076213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu        }
2077213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu
2078213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu        @Override
2079213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu        public void drawBackground(Canvas canvas) {
2080213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu            if (mBackground != null && mBackgroundOverlay != null) {
2081213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu                canvas.drawBitmap(mBackground, null, mBackgroundDrawArea, null);
20820496fcaa51f88f6c753975473b971941e4090cfaSpike Sprague                canvas.drawPaint(mCoverPaint);
2083213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu                canvas.drawBitmap(mBackgroundOverlay, 0, 0, null);
20840496fcaa51f88f6c753975473b971941e4090cfaSpike Sprague
20850496fcaa51f88f6c753975473b971941e4090cfaSpike Sprague                if (mCircleDrawable != null) {
20860496fcaa51f88f6c753975473b971941e4090cfaSpike Sprague                    mCircleDrawable.draw(canvas);
20870496fcaa51f88f6c753975473b971941e4090cfaSpike Sprague                }
20880496fcaa51f88f6c753975473b971941e4090cfaSpike Sprague            }
20890496fcaa51f88f6c753975473b971941e4090cfaSpike Sprague        }
20900496fcaa51f88f6c753975473b971941e4090cfaSpike Sprague
20910496fcaa51f88f6c753975473b971941e4090cfaSpike Sprague        @Override
2092bee2022d8dcde61d2b0c6cf9b709d8e34cdcb739Doris Liu        public boolean shouldDrawSuper() {
2093bee2022d8dcde61d2b0c6cf9b709d8e34cdcb739Doris Liu            // No need to draw super when mBackgroundOverlay is being drawn, as
2094bee2022d8dcde61d2b0c6cf9b709d8e34cdcb739Doris Liu            // background overlay already contains what's drawn in super.
2095bee2022d8dcde61d2b0c6cf9b709d8e34cdcb739Doris Liu            return (mBackground == null || mBackgroundOverlay == null);
2096bee2022d8dcde61d2b0c6cf9b709d8e34cdcb739Doris Liu        }
2097bee2022d8dcde61d2b0c6cf9b709d8e34cdcb739Doris Liu
20980496fcaa51f88f6c753975473b971941e4090cfaSpike Sprague        public void startFadeoutAnimation(Animator.AnimatorListener listener,
20990496fcaa51f88f6c753975473b971941e4090cfaSpike Sprague                final ModeSelectorItem selectedItem,
21000496fcaa51f88f6c753975473b971941e4090cfaSpike Sprague                int x, int y, final int modeId) {
21010496fcaa51f88f6c753975473b971941e4090cfaSpike Sprague            mCoverPaint.setColor(0);
21020496fcaa51f88f6c753975473b971941e4090cfaSpike Sprague            mCoverPaint.setAlpha(0);
21030496fcaa51f88f6c753975473b971941e4090cfaSpike Sprague
21040496fcaa51f88f6c753975473b971941e4090cfaSpike Sprague            mCircleDrawable.setIconDrawable(
21050496fcaa51f88f6c753975473b971941e4090cfaSpike Sprague                    selectedItem.getIcon().getIconDrawableClone(),
21060496fcaa51f88f6c753975473b971941e4090cfaSpike Sprague                    selectedItem.getIcon().getIconDrawableSize());
21070496fcaa51f88f6c753975473b971941e4090cfaSpike Sprague            mCircleDrawable.setCenter(new Point(x, y));
21080496fcaa51f88f6c753975473b971941e4090cfaSpike Sprague            mCircleDrawable.setColor(selectedItem.getHighlightColor());
21090496fcaa51f88f6c753975473b971941e4090cfaSpike Sprague            mCircleDrawable.setAnimatorListener(new AnimatorListenerAdapter() {
21100496fcaa51f88f6c753975473b971941e4090cfaSpike Sprague                @Override
21110496fcaa51f88f6c753975473b971941e4090cfaSpike Sprague                public void onAnimationEnd(Animator animation) {
21120496fcaa51f88f6c753975473b971941e4090cfaSpike Sprague                    // Post mode selection runnable to the end of the message queue
21130496fcaa51f88f6c753975473b971941e4090cfaSpike Sprague                    // so that current UI changes can finish before mode initialization
21140496fcaa51f88f6c753975473b971941e4090cfaSpike Sprague                    // clogs up UI thread.
21150496fcaa51f88f6c753975473b971941e4090cfaSpike Sprague                    post(new Runnable() {
21160496fcaa51f88f6c753975473b971941e4090cfaSpike Sprague                        @Override
21170496fcaa51f88f6c753975473b971941e4090cfaSpike Sprague                        public void run() {
21180496fcaa51f88f6c753975473b971941e4090cfaSpike Sprague                            // Select the focused item.
21190496fcaa51f88f6c753975473b971941e4090cfaSpike Sprague                            selectedItem.setSelected(true);
21200496fcaa51f88f6c753975473b971941e4090cfaSpike Sprague                            onModeSelected(modeId);
21210496fcaa51f88f6c753975473b971941e4090cfaSpike Sprague                        }
21220496fcaa51f88f6c753975473b971941e4090cfaSpike Sprague                    });
21230496fcaa51f88f6c753975473b971941e4090cfaSpike Sprague                }
21240496fcaa51f88f6c753975473b971941e4090cfaSpike Sprague            });
21253c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague
21263c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague            // add fade out animator to a set, so we can freely add
21273c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague            // the listener without having to worry about listener dupes
21283c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague            AnimatorSet s = new AnimatorSet();
21293c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague            s.play(mFadeOutAlphaAnimator);
21303c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague            if (listener != null) {
21313c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague                s.addListener(listener);
21323c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague            }
21330496fcaa51f88f6c753975473b971941e4090cfaSpike Sprague            mCircleDrawable.animate();
21343c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague            s.start();
21350496fcaa51f88f6c753975473b971941e4090cfaSpike Sprague        }
21360496fcaa51f88f6c753975473b971941e4090cfaSpike Sprague
2137bee2022d8dcde61d2b0c6cf9b709d8e34cdcb739Doris Liu        @Override
2138a8a24e0241ad3a3d3c8d8b77ac5cbd7138736bd5Doris Liu        public void startAnimation(Animator.AnimatorListener listener) {
2139b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liu            if (mPeepHoleAnimator != null && mPeepHoleAnimator.isRunning()) {
2140b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liu                return;
2141b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liu            }
2142b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liu            if (mPeepHoleCenterY == UNSET || mPeepHoleCenterX == UNSET) {
2143b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liu                mPeepHoleCenterX = mWidth / 2;
2144b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liu                mPeepHoleCenterY = mHeight / 2;
2145b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liu            }
2146b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liu
21470496fcaa51f88f6c753975473b971941e4090cfaSpike Sprague            mCirclePaint.setAlpha(255);
21480496fcaa51f88f6c753975473b971941e4090cfaSpike Sprague            mCoverPaint.setAlpha(255);
2149759122e8b3071b79fa2a388f00403af4e21be68cDoris Liu
21503c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague            // add peephole and reveal animators to a set, so we can
21513c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague            // freely add the listener without having to worry about
21523c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague            // listener dupes
21533c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague            AnimatorSet s = new AnimatorSet();
21543c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague            s.play(mPeepHoleAnimator).with(mRevealAlphaAnimator);
21553c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague            if (listener != null) {
21563c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague                s.addListener(listener);
21573c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague            }
21583c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague            s.start();
2159b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liu        }
2160b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liu
216169cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu        @Override
216269cb84076c2f290ee2cd782ab668a661c9d5386eDoris Liu        public void endAnimation() {
2163b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liu        }
2164213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu
2165fa50f7d71a77dc730746c33fd17a570d42453759Doris Liu        @Override
2166fa50f7d71a77dc730746c33fd17a570d42453759Doris Liu        public boolean cancelAnimation() {
2167fa50f7d71a77dc730746c33fd17a570d42453759Doris Liu            if (mPeepHoleAnimator == null || !mPeepHoleAnimator.isRunning()) {
2168fa50f7d71a77dc730746c33fd17a570d42453759Doris Liu                return false;
2169fa50f7d71a77dc730746c33fd17a570d42453759Doris Liu            } else {
2170fa50f7d71a77dc730746c33fd17a570d42453759Doris Liu                mPeepHoleAnimator.cancel();
2171fa50f7d71a77dc730746c33fd17a570d42453759Doris Liu                return true;
2172fa50f7d71a77dc730746c33fd17a570d42453759Doris Liu            }
2173fa50f7d71a77dc730746c33fd17a570d42453759Doris Liu        }
2174b6eaa8c70da19fb3233c3c4b1a2625ce90fc35afDoris Liu    }
21751c94b7d0fa0143230d04b9f14c2ff93c6886d6ccDoris Liu}
2176