StackView.java revision 026e121bc35b9012943d8ea86518b51270d0c0ef
144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen/* 244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen * Copyright (C) 2010 The Android Open Source Project 344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen * 444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen * Licensed under the Apache License, Version 2.0 (the "License"); 544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen * you may not use this file except in compliance with the License. 644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen * You may obtain a copy of the License at 744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen * 844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen * http://www.apache.org/licenses/LICENSE-2.0 944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen * 1044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen * Unless required by applicable law or agreed to in writing, software 1144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen * distributed under the License is distributed on an "AS IS" BASIS, 1244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen * See the License for the specific language governing permissions and 1444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen * limitations under the License. 1544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen */ 1644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen 1744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohenpackage android.widget; 1844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen 19a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haaseimport android.animation.ObjectAnimator; 20e5ebcb0107a939395e03592fd44c746cd09e311dRomain Guyimport android.animation.PropertyValuesHolder; 2144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohenimport android.content.Context; 2232a42f1587db77b958d62c3de4f2734eb0a3b965Adam Cohenimport android.graphics.Bitmap; 23839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohenimport android.graphics.BlurMaskFilter; 2432a42f1587db77b958d62c3de4f2734eb0a3b965Adam Cohenimport android.graphics.Canvas; 25026e121bc35b9012943d8ea86518b51270d0c0efAdam Cohenimport android.graphics.Color; 2632a42f1587db77b958d62c3de4f2734eb0a3b965Adam Cohenimport android.graphics.Matrix; 2732a42f1587db77b958d62c3de4f2734eb0a3b965Adam Cohenimport android.graphics.Paint; 2832a42f1587db77b958d62c3de4f2734eb0a3b965Adam Cohenimport android.graphics.PorterDuff; 2932a42f1587db77b958d62c3de4f2734eb0a3b965Adam Cohenimport android.graphics.PorterDuffXfermode; 3044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohenimport android.graphics.Rect; 319b073948cfb84c0dd04f8a94ee1f7f263f027c83Adam Cohenimport android.graphics.RectF; 32d51bbb5b56446519db88f49f2682da782b0069acAdam Cohenimport android.graphics.Region; 33839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohenimport android.graphics.TableMaskFilter; 3444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohenimport android.util.AttributeSet; 3544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohenimport android.util.Log; 3644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohenimport android.view.MotionEvent; 3744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohenimport android.view.VelocityTracker; 3844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohenimport android.view.View; 3944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohenimport android.view.ViewConfiguration; 4044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohenimport android.view.ViewGroup; 41b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohenimport android.view.animation.LinearInterpolator; 4244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohenimport android.widget.RemoteViews.RemoteView; 4344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen 4444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen@RemoteView 4544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen/** 4644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen * A view that displays its children in a stack and allows users to discretely swipe 4744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen * through the children. 4844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen */ 4944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohenpublic class StackView extends AdapterViewAnimator { 5044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen private final String TAG = "StackView"; 5144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen 5244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen /** 5344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen * Default animation parameters 5444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen */ 55e5ebcb0107a939395e03592fd44c746cd09e311dRomain Guy private static final int DEFAULT_ANIMATION_DURATION = 400; 56026e121bc35b9012943d8ea86518b51270d0c0efAdam Cohen private static final int FADE_IN_ANIMATION_DURATION = 800; 57e5ebcb0107a939395e03592fd44c746cd09e311dRomain Guy private static final int MINIMUM_ANIMATION_DURATION = 50; 5844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen 5944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen /** 60839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen * Parameters effecting the perspective visuals 61839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen */ 62026e121bc35b9012943d8ea86518b51270d0c0efAdam Cohen private static final float PERSPECTIVE_SHIFT_FACTOR_Y = 0.1f; 63026e121bc35b9012943d8ea86518b51270d0c0efAdam Cohen private static final float PERSPECTIVE_SHIFT_FACTOR_X = 0.1f; 64026e121bc35b9012943d8ea86518b51270d0c0efAdam Cohen 65e5ebcb0107a939395e03592fd44c746cd09e311dRomain Guy @SuppressWarnings({"FieldCanBeLocal"}) 66026e121bc35b9012943d8ea86518b51270d0c0efAdam Cohen private static final float PERSPECTIVE_SCALE_FACTOR = 0.f; 67839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen 68839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen /** 69839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen * Represent the two possible stack modes, one where items slide up, and the other 70839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen * where items slide down. The perspective is also inverted between these two modes. 71839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen */ 72839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen private static final int ITEMS_SLIDE_UP = 0; 73839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen private static final int ITEMS_SLIDE_DOWN = 1; 74839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen 75839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen /** 7644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen * These specify the different gesture states 7744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen */ 785b53f9186e7812c93bc578d18e92cb123481fcbcRomain Guy private static final int GESTURE_NONE = 0; 795b53f9186e7812c93bc578d18e92cb123481fcbcRomain Guy private static final int GESTURE_SLIDE_UP = 1; 805b53f9186e7812c93bc578d18e92cb123481fcbcRomain Guy private static final int GESTURE_SLIDE_DOWN = 2; 8144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen 8244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen /** 8344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen * Specifies how far you need to swipe (up or down) before it 8444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen * will be consider a completed gesture when you lift your finger 8544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen */ 86a9238c89a43500ed0bcdeaee182be08ff991c627Adam Cohen private static final float SWIPE_THRESHOLD_RATIO = 0.2f; 87a9238c89a43500ed0bcdeaee182be08ff991c627Adam Cohen 88a9238c89a43500ed0bcdeaee182be08ff991c627Adam Cohen /** 89a9238c89a43500ed0bcdeaee182be08ff991c627Adam Cohen * Specifies the total distance, relative to the size of the stack, 90a9238c89a43500ed0bcdeaee182be08ff991c627Adam Cohen * that views will be slid, either up or down 91a9238c89a43500ed0bcdeaee182be08ff991c627Adam Cohen */ 925b53f9186e7812c93bc578d18e92cb123481fcbcRomain Guy private static final float SLIDE_UP_RATIO = 0.7f; 9344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen 9444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen /** 9544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen * Sentinel value for no current active pointer. 9644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen * Used by {@link #mActivePointerId}. 9744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen */ 9844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen private static final int INVALID_POINTER = -1; 9944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen 10044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen /** 101839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen * Number of active views in the stack. One fewer view is actually visible, as one is hidden. 102839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen */ 103839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen private static final int NUM_ACTIVE_VIEWS = 5; 104839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen 105dfcdddd7c408dddb22fb0867e4799d4c29d2f55fAdam Cohen private static final int FRAME_PADDING = 4; 106839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen 107e80202d5c3c2a45cc34976e09958883e5366f1bbPatrick Dubroy private final Rect mTouchRect = new Rect(); 108e80202d5c3c2a45cc34976e09958883e5366f1bbPatrick Dubroy 10926e30bb7fe373ad4bb569a5de2577e0c857e7c27Adam Cohen private static final int MIN_TIME_BETWEEN_INTERACTION_AND_AUTOADVANCE = 5000; 11026e30bb7fe373ad4bb569a5de2577e0c857e7c27Adam Cohen 111839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen /** 11244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen * These variables are all related to the current state of touch interaction 11344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen * with the stack 11444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen */ 11544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen private float mInitialY; 11644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen private float mInitialX; 11744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen private int mActivePointerId; 11844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen private int mYVelocity = 0; 11944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen private int mSwipeGestureType = GESTURE_NONE; 120dfcdddd7c408dddb22fb0867e4799d4c29d2f55fAdam Cohen private int mSlideAmount; 12144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen private int mSwipeThreshold; 12244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen private int mTouchSlop; 12344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen private int mMaximumVelocity; 12444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen private VelocityTracker mVelocityTracker; 1253352b6807f9b728b91cceb3ea5f2a065ace95da3Adam Cohen private boolean mTransitionIsSetup = false; 12644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen 1279b073948cfb84c0dd04f8a94ee1f7f263f027c83Adam Cohen private static HolographicHelper sHolographicHelper; 12832a42f1587db77b958d62c3de4f2734eb0a3b965Adam Cohen private ImageView mHighlight; 1298baf5df0fa5b3453a7f17e95746c5d8cadc00163Adam Cohen private ImageView mClickFeedback; 1308baf5df0fa5b3453a7f17e95746c5d8cadc00163Adam Cohen private boolean mClickFeedbackIsValid = false; 13132a42f1587db77b958d62c3de4f2734eb0a3b965Adam Cohen private StackSlider mStackSlider; 13244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen private boolean mFirstLayoutHappened = false; 13326e30bb7fe373ad4bb569a5de2577e0c857e7c27Adam Cohen private long mLastInteractionTime = 0; 134839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen private int mStackMode; 135dfcdddd7c408dddb22fb0867e4799d4c29d2f55fAdam Cohen private int mFramePadding; 1360ac116b688380489c3690f6f65b282990c221f17Adam Cohen private final Rect stackInvalidateRect = new Rect(); 13744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen 13844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen public StackView(Context context) { 13944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen super(context); 14044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen initStackView(); 14144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen } 14244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen 14344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen public StackView(Context context, AttributeSet attrs) { 14444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen super(context, attrs); 14544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen initStackView(); 14644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen } 14744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen 14844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen private void initStackView() { 14996d8d56302da81b24333b204e6d7f15064538036Adam Cohen configureViewAnimator(NUM_ACTIVE_VIEWS, 1); 15044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen setStaticTransformationsEnabled(true); 15144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen final ViewConfiguration configuration = ViewConfiguration.get(getContext()); 152b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen mTouchSlop = configuration.getScaledTouchSlop(); 15344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen mMaximumVelocity = configuration.getScaledMaximumFlingVelocity(); 15444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen mActivePointerId = INVALID_POINTER; 15532a42f1587db77b958d62c3de4f2734eb0a3b965Adam Cohen 15632a42f1587db77b958d62c3de4f2734eb0a3b965Adam Cohen mHighlight = new ImageView(getContext()); 15732a42f1587db77b958d62c3de4f2734eb0a3b965Adam Cohen mHighlight.setLayoutParams(new LayoutParams(mHighlight)); 15832a42f1587db77b958d62c3de4f2734eb0a3b965Adam Cohen addViewInLayout(mHighlight, -1, new LayoutParams(mHighlight)); 1598baf5df0fa5b3453a7f17e95746c5d8cadc00163Adam Cohen 1608baf5df0fa5b3453a7f17e95746c5d8cadc00163Adam Cohen mClickFeedback = new ImageView(getContext()); 1618baf5df0fa5b3453a7f17e95746c5d8cadc00163Adam Cohen mClickFeedback.setLayoutParams(new LayoutParams(mClickFeedback)); 1628baf5df0fa5b3453a7f17e95746c5d8cadc00163Adam Cohen addViewInLayout(mClickFeedback, -1, new LayoutParams(mClickFeedback)); 1638baf5df0fa5b3453a7f17e95746c5d8cadc00163Adam Cohen mClickFeedback.setVisibility(INVISIBLE); 1648baf5df0fa5b3453a7f17e95746c5d8cadc00163Adam Cohen 16532a42f1587db77b958d62c3de4f2734eb0a3b965Adam Cohen mStackSlider = new StackSlider(); 16632a42f1587db77b958d62c3de4f2734eb0a3b965Adam Cohen 1679b073948cfb84c0dd04f8a94ee1f7f263f027c83Adam Cohen if (sHolographicHelper == null) { 168dfcdddd7c408dddb22fb0867e4799d4c29d2f55fAdam Cohen sHolographicHelper = new HolographicHelper(mContext); 16932a42f1587db77b958d62c3de4f2734eb0a3b965Adam Cohen } 1709b073948cfb84c0dd04f8a94ee1f7f263f027c83Adam Cohen setClipChildren(false); 1719b073948cfb84c0dd04f8a94ee1f7f263f027c83Adam Cohen setClipToPadding(false); 1721480fddea874a42adb43b4bcdac6704e4c3e110bAdam Cohen 173839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen // This sets the form of the StackView, which is currently to have the perspective-shifted 174839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen // views above the active view, and have items slide down when sliding out. The opposite is 175839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen // available by using ITEMS_SLIDE_UP. 176839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen mStackMode = ITEMS_SLIDE_DOWN; 177839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen 1781480fddea874a42adb43b4bcdac6704e4c3e110bAdam Cohen // This is a flag to indicate the the stack is loading for the first time 1791480fddea874a42adb43b4bcdac6704e4c3e110bAdam Cohen mWhichChild = -1; 180dfcdddd7c408dddb22fb0867e4799d4c29d2f55fAdam Cohen 181dfcdddd7c408dddb22fb0867e4799d4c29d2f55fAdam Cohen // Adjust the frame padding based on the density, since the highlight changes based 182dfcdddd7c408dddb22fb0867e4799d4c29d2f55fAdam Cohen // on the density 183dfcdddd7c408dddb22fb0867e4799d4c29d2f55fAdam Cohen final float density = mContext.getResources().getDisplayMetrics().density; 184dfcdddd7c408dddb22fb0867e4799d4c29d2f55fAdam Cohen mFramePadding = (int) Math.ceil(density * FRAME_PADDING); 18544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen } 18644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen 18744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen /** 18844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen * Animate the views between different relative indexes within the {@link AdapterViewAnimator} 18944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen */ 19044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen void animateViewForTransition(int fromIndex, int toIndex, View view) { 191026e121bc35b9012943d8ea86518b51270d0c0efAdam Cohen if (fromIndex == -1 && toIndex > 0) { 19244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen // Fade item in 19344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen if (view.getAlpha() == 1) { 19444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen view.setAlpha(0); 19544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen } 196b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen view.setVisibility(VISIBLE); 197b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen 1982794eb3b02e2404d453d3ad22a8a85a138130a07Chet Haase ObjectAnimator fadeIn = ObjectAnimator.ofFloat(view, "alpha", view.getAlpha(), 1.0f); 199026e121bc35b9012943d8ea86518b51270d0c0efAdam Cohen fadeIn.setDuration(FADE_IN_ANIMATION_DURATION); 20044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen fadeIn.start(); 20196d8d56302da81b24333b204e6d7f15064538036Adam Cohen } else if (fromIndex == 0 && toIndex == 1) { 20244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen // Slide item in 20344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen view.setVisibility(VISIBLE); 20432a42f1587db77b958d62c3de4f2734eb0a3b965Adam Cohen 205839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen int duration = Math.round(mStackSlider.getDurationForNeutralPosition(mYVelocity)); 20644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen 207b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen StackSlider animationSlider = new StackSlider(mStackSlider); 2082794eb3b02e2404d453d3ad22a8a85a138130a07Chet Haase PropertyValuesHolder slideInY = PropertyValuesHolder.ofFloat("YProgress", 0.0f); 2092794eb3b02e2404d453d3ad22a8a85a138130a07Chet Haase PropertyValuesHolder slideInX = PropertyValuesHolder.ofFloat("XProgress", 0.0f); 2102794eb3b02e2404d453d3ad22a8a85a138130a07Chet Haase ObjectAnimator pa = ObjectAnimator.ofPropertyValuesHolder(animationSlider, 211839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen slideInX, slideInY); 2122794eb3b02e2404d453d3ad22a8a85a138130a07Chet Haase pa.setDuration(duration); 213839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen pa.setInterpolator(new LinearInterpolator()); 214839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen pa.start(); 21596d8d56302da81b24333b204e6d7f15064538036Adam Cohen } else if (fromIndex == 1 && toIndex == 0) { 21644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen // Slide item out 217839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen int duration = Math.round(mStackSlider.getDurationForOffscreenPosition(mYVelocity)); 21844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen 219b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen StackSlider animationSlider = new StackSlider(mStackSlider); 2202794eb3b02e2404d453d3ad22a8a85a138130a07Chet Haase PropertyValuesHolder slideOutY = PropertyValuesHolder.ofFloat("YProgress", 1.0f); 2212794eb3b02e2404d453d3ad22a8a85a138130a07Chet Haase PropertyValuesHolder slideOutX = PropertyValuesHolder.ofFloat("XProgress", 0.0f); 2222794eb3b02e2404d453d3ad22a8a85a138130a07Chet Haase ObjectAnimator pa = ObjectAnimator.ofPropertyValuesHolder(animationSlider, 2232794eb3b02e2404d453d3ad22a8a85a138130a07Chet Haase slideOutX, slideOutY); 2242794eb3b02e2404d453d3ad22a8a85a138130a07Chet Haase pa.setDuration(duration); 225839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen pa.setInterpolator(new LinearInterpolator()); 226839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen pa.start(); 22796d8d56302da81b24333b204e6d7f15064538036Adam Cohen } else if (fromIndex == -1 && toIndex == 0) { 22844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen // Make sure this view that is "waiting in the wings" is invisible 22944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen view.setAlpha(0.0f); 230b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen view.setVisibility(INVISIBLE); 231b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen LayoutParams lp = (LayoutParams) view.getLayoutParams(); 232dfcdddd7c408dddb22fb0867e4799d4c29d2f55fAdam Cohen lp.setVerticalOffset(-mSlideAmount); 23344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen } else if (toIndex == -1) { 23444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen // Fade item out 2352794eb3b02e2404d453d3ad22a8a85a138130a07Chet Haase ObjectAnimator fadeOut = ObjectAnimator.ofFloat(view, "alpha", view.getAlpha(), 0.0f); 2362794eb3b02e2404d453d3ad22a8a85a138130a07Chet Haase fadeOut.setDuration(DEFAULT_ANIMATION_DURATION); 23744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen fadeOut.start(); 23844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen } 239839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen 240839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen // Implement the faked perspective 241839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen if (toIndex != -1) { 242f04e22571f17bf850ef89adddf2b12171e536619Adam Cohen transformViewAtIndex(toIndex, view); 243f04e22571f17bf850ef89adddf2b12171e536619Adam Cohen } 244f04e22571f17bf850ef89adddf2b12171e536619Adam Cohen } 245839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen 246026e121bc35b9012943d8ea86518b51270d0c0efAdam Cohen private void transformViewAtIndex(int index, View view) { 247026e121bc35b9012943d8ea86518b51270d0c0efAdam Cohen float maxPerspectiveShiftY = getMeasuredHeight() * PERSPECTIVE_SHIFT_FACTOR_Y; 248026e121bc35b9012943d8ea86518b51270d0c0efAdam Cohen float maxPerspectiveShiftX = getMeasuredHeight() * PERSPECTIVE_SHIFT_FACTOR_X; 249026e121bc35b9012943d8ea86518b51270d0c0efAdam Cohen 250026e121bc35b9012943d8ea86518b51270d0c0efAdam Cohen index = mMaxNumActiveViews - index - 1; 251026e121bc35b9012943d8ea86518b51270d0c0efAdam Cohen if (index == mMaxNumActiveViews - 1) index--; 252026e121bc35b9012943d8ea86518b51270d0c0efAdam Cohen 253026e121bc35b9012943d8ea86518b51270d0c0efAdam Cohen float r = (index * 1.0f) / (mMaxNumActiveViews - 2); 254026e121bc35b9012943d8ea86518b51270d0c0efAdam Cohen 255026e121bc35b9012943d8ea86518b51270d0c0efAdam Cohen float scale = 1 - PERSPECTIVE_SCALE_FACTOR * (1 - r); 256026e121bc35b9012943d8ea86518b51270d0c0efAdam Cohen PropertyValuesHolder scalePropX = PropertyValuesHolder.ofFloat("scaleX", scale); 257026e121bc35b9012943d8ea86518b51270d0c0efAdam Cohen PropertyValuesHolder scalePropY = PropertyValuesHolder.ofFloat("scaleY", scale); 258026e121bc35b9012943d8ea86518b51270d0c0efAdam Cohen 259026e121bc35b9012943d8ea86518b51270d0c0efAdam Cohen int stackDirection = (mStackMode == ITEMS_SLIDE_UP) ? 1 : -1; 260026e121bc35b9012943d8ea86518b51270d0c0efAdam Cohen float perspectiveTranslationY = -stackDirection * r * maxPerspectiveShiftY; 261026e121bc35b9012943d8ea86518b51270d0c0efAdam Cohen float scaleShiftCorrectionY = stackDirection * (1 - scale) * 262026e121bc35b9012943d8ea86518b51270d0c0efAdam Cohen (getMeasuredHeight() * (1 - PERSPECTIVE_SHIFT_FACTOR_Y) / 2.0f); 263026e121bc35b9012943d8ea86518b51270d0c0efAdam Cohen float transY = perspectiveTranslationY + scaleShiftCorrectionY; 264026e121bc35b9012943d8ea86518b51270d0c0efAdam Cohen 265026e121bc35b9012943d8ea86518b51270d0c0efAdam Cohen float perspectiveTranslationX = (1 - r) * maxPerspectiveShiftX; 266026e121bc35b9012943d8ea86518b51270d0c0efAdam Cohen float scaleShiftCorrectionX = (1 - scale) * 267026e121bc35b9012943d8ea86518b51270d0c0efAdam Cohen (getMeasuredWidth() * (1 - PERSPECTIVE_SHIFT_FACTOR_X) / 2.0f); 268026e121bc35b9012943d8ea86518b51270d0c0efAdam Cohen float transX = perspectiveTranslationX + scaleShiftCorrectionX; 269026e121bc35b9012943d8ea86518b51270d0c0efAdam Cohen 270026e121bc35b9012943d8ea86518b51270d0c0efAdam Cohen PropertyValuesHolder translationX = PropertyValuesHolder.ofFloat("translationX", transX); 271026e121bc35b9012943d8ea86518b51270d0c0efAdam Cohen PropertyValuesHolder translationY = PropertyValuesHolder.ofFloat("translationY", transY); 272026e121bc35b9012943d8ea86518b51270d0c0efAdam Cohen 273026e121bc35b9012943d8ea86518b51270d0c0efAdam Cohen ObjectAnimator pa = ObjectAnimator.ofPropertyValuesHolder(view, scalePropX, scalePropY, 274026e121bc35b9012943d8ea86518b51270d0c0efAdam Cohen translationY, translationX); 275026e121bc35b9012943d8ea86518b51270d0c0efAdam Cohen pa.setDuration(100); 276026e121bc35b9012943d8ea86518b51270d0c0efAdam Cohen pa.start(); 277026e121bc35b9012943d8ea86518b51270d0c0efAdam Cohen } 278026e121bc35b9012943d8ea86518b51270d0c0efAdam Cohen 2793352b6807f9b728b91cceb3ea5f2a065ace95da3Adam Cohen private void setupStackSlider(View v, int mode) { 2803352b6807f9b728b91cceb3ea5f2a065ace95da3Adam Cohen mStackSlider.setMode(mode); 2813352b6807f9b728b91cceb3ea5f2a065ace95da3Adam Cohen if (v != null) { 2823352b6807f9b728b91cceb3ea5f2a065ace95da3Adam Cohen mHighlight.setImageBitmap(sHolographicHelper.createOutline(v)); 2833352b6807f9b728b91cceb3ea5f2a065ace95da3Adam Cohen mHighlight.setRotation(v.getRotation()); 2843352b6807f9b728b91cceb3ea5f2a065ace95da3Adam Cohen mHighlight.setTranslationY(v.getTranslationY()); 285026e121bc35b9012943d8ea86518b51270d0c0efAdam Cohen mHighlight.setTranslationX(v.getTranslationX()); 2863352b6807f9b728b91cceb3ea5f2a065ace95da3Adam Cohen mHighlight.bringToFront(); 2873352b6807f9b728b91cceb3ea5f2a065ace95da3Adam Cohen v.bringToFront(); 2883352b6807f9b728b91cceb3ea5f2a065ace95da3Adam Cohen mStackSlider.setView(v); 2893352b6807f9b728b91cceb3ea5f2a065ace95da3Adam Cohen 2903352b6807f9b728b91cceb3ea5f2a065ace95da3Adam Cohen v.setVisibility(VISIBLE); 2913352b6807f9b728b91cceb3ea5f2a065ace95da3Adam Cohen } 2923352b6807f9b728b91cceb3ea5f2a065ace95da3Adam Cohen } 2933352b6807f9b728b91cceb3ea5f2a065ace95da3Adam Cohen 2943352b6807f9b728b91cceb3ea5f2a065ace95da3Adam Cohen @Override 2953352b6807f9b728b91cceb3ea5f2a065ace95da3Adam Cohen @android.view.RemotableViewMethod 2963352b6807f9b728b91cceb3ea5f2a065ace95da3Adam Cohen public void showNext() { 2970ac116b688380489c3690f6f65b282990c221f17Adam Cohen if (mSwipeGestureType != GESTURE_NONE) return; 2983352b6807f9b728b91cceb3ea5f2a065ace95da3Adam Cohen if (!mTransitionIsSetup) { 2993352b6807f9b728b91cceb3ea5f2a065ace95da3Adam Cohen View v = getViewAtRelativeIndex(1); 3003352b6807f9b728b91cceb3ea5f2a065ace95da3Adam Cohen if (v != null) { 3013352b6807f9b728b91cceb3ea5f2a065ace95da3Adam Cohen setupStackSlider(v, StackSlider.NORMAL_MODE); 3023352b6807f9b728b91cceb3ea5f2a065ace95da3Adam Cohen mStackSlider.setYProgress(0); 3033352b6807f9b728b91cceb3ea5f2a065ace95da3Adam Cohen mStackSlider.setXProgress(0); 3043352b6807f9b728b91cceb3ea5f2a065ace95da3Adam Cohen } 3053352b6807f9b728b91cceb3ea5f2a065ace95da3Adam Cohen } 3063352b6807f9b728b91cceb3ea5f2a065ace95da3Adam Cohen super.showNext(); 3073352b6807f9b728b91cceb3ea5f2a065ace95da3Adam Cohen } 3083352b6807f9b728b91cceb3ea5f2a065ace95da3Adam Cohen 3093352b6807f9b728b91cceb3ea5f2a065ace95da3Adam Cohen @Override 3103352b6807f9b728b91cceb3ea5f2a065ace95da3Adam Cohen @android.view.RemotableViewMethod 3113352b6807f9b728b91cceb3ea5f2a065ace95da3Adam Cohen public void showPrevious() { 3120ac116b688380489c3690f6f65b282990c221f17Adam Cohen if (mSwipeGestureType != GESTURE_NONE) return; 3133352b6807f9b728b91cceb3ea5f2a065ace95da3Adam Cohen if (!mTransitionIsSetup) { 3143352b6807f9b728b91cceb3ea5f2a065ace95da3Adam Cohen View v = getViewAtRelativeIndex(0); 3153352b6807f9b728b91cceb3ea5f2a065ace95da3Adam Cohen if (v != null) { 3163352b6807f9b728b91cceb3ea5f2a065ace95da3Adam Cohen setupStackSlider(v, StackSlider.NORMAL_MODE); 3173352b6807f9b728b91cceb3ea5f2a065ace95da3Adam Cohen mStackSlider.setYProgress(1); 3183352b6807f9b728b91cceb3ea5f2a065ace95da3Adam Cohen mStackSlider.setXProgress(0); 3193352b6807f9b728b91cceb3ea5f2a065ace95da3Adam Cohen } 3203352b6807f9b728b91cceb3ea5f2a065ace95da3Adam Cohen } 3213352b6807f9b728b91cceb3ea5f2a065ace95da3Adam Cohen super.showPrevious(); 3223352b6807f9b728b91cceb3ea5f2a065ace95da3Adam Cohen } 3233352b6807f9b728b91cceb3ea5f2a065ace95da3Adam Cohen 32496d8d56302da81b24333b204e6d7f15064538036Adam Cohen @Override 32596d8d56302da81b24333b204e6d7f15064538036Adam Cohen void showOnly(int childIndex, boolean animate, boolean onLayout) { 32696d8d56302da81b24333b204e6d7f15064538036Adam Cohen super.showOnly(childIndex, animate, onLayout); 32796d8d56302da81b24333b204e6d7f15064538036Adam Cohen 32896d8d56302da81b24333b204e6d7f15064538036Adam Cohen // Here we need to make sure that the z-order of the children is correct 3293352b6807f9b728b91cceb3ea5f2a065ace95da3Adam Cohen for (int i = mCurrentWindowEnd; i >= mCurrentWindowStart; i--) { 33096d8d56302da81b24333b204e6d7f15064538036Adam Cohen int index = modulo(i, getWindowSize()); 3310ac116b688380489c3690f6f65b282990c221f17Adam Cohen ViewAndIndex vi = mViewsMap.get(index); 3320ac116b688380489c3690f6f65b282990c221f17Adam Cohen if (vi != null) { 3330ac116b688380489c3690f6f65b282990c221f17Adam Cohen View v = mViewsMap.get(index).view; 3340ac116b688380489c3690f6f65b282990c221f17Adam Cohen if (v != null) v.bringToFront(); 3350ac116b688380489c3690f6f65b282990c221f17Adam Cohen } 33696d8d56302da81b24333b204e6d7f15064538036Adam Cohen } 3373352b6807f9b728b91cceb3ea5f2a065ace95da3Adam Cohen mTransitionIsSetup = false; 3388baf5df0fa5b3453a7f17e95746c5d8cadc00163Adam Cohen mClickFeedbackIsValid = false; 3398baf5df0fa5b3453a7f17e95746c5d8cadc00163Adam Cohen } 3408baf5df0fa5b3453a7f17e95746c5d8cadc00163Adam Cohen 3418baf5df0fa5b3453a7f17e95746c5d8cadc00163Adam Cohen void updateClickFeedback() { 3428baf5df0fa5b3453a7f17e95746c5d8cadc00163Adam Cohen if (!mClickFeedbackIsValid) { 3439c295482dd739e80dd49ea0dd3102ad6be6742ddAdam Cohen View v = getViewAtRelativeIndex(1); 3448baf5df0fa5b3453a7f17e95746c5d8cadc00163Adam Cohen if (v != null) { 3458baf5df0fa5b3453a7f17e95746c5d8cadc00163Adam Cohen mClickFeedback.setImageBitmap(sHolographicHelper.createOutline(v, 3468baf5df0fa5b3453a7f17e95746c5d8cadc00163Adam Cohen HolographicHelper.CLICK_FEEDBACK)); 3478baf5df0fa5b3453a7f17e95746c5d8cadc00163Adam Cohen mClickFeedback.setTranslationX(v.getTranslationX()); 3488baf5df0fa5b3453a7f17e95746c5d8cadc00163Adam Cohen mClickFeedback.setTranslationY(v.getTranslationY()); 3498baf5df0fa5b3453a7f17e95746c5d8cadc00163Adam Cohen } 3508baf5df0fa5b3453a7f17e95746c5d8cadc00163Adam Cohen mClickFeedbackIsValid = true; 3518baf5df0fa5b3453a7f17e95746c5d8cadc00163Adam Cohen } 3528baf5df0fa5b3453a7f17e95746c5d8cadc00163Adam Cohen } 3538baf5df0fa5b3453a7f17e95746c5d8cadc00163Adam Cohen 3548baf5df0fa5b3453a7f17e95746c5d8cadc00163Adam Cohen @Override 3558baf5df0fa5b3453a7f17e95746c5d8cadc00163Adam Cohen void showTapFeedback(View v) { 3568baf5df0fa5b3453a7f17e95746c5d8cadc00163Adam Cohen updateClickFeedback(); 3578baf5df0fa5b3453a7f17e95746c5d8cadc00163Adam Cohen mClickFeedback.setVisibility(VISIBLE); 3588baf5df0fa5b3453a7f17e95746c5d8cadc00163Adam Cohen mClickFeedback.bringToFront(); 3598baf5df0fa5b3453a7f17e95746c5d8cadc00163Adam Cohen invalidate(); 3608baf5df0fa5b3453a7f17e95746c5d8cadc00163Adam Cohen } 3618baf5df0fa5b3453a7f17e95746c5d8cadc00163Adam Cohen 3628baf5df0fa5b3453a7f17e95746c5d8cadc00163Adam Cohen @Override 3638baf5df0fa5b3453a7f17e95746c5d8cadc00163Adam Cohen void hideTapFeedback(View v) { 3648baf5df0fa5b3453a7f17e95746c5d8cadc00163Adam Cohen mClickFeedback.setVisibility(INVISIBLE); 3658baf5df0fa5b3453a7f17e95746c5d8cadc00163Adam Cohen invalidate(); 36696d8d56302da81b24333b204e6d7f15064538036Adam Cohen } 36796d8d56302da81b24333b204e6d7f15064538036Adam Cohen 368f04e22571f17bf850ef89adddf2b12171e536619Adam Cohen private void updateChildTransforms() { 36996d8d56302da81b24333b204e6d7f15064538036Adam Cohen for (int i = 0; i < getNumActiveViews(); i++) { 370f04e22571f17bf850ef89adddf2b12171e536619Adam Cohen View v = getViewAtRelativeIndex(i); 371f04e22571f17bf850ef89adddf2b12171e536619Adam Cohen if (v != null) { 372f04e22571f17bf850ef89adddf2b12171e536619Adam Cohen transformViewAtIndex(i, v); 373f04e22571f17bf850ef89adddf2b12171e536619Adam Cohen } 374839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen } 37544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen } 37644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen 377dfcdddd7c408dddb22fb0867e4799d4c29d2f55fAdam Cohen @Override 378dfcdddd7c408dddb22fb0867e4799d4c29d2f55fAdam Cohen FrameLayout getFrameForChild() { 379dfcdddd7c408dddb22fb0867e4799d4c29d2f55fAdam Cohen FrameLayout fl = new FrameLayout(mContext); 380dfcdddd7c408dddb22fb0867e4799d4c29d2f55fAdam Cohen fl.setPadding(mFramePadding, mFramePadding, mFramePadding, mFramePadding); 381dfcdddd7c408dddb22fb0867e4799d4c29d2f55fAdam Cohen return fl; 382dfcdddd7c408dddb22fb0867e4799d4c29d2f55fAdam Cohen } 383dfcdddd7c408dddb22fb0867e4799d4c29d2f55fAdam Cohen 38444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen /** 38544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen * Apply any necessary tranforms for the child that is being added. 38644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen */ 38744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen void applyTransformForChildAtIndex(View child, int relativeIndex) { 38844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen } 38944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen 39044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen @Override 3919b073948cfb84c0dd04f8a94ee1f7f263f027c83Adam Cohen protected void dispatchDraw(Canvas canvas) { 3920ac116b688380489c3690f6f65b282990c221f17Adam Cohen canvas.getClipBounds(stackInvalidateRect); 393d51bbb5b56446519db88f49f2682da782b0069acAdam Cohen final int childCount = getChildCount(); 394d51bbb5b56446519db88f49f2682da782b0069acAdam Cohen for (int i = 0; i < childCount; i++) { 395d51bbb5b56446519db88f49f2682da782b0069acAdam Cohen LayoutParams lp = (LayoutParams) getChildAt(i).getLayoutParams(); 3960ac116b688380489c3690f6f65b282990c221f17Adam Cohen stackInvalidateRect.union(lp.getInvalidateRect()); 397d51bbb5b56446519db88f49f2682da782b0069acAdam Cohen lp.resetInvalidateRect(); 39844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen } 399d51bbb5b56446519db88f49f2682da782b0069acAdam Cohen canvas.save(Canvas.CLIP_SAVE_FLAG); 4000ac116b688380489c3690f6f65b282990c221f17Adam Cohen canvas.clipRect(stackInvalidateRect, Region.Op.UNION); 401d51bbb5b56446519db88f49f2682da782b0069acAdam Cohen super.dispatchDraw(canvas); 402d51bbb5b56446519db88f49f2682da782b0069acAdam Cohen canvas.restore(); 4039b073948cfb84c0dd04f8a94ee1f7f263f027c83Adam Cohen } 40444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen 4059b073948cfb84c0dd04f8a94ee1f7f263f027c83Adam Cohen private void onLayout() { 40644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen if (!mFirstLayoutHappened) { 407dfcdddd7c408dddb22fb0867e4799d4c29d2f55fAdam Cohen mSlideAmount = Math.round(SLIDE_UP_RATIO * getMeasuredHeight()); 408f04e22571f17bf850ef89adddf2b12171e536619Adam Cohen updateChildTransforms(); 409dfcdddd7c408dddb22fb0867e4799d4c29d2f55fAdam Cohen mSwipeThreshold = Math.round(SWIPE_THRESHOLD_RATIO * mSlideAmount); 41044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen mFirstLayoutHappened = true; 41144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen } 41244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen } 41344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen 41444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen @Override 41544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen public boolean onInterceptTouchEvent(MotionEvent ev) { 41644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen int action = ev.getAction(); 41744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen switch(action & MotionEvent.ACTION_MASK) { 41844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen case MotionEvent.ACTION_DOWN: { 41944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen if (mActivePointerId == INVALID_POINTER) { 42044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen mInitialX = ev.getX(); 42144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen mInitialY = ev.getY(); 42244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen mActivePointerId = ev.getPointerId(0); 42344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen } 42444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen break; 42544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen } 42644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen case MotionEvent.ACTION_MOVE: { 42744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen int pointerIndex = ev.findPointerIndex(mActivePointerId); 42844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen if (pointerIndex == INVALID_POINTER) { 42944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen // no data for our primary pointer, this shouldn't happen, log it 43044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen Log.d(TAG, "Error: No data for our primary pointer."); 43144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen return false; 43244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen } 43344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen float newY = ev.getY(pointerIndex); 43444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen float deltaY = newY - mInitialY; 43544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen 43632a42f1587db77b958d62c3de4f2734eb0a3b965Adam Cohen beginGestureIfNeeded(deltaY); 43744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen break; 43844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen } 43944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen case MotionEvent.ACTION_POINTER_UP: { 44044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen onSecondaryPointerUp(ev); 44144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen break; 44244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen } 44344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen case MotionEvent.ACTION_UP: 44444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen case MotionEvent.ACTION_CANCEL: { 44544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen mActivePointerId = INVALID_POINTER; 44644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen mSwipeGestureType = GESTURE_NONE; 44744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen } 44844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen } 44944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen 45044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen return mSwipeGestureType != GESTURE_NONE; 45144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen } 45244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen 45332a42f1587db77b958d62c3de4f2734eb0a3b965Adam Cohen private void beginGestureIfNeeded(float deltaY) { 45432a42f1587db77b958d62c3de4f2734eb0a3b965Adam Cohen if ((int) Math.abs(deltaY) > mTouchSlop && mSwipeGestureType == GESTURE_NONE) { 4553d07af03421f4727ef7e97c5c19e6ade50b19060Adam Cohen int swipeGestureType = deltaY < 0 ? GESTURE_SLIDE_UP : GESTURE_SLIDE_DOWN; 45632a42f1587db77b958d62c3de4f2734eb0a3b965Adam Cohen cancelLongPress(); 45732a42f1587db77b958d62c3de4f2734eb0a3b965Adam Cohen requestDisallowInterceptTouchEvent(true); 45832a42f1587db77b958d62c3de4f2734eb0a3b965Adam Cohen 459d51bbb5b56446519db88f49f2682da782b0069acAdam Cohen if (mAdapter == null) return; 460d51bbb5b56446519db88f49f2682da782b0069acAdam Cohen 461839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen int activeIndex; 462839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen if (mStackMode == ITEMS_SLIDE_UP) { 46396d8d56302da81b24333b204e6d7f15064538036Adam Cohen activeIndex = (swipeGestureType == GESTURE_SLIDE_DOWN) ? 0 : 1; 464839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen } else { 46596d8d56302da81b24333b204e6d7f15064538036Adam Cohen activeIndex = (swipeGestureType == GESTURE_SLIDE_DOWN) ? 1 : 0; 466839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen } 46732a42f1587db77b958d62c3de4f2734eb0a3b965Adam Cohen 4683352b6807f9b728b91cceb3ea5f2a065ace95da3Adam Cohen int stackMode; 4691b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen if (mLoopViews) { 4703352b6807f9b728b91cceb3ea5f2a065ace95da3Adam Cohen stackMode = StackSlider.NORMAL_MODE; 47196d8d56302da81b24333b204e6d7f15064538036Adam Cohen } else if (mCurrentWindowStartUnbounded + activeIndex == -1) { 47296d8d56302da81b24333b204e6d7f15064538036Adam Cohen activeIndex++; 4733352b6807f9b728b91cceb3ea5f2a065ace95da3Adam Cohen stackMode = StackSlider.BEGINNING_OF_STACK_MODE; 47496d8d56302da81b24333b204e6d7f15064538036Adam Cohen } else if (mCurrentWindowStartUnbounded + activeIndex == mAdapter.getCount() - 1) { 4753352b6807f9b728b91cceb3ea5f2a065ace95da3Adam Cohen stackMode = StackSlider.END_OF_STACK_MODE; 4763d07af03421f4727ef7e97c5c19e6ade50b19060Adam Cohen } else { 4773352b6807f9b728b91cceb3ea5f2a065ace95da3Adam Cohen stackMode = StackSlider.NORMAL_MODE; 47832a42f1587db77b958d62c3de4f2734eb0a3b965Adam Cohen } 4793d07af03421f4727ef7e97c5c19e6ade50b19060Adam Cohen 4803352b6807f9b728b91cceb3ea5f2a065ace95da3Adam Cohen mTransitionIsSetup = stackMode == StackSlider.NORMAL_MODE; 4813352b6807f9b728b91cceb3ea5f2a065ace95da3Adam Cohen 4823d07af03421f4727ef7e97c5c19e6ade50b19060Adam Cohen View v = getViewAtRelativeIndex(activeIndex); 4833d07af03421f4727ef7e97c5c19e6ade50b19060Adam Cohen if (v == null) return; 4843d07af03421f4727ef7e97c5c19e6ade50b19060Adam Cohen 4853352b6807f9b728b91cceb3ea5f2a065ace95da3Adam Cohen setupStackSlider(v, stackMode); 4863d07af03421f4727ef7e97c5c19e6ade50b19060Adam Cohen 4873d07af03421f4727ef7e97c5c19e6ade50b19060Adam Cohen // We only register this gesture if we've made it this far without a problem 4883d07af03421f4727ef7e97c5c19e6ade50b19060Adam Cohen mSwipeGestureType = swipeGestureType; 489a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen cancelHandleClick(); 49032a42f1587db77b958d62c3de4f2734eb0a3b965Adam Cohen } 49132a42f1587db77b958d62c3de4f2734eb0a3b965Adam Cohen } 49232a42f1587db77b958d62c3de4f2734eb0a3b965Adam Cohen 49344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen @Override 49444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen public boolean onTouchEvent(MotionEvent ev) { 495a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen super.onTouchEvent(ev); 496a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen 49744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen int action = ev.getAction(); 49844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen int pointerIndex = ev.findPointerIndex(mActivePointerId); 49944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen if (pointerIndex == INVALID_POINTER) { 50044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen // no data for our primary pointer, this shouldn't happen, log it 50144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen Log.d(TAG, "Error: No data for our primary pointer."); 50244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen return false; 50344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen } 50444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen 50544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen float newY = ev.getY(pointerIndex); 50632a42f1587db77b958d62c3de4f2734eb0a3b965Adam Cohen float newX = ev.getX(pointerIndex); 50744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen float deltaY = newY - mInitialY; 50832a42f1587db77b958d62c3de4f2734eb0a3b965Adam Cohen float deltaX = newX - mInitialX; 50944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen if (mVelocityTracker == null) { 51044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen mVelocityTracker = VelocityTracker.obtain(); 51144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen } 51244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen mVelocityTracker.addMovement(ev); 51344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen 51444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen switch (action & MotionEvent.ACTION_MASK) { 51544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen case MotionEvent.ACTION_MOVE: { 51632a42f1587db77b958d62c3de4f2734eb0a3b965Adam Cohen beginGestureIfNeeded(deltaY); 51732a42f1587db77b958d62c3de4f2734eb0a3b965Adam Cohen 518dfcdddd7c408dddb22fb0867e4799d4c29d2f55fAdam Cohen float rx = deltaX / (mSlideAmount * 1.0f); 51932a42f1587db77b958d62c3de4f2734eb0a3b965Adam Cohen if (mSwipeGestureType == GESTURE_SLIDE_DOWN) { 520dfcdddd7c408dddb22fb0867e4799d4c29d2f55fAdam Cohen float r = (deltaY - mTouchSlop * 1.0f) / mSlideAmount * 1.0f; 521839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen if (mStackMode == ITEMS_SLIDE_DOWN) r = 1 - r; 52232a42f1587db77b958d62c3de4f2734eb0a3b965Adam Cohen mStackSlider.setYProgress(1 - r); 52332a42f1587db77b958d62c3de4f2734eb0a3b965Adam Cohen mStackSlider.setXProgress(rx); 52432a42f1587db77b958d62c3de4f2734eb0a3b965Adam Cohen return true; 52532a42f1587db77b958d62c3de4f2734eb0a3b965Adam Cohen } else if (mSwipeGestureType == GESTURE_SLIDE_UP) { 526dfcdddd7c408dddb22fb0867e4799d4c29d2f55fAdam Cohen float r = -(deltaY + mTouchSlop * 1.0f) / mSlideAmount * 1.0f; 527839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen if (mStackMode == ITEMS_SLIDE_DOWN) r = 1 - r; 52832a42f1587db77b958d62c3de4f2734eb0a3b965Adam Cohen mStackSlider.setYProgress(r); 52932a42f1587db77b958d62c3de4f2734eb0a3b965Adam Cohen mStackSlider.setXProgress(rx); 53032a42f1587db77b958d62c3de4f2734eb0a3b965Adam Cohen return true; 53144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen } 53244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen break; 53344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen } 53444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen case MotionEvent.ACTION_UP: { 53544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen handlePointerUp(ev); 53644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen break; 53744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen } 53844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen case MotionEvent.ACTION_POINTER_UP: { 53944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen onSecondaryPointerUp(ev); 54044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen break; 54144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen } 54244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen case MotionEvent.ACTION_CANCEL: { 54344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen mActivePointerId = INVALID_POINTER; 54444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen mSwipeGestureType = GESTURE_NONE; 54544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen break; 54644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen } 54744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen } 54844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen return true; 54944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen } 55044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen 55144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen private void onSecondaryPointerUp(MotionEvent ev) { 55244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen final int activePointerIndex = ev.getActionIndex(); 55344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen final int pointerId = ev.getPointerId(activePointerIndex); 55444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen if (pointerId == mActivePointerId) { 55544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen 55696d8d56302da81b24333b204e6d7f15064538036Adam Cohen int activeViewIndex = (mSwipeGestureType == GESTURE_SLIDE_DOWN) ? 0 : 1; 55744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen 55844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen View v = getViewAtRelativeIndex(activeViewIndex); 55944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen if (v == null) return; 56044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen 56144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen // Our primary pointer has gone up -- let's see if we can find 56244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen // another pointer on the view. If so, then we should replace 56344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen // our primary pointer with this new pointer and adjust things 56444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen // so that the view doesn't jump 56544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen for (int index = 0; index < ev.getPointerCount(); index++) { 56644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen if (index != activePointerIndex) { 56744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen 56844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen float x = ev.getX(index); 56944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen float y = ev.getY(index); 57044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen 571e80202d5c3c2a45cc34976e09958883e5366f1bbPatrick Dubroy mTouchRect.set(v.getLeft(), v.getTop(), v.getRight(), v.getBottom()); 572e80202d5c3c2a45cc34976e09958883e5366f1bbPatrick Dubroy if (mTouchRect.contains(Math.round(x), Math.round(y))) { 57344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen float oldX = ev.getX(activePointerIndex); 57444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen float oldY = ev.getY(activePointerIndex); 57544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen 57644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen // adjust our frame of reference to avoid a jump 57744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen mInitialY += (y - oldY); 57844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen mInitialX += (x - oldX); 57944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen 58044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen mActivePointerId = ev.getPointerId(index); 58144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen if (mVelocityTracker != null) { 58244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen mVelocityTracker.clear(); 58344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen } 58444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen // ok, we're good, we found a new pointer which is touching the active view 58544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen return; 58644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen } 58744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen } 58844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen } 58944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen // if we made it this far, it means we didn't find a satisfactory new pointer :(, 5903d07af03421f4727ef7e97c5c19e6ade50b19060Adam Cohen // so end the gesture 59144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen handlePointerUp(ev); 59244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen } 59344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen } 59444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen 59544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen private void handlePointerUp(MotionEvent ev) { 59644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen int pointerIndex = ev.findPointerIndex(mActivePointerId); 59744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen float newY = ev.getY(pointerIndex); 59844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen int deltaY = (int) (newY - mInitialY); 59926e30bb7fe373ad4bb569a5de2577e0c857e7c27Adam Cohen mLastInteractionTime = System.currentTimeMillis(); 60044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen 6013d07af03421f4727ef7e97c5c19e6ade50b19060Adam Cohen if (mVelocityTracker != null) { 6023d07af03421f4727ef7e97c5c19e6ade50b19060Adam Cohen mVelocityTracker.computeCurrentVelocity(1000, mMaximumVelocity); 6033d07af03421f4727ef7e97c5c19e6ade50b19060Adam Cohen mYVelocity = (int) mVelocityTracker.getYVelocity(mActivePointerId); 6043d07af03421f4727ef7e97c5c19e6ade50b19060Adam Cohen } 60544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen 60644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen if (mVelocityTracker != null) { 60744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen mVelocityTracker.recycle(); 60844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen mVelocityTracker = null; 60944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen } 61044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen 6113d07af03421f4727ef7e97c5c19e6ade50b19060Adam Cohen if (deltaY > mSwipeThreshold && mSwipeGestureType == GESTURE_SLIDE_DOWN 6123d07af03421f4727ef7e97c5c19e6ade50b19060Adam Cohen && mStackSlider.mMode == StackSlider.NORMAL_MODE) { 6130ac116b688380489c3690f6f65b282990c221f17Adam Cohen // We reset the gesture variable, because otherwise we will ignore showPrevious() / 6140ac116b688380489c3690f6f65b282990c221f17Adam Cohen // showNext(); 6150ac116b688380489c3690f6f65b282990c221f17Adam Cohen mSwipeGestureType = GESTURE_NONE; 6160ac116b688380489c3690f6f65b282990c221f17Adam Cohen 61744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen // Swipe threshold exceeded, swipe down 618839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen if (mStackMode == ITEMS_SLIDE_UP) { 619839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen showPrevious(); 62096d8d56302da81b24333b204e6d7f15064538036Adam Cohen } else { 62196d8d56302da81b24333b204e6d7f15064538036Adam Cohen showNext(); 622839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen } 62332a42f1587db77b958d62c3de4f2734eb0a3b965Adam Cohen mHighlight.bringToFront(); 6243d07af03421f4727ef7e97c5c19e6ade50b19060Adam Cohen } else if (deltaY < -mSwipeThreshold && mSwipeGestureType == GESTURE_SLIDE_UP 6253d07af03421f4727ef7e97c5c19e6ade50b19060Adam Cohen && mStackSlider.mMode == StackSlider.NORMAL_MODE) { 6260ac116b688380489c3690f6f65b282990c221f17Adam Cohen // We reset the gesture variable, because otherwise we will ignore showPrevious() / 6270ac116b688380489c3690f6f65b282990c221f17Adam Cohen // showNext(); 6280ac116b688380489c3690f6f65b282990c221f17Adam Cohen mSwipeGestureType = GESTURE_NONE; 6290ac116b688380489c3690f6f65b282990c221f17Adam Cohen 63044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen // Swipe threshold exceeded, swipe up 631839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen if (mStackMode == ITEMS_SLIDE_UP) { 632839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen showNext(); 63396d8d56302da81b24333b204e6d7f15064538036Adam Cohen } else { 63496d8d56302da81b24333b204e6d7f15064538036Adam Cohen showPrevious(); 635839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen } 636839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen 63732a42f1587db77b958d62c3de4f2734eb0a3b965Adam Cohen mHighlight.bringToFront(); 638839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen } else if (mSwipeGestureType == GESTURE_SLIDE_UP ) { 63944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen // Didn't swipe up far enough, snap back down 640839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen int duration; 641839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen float finalYProgress = (mStackMode == ITEMS_SLIDE_DOWN) ? 1 : 0; 642839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen if (mStackMode == ITEMS_SLIDE_UP || mStackSlider.mMode != StackSlider.NORMAL_MODE) { 643839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen duration = Math.round(mStackSlider.getDurationForNeutralPosition()); 644839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen } else { 645839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen duration = Math.round(mStackSlider.getDurationForOffscreenPosition()); 646839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen } 64732a42f1587db77b958d62c3de4f2734eb0a3b965Adam Cohen 648b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen StackSlider animationSlider = new StackSlider(mStackSlider); 6492794eb3b02e2404d453d3ad22a8a85a138130a07Chet Haase PropertyValuesHolder snapBackY = PropertyValuesHolder.ofFloat("YProgress", finalYProgress); 6502794eb3b02e2404d453d3ad22a8a85a138130a07Chet Haase PropertyValuesHolder snapBackX = PropertyValuesHolder.ofFloat("XProgress", 0.0f); 6512794eb3b02e2404d453d3ad22a8a85a138130a07Chet Haase ObjectAnimator pa = ObjectAnimator.ofPropertyValuesHolder(animationSlider, 652839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen snapBackX, snapBackY); 6532794eb3b02e2404d453d3ad22a8a85a138130a07Chet Haase pa.setDuration(duration); 654839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen pa.setInterpolator(new LinearInterpolator()); 655839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen pa.start(); 65632a42f1587db77b958d62c3de4f2734eb0a3b965Adam Cohen } else if (mSwipeGestureType == GESTURE_SLIDE_DOWN) { 65744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen // Didn't swipe down far enough, snap back up 658839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen float finalYProgress = (mStackMode == ITEMS_SLIDE_DOWN) ? 0 : 1; 659839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen int duration; 660839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen if (mStackMode == ITEMS_SLIDE_DOWN || mStackSlider.mMode != StackSlider.NORMAL_MODE) { 661839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen duration = Math.round(mStackSlider.getDurationForNeutralPosition()); 662839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen } else { 663839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen duration = Math.round(mStackSlider.getDurationForOffscreenPosition()); 664839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen } 6653d07af03421f4727ef7e97c5c19e6ade50b19060Adam Cohen 666b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen StackSlider animationSlider = new StackSlider(mStackSlider); 6672794eb3b02e2404d453d3ad22a8a85a138130a07Chet Haase PropertyValuesHolder snapBackY = 6682794eb3b02e2404d453d3ad22a8a85a138130a07Chet Haase PropertyValuesHolder.ofFloat("YProgress",finalYProgress); 6692794eb3b02e2404d453d3ad22a8a85a138130a07Chet Haase PropertyValuesHolder snapBackX = PropertyValuesHolder.ofFloat("XProgress", 0.0f); 6702794eb3b02e2404d453d3ad22a8a85a138130a07Chet Haase ObjectAnimator pa = ObjectAnimator.ofPropertyValuesHolder(animationSlider, 671839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen snapBackX, snapBackY); 6722794eb3b02e2404d453d3ad22a8a85a138130a07Chet Haase pa.setDuration(duration); 673839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen pa.start(); 67444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen } 67544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen 67644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen mActivePointerId = INVALID_POINTER; 67744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen mSwipeGestureType = GESTURE_NONE; 67832a42f1587db77b958d62c3de4f2734eb0a3b965Adam Cohen } 67932a42f1587db77b958d62c3de4f2734eb0a3b965Adam Cohen 68032a42f1587db77b958d62c3de4f2734eb0a3b965Adam Cohen private class StackSlider { 68132a42f1587db77b958d62c3de4f2734eb0a3b965Adam Cohen View mView; 68232a42f1587db77b958d62c3de4f2734eb0a3b965Adam Cohen float mYProgress; 68332a42f1587db77b958d62c3de4f2734eb0a3b965Adam Cohen float mXProgress; 68432a42f1587db77b958d62c3de4f2734eb0a3b965Adam Cohen 6853d07af03421f4727ef7e97c5c19e6ade50b19060Adam Cohen static final int NORMAL_MODE = 0; 6863d07af03421f4727ef7e97c5c19e6ade50b19060Adam Cohen static final int BEGINNING_OF_STACK_MODE = 1; 6873d07af03421f4727ef7e97c5c19e6ade50b19060Adam Cohen static final int END_OF_STACK_MODE = 2; 6883d07af03421f4727ef7e97c5c19e6ade50b19060Adam Cohen 6893d07af03421f4727ef7e97c5c19e6ade50b19060Adam Cohen int mMode = NORMAL_MODE; 6903d07af03421f4727ef7e97c5c19e6ade50b19060Adam Cohen 691b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen public StackSlider() { 692b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen } 693b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen 694b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen public StackSlider(StackSlider copy) { 695b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen mView = copy.mView; 696b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen mYProgress = copy.mYProgress; 697b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen mXProgress = copy.mXProgress; 6983d07af03421f4727ef7e97c5c19e6ade50b19060Adam Cohen mMode = copy.mMode; 699b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen } 700b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen 70132a42f1587db77b958d62c3de4f2734eb0a3b965Adam Cohen private float cubic(float r) { 702839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen return (float) (Math.pow(2 * r - 1, 3) + 1) / 2.0f; 70332a42f1587db77b958d62c3de4f2734eb0a3b965Adam Cohen } 70432a42f1587db77b958d62c3de4f2734eb0a3b965Adam Cohen 70532a42f1587db77b958d62c3de4f2734eb0a3b965Adam Cohen private float highlightAlphaInterpolator(float r) { 70632a42f1587db77b958d62c3de4f2734eb0a3b965Adam Cohen float pivot = 0.4f; 70732a42f1587db77b958d62c3de4f2734eb0a3b965Adam Cohen if (r < pivot) { 708839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen return 0.85f * cubic(r / pivot); 70932a42f1587db77b958d62c3de4f2734eb0a3b965Adam Cohen } else { 710839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen return 0.85f * cubic(1 - (r - pivot) / (1 - pivot)); 71132a42f1587db77b958d62c3de4f2734eb0a3b965Adam Cohen } 71232a42f1587db77b958d62c3de4f2734eb0a3b965Adam Cohen } 71332a42f1587db77b958d62c3de4f2734eb0a3b965Adam Cohen 71432a42f1587db77b958d62c3de4f2734eb0a3b965Adam Cohen private float viewAlphaInterpolator(float r) { 71532a42f1587db77b958d62c3de4f2734eb0a3b965Adam Cohen float pivot = 0.3f; 71632a42f1587db77b958d62c3de4f2734eb0a3b965Adam Cohen if (r > pivot) { 717839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen return (r - pivot) / (1 - pivot); 71832a42f1587db77b958d62c3de4f2734eb0a3b965Adam Cohen } else { 71932a42f1587db77b958d62c3de4f2734eb0a3b965Adam Cohen return 0; 72032a42f1587db77b958d62c3de4f2734eb0a3b965Adam Cohen } 72132a42f1587db77b958d62c3de4f2734eb0a3b965Adam Cohen } 72232a42f1587db77b958d62c3de4f2734eb0a3b965Adam Cohen 723b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen private float rotationInterpolator(float r) { 724b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen float pivot = 0.2f; 725b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen if (r < pivot) { 726b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen return 0; 727b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen } else { 728839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen return (r - pivot) / (1 - pivot); 729b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen } 730b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen } 731b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen 73232a42f1587db77b958d62c3de4f2734eb0a3b965Adam Cohen void setView(View v) { 73332a42f1587db77b958d62c3de4f2734eb0a3b965Adam Cohen mView = v; 73432a42f1587db77b958d62c3de4f2734eb0a3b965Adam Cohen } 73532a42f1587db77b958d62c3de4f2734eb0a3b965Adam Cohen 73632a42f1587db77b958d62c3de4f2734eb0a3b965Adam Cohen public void setYProgress(float r) { 73732a42f1587db77b958d62c3de4f2734eb0a3b965Adam Cohen // enforce r between 0 and 1 73832a42f1587db77b958d62c3de4f2734eb0a3b965Adam Cohen r = Math.min(1.0f, r); 73932a42f1587db77b958d62c3de4f2734eb0a3b965Adam Cohen r = Math.max(0, r); 74032a42f1587db77b958d62c3de4f2734eb0a3b965Adam Cohen 74132a42f1587db77b958d62c3de4f2734eb0a3b965Adam Cohen mYProgress = r; 742a02fdf1ba03fad71cc80a89dfc74b17456d5b4a5Adam Cohen if (mView == null) return; 743a02fdf1ba03fad71cc80a89dfc74b17456d5b4a5Adam Cohen 74432a42f1587db77b958d62c3de4f2734eb0a3b965Adam Cohen final LayoutParams viewLp = (LayoutParams) mView.getLayoutParams(); 74532a42f1587db77b958d62c3de4f2734eb0a3b965Adam Cohen final LayoutParams highlightLp = (LayoutParams) mHighlight.getLayoutParams(); 74632a42f1587db77b958d62c3de4f2734eb0a3b965Adam Cohen 747839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen int stackDirection = (mStackMode == ITEMS_SLIDE_UP) ? 1 : -1; 748839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen 7493d07af03421f4727ef7e97c5c19e6ade50b19060Adam Cohen switch (mMode) { 7503d07af03421f4727ef7e97c5c19e6ade50b19060Adam Cohen case NORMAL_MODE: 751dfcdddd7c408dddb22fb0867e4799d4c29d2f55fAdam Cohen viewLp.setVerticalOffset(Math.round(-r * stackDirection * mSlideAmount)); 752dfcdddd7c408dddb22fb0867e4799d4c29d2f55fAdam Cohen highlightLp.setVerticalOffset(Math.round(-r * stackDirection * mSlideAmount)); 7533d07af03421f4727ef7e97c5c19e6ade50b19060Adam Cohen mHighlight.setAlpha(highlightAlphaInterpolator(r)); 7543d07af03421f4727ef7e97c5c19e6ade50b19060Adam Cohen 755839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen float alpha = viewAlphaInterpolator(1 - r); 7563d07af03421f4727ef7e97c5c19e6ade50b19060Adam Cohen 7573d07af03421f4727ef7e97c5c19e6ade50b19060Adam Cohen // We make sure that views which can't be seen (have 0 alpha) are also invisible 7583d07af03421f4727ef7e97c5c19e6ade50b19060Adam Cohen // so that they don't interfere with click events. 7593d07af03421f4727ef7e97c5c19e6ade50b19060Adam Cohen if (mView.getAlpha() == 0 && alpha != 0 && mView.getVisibility() != VISIBLE) { 7603d07af03421f4727ef7e97c5c19e6ade50b19060Adam Cohen mView.setVisibility(VISIBLE); 7613d07af03421f4727ef7e97c5c19e6ade50b19060Adam Cohen } else if (alpha == 0 && mView.getAlpha() != 0 7623d07af03421f4727ef7e97c5c19e6ade50b19060Adam Cohen && mView.getVisibility() == VISIBLE) { 7633d07af03421f4727ef7e97c5c19e6ade50b19060Adam Cohen mView.setVisibility(INVISIBLE); 7643d07af03421f4727ef7e97c5c19e6ade50b19060Adam Cohen } 765b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen 7663d07af03421f4727ef7e97c5c19e6ade50b19060Adam Cohen mView.setAlpha(alpha); 767839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen mView.setRotationX(stackDirection * 90.0f * rotationInterpolator(r)); 768839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen mHighlight.setRotationX(stackDirection * 90.0f * rotationInterpolator(r)); 7693d07af03421f4727ef7e97c5c19e6ade50b19060Adam Cohen break; 77096d8d56302da81b24333b204e6d7f15064538036Adam Cohen case END_OF_STACK_MODE: 771839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen r = r * 0.2f; 772dfcdddd7c408dddb22fb0867e4799d4c29d2f55fAdam Cohen viewLp.setVerticalOffset(Math.round(-stackDirection * r * mSlideAmount)); 773dfcdddd7c408dddb22fb0867e4799d4c29d2f55fAdam Cohen highlightLp.setVerticalOffset(Math.round(-stackDirection * r * mSlideAmount)); 7743d07af03421f4727ef7e97c5c19e6ade50b19060Adam Cohen mHighlight.setAlpha(highlightAlphaInterpolator(r)); 7753d07af03421f4727ef7e97c5c19e6ade50b19060Adam Cohen break; 77696d8d56302da81b24333b204e6d7f15064538036Adam Cohen case BEGINNING_OF_STACK_MODE: 777839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen r = (1-r) * 0.2f; 778dfcdddd7c408dddb22fb0867e4799d4c29d2f55fAdam Cohen viewLp.setVerticalOffset(Math.round(stackDirection * r * mSlideAmount)); 779dfcdddd7c408dddb22fb0867e4799d4c29d2f55fAdam Cohen highlightLp.setVerticalOffset(Math.round(stackDirection * r * mSlideAmount)); 7803d07af03421f4727ef7e97c5c19e6ade50b19060Adam Cohen mHighlight.setAlpha(highlightAlphaInterpolator(r)); 7813d07af03421f4727ef7e97c5c19e6ade50b19060Adam Cohen break; 782b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen } 78332a42f1587db77b958d62c3de4f2734eb0a3b965Adam Cohen } 78432a42f1587db77b958d62c3de4f2734eb0a3b965Adam Cohen 78532a42f1587db77b958d62c3de4f2734eb0a3b965Adam Cohen public void setXProgress(float r) { 78632a42f1587db77b958d62c3de4f2734eb0a3b965Adam Cohen // enforce r between 0 and 1 7873d07af03421f4727ef7e97c5c19e6ade50b19060Adam Cohen r = Math.min(2.0f, r); 7883d07af03421f4727ef7e97c5c19e6ade50b19060Adam Cohen r = Math.max(-2.0f, r); 78932a42f1587db77b958d62c3de4f2734eb0a3b965Adam Cohen 79032a42f1587db77b958d62c3de4f2734eb0a3b965Adam Cohen mXProgress = r; 79132a42f1587db77b958d62c3de4f2734eb0a3b965Adam Cohen 792a02fdf1ba03fad71cc80a89dfc74b17456d5b4a5Adam Cohen if (mView == null) return; 79332a42f1587db77b958d62c3de4f2734eb0a3b965Adam Cohen final LayoutParams viewLp = (LayoutParams) mView.getLayoutParams(); 79432a42f1587db77b958d62c3de4f2734eb0a3b965Adam Cohen final LayoutParams highlightLp = (LayoutParams) mHighlight.getLayoutParams(); 79532a42f1587db77b958d62c3de4f2734eb0a3b965Adam Cohen 7963d07af03421f4727ef7e97c5c19e6ade50b19060Adam Cohen r *= 0.2f; 797dfcdddd7c408dddb22fb0867e4799d4c29d2f55fAdam Cohen viewLp.setHorizontalOffset(Math.round(r * mSlideAmount)); 798dfcdddd7c408dddb22fb0867e4799d4c29d2f55fAdam Cohen highlightLp.setHorizontalOffset(Math.round(r * mSlideAmount)); 79932a42f1587db77b958d62c3de4f2734eb0a3b965Adam Cohen } 80032a42f1587db77b958d62c3de4f2734eb0a3b965Adam Cohen 8013d07af03421f4727ef7e97c5c19e6ade50b19060Adam Cohen void setMode(int mode) { 8023d07af03421f4727ef7e97c5c19e6ade50b19060Adam Cohen mMode = mode; 8033d07af03421f4727ef7e97c5c19e6ade50b19060Adam Cohen } 8043d07af03421f4727ef7e97c5c19e6ade50b19060Adam Cohen 8053d07af03421f4727ef7e97c5c19e6ade50b19060Adam Cohen float getDurationForNeutralPosition() { 806839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen return getDuration(false, 0); 8073d07af03421f4727ef7e97c5c19e6ade50b19060Adam Cohen } 8083d07af03421f4727ef7e97c5c19e6ade50b19060Adam Cohen 8093d07af03421f4727ef7e97c5c19e6ade50b19060Adam Cohen float getDurationForOffscreenPosition() { 810839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen return getDuration(true, 0); 811839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen } 812839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen 813839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen float getDurationForNeutralPosition(float velocity) { 814839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen return getDuration(false, velocity); 815839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen } 816839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen 817839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen float getDurationForOffscreenPosition(float velocity) { 818839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen return getDuration(true, velocity); 8193d07af03421f4727ef7e97c5c19e6ade50b19060Adam Cohen } 8203d07af03421f4727ef7e97c5c19e6ade50b19060Adam Cohen 821839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen private float getDuration(boolean invert, float velocity) { 8223d07af03421f4727ef7e97c5c19e6ade50b19060Adam Cohen if (mView != null) { 8233d07af03421f4727ef7e97c5c19e6ade50b19060Adam Cohen final LayoutParams viewLp = (LayoutParams) mView.getLayoutParams(); 8243d07af03421f4727ef7e97c5c19e6ade50b19060Adam Cohen 825839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen float d = (float) Math.sqrt(Math.pow(viewLp.horizontalOffset, 2) + 826839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen Math.pow(viewLp.verticalOffset, 2)); 827dfcdddd7c408dddb22fb0867e4799d4c29d2f55fAdam Cohen float maxd = (float) Math.sqrt(Math.pow(mSlideAmount, 2) + 828dfcdddd7c408dddb22fb0867e4799d4c29d2f55fAdam Cohen Math.pow(0.4f * mSlideAmount, 2)); 829839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen 830839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen if (velocity == 0) { 831839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen return (invert ? (1 - d / maxd) : d / maxd) * DEFAULT_ANIMATION_DURATION; 832839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen } else { 833839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen float duration = invert ? d / Math.abs(velocity) : 834839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen (maxd - d) / Math.abs(velocity); 835839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen if (duration < MINIMUM_ANIMATION_DURATION || 836839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen duration > DEFAULT_ANIMATION_DURATION) { 837839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen return getDuration(invert, 0); 838839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen } else { 839839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen return duration; 840839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen } 841839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen } 8423d07af03421f4727ef7e97c5c19e6ade50b19060Adam Cohen } 8433d07af03421f4727ef7e97c5c19e6ade50b19060Adam Cohen return 0; 8443d07af03421f4727ef7e97c5c19e6ade50b19060Adam Cohen } 8453d07af03421f4727ef7e97c5c19e6ade50b19060Adam Cohen 846e5ebcb0107a939395e03592fd44c746cd09e311dRomain Guy // Used for animations 847e5ebcb0107a939395e03592fd44c746cd09e311dRomain Guy @SuppressWarnings({"UnusedDeclaration"}) 848839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen public float getYProgress() { 84932a42f1587db77b958d62c3de4f2734eb0a3b965Adam Cohen return mYProgress; 85032a42f1587db77b958d62c3de4f2734eb0a3b965Adam Cohen } 85132a42f1587db77b958d62c3de4f2734eb0a3b965Adam Cohen 852e5ebcb0107a939395e03592fd44c746cd09e311dRomain Guy // Used for animations 853e5ebcb0107a939395e03592fd44c746cd09e311dRomain Guy @SuppressWarnings({"UnusedDeclaration"}) 854839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen public float getXProgress() { 85532a42f1587db77b958d62c3de4f2734eb0a3b965Adam Cohen return mXProgress; 85632a42f1587db77b958d62c3de4f2734eb0a3b965Adam Cohen } 85744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen } 85844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen 85944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen @Override 86044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen public void onRemoteAdapterConnected() { 86144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen super.onRemoteAdapterConnected(); 8621480fddea874a42adb43b4bcdac6704e4c3e110bAdam Cohen // On first run, we want to set the stack to the end. 86396d8d56302da81b24333b204e6d7f15064538036Adam Cohen if (mWhichChild == -1) { 86496d8d56302da81b24333b204e6d7f15064538036Adam Cohen mWhichChild = 0; 8653042944c6ec68210ba1746540b53789e70d15ef4Adam Cohen } 86696d8d56302da81b24333b204e6d7f15064538036Adam Cohen setDisplayedChild(mWhichChild); 86744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen } 86832a42f1587db77b958d62c3de4f2734eb0a3b965Adam Cohen 8699b073948cfb84c0dd04f8a94ee1f7f263f027c83Adam Cohen LayoutParams createOrReuseLayoutParams(View v) { 8709b073948cfb84c0dd04f8a94ee1f7f263f027c83Adam Cohen final ViewGroup.LayoutParams currentLp = v.getLayoutParams(); 8719b073948cfb84c0dd04f8a94ee1f7f263f027c83Adam Cohen if (currentLp instanceof LayoutParams) { 8729b073948cfb84c0dd04f8a94ee1f7f263f027c83Adam Cohen LayoutParams lp = (LayoutParams) currentLp; 8739b073948cfb84c0dd04f8a94ee1f7f263f027c83Adam Cohen lp.setHorizontalOffset(0); 8749b073948cfb84c0dd04f8a94ee1f7f263f027c83Adam Cohen lp.setVerticalOffset(0); 875839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen lp.width = 0; 876839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen lp.width = 0; 8779b073948cfb84c0dd04f8a94ee1f7f263f027c83Adam Cohen return lp; 8789b073948cfb84c0dd04f8a94ee1f7f263f027c83Adam Cohen } 8799b073948cfb84c0dd04f8a94ee1f7f263f027c83Adam Cohen return new LayoutParams(v); 88032a42f1587db77b958d62c3de4f2734eb0a3b965Adam Cohen } 88132a42f1587db77b958d62c3de4f2734eb0a3b965Adam Cohen 8829b073948cfb84c0dd04f8a94ee1f7f263f027c83Adam Cohen @Override 8839b073948cfb84c0dd04f8a94ee1f7f263f027c83Adam Cohen protected void onLayout(boolean changed, int left, int top, int right, int bottom) { 8849b073948cfb84c0dd04f8a94ee1f7f263f027c83Adam Cohen boolean dataChanged = mDataChanged; 8859b073948cfb84c0dd04f8a94ee1f7f263f027c83Adam Cohen if (dataChanged) { 8869b073948cfb84c0dd04f8a94ee1f7f263f027c83Adam Cohen handleDataChanged(); 8879b073948cfb84c0dd04f8a94ee1f7f263f027c83Adam Cohen 8889b073948cfb84c0dd04f8a94ee1f7f263f027c83Adam Cohen // if the data changes, mWhichChild might be out of the bounds of the adapter 8899b073948cfb84c0dd04f8a94ee1f7f263f027c83Adam Cohen // in this case, we reset mWhichChild to the beginning 8909b073948cfb84c0dd04f8a94ee1f7f263f027c83Adam Cohen if (mWhichChild >= mAdapter.getCount()) 8919b073948cfb84c0dd04f8a94ee1f7f263f027c83Adam Cohen mWhichChild = 0; 8929b073948cfb84c0dd04f8a94ee1f7f263f027c83Adam Cohen 8939b073948cfb84c0dd04f8a94ee1f7f263f027c83Adam Cohen showOnly(mWhichChild, true, true); 8946364f2bbe5254b4274f3feffc48f4259eacc205eWinson Chung refreshChildren(); 8959b073948cfb84c0dd04f8a94ee1f7f263f027c83Adam Cohen } 8969b073948cfb84c0dd04f8a94ee1f7f263f027c83Adam Cohen 8979b073948cfb84c0dd04f8a94ee1f7f263f027c83Adam Cohen final int childCount = getChildCount(); 8989b073948cfb84c0dd04f8a94ee1f7f263f027c83Adam Cohen for (int i = 0; i < childCount; i++) { 8999b073948cfb84c0dd04f8a94ee1f7f263f027c83Adam Cohen final View child = getChildAt(i); 9009b073948cfb84c0dd04f8a94ee1f7f263f027c83Adam Cohen 9019b073948cfb84c0dd04f8a94ee1f7f263f027c83Adam Cohen int childRight = mPaddingLeft + child.getMeasuredWidth(); 9029b073948cfb84c0dd04f8a94ee1f7f263f027c83Adam Cohen int childBottom = mPaddingTop + child.getMeasuredHeight(); 9039b073948cfb84c0dd04f8a94ee1f7f263f027c83Adam Cohen LayoutParams lp = (LayoutParams) child.getLayoutParams(); 9049b073948cfb84c0dd04f8a94ee1f7f263f027c83Adam Cohen 9059b073948cfb84c0dd04f8a94ee1f7f263f027c83Adam Cohen child.layout(mPaddingLeft + lp.horizontalOffset, mPaddingTop + lp.verticalOffset, 9069b073948cfb84c0dd04f8a94ee1f7f263f027c83Adam Cohen childRight + lp.horizontalOffset, childBottom + lp.verticalOffset); 9079b073948cfb84c0dd04f8a94ee1f7f263f027c83Adam Cohen 9089b073948cfb84c0dd04f8a94ee1f7f263f027c83Adam Cohen } 9099b073948cfb84c0dd04f8a94ee1f7f263f027c83Adam Cohen 9109b073948cfb84c0dd04f8a94ee1f7f263f027c83Adam Cohen mDataChanged = false; 9119b073948cfb84c0dd04f8a94ee1f7f263f027c83Adam Cohen onLayout(); 9129b073948cfb84c0dd04f8a94ee1f7f263f027c83Adam Cohen } 9139b073948cfb84c0dd04f8a94ee1f7f263f027c83Adam Cohen 91426e30bb7fe373ad4bb569a5de2577e0c857e7c27Adam Cohen @Override 91526e30bb7fe373ad4bb569a5de2577e0c857e7c27Adam Cohen public void advance() { 91626e30bb7fe373ad4bb569a5de2577e0c857e7c27Adam Cohen long timeSinceLastInteraction = System.currentTimeMillis() - mLastInteractionTime; 91726e30bb7fe373ad4bb569a5de2577e0c857e7c27Adam Cohen if (mSwipeGestureType == GESTURE_NONE && 91826e30bb7fe373ad4bb569a5de2577e0c857e7c27Adam Cohen timeSinceLastInteraction > MIN_TIME_BETWEEN_INTERACTION_AND_AUTOADVANCE) { 91926e30bb7fe373ad4bb569a5de2577e0c857e7c27Adam Cohen showNext(); 92026e30bb7fe373ad4bb569a5de2577e0c857e7c27Adam Cohen } 92126e30bb7fe373ad4bb569a5de2577e0c857e7c27Adam Cohen } 92226e30bb7fe373ad4bb569a5de2577e0c857e7c27Adam Cohen 923839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen private void measureChildren() { 924839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen final int count = getChildCount(); 925026e121bc35b9012943d8ea86518b51270d0c0efAdam Cohen final int childWidth = Math.round(getMeasuredWidth()*(1-PERSPECTIVE_SHIFT_FACTOR_X)) 926026e121bc35b9012943d8ea86518b51270d0c0efAdam Cohen - mPaddingLeft - mPaddingRight; 927026e121bc35b9012943d8ea86518b51270d0c0efAdam Cohen final int childHeight = Math.round(getMeasuredHeight()*(1-PERSPECTIVE_SHIFT_FACTOR_Y)) 928839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen - mPaddingTop - mPaddingBottom; 929839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen 930839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen for (int i = 0; i < count; i++) { 931839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen final View child = getChildAt(i); 932839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen child.measure(MeasureSpec.makeMeasureSpec(childWidth, MeasureSpec.EXACTLY), 933839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen MeasureSpec.makeMeasureSpec(childHeight, MeasureSpec.EXACTLY)); 934839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen } 935839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen } 936839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen 937839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen @Override 938839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 939839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec); 940839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec); 941839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen final int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec); 942839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen final int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec); 943839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen 944839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen boolean haveChildRefSize = (mReferenceChildWidth != -1 && mReferenceChildHeight != -1); 945839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen 946839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen // We need to deal with the case where our parent hasn't told us how 947839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen // big we should be. In this case we should 948026e121bc35b9012943d8ea86518b51270d0c0efAdam Cohen float factorY = 1/(1 - PERSPECTIVE_SHIFT_FACTOR_Y); 949839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen if (heightSpecMode == MeasureSpec.UNSPECIFIED) { 950839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen heightSpecSize = haveChildRefSize ? 951026e121bc35b9012943d8ea86518b51270d0c0efAdam Cohen Math.round(mReferenceChildHeight * (1 + factorY)) + 952839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen mPaddingTop + mPaddingBottom : 0; 953839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen } else if (heightSpecMode == MeasureSpec.AT_MOST) { 954189ee18d6c6483ad63cc864267328259e2e00b95Dianne Hackborn if (haveChildRefSize) { 955026e121bc35b9012943d8ea86518b51270d0c0efAdam Cohen int height = Math.round(mReferenceChildHeight * (1 + factorY)) 956189ee18d6c6483ad63cc864267328259e2e00b95Dianne Hackborn + mPaddingTop + mPaddingBottom; 957189ee18d6c6483ad63cc864267328259e2e00b95Dianne Hackborn if (height <= heightSpecSize) { 958189ee18d6c6483ad63cc864267328259e2e00b95Dianne Hackborn heightSpecSize = height; 959189ee18d6c6483ad63cc864267328259e2e00b95Dianne Hackborn } else { 960189ee18d6c6483ad63cc864267328259e2e00b95Dianne Hackborn heightSpecSize |= MEASURED_STATE_TOO_SMALL; 961189ee18d6c6483ad63cc864267328259e2e00b95Dianne Hackborn } 962189ee18d6c6483ad63cc864267328259e2e00b95Dianne Hackborn } else { 963189ee18d6c6483ad63cc864267328259e2e00b95Dianne Hackborn heightSpecSize = 0; 964189ee18d6c6483ad63cc864267328259e2e00b95Dianne Hackborn } 965839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen } 966839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen 967026e121bc35b9012943d8ea86518b51270d0c0efAdam Cohen float factorX = 1/(1 - PERSPECTIVE_SHIFT_FACTOR_X); 968839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen if (widthSpecMode == MeasureSpec.UNSPECIFIED) { 969026e121bc35b9012943d8ea86518b51270d0c0efAdam Cohen widthSpecSize = haveChildRefSize ? 970026e121bc35b9012943d8ea86518b51270d0c0efAdam Cohen Math.round(mReferenceChildWidth * (1 + factorX)) + 971026e121bc35b9012943d8ea86518b51270d0c0efAdam Cohen mPaddingLeft + mPaddingRight : 0; 972839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen } else if (heightSpecMode == MeasureSpec.AT_MOST) { 973189ee18d6c6483ad63cc864267328259e2e00b95Dianne Hackborn if (haveChildRefSize) { 974189ee18d6c6483ad63cc864267328259e2e00b95Dianne Hackborn int width = mReferenceChildWidth + mPaddingLeft + mPaddingRight; 975189ee18d6c6483ad63cc864267328259e2e00b95Dianne Hackborn if (width <= widthSpecSize) { 976189ee18d6c6483ad63cc864267328259e2e00b95Dianne Hackborn widthSpecSize = width; 977189ee18d6c6483ad63cc864267328259e2e00b95Dianne Hackborn } else { 978189ee18d6c6483ad63cc864267328259e2e00b95Dianne Hackborn widthSpecSize |= MEASURED_STATE_TOO_SMALL; 979189ee18d6c6483ad63cc864267328259e2e00b95Dianne Hackborn } 980189ee18d6c6483ad63cc864267328259e2e00b95Dianne Hackborn } else { 981189ee18d6c6483ad63cc864267328259e2e00b95Dianne Hackborn widthSpecSize = 0; 982189ee18d6c6483ad63cc864267328259e2e00b95Dianne Hackborn } 983839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen } 984839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen 985839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen setMeasuredDimension(widthSpecSize, heightSpecSize); 986839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen measureChildren(); 987839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen } 988839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen 9899b073948cfb84c0dd04f8a94ee1f7f263f027c83Adam Cohen class LayoutParams extends ViewGroup.LayoutParams { 9909b073948cfb84c0dd04f8a94ee1f7f263f027c83Adam Cohen int horizontalOffset; 9919b073948cfb84c0dd04f8a94ee1f7f263f027c83Adam Cohen int verticalOffset; 9929b073948cfb84c0dd04f8a94ee1f7f263f027c83Adam Cohen View mView; 993d51bbb5b56446519db88f49f2682da782b0069acAdam Cohen private final Rect parentRect = new Rect(); 994d51bbb5b56446519db88f49f2682da782b0069acAdam Cohen private final Rect invalidateRect = new Rect(); 995d51bbb5b56446519db88f49f2682da782b0069acAdam Cohen private final RectF invalidateRectf = new RectF(); 996d51bbb5b56446519db88f49f2682da782b0069acAdam Cohen private final Rect globalInvalidateRect = new Rect(); 9979b073948cfb84c0dd04f8a94ee1f7f263f027c83Adam Cohen 9989b073948cfb84c0dd04f8a94ee1f7f263f027c83Adam Cohen LayoutParams(View view) { 9999b073948cfb84c0dd04f8a94ee1f7f263f027c83Adam Cohen super(0, 0); 1000839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen width = 0; 1001839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen height = 0; 10029b073948cfb84c0dd04f8a94ee1f7f263f027c83Adam Cohen horizontalOffset = 0; 10039b073948cfb84c0dd04f8a94ee1f7f263f027c83Adam Cohen verticalOffset = 0; 10049b073948cfb84c0dd04f8a94ee1f7f263f027c83Adam Cohen mView = view; 10059b073948cfb84c0dd04f8a94ee1f7f263f027c83Adam Cohen } 10069b073948cfb84c0dd04f8a94ee1f7f263f027c83Adam Cohen 10079b073948cfb84c0dd04f8a94ee1f7f263f027c83Adam Cohen LayoutParams(Context c, AttributeSet attrs) { 10089b073948cfb84c0dd04f8a94ee1f7f263f027c83Adam Cohen super(c, attrs); 10099b073948cfb84c0dd04f8a94ee1f7f263f027c83Adam Cohen horizontalOffset = 0; 10109b073948cfb84c0dd04f8a94ee1f7f263f027c83Adam Cohen verticalOffset = 0; 1011839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen width = 0; 1012839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen height = 0; 1013b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen } 1014b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen 10159b073948cfb84c0dd04f8a94ee1f7f263f027c83Adam Cohen void invalidateGlobalRegion(View v, Rect r) { 1016d51bbb5b56446519db88f49f2682da782b0069acAdam Cohen // We need to make a new rect here, so as not to modify the one passed 1017d51bbb5b56446519db88f49f2682da782b0069acAdam Cohen globalInvalidateRect.set(r); 10189b073948cfb84c0dd04f8a94ee1f7f263f027c83Adam Cohen View p = v; 10199b073948cfb84c0dd04f8a94ee1f7f263f027c83Adam Cohen if (!(v.getParent() != null && v.getParent() instanceof View)) return; 10209b073948cfb84c0dd04f8a94ee1f7f263f027c83Adam Cohen 10219b073948cfb84c0dd04f8a94ee1f7f263f027c83Adam Cohen boolean firstPass = true; 10229b073948cfb84c0dd04f8a94ee1f7f263f027c83Adam Cohen parentRect.set(0, 0, 0, 0); 10239b073948cfb84c0dd04f8a94ee1f7f263f027c83Adam Cohen int depth = 0; 1024b7f4d030a2ed9301bf47c41fefc1b338f4347ffeAdam Cohen while (p.getParent() != null && p.getParent() instanceof View 1025d51bbb5b56446519db88f49f2682da782b0069acAdam Cohen && !parentRect.contains(globalInvalidateRect)) { 10269b073948cfb84c0dd04f8a94ee1f7f263f027c83Adam Cohen if (!firstPass) { 1027d51bbb5b56446519db88f49f2682da782b0069acAdam Cohen globalInvalidateRect.offset(p.getLeft() - p.getScrollX(), p.getTop() 1028d51bbb5b56446519db88f49f2682da782b0069acAdam Cohen - p.getScrollY()); 10299b073948cfb84c0dd04f8a94ee1f7f263f027c83Adam Cohen depth++; 10309b073948cfb84c0dd04f8a94ee1f7f263f027c83Adam Cohen } 10319b073948cfb84c0dd04f8a94ee1f7f263f027c83Adam Cohen firstPass = false; 10329b073948cfb84c0dd04f8a94ee1f7f263f027c83Adam Cohen p = (View) p.getParent(); 1033b7f4d030a2ed9301bf47c41fefc1b338f4347ffeAdam Cohen parentRect.set(p.getScrollX(), p.getScrollY(), 1034b7f4d030a2ed9301bf47c41fefc1b338f4347ffeAdam Cohen p.getWidth() + p.getScrollX(), p.getHeight() + p.getScrollY()); 1035839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen 10369b073948cfb84c0dd04f8a94ee1f7f263f027c83Adam Cohen } 10379b073948cfb84c0dd04f8a94ee1f7f263f027c83Adam Cohen 1038d51bbb5b56446519db88f49f2682da782b0069acAdam Cohen p.invalidate(globalInvalidateRect.left, globalInvalidateRect.top, 1039d51bbb5b56446519db88f49f2682da782b0069acAdam Cohen globalInvalidateRect.right, globalInvalidateRect.bottom); 1040d51bbb5b56446519db88f49f2682da782b0069acAdam Cohen } 1041d51bbb5b56446519db88f49f2682da782b0069acAdam Cohen 1042d51bbb5b56446519db88f49f2682da782b0069acAdam Cohen Rect getInvalidateRect() { 1043d51bbb5b56446519db88f49f2682da782b0069acAdam Cohen return invalidateRect; 1044d51bbb5b56446519db88f49f2682da782b0069acAdam Cohen } 10459b073948cfb84c0dd04f8a94ee1f7f263f027c83Adam Cohen 1046d51bbb5b56446519db88f49f2682da782b0069acAdam Cohen void resetInvalidateRect() { 10470ac116b688380489c3690f6f65b282990c221f17Adam Cohen invalidateRect.set(0, 0, 0, 0); 10489b073948cfb84c0dd04f8a94ee1f7f263f027c83Adam Cohen } 104932a42f1587db77b958d62c3de4f2734eb0a3b965Adam Cohen 1050a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase // This is public so that ObjectAnimator can access it 10519b073948cfb84c0dd04f8a94ee1f7f263f027c83Adam Cohen public void setVerticalOffset(int newVerticalOffset) { 10520ac116b688380489c3690f6f65b282990c221f17Adam Cohen setOffsets(horizontalOffset, newVerticalOffset); 10539b073948cfb84c0dd04f8a94ee1f7f263f027c83Adam Cohen } 10549b073948cfb84c0dd04f8a94ee1f7f263f027c83Adam Cohen 10559b073948cfb84c0dd04f8a94ee1f7f263f027c83Adam Cohen public void setHorizontalOffset(int newHorizontalOffset) { 10560ac116b688380489c3690f6f65b282990c221f17Adam Cohen setOffsets(newHorizontalOffset, verticalOffset); 10570ac116b688380489c3690f6f65b282990c221f17Adam Cohen } 10580ac116b688380489c3690f6f65b282990c221f17Adam Cohen 10590ac116b688380489c3690f6f65b282990c221f17Adam Cohen public void setOffsets(int newHorizontalOffset, int newVerticalOffset) { 10600ac116b688380489c3690f6f65b282990c221f17Adam Cohen int horizontalOffsetDelta = newHorizontalOffset - horizontalOffset; 10619b073948cfb84c0dd04f8a94ee1f7f263f027c83Adam Cohen horizontalOffset = newHorizontalOffset; 10620ac116b688380489c3690f6f65b282990c221f17Adam Cohen int verticalOffsetDelta = newVerticalOffset - verticalOffset; 10630ac116b688380489c3690f6f65b282990c221f17Adam Cohen verticalOffset = newVerticalOffset; 10649b073948cfb84c0dd04f8a94ee1f7f263f027c83Adam Cohen 10659b073948cfb84c0dd04f8a94ee1f7f263f027c83Adam Cohen if (mView != null) { 10669b073948cfb84c0dd04f8a94ee1f7f263f027c83Adam Cohen mView.requestLayout(); 10670ac116b688380489c3690f6f65b282990c221f17Adam Cohen int left = Math.min(mView.getLeft() + horizontalOffsetDelta, mView.getLeft()); 10680ac116b688380489c3690f6f65b282990c221f17Adam Cohen int right = Math.max(mView.getRight() + horizontalOffsetDelta, mView.getRight()); 10690ac116b688380489c3690f6f65b282990c221f17Adam Cohen int top = Math.min(mView.getTop() + verticalOffsetDelta, mView.getTop()); 10700ac116b688380489c3690f6f65b282990c221f17Adam Cohen int bottom = Math.max(mView.getBottom() + verticalOffsetDelta, mView.getBottom()); 10710ac116b688380489c3690f6f65b282990c221f17Adam Cohen 10720ac116b688380489c3690f6f65b282990c221f17Adam Cohen invalidateRectf.set(left, top, right, bottom); 10739b073948cfb84c0dd04f8a94ee1f7f263f027c83Adam Cohen 10749b073948cfb84c0dd04f8a94ee1f7f263f027c83Adam Cohen float xoffset = -invalidateRectf.left; 10759b073948cfb84c0dd04f8a94ee1f7f263f027c83Adam Cohen float yoffset = -invalidateRectf.top; 10769b073948cfb84c0dd04f8a94ee1f7f263f027c83Adam Cohen invalidateRectf.offset(xoffset, yoffset); 10779b073948cfb84c0dd04f8a94ee1f7f263f027c83Adam Cohen mView.getMatrix().mapRect(invalidateRectf); 10789b073948cfb84c0dd04f8a94ee1f7f263f027c83Adam Cohen invalidateRectf.offset(-xoffset, -yoffset); 10799b073948cfb84c0dd04f8a94ee1f7f263f027c83Adam Cohen 10800ac116b688380489c3690f6f65b282990c221f17Adam Cohen invalidateRect.set((int) Math.floor(invalidateRectf.left), 10819b073948cfb84c0dd04f8a94ee1f7f263f027c83Adam Cohen (int) Math.floor(invalidateRectf.top), 10829b073948cfb84c0dd04f8a94ee1f7f263f027c83Adam Cohen (int) Math.ceil(invalidateRectf.right), 10839b073948cfb84c0dd04f8a94ee1f7f263f027c83Adam Cohen (int) Math.ceil(invalidateRectf.bottom)); 10849b073948cfb84c0dd04f8a94ee1f7f263f027c83Adam Cohen 10859b073948cfb84c0dd04f8a94ee1f7f263f027c83Adam Cohen invalidateGlobalRegion(mView, invalidateRect); 10869b073948cfb84c0dd04f8a94ee1f7f263f027c83Adam Cohen } 10879b073948cfb84c0dd04f8a94ee1f7f263f027c83Adam Cohen } 108832a42f1587db77b958d62c3de4f2734eb0a3b965Adam Cohen } 108932a42f1587db77b958d62c3de4f2734eb0a3b965Adam Cohen 10909b073948cfb84c0dd04f8a94ee1f7f263f027c83Adam Cohen private static class HolographicHelper { 10919b073948cfb84c0dd04f8a94ee1f7f263f027c83Adam Cohen private final Paint mHolographicPaint = new Paint(); 10929b073948cfb84c0dd04f8a94ee1f7f263f027c83Adam Cohen private final Paint mErasePaint = new Paint(); 1093839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen private final Paint mBlurPaint = new Paint(); 10948baf5df0fa5b3453a7f17e95746c5d8cadc00163Adam Cohen private static final int RES_OUT = 0; 10958baf5df0fa5b3453a7f17e95746c5d8cadc00163Adam Cohen private static final int CLICK_FEEDBACK = 1; 10968baf5df0fa5b3453a7f17e95746c5d8cadc00163Adam Cohen private float mDensity; 1097e80202d5c3c2a45cc34976e09958883e5366f1bbPatrick Dubroy private BlurMaskFilter mSmallBlurMaskFilter; 1098e80202d5c3c2a45cc34976e09958883e5366f1bbPatrick Dubroy private BlurMaskFilter mLargeBlurMaskFilter; 1099e80202d5c3c2a45cc34976e09958883e5366f1bbPatrick Dubroy private final Canvas mCanvas = new Canvas(); 1100e80202d5c3c2a45cc34976e09958883e5366f1bbPatrick Dubroy private final Canvas mMaskCanvas = new Canvas(); 1101e80202d5c3c2a45cc34976e09958883e5366f1bbPatrick Dubroy private final int[] mTmpXY = new int[2]; 1102e80202d5c3c2a45cc34976e09958883e5366f1bbPatrick Dubroy private final Matrix mIdentityMatrix = new Matrix(); 110332a42f1587db77b958d62c3de4f2734eb0a3b965Adam Cohen 1104dfcdddd7c408dddb22fb0867e4799d4c29d2f55fAdam Cohen HolographicHelper(Context context) { 11058baf5df0fa5b3453a7f17e95746c5d8cadc00163Adam Cohen mDensity = context.getResources().getDisplayMetrics().density; 1106dfcdddd7c408dddb22fb0867e4799d4c29d2f55fAdam Cohen 11079b073948cfb84c0dd04f8a94ee1f7f263f027c83Adam Cohen mHolographicPaint.setFilterBitmap(true); 1108839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen mHolographicPaint.setMaskFilter(TableMaskFilter.CreateClipTable(0, 30)); 11099b073948cfb84c0dd04f8a94ee1f7f263f027c83Adam Cohen mErasePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT)); 11109b073948cfb84c0dd04f8a94ee1f7f263f027c83Adam Cohen mErasePaint.setFilterBitmap(true); 1111e80202d5c3c2a45cc34976e09958883e5366f1bbPatrick Dubroy 1112e80202d5c3c2a45cc34976e09958883e5366f1bbPatrick Dubroy mSmallBlurMaskFilter = new BlurMaskFilter(2 * mDensity, BlurMaskFilter.Blur.NORMAL); 1113e80202d5c3c2a45cc34976e09958883e5366f1bbPatrick Dubroy mLargeBlurMaskFilter = new BlurMaskFilter(4 * mDensity, BlurMaskFilter.Blur.NORMAL); 11149b073948cfb84c0dd04f8a94ee1f7f263f027c83Adam Cohen } 11159b073948cfb84c0dd04f8a94ee1f7f263f027c83Adam Cohen 11169b073948cfb84c0dd04f8a94ee1f7f263f027c83Adam Cohen Bitmap createOutline(View v) { 11178baf5df0fa5b3453a7f17e95746c5d8cadc00163Adam Cohen return createOutline(v, RES_OUT); 11188baf5df0fa5b3453a7f17e95746c5d8cadc00163Adam Cohen } 11198baf5df0fa5b3453a7f17e95746c5d8cadc00163Adam Cohen 11208baf5df0fa5b3453a7f17e95746c5d8cadc00163Adam Cohen Bitmap createOutline(View v, int type) { 11218baf5df0fa5b3453a7f17e95746c5d8cadc00163Adam Cohen if (type == RES_OUT) { 11228baf5df0fa5b3453a7f17e95746c5d8cadc00163Adam Cohen mHolographicPaint.setColor(0xff6699ff); 1123e80202d5c3c2a45cc34976e09958883e5366f1bbPatrick Dubroy mBlurPaint.setMaskFilter(mSmallBlurMaskFilter); 11248baf5df0fa5b3453a7f17e95746c5d8cadc00163Adam Cohen } else if (type == CLICK_FEEDBACK) { 11258baf5df0fa5b3453a7f17e95746c5d8cadc00163Adam Cohen mHolographicPaint.setColor(0x886699ff); 1126e80202d5c3c2a45cc34976e09958883e5366f1bbPatrick Dubroy mBlurPaint.setMaskFilter(mLargeBlurMaskFilter); 11278baf5df0fa5b3453a7f17e95746c5d8cadc00163Adam Cohen } 11288baf5df0fa5b3453a7f17e95746c5d8cadc00163Adam Cohen 11299b073948cfb84c0dd04f8a94ee1f7f263f027c83Adam Cohen if (v.getMeasuredWidth() == 0 || v.getMeasuredHeight() == 0) { 11309b073948cfb84c0dd04f8a94ee1f7f263f027c83Adam Cohen return null; 11319b073948cfb84c0dd04f8a94ee1f7f263f027c83Adam Cohen } 11329b073948cfb84c0dd04f8a94ee1f7f263f027c83Adam Cohen 11339b073948cfb84c0dd04f8a94ee1f7f263f027c83Adam Cohen Bitmap bitmap = Bitmap.createBitmap(v.getMeasuredWidth(), v.getMeasuredHeight(), 11349b073948cfb84c0dd04f8a94ee1f7f263f027c83Adam Cohen Bitmap.Config.ARGB_8888); 1135e80202d5c3c2a45cc34976e09958883e5366f1bbPatrick Dubroy mCanvas.setBitmap(bitmap); 113632a42f1587db77b958d62c3de4f2734eb0a3b965Adam Cohen 11379b073948cfb84c0dd04f8a94ee1f7f263f027c83Adam Cohen float rotationX = v.getRotationX(); 1138839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen float rotation = v.getRotation(); 1139839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen float translationY = v.getTranslationY(); 1140026e121bc35b9012943d8ea86518b51270d0c0efAdam Cohen float translationX = v.getTranslationX(); 11419b073948cfb84c0dd04f8a94ee1f7f263f027c83Adam Cohen v.setRotationX(0); 1142839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen v.setRotation(0); 1143839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen v.setTranslationY(0); 1144026e121bc35b9012943d8ea86518b51270d0c0efAdam Cohen v.setTranslationX(0); 1145e80202d5c3c2a45cc34976e09958883e5366f1bbPatrick Dubroy v.draw(mCanvas); 11469b073948cfb84c0dd04f8a94ee1f7f263f027c83Adam Cohen v.setRotationX(rotationX); 1147839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen v.setRotation(rotation); 1148839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen v.setTranslationY(translationY); 1149026e121bc35b9012943d8ea86518b51270d0c0efAdam Cohen v.setTranslationX(translationX); 11509b073948cfb84c0dd04f8a94ee1f7f263f027c83Adam Cohen 1151e80202d5c3c2a45cc34976e09958883e5366f1bbPatrick Dubroy drawOutline(mCanvas, bitmap); 11529b073948cfb84c0dd04f8a94ee1f7f263f027c83Adam Cohen return bitmap; 11539b073948cfb84c0dd04f8a94ee1f7f263f027c83Adam Cohen } 11549b073948cfb84c0dd04f8a94ee1f7f263f027c83Adam Cohen 11559b073948cfb84c0dd04f8a94ee1f7f263f027c83Adam Cohen void drawOutline(Canvas dest, Bitmap src) { 1156e80202d5c3c2a45cc34976e09958883e5366f1bbPatrick Dubroy final int[] xy = mTmpXY; 1157839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen Bitmap mask = src.extractAlpha(mBlurPaint, xy); 1158e80202d5c3c2a45cc34976e09958883e5366f1bbPatrick Dubroy mMaskCanvas.setBitmap(mask); 1159e80202d5c3c2a45cc34976e09958883e5366f1bbPatrick Dubroy mMaskCanvas.drawBitmap(src, -xy[0], -xy[1], mErasePaint); 11609b073948cfb84c0dd04f8a94ee1f7f263f027c83Adam Cohen dest.drawColor(0, PorterDuff.Mode.CLEAR); 1161e80202d5c3c2a45cc34976e09958883e5366f1bbPatrick Dubroy dest.setMatrix(mIdentityMatrix); 1162839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen dest.drawBitmap(mask, xy[0], xy[1], mHolographicPaint); 11639b073948cfb84c0dd04f8a94ee1f7f263f027c83Adam Cohen mask.recycle(); 11649b073948cfb84c0dd04f8a94ee1f7f263f027c83Adam Cohen } 116532a42f1587db77b958d62c3de4f2734eb0a3b965Adam Cohen } 116644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen} 1167