• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2012 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 package com.android.keyguard;
17 
18 import android.content.Context;
19 import android.graphics.Rect;
20 import android.os.SystemClock;
21 import android.text.TextUtils;
22 import android.util.AttributeSet;
23 import android.view.MotionEvent;
24 import android.view.View;
25 import android.view.ViewGroup;
26 import android.view.animation.AnimationUtils;
27 import android.view.animation.Interpolator;
28 
29 import com.android.internal.jank.InteractionJankMonitor;
30 import com.android.internal.widget.LockPatternView;
31 import com.android.settingslib.animation.AppearAnimationCreator;
32 import com.android.settingslib.animation.AppearAnimationUtils;
33 import com.android.settingslib.animation.DisappearAnimationUtils;
34 import com.android.systemui.R;
35 
36 public class KeyguardPatternView extends KeyguardInputView
37         implements AppearAnimationCreator<LockPatternView.CellState> {
38 
39     private static final String TAG = "SecurityPatternView";
40     private static final boolean DEBUG = KeyguardConstants.DEBUG;
41 
42 
43     // how long we stay awake after each key beyond MIN_PATTERN_BEFORE_POKE_WAKELOCK
44     private static final int UNLOCK_PATTERN_WAKE_INTERVAL_MS = 7000;
45 
46     // How much we scale up the duration of the disappear animation when the current user is locked
47     public static final float DISAPPEAR_MULTIPLIER_LOCKED = 1.5f;
48 
49     // Extra padding, in pixels, that should eat touch events.
50     private static final int PATTERNS_TOUCH_AREA_EXTENSION = 40;
51 
52     private final AppearAnimationUtils mAppearAnimationUtils;
53     private final DisappearAnimationUtils mDisappearAnimationUtils;
54     private final DisappearAnimationUtils mDisappearAnimationUtilsLocked;
55     private final int[] mTmpPosition = new int[2];
56     private final Rect mTempRect = new Rect();
57     private final Rect mLockPatternScreenBounds = new Rect();
58 
59     private LockPatternView mLockPatternView;
60 
61     /**
62      * Keeps track of the last time we poked the wake lock during dispatching of the touch event.
63      * Initialized to something guaranteed to make us poke the wakelock when the user starts
64      * drawing the pattern.
65      * @see #dispatchTouchEvent(android.view.MotionEvent)
66      */
67     private long mLastPokeTime = -UNLOCK_PATTERN_WAKE_INTERVAL_MS;
68 
69     KeyguardMessageArea mSecurityMessageDisplay;
70     private View mEcaView;
71     private ViewGroup mContainer;
72 
KeyguardPatternView(Context context)73     public KeyguardPatternView(Context context) {
74         this(context, null);
75     }
76 
KeyguardPatternView(Context context, AttributeSet attrs)77     public KeyguardPatternView(Context context, AttributeSet attrs) {
78         super(context, attrs);
79         mAppearAnimationUtils = new AppearAnimationUtils(context,
80                 AppearAnimationUtils.DEFAULT_APPEAR_DURATION, 1.5f /* translationScale */,
81                 2.0f /* delayScale */, AnimationUtils.loadInterpolator(
82                         mContext, android.R.interpolator.linear_out_slow_in));
83         mDisappearAnimationUtils = new DisappearAnimationUtils(context,
84                 125, 1.2f /* translationScale */,
85                 0.6f /* delayScale */, AnimationUtils.loadInterpolator(
86                         mContext, android.R.interpolator.fast_out_linear_in));
87         mDisappearAnimationUtilsLocked = new DisappearAnimationUtils(context,
88                 (long) (125 * DISAPPEAR_MULTIPLIER_LOCKED), 1.2f /* translationScale */,
89                 0.6f /* delayScale */, AnimationUtils.loadInterpolator(
90                 mContext, android.R.interpolator.fast_out_linear_in));
91     }
92 
93     @Override
onFinishInflate()94     protected void onFinishInflate() {
95         super.onFinishInflate();
96 
97         mLockPatternView = findViewById(R.id.lockPatternView);
98 
99         mEcaView = findViewById(R.id.keyguard_selector_fade_container);
100         mContainer = findViewById(R.id.pattern_container);
101     }
102 
103     @Override
onAttachedToWindow()104     protected void onAttachedToWindow() {
105         super.onAttachedToWindow();
106         mSecurityMessageDisplay = KeyguardMessageArea.findSecurityMessageDisplay(this);
107     }
108 
109     @Override
onTouchEvent(MotionEvent ev)110     public boolean onTouchEvent(MotionEvent ev) {
111         boolean result = super.onTouchEvent(ev);
112         // as long as the user is entering a pattern (i.e sending a touch event that was handled
113         // by this screen), keep poking the wake lock so that the screen will stay on.
114         final long elapsed = SystemClock.elapsedRealtime() - mLastPokeTime;
115         if (result && (elapsed > (UNLOCK_PATTERN_WAKE_INTERVAL_MS - 100))) {
116             mLastPokeTime = SystemClock.elapsedRealtime();
117         }
118         mTempRect.set(0, 0, 0, 0);
119         offsetRectIntoDescendantCoords(mLockPatternView, mTempRect);
120         ev.offsetLocation(mTempRect.left, mTempRect.top);
121         result = mLockPatternView.dispatchTouchEvent(ev) || result;
122         ev.offsetLocation(-mTempRect.left, -mTempRect.top);
123         return result;
124     }
125 
126     @Override
onLayout(boolean changed, int l, int t, int r, int b)127     protected void onLayout(boolean changed, int l, int t, int r, int b) {
128         super.onLayout(changed, l, t, r, b);
129         mLockPatternView.getLocationOnScreen(mTmpPosition);
130         mLockPatternScreenBounds.set(mTmpPosition[0] - PATTERNS_TOUCH_AREA_EXTENSION,
131                 mTmpPosition[1] - PATTERNS_TOUCH_AREA_EXTENSION,
132                 mTmpPosition[0] + mLockPatternView.getWidth() + PATTERNS_TOUCH_AREA_EXTENSION,
133                 mTmpPosition[1] + mLockPatternView.getHeight() + PATTERNS_TOUCH_AREA_EXTENSION);
134     }
135 
136     @Override
disallowInterceptTouch(MotionEvent event)137     boolean disallowInterceptTouch(MotionEvent event) {
138         return !mLockPatternView.isEmpty()
139                 || mLockPatternScreenBounds.contains((int) event.getRawX(), (int) event.getRawY());
140     }
141 
startAppearAnimation()142     public void startAppearAnimation() {
143         enableClipping(false);
144         setAlpha(1f);
145         setTranslationY(mAppearAnimationUtils.getStartTranslation());
146         AppearAnimationUtils.startTranslationYAnimation(this, 0 /* delay */, 500 /* duration */,
147                 0, mAppearAnimationUtils.getInterpolator(),
148                 getAnimationListener(InteractionJankMonitor.CUJ_LOCKSCREEN_PATTERN_APPEAR));
149         mAppearAnimationUtils.startAnimation2d(
150                 mLockPatternView.getCellStates(),
151                 () -> enableClipping(true),
152                 this);
153         if (!TextUtils.isEmpty(mSecurityMessageDisplay.getText())) {
154             mAppearAnimationUtils.createAnimation(mSecurityMessageDisplay, 0,
155                     AppearAnimationUtils.DEFAULT_APPEAR_DURATION,
156                     mAppearAnimationUtils.getStartTranslation(),
157                     true /* appearing */,
158                     mAppearAnimationUtils.getInterpolator(),
159                     null /* finishRunnable */);
160         }
161     }
162 
startDisappearAnimation(boolean needsSlowUnlockTransition, final Runnable finishRunnable)163     public boolean startDisappearAnimation(boolean needsSlowUnlockTransition,
164             final Runnable finishRunnable) {
165         float durationMultiplier = needsSlowUnlockTransition ? DISAPPEAR_MULTIPLIER_LOCKED : 1f;
166         mLockPatternView.clearPattern();
167         enableClipping(false);
168         setTranslationY(0);
169         AppearAnimationUtils.startTranslationYAnimation(this, 0 /* delay */,
170                 (long) (300 * durationMultiplier),
171                 -mDisappearAnimationUtils.getStartTranslation(),
172                 mDisappearAnimationUtils.getInterpolator(),
173                 getAnimationListener(InteractionJankMonitor.CUJ_LOCKSCREEN_PATTERN_DISAPPEAR));
174 
175         DisappearAnimationUtils disappearAnimationUtils = needsSlowUnlockTransition
176                         ? mDisappearAnimationUtilsLocked : mDisappearAnimationUtils;
177         disappearAnimationUtils.startAnimation2d(mLockPatternView.getCellStates(),
178                 () -> {
179                     enableClipping(true);
180                     if (finishRunnable != null) {
181                         finishRunnable.run();
182                     }
183                 }, KeyguardPatternView.this);
184         if (!TextUtils.isEmpty(mSecurityMessageDisplay.getText())) {
185             mDisappearAnimationUtils.createAnimation(mSecurityMessageDisplay, 0,
186                     (long) (200 * durationMultiplier),
187                     - mDisappearAnimationUtils.getStartTranslation() * 3,
188                     false /* appearing */,
189                     mDisappearAnimationUtils.getInterpolator(),
190                     null /* finishRunnable */);
191         }
192         return true;
193     }
194 
enableClipping(boolean enable)195     private void enableClipping(boolean enable) {
196         setClipChildren(enable);
197         mContainer.setClipToPadding(enable);
198         mContainer.setClipChildren(enable);
199     }
200 
201     @Override
createAnimation(final LockPatternView.CellState animatedCell, long delay, long duration, float translationY, final boolean appearing, Interpolator interpolator, final Runnable finishListener)202     public void createAnimation(final LockPatternView.CellState animatedCell, long delay,
203             long duration, float translationY, final boolean appearing,
204             Interpolator interpolator,
205             final Runnable finishListener) {
206         mLockPatternView.startCellStateAnimation(animatedCell,
207                 1f, appearing ? 1f : 0f, /* alpha */
208                 appearing ? translationY : 0f, appearing ? 0f : translationY, /* translation */
209                 appearing ? 0f : 1f, 1f /* scale */,
210                 delay, duration, interpolator, finishListener);
211         if (finishListener != null) {
212             // Also animate the Emergency call
213             mAppearAnimationUtils.createAnimation(mEcaView, delay, duration, translationY,
214                     appearing, interpolator, null);
215         }
216     }
217 
218     @Override
hasOverlappingRendering()219     public boolean hasOverlappingRendering() {
220         return false;
221     }
222 
223     @Override
getTitle()224     public CharSequence getTitle() {
225         return getResources().getString(
226                 com.android.internal.R.string.keyguard_accessibility_pattern_unlock);
227     }
228 }
229