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