1 /*
2  * Copyright (C) 2014 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License
15  */
16 
17 package com.android.systemui.statusbar.phone;
18 
19 import android.animation.Animator;
20 import android.animation.AnimatorListenerAdapter;
21 import android.animation.ValueAnimator;
22 import android.content.Context;
23 import android.view.MotionEvent;
24 import android.view.VelocityTracker;
25 import android.view.View;
26 import android.view.ViewConfiguration;
27 
28 import com.android.systemui.Interpolators;
29 import com.android.systemui.R;
30 import com.android.systemui.classifier.FalsingManager;
31 import com.android.systemui.statusbar.FlingAnimationUtils;
32 import com.android.systemui.statusbar.KeyguardAffordanceView;
33 
34 /**
35  * A touch handler of the keyguard which is responsible for launching phone and camera affordances.
36  */
37 public class KeyguardAffordanceHelper {
38 
39     public static final float SWIPE_RESTING_ALPHA_AMOUNT = 0.5f;
40     public static final long HINT_PHASE1_DURATION = 200;
41     private static final long HINT_PHASE2_DURATION = 350;
42     private static final float BACKGROUND_RADIUS_SCALE_FACTOR = 0.25f;
43     private static final int HINT_CIRCLE_OPEN_DURATION = 500;
44 
45     private final Context mContext;
46     private final Callback mCallback;
47 
48     private FlingAnimationUtils mFlingAnimationUtils;
49     private VelocityTracker mVelocityTracker;
50     private boolean mSwipingInProgress;
51     private float mInitialTouchX;
52     private float mInitialTouchY;
53     private float mTranslation;
54     private float mTranslationOnDown;
55     private int mTouchSlop;
56     private int mMinTranslationAmount;
57     private int mMinFlingVelocity;
58     private int mHintGrowAmount;
59     private KeyguardAffordanceView mLeftIcon;
60     private KeyguardAffordanceView mCenterIcon;
61     private KeyguardAffordanceView mRightIcon;
62     private Animator mSwipeAnimator;
63     private FalsingManager mFalsingManager;
64     private int mMinBackgroundRadius;
65     private boolean mMotionCancelled;
66     private int mTouchTargetSize;
67     private View mTargetedView;
68     private boolean mTouchSlopExeeded;
69     private AnimatorListenerAdapter mFlingEndListener = new AnimatorListenerAdapter() {
70         @Override
71         public void onAnimationEnd(Animator animation) {
72             mSwipeAnimator = null;
73             mSwipingInProgress = false;
74             mTargetedView = null;
75         }
76     };
77     private Runnable mAnimationEndRunnable = new Runnable() {
78         @Override
79         public void run() {
80             mCallback.onAnimationToSideEnded();
81         }
82     };
83 
KeyguardAffordanceHelper(Callback callback, Context context)84     KeyguardAffordanceHelper(Callback callback, Context context) {
85         mContext = context;
86         mCallback = callback;
87         initIcons();
88         updateIcon(mLeftIcon, 0.0f, mLeftIcon.getRestingAlpha(), false, false, true, false);
89         updateIcon(mCenterIcon, 0.0f, mCenterIcon.getRestingAlpha(), false, false, true, false);
90         updateIcon(mRightIcon, 0.0f, mRightIcon.getRestingAlpha(), false, false, true, false);
91         initDimens();
92     }
93 
initDimens()94     private void initDimens() {
95         final ViewConfiguration configuration = ViewConfiguration.get(mContext);
96         mTouchSlop = configuration.getScaledPagingTouchSlop();
97         mMinFlingVelocity = configuration.getScaledMinimumFlingVelocity();
98         mMinTranslationAmount = mContext.getResources().getDimensionPixelSize(
99                 R.dimen.keyguard_min_swipe_amount);
100         mMinBackgroundRadius = mContext.getResources().getDimensionPixelSize(
101                 R.dimen.keyguard_affordance_min_background_radius);
102         mTouchTargetSize = mContext.getResources().getDimensionPixelSize(
103                 R.dimen.keyguard_affordance_touch_target_size);
104         mHintGrowAmount =
105                 mContext.getResources().getDimensionPixelSize(R.dimen.hint_grow_amount_sideways);
106         mFlingAnimationUtils = new FlingAnimationUtils(mContext, 0.4f);
107         mFalsingManager = FalsingManager.getInstance(mContext);
108     }
109 
initIcons()110     private void initIcons() {
111         mLeftIcon = mCallback.getLeftIcon();
112         mCenterIcon = mCallback.getCenterIcon();
113         mRightIcon = mCallback.getRightIcon();
114         updatePreviews();
115     }
116 
updatePreviews()117     public void updatePreviews() {
118         mLeftIcon.setPreviewView(mCallback.getLeftPreview());
119         mRightIcon.setPreviewView(mCallback.getRightPreview());
120     }
121 
onTouchEvent(MotionEvent event)122     public boolean onTouchEvent(MotionEvent event) {
123         int action = event.getActionMasked();
124         if (mMotionCancelled && action != MotionEvent.ACTION_DOWN) {
125             return false;
126         }
127         final float y = event.getY();
128         final float x = event.getX();
129 
130         boolean isUp = false;
131         switch (action) {
132             case MotionEvent.ACTION_DOWN:
133                 View targetView = getIconAtPosition(x, y);
134                 if (targetView == null || (mTargetedView != null && mTargetedView != targetView)) {
135                     mMotionCancelled = true;
136                     return false;
137                 }
138                 if (mTargetedView != null) {
139                     cancelAnimation();
140                 } else {
141                     mTouchSlopExeeded = false;
142                 }
143                 startSwiping(targetView);
144                 mInitialTouchX = x;
145                 mInitialTouchY = y;
146                 mTranslationOnDown = mTranslation;
147                 initVelocityTracker();
148                 trackMovement(event);
149                 mMotionCancelled = false;
150                 break;
151             case MotionEvent.ACTION_POINTER_DOWN:
152                 mMotionCancelled = true;
153                 endMotion(true /* forceSnapBack */, x, y);
154                 break;
155             case MotionEvent.ACTION_MOVE:
156                 trackMovement(event);
157                 float xDist = x - mInitialTouchX;
158                 float yDist = y - mInitialTouchY;
159                 float distance = (float) Math.hypot(xDist, yDist);
160                 if (!mTouchSlopExeeded && distance > mTouchSlop) {
161                     mTouchSlopExeeded = true;
162                 }
163                 if (mSwipingInProgress) {
164                     if (mTargetedView == mRightIcon) {
165                         distance = mTranslationOnDown - distance;
166                         distance = Math.min(0, distance);
167                     } else {
168                         distance = mTranslationOnDown + distance;
169                         distance = Math.max(0, distance);
170                     }
171                     setTranslation(distance, false /* isReset */, false /* animateReset */,
172                             false /* force */);
173                 }
174                 break;
175 
176             case MotionEvent.ACTION_UP:
177                 isUp = true;
178             case MotionEvent.ACTION_CANCEL:
179                 boolean hintOnTheRight = mTargetedView == mRightIcon;
180                 trackMovement(event);
181                 endMotion(!isUp, x, y);
182                 if (!mTouchSlopExeeded && isUp) {
183                     mCallback.onIconClicked(hintOnTheRight);
184                 }
185                 break;
186         }
187         return true;
188     }
189 
startSwiping(View targetView)190     private void startSwiping(View targetView) {
191         mCallback.onSwipingStarted(targetView == mRightIcon);
192         mSwipingInProgress = true;
193         mTargetedView = targetView;
194     }
195 
getIconAtPosition(float x, float y)196     private View getIconAtPosition(float x, float y) {
197         if (leftSwipePossible() && isOnIcon(mLeftIcon, x, y)) {
198             return mLeftIcon;
199         }
200         if (rightSwipePossible() && isOnIcon(mRightIcon, x, y)) {
201             return mRightIcon;
202         }
203         return null;
204     }
205 
isOnAffordanceIcon(float x, float y)206     public boolean isOnAffordanceIcon(float x, float y) {
207         return isOnIcon(mLeftIcon, x, y) || isOnIcon(mRightIcon, x, y);
208     }
209 
isOnIcon(View icon, float x, float y)210     private boolean isOnIcon(View icon, float x, float y) {
211         float iconX = icon.getX() + icon.getWidth() / 2.0f;
212         float iconY = icon.getY() + icon.getHeight() / 2.0f;
213         double distance = Math.hypot(x - iconX, y - iconY);
214         return distance <= mTouchTargetSize / 2;
215     }
216 
endMotion(boolean forceSnapBack, float lastX, float lastY)217     private void endMotion(boolean forceSnapBack, float lastX, float lastY) {
218         if (mSwipingInProgress) {
219             flingWithCurrentVelocity(forceSnapBack, lastX, lastY);
220         } else {
221             mTargetedView = null;
222         }
223         if (mVelocityTracker != null) {
224             mVelocityTracker.recycle();
225             mVelocityTracker = null;
226         }
227     }
228 
rightSwipePossible()229     private boolean rightSwipePossible() {
230         return mRightIcon.getVisibility() == View.VISIBLE;
231     }
232 
leftSwipePossible()233     private boolean leftSwipePossible() {
234         return mLeftIcon.getVisibility() == View.VISIBLE;
235     }
236 
onInterceptTouchEvent(MotionEvent ev)237     public boolean onInterceptTouchEvent(MotionEvent ev) {
238         return false;
239     }
240 
startHintAnimation(boolean right, Runnable onFinishedListener)241     public void startHintAnimation(boolean right,
242             Runnable onFinishedListener) {
243         cancelAnimation();
244         startHintAnimationPhase1(right, onFinishedListener);
245     }
246 
startHintAnimationPhase1(final boolean right, final Runnable onFinishedListener)247     private void startHintAnimationPhase1(final boolean right, final Runnable onFinishedListener) {
248         final KeyguardAffordanceView targetView = right ? mRightIcon : mLeftIcon;
249         ValueAnimator animator = getAnimatorToRadius(right, mHintGrowAmount);
250         animator.addListener(new AnimatorListenerAdapter() {
251             private boolean mCancelled;
252 
253             @Override
254             public void onAnimationCancel(Animator animation) {
255                 mCancelled = true;
256             }
257 
258             @Override
259             public void onAnimationEnd(Animator animation) {
260                 if (mCancelled) {
261                     mSwipeAnimator = null;
262                     mTargetedView = null;
263                     onFinishedListener.run();
264                 } else {
265                     startUnlockHintAnimationPhase2(right, onFinishedListener);
266                 }
267             }
268         });
269         animator.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
270         animator.setDuration(HINT_PHASE1_DURATION);
271         animator.start();
272         mSwipeAnimator = animator;
273         mTargetedView = targetView;
274     }
275 
276     /**
277      * Phase 2: Move back.
278      */
startUnlockHintAnimationPhase2(boolean right, final Runnable onFinishedListener)279     private void startUnlockHintAnimationPhase2(boolean right, final Runnable onFinishedListener) {
280         ValueAnimator animator = getAnimatorToRadius(right, 0);
281         animator.addListener(new AnimatorListenerAdapter() {
282             @Override
283             public void onAnimationEnd(Animator animation) {
284                 mSwipeAnimator = null;
285                 mTargetedView = null;
286                 onFinishedListener.run();
287             }
288         });
289         animator.setInterpolator(Interpolators.FAST_OUT_LINEAR_IN);
290         animator.setDuration(HINT_PHASE2_DURATION);
291         animator.setStartDelay(HINT_CIRCLE_OPEN_DURATION);
292         animator.start();
293         mSwipeAnimator = animator;
294     }
295 
getAnimatorToRadius(final boolean right, int radius)296     private ValueAnimator getAnimatorToRadius(final boolean right, int radius) {
297         final KeyguardAffordanceView targetView = right ? mRightIcon : mLeftIcon;
298         ValueAnimator animator = ValueAnimator.ofFloat(targetView.getCircleRadius(), radius);
299         animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
300             @Override
301             public void onAnimationUpdate(ValueAnimator animation) {
302                 float newRadius = (float) animation.getAnimatedValue();
303                 targetView.setCircleRadiusWithoutAnimation(newRadius);
304                 float translation = getTranslationFromRadius(newRadius);
305                 mTranslation = right ? -translation : translation;
306                 updateIconsFromTranslation(targetView);
307             }
308         });
309         return animator;
310     }
311 
cancelAnimation()312     private void cancelAnimation() {
313         if (mSwipeAnimator != null) {
314             mSwipeAnimator.cancel();
315         }
316     }
317 
flingWithCurrentVelocity(boolean forceSnapBack, float lastX, float lastY)318     private void flingWithCurrentVelocity(boolean forceSnapBack, float lastX, float lastY) {
319         float vel = getCurrentVelocity(lastX, lastY);
320 
321         // We snap back if the current translation is not far enough
322         boolean snapBack = false;
323         if (mCallback.needsAntiFalsing()) {
324             snapBack = snapBack || mFalsingManager.isFalseTouch();
325         }
326         snapBack = snapBack || isBelowFalsingThreshold();
327 
328         // or if the velocity is in the opposite direction.
329         boolean velIsInWrongDirection = vel * mTranslation < 0;
330         snapBack |= Math.abs(vel) > mMinFlingVelocity && velIsInWrongDirection;
331         vel = snapBack ^ velIsInWrongDirection ? 0 : vel;
332         fling(vel, snapBack || forceSnapBack, mTranslation < 0);
333     }
334 
isBelowFalsingThreshold()335     private boolean isBelowFalsingThreshold() {
336         return Math.abs(mTranslation) < Math.abs(mTranslationOnDown) + getMinTranslationAmount();
337     }
338 
getMinTranslationAmount()339     private int getMinTranslationAmount() {
340         float factor = mCallback.getAffordanceFalsingFactor();
341         return (int) (mMinTranslationAmount * factor);
342     }
343 
fling(float vel, final boolean snapBack, boolean right)344     private void fling(float vel, final boolean snapBack, boolean right) {
345         float target = right ? -mCallback.getMaxTranslationDistance()
346                 : mCallback.getMaxTranslationDistance();
347         target = snapBack ? 0 : target;
348 
349         ValueAnimator animator = ValueAnimator.ofFloat(mTranslation, target);
350         mFlingAnimationUtils.apply(animator, mTranslation, target, vel);
351         animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
352             @Override
353             public void onAnimationUpdate(ValueAnimator animation) {
354                 mTranslation = (float) animation.getAnimatedValue();
355             }
356         });
357         animator.addListener(mFlingEndListener);
358         if (!snapBack) {
359             startFinishingCircleAnimation(vel * 0.375f, mAnimationEndRunnable, right);
360             mCallback.onAnimationToSideStarted(right, mTranslation, vel);
361         } else {
362             reset(true);
363         }
364         animator.start();
365         mSwipeAnimator = animator;
366         if (snapBack) {
367             mCallback.onSwipingAborted();
368         }
369     }
370 
startFinishingCircleAnimation(float velocity, Runnable mAnimationEndRunnable, boolean right)371     private void startFinishingCircleAnimation(float velocity, Runnable mAnimationEndRunnable,
372             boolean right) {
373         KeyguardAffordanceView targetView = right ? mRightIcon : mLeftIcon;
374         targetView.finishAnimation(velocity, mAnimationEndRunnable);
375     }
376 
setTranslation(float translation, boolean isReset, boolean animateReset, boolean force)377     private void setTranslation(float translation, boolean isReset, boolean animateReset,
378             boolean force) {
379         translation = rightSwipePossible() ? translation : Math.max(0, translation);
380         translation = leftSwipePossible() ? translation : Math.min(0, translation);
381         float absTranslation = Math.abs(translation);
382         if (translation != mTranslation || isReset || force) {
383             KeyguardAffordanceView targetView = translation > 0 ? mLeftIcon : mRightIcon;
384             KeyguardAffordanceView otherView = translation > 0 ? mRightIcon : mLeftIcon;
385             float alpha = absTranslation / getMinTranslationAmount();
386 
387             // We interpolate the alpha of the other icons to 0
388             float fadeOutAlpha = 1.0f - alpha;
389             fadeOutAlpha = Math.max(fadeOutAlpha, 0.0f);
390 
391             boolean animateIcons = isReset && animateReset;
392             boolean forceNoCircleAnimation = isReset && !animateReset;
393             float radius = getRadiusFromTranslation(absTranslation);
394             boolean slowAnimation = isReset && isBelowFalsingThreshold();
395             if (!isReset) {
396                 updateIcon(targetView, radius, alpha + fadeOutAlpha * targetView.getRestingAlpha(),
397                         false, false, force, false);
398             } else {
399                 updateIcon(targetView, 0.0f, fadeOutAlpha * targetView.getRestingAlpha(),
400                         animateIcons, slowAnimation, force, forceNoCircleAnimation);
401             }
402             updateIcon(otherView, 0.0f, fadeOutAlpha * otherView.getRestingAlpha(),
403                     animateIcons, slowAnimation, force, forceNoCircleAnimation);
404             updateIcon(mCenterIcon, 0.0f, fadeOutAlpha * mCenterIcon.getRestingAlpha(),
405                     animateIcons, slowAnimation, force, forceNoCircleAnimation);
406 
407             mTranslation = translation;
408         }
409     }
410 
updateIconsFromTranslation(KeyguardAffordanceView targetView)411     private void updateIconsFromTranslation(KeyguardAffordanceView targetView) {
412         float absTranslation = Math.abs(mTranslation);
413         float alpha = absTranslation / getMinTranslationAmount();
414 
415         // We interpolate the alpha of the other icons to 0
416         float fadeOutAlpha =  1.0f - alpha;
417         fadeOutAlpha = Math.max(0.0f, fadeOutAlpha);
418 
419         // We interpolate the alpha of the targetView to 1
420         KeyguardAffordanceView otherView = targetView == mRightIcon ? mLeftIcon : mRightIcon;
421         updateIconAlpha(targetView, alpha + fadeOutAlpha * targetView.getRestingAlpha(), false);
422         updateIconAlpha(otherView, fadeOutAlpha * otherView.getRestingAlpha(), false);
423         updateIconAlpha(mCenterIcon, fadeOutAlpha * mCenterIcon.getRestingAlpha(), false);
424     }
425 
getTranslationFromRadius(float circleSize)426     private float getTranslationFromRadius(float circleSize) {
427         float translation = (circleSize - mMinBackgroundRadius)
428                 / BACKGROUND_RADIUS_SCALE_FACTOR;
429         return translation > 0.0f ? translation + mTouchSlop : 0.0f;
430     }
431 
getRadiusFromTranslation(float translation)432     private float getRadiusFromTranslation(float translation) {
433         if (translation <= mTouchSlop) {
434             return 0.0f;
435         }
436         return (translation - mTouchSlop)  * BACKGROUND_RADIUS_SCALE_FACTOR + mMinBackgroundRadius;
437     }
438 
animateHideLeftRightIcon()439     public void animateHideLeftRightIcon() {
440         cancelAnimation();
441         updateIcon(mRightIcon, 0f, 0f, true, false, false, false);
442         updateIcon(mLeftIcon, 0f, 0f, true, false, false, false);
443     }
444 
updateIcon(KeyguardAffordanceView view, float circleRadius, float alpha, boolean animate, boolean slowRadiusAnimation, boolean force, boolean forceNoCircleAnimation)445     private void updateIcon(KeyguardAffordanceView view, float circleRadius, float alpha,
446                             boolean animate, boolean slowRadiusAnimation, boolean force,
447                             boolean forceNoCircleAnimation) {
448         if (view.getVisibility() != View.VISIBLE && !force) {
449             return;
450         }
451         if (forceNoCircleAnimation) {
452             view.setCircleRadiusWithoutAnimation(circleRadius);
453         } else {
454             view.setCircleRadius(circleRadius, slowRadiusAnimation);
455         }
456         updateIconAlpha(view, alpha, animate);
457     }
458 
updateIconAlpha(KeyguardAffordanceView view, float alpha, boolean animate)459     private void updateIconAlpha(KeyguardAffordanceView view, float alpha, boolean animate) {
460         float scale = getScale(alpha, view);
461         alpha = Math.min(1.0f, alpha);
462         view.setImageAlpha(alpha, animate);
463         view.setImageScale(scale, animate);
464     }
465 
getScale(float alpha, KeyguardAffordanceView icon)466     private float getScale(float alpha, KeyguardAffordanceView icon) {
467         float scale = alpha / icon.getRestingAlpha() * 0.2f +
468                 KeyguardAffordanceView.MIN_ICON_SCALE_AMOUNT;
469         return Math.min(scale, KeyguardAffordanceView.MAX_ICON_SCALE_AMOUNT);
470     }
471 
trackMovement(MotionEvent event)472     private void trackMovement(MotionEvent event) {
473         if (mVelocityTracker != null) {
474             mVelocityTracker.addMovement(event);
475         }
476     }
477 
initVelocityTracker()478     private void initVelocityTracker() {
479         if (mVelocityTracker != null) {
480             mVelocityTracker.recycle();
481         }
482         mVelocityTracker = VelocityTracker.obtain();
483     }
484 
getCurrentVelocity(float lastX, float lastY)485     private float getCurrentVelocity(float lastX, float lastY) {
486         if (mVelocityTracker == null) {
487             return 0;
488         }
489         mVelocityTracker.computeCurrentVelocity(1000);
490         float aX = mVelocityTracker.getXVelocity();
491         float aY = mVelocityTracker.getYVelocity();
492         float bX = lastX - mInitialTouchX;
493         float bY = lastY - mInitialTouchY;
494         float bLen = (float) Math.hypot(bX, bY);
495         // Project the velocity onto the distance vector: a * b / |b|
496         float projectedVelocity = (aX * bX + aY * bY) / bLen;
497         if (mTargetedView == mRightIcon) {
498             projectedVelocity = -projectedVelocity;
499         }
500         return projectedVelocity;
501     }
502 
onConfigurationChanged()503     public void onConfigurationChanged() {
504         initDimens();
505         initIcons();
506     }
507 
onRtlPropertiesChanged()508     public void onRtlPropertiesChanged() {
509         initIcons();
510     }
511 
reset(boolean animate)512     public void reset(boolean animate) {
513         reset(animate, false /* force */);
514     }
515 
reset(boolean animate, boolean force)516     public void reset(boolean animate, boolean force) {
517         cancelAnimation();
518         setTranslation(0.0f, true, animate, force);
519         mMotionCancelled = true;
520         if (mSwipingInProgress) {
521             mCallback.onSwipingAborted();
522             mSwipingInProgress = false;
523         }
524     }
525 
resetImmediately()526     public void resetImmediately() {
527         reset(false /* animate */, true /* force */);
528     }
529 
isSwipingInProgress()530     public boolean isSwipingInProgress() {
531         return mSwipingInProgress;
532     }
533 
launchAffordance(boolean animate, boolean left)534     public void launchAffordance(boolean animate, boolean left) {
535         if (mSwipingInProgress) {
536             // We don't want to mess with the state if the user is actually swiping already.
537             return;
538         }
539         KeyguardAffordanceView targetView = left ? mLeftIcon : mRightIcon;
540         KeyguardAffordanceView otherView = left ? mRightIcon : mLeftIcon;
541         startSwiping(targetView);
542         if (animate) {
543             fling(0, false, !left);
544             updateIcon(otherView, 0.0f, 0, true, false, true, false);
545             updateIcon(mCenterIcon, 0.0f, 0, true, false, true, false);
546         } else {
547             mCallback.onAnimationToSideStarted(!left, mTranslation, 0);
548             mTranslation = left ? mCallback.getMaxTranslationDistance()
549                     : mCallback.getMaxTranslationDistance();
550             updateIcon(mCenterIcon, 0.0f, 0.0f, false, false, true, false);
551             updateIcon(otherView, 0.0f, 0.0f, false, false, true, false);
552             targetView.instantFinishAnimation();
553             mFlingEndListener.onAnimationEnd(null);
554             mAnimationEndRunnable.run();
555         }
556     }
557 
558     public interface Callback {
559 
560         /**
561          * Notifies the callback when an animation to a side page was started.
562          *
563          * @param rightPage Is the page animated to the right page?
564          */
onAnimationToSideStarted(boolean rightPage, float translation, float vel)565         void onAnimationToSideStarted(boolean rightPage, float translation, float vel);
566 
567         /**
568          * Notifies the callback the animation to a side page has ended.
569          */
onAnimationToSideEnded()570         void onAnimationToSideEnded();
571 
getMaxTranslationDistance()572         float getMaxTranslationDistance();
573 
onSwipingStarted(boolean rightIcon)574         void onSwipingStarted(boolean rightIcon);
575 
onSwipingAborted()576         void onSwipingAborted();
577 
onIconClicked(boolean rightIcon)578         void onIconClicked(boolean rightIcon);
579 
getLeftIcon()580         KeyguardAffordanceView getLeftIcon();
581 
getCenterIcon()582         KeyguardAffordanceView getCenterIcon();
583 
getRightIcon()584         KeyguardAffordanceView getRightIcon();
585 
getLeftPreview()586         View getLeftPreview();
587 
getRightPreview()588         View getRightPreview();
589 
590         /**
591          * @return The factor the minimum swipe amount should be multiplied with.
592          */
getAffordanceFalsingFactor()593         float getAffordanceFalsingFactor();
594 
needsAntiFalsing()595         boolean needsAntiFalsing();
596     }
597 }
598