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