1 /*
2  * Copyright (C) 2020 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.keyguard;
18 
19 import android.view.KeyEvent;
20 import android.view.MotionEvent;
21 import android.view.View;
22 import android.view.View.OnKeyListener;
23 import android.view.View.OnTouchListener;
24 
25 import com.android.internal.util.LatencyTracker;
26 import com.android.internal.widget.LockPatternUtils;
27 import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
28 import com.android.systemui.R;
29 import com.android.systemui.classifier.FalsingCollector;
30 import com.android.systemui.flags.FeatureFlags;
31 
32 public abstract class KeyguardPinBasedInputViewController<T extends KeyguardPinBasedInputView>
33         extends KeyguardAbsKeyInputViewController<T> {
34 
35     private final LiftToActivateListener mLiftToActivateListener;
36     private final FalsingCollector mFalsingCollector;
37     protected PasswordTextView mPasswordEntry;
38 
39     private final OnKeyListener mOnKeyListener = (v, keyCode, event) -> {
40         if (event.getAction() == KeyEvent.ACTION_DOWN) {
41             return mView.onKeyDown(keyCode, event);
42         }
43         return false;
44     };
45 
46     private final OnTouchListener mActionButtonTouchListener = (v, event) -> {
47         if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
48             mView.doHapticKeyClick();
49         }
50         return false;
51     };
52 
KeyguardPinBasedInputViewController(T view, KeyguardUpdateMonitor keyguardUpdateMonitor, SecurityMode securityMode, LockPatternUtils lockPatternUtils, KeyguardSecurityCallback keyguardSecurityCallback, KeyguardMessageAreaController.Factory messageAreaControllerFactory, LatencyTracker latencyTracker, LiftToActivateListener liftToActivateListener, EmergencyButtonController emergencyButtonController, FalsingCollector falsingCollector, FeatureFlags featureFlags)53     protected KeyguardPinBasedInputViewController(T view,
54             KeyguardUpdateMonitor keyguardUpdateMonitor,
55             SecurityMode securityMode,
56             LockPatternUtils lockPatternUtils,
57             KeyguardSecurityCallback keyguardSecurityCallback,
58             KeyguardMessageAreaController.Factory messageAreaControllerFactory,
59             LatencyTracker latencyTracker,
60             LiftToActivateListener liftToActivateListener,
61             EmergencyButtonController emergencyButtonController,
62             FalsingCollector falsingCollector,
63             FeatureFlags featureFlags) {
64         super(view, keyguardUpdateMonitor, securityMode, lockPatternUtils, keyguardSecurityCallback,
65                 messageAreaControllerFactory, latencyTracker, falsingCollector,
66                 emergencyButtonController, featureFlags);
67         mLiftToActivateListener = liftToActivateListener;
68         mFalsingCollector = falsingCollector;
69         mPasswordEntry = mView.findViewById(mView.getPasswordTextViewId());
70     }
71 
72     @Override
onViewAttached()73     protected void onViewAttached() {
74         super.onViewAttached();
75 
76         boolean showAnimations = !mLockPatternUtils
77                 .isPinEnhancedPrivacyEnabled(KeyguardUpdateMonitor.getCurrentUser());
78         mPasswordEntry.setShowPassword(showAnimations);
79         for (NumPadKey button : mView.getButtons()) {
80             button.setOnTouchListener((v, event) -> {
81                 if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
82                     mFalsingCollector.avoidGesture();
83                 }
84                 return false;
85             });
86             button.setAnimationEnabled(showAnimations);
87         }
88         mPasswordEntry.setOnKeyListener(mOnKeyListener);
89         mPasswordEntry.setUserActivityListener(this::onUserInput);
90 
91         View deleteButton = mView.findViewById(R.id.delete_button);
92         deleteButton.setOnTouchListener(mActionButtonTouchListener);
93         deleteButton.setOnClickListener(v -> {
94             // check for time-based lockouts
95             if (mPasswordEntry.isEnabled()) {
96                 mPasswordEntry.deleteLastChar();
97             }
98         });
99         deleteButton.setOnLongClickListener(v -> {
100             // check for time-based lockouts
101             if (mPasswordEntry.isEnabled()) {
102                 mView.resetPasswordText(true /* animate */, true /* announce */);
103             }
104             mView.doHapticKeyClick();
105             return true;
106         });
107 
108         View okButton = mView.findViewById(R.id.key_enter);
109         if (okButton != null) {
110             okButton.setOnTouchListener(mActionButtonTouchListener);
111             okButton.setOnClickListener(v -> {
112                 if (mPasswordEntry.isEnabled()) {
113                     verifyPasswordAndUnlock();
114                 }
115             });
116             okButton.setOnHoverListener(mLiftToActivateListener);
117         }
118     }
119 
120     @Override
onViewDetached()121     protected void onViewDetached() {
122         super.onViewDetached();
123 
124         for (NumPadKey button : mView.getButtons()) {
125             button.setOnTouchListener(null);
126         }
127     }
128 
129     @Override
onResume(int reason)130     public void onResume(int reason) {
131         super.onResume(reason);
132         // It's possible to reach a state here where mPasswordEntry believes it is focused
133         // but it is not actually focused. This state will prevent the view from gaining focus,
134         // as requestFocus will no-op since the focus flag is already set. By clearing focus first,
135         // it's guaranteed that the view has focus.
136         mPasswordEntry.clearFocus();
137         mPasswordEntry.requestFocus();
138     }
139 
140     @Override
resetState()141     void resetState() {
142         mMessageAreaController.setMessage(getInitialMessageResId());
143         mView.setPasswordEntryEnabled(true);
144     }
145 
146     @Override
startErrorAnimation()147     protected void startErrorAnimation() {
148         super.startErrorAnimation();
149         mView.startErrorAnimation();
150     }
151 
152     @Override
getInitialMessageResId()153     protected int getInitialMessageResId() {
154         return R.string.keyguard_enter_your_pin;
155     }
156 }
157