From bdc667325a33a41cc51fe2f09b80f7269f17ac64 Mon Sep 17 00:00:00 2001 From: Dave Mankoff Date: Tue, 22 Sep 2020 00:24:04 +0000 Subject: [PATCH] Revert "7/N controllers for remaining Keyguard Password Views." Revert submission 12585643-b166448040-keyguard-message-area Reason for revert: http://b/169081305 & http://b/169020145 Reverted Changes: I6fa05012c:4/N Setup Controller fo KeyguardSecurityContainer.... Iecf265744:5/N Add KeyguardSecurityViewFlipperController. I90ab99b2f:6/N Add Controller for KeyguardPatternView I4b74eddd1:7/N controllers for remaining Keyguard Password Vi... I805286374:8/N Remove View Injection from KeyguardMessageArea... I362755980:9/N Clean Up Keyguard Class Structure Change-Id: I55c250121c2e234a41b30f2d647c44f6cb631028 Fixes: 169081305 Fixes: 169020145 --- .../keyguard/KeyguardAbsKeyInputView.java | 229 +++++++++- .../KeyguardAbsKeyInputViewController.java | 257 ----------- .../keyguard/KeyguardHostViewController.java | 10 +- .../keyguard/KeyguardInputViewController.java | 54 +-- .../com/android/keyguard/KeyguardPINView.java | 39 +- .../keyguard/KeyguardPasswordView.java | 235 +++++++++- .../KeyguardPasswordViewController.java | 270 ------------ .../KeyguardPatternViewController.java | 8 +- .../keyguard/KeyguardPinBasedInputView.java | 140 +++++- .../KeyguardPinBasedInputViewController.java | 113 ----- .../keyguard/KeyguardPinViewController.java | 50 --- .../KeyguardSecurityContainerController.java | 1 - .../android/keyguard/KeyguardSimPinView.java | 324 +++++++++++++- .../KeyguardSimPinViewController.java | 350 --------------- .../android/keyguard/KeyguardSimPukView.java | 376 +++++++++++++++- .../KeyguardSimPukViewController.java | 404 ------------------ .../keyguard/LiftToActivateListener.java | 9 +- .../src/com/android/keyguard/NumPadKey.java | 4 +- .../dagger/FrameworkServicesModule.java | 7 - .../android/systemui/util/ViewController.java | 15 +- ...KeyguardAbsKeyInputViewControllerTest.java | 120 ------ ...yguardPinBasedInputViewControllerTest.java | 106 ----- .../KeyguardPinBasedInputViewTest.java | 79 ++++ 23 files changed, 1363 insertions(+), 1837 deletions(-) delete mode 100644 packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java delete mode 100644 packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java delete mode 100644 packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputViewController.java delete mode 100644 packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java delete mode 100644 packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java delete mode 100644 packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java delete mode 100644 packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java delete mode 100644 packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java create mode 100644 packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewTest.java diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java index 1296990d49314..889309dd72e72 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java @@ -16,30 +16,45 @@ package com.android.keyguard; +import static com.android.internal.util.LatencyTracker.ACTION_CHECK_CREDENTIAL; +import static com.android.internal.util.LatencyTracker.ACTION_CHECK_CREDENTIAL_UNLOCKED; + import android.content.Context; import android.content.res.ColorStateList; +import android.os.AsyncTask; +import android.os.CountDownTimer; +import android.os.SystemClock; import android.util.AttributeSet; import android.view.HapticFeedbackConstants; import android.view.KeyEvent; import android.view.View; +import com.android.internal.util.LatencyTracker; +import com.android.internal.widget.LockPatternChecker; import com.android.internal.widget.LockPatternUtils; import com.android.internal.widget.LockscreenCredential; +import com.android.systemui.Dependency; import com.android.systemui.R; /** * Base class for PIN and password unlock screens. */ -public abstract class KeyguardAbsKeyInputView extends KeyguardInputView { +public abstract class KeyguardAbsKeyInputView extends KeyguardInputView + implements EmergencyButton.EmergencyButtonCallback { protected KeyguardSecurityCallback mCallback; + protected LockPatternUtils mLockPatternUtils; + protected AsyncTask mPendingLockCheck; protected SecurityMessageDisplay mSecurityMessageDisplay; protected View mEcaView; protected boolean mEnableHaptics; + private boolean mDismissing; + protected boolean mResumed; + private CountDownTimer mCountdownTimer = null; + private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; // To avoid accidental lockout due to events while the device in in the pocket, ignore // any passwords with length less than or equal to this length. protected static final int MINIMUM_PASSWORD_LENGTH_BEFORE_REPORT = 3; - private KeyDownListener mKeyDownListener; public KeyguardAbsKeyInputView(Context context) { this(context, null); @@ -47,10 +62,7 @@ public abstract class KeyguardAbsKeyInputView extends KeyguardInputView { public KeyguardAbsKeyInputView(Context context, AttributeSet attrs) { super(context, attrs); - } - - void setEnableHaptics(boolean enableHaptics) { - mEnableHaptics = enableHaptics; + mKeyguardUpdateMonitor = Dependency.get(KeyguardUpdateMonitor.class); } @Override @@ -60,10 +72,28 @@ public abstract class KeyguardAbsKeyInputView extends KeyguardInputView { @Override public void setLockPatternUtils(LockPatternUtils utils) { + mLockPatternUtils = utils; + mEnableHaptics = mLockPatternUtils.isTactileFeedbackEnabled(); } @Override public void reset() { + // start fresh + mDismissing = false; + resetPasswordText(false /* animate */, false /* announce */); + // if the user is currently locked out, enforce it. + long deadline = mLockPatternUtils.getLockoutAttemptDeadline( + KeyguardUpdateMonitor.getCurrentUser()); + if (shouldLockout(deadline)) { + handleAttemptLockout(deadline); + } else { + resetState(); + } + } + + // Allow subclasses to override this behavior + protected boolean shouldLockout(long deadline) { + return deadline != 0; } protected abstract int getPasswordTextViewId(); @@ -71,7 +101,24 @@ public abstract class KeyguardAbsKeyInputView extends KeyguardInputView { @Override protected void onFinishInflate() { + mLockPatternUtils = new LockPatternUtils(mContext); mEcaView = findViewById(R.id.keyguard_selector_fade_container); + + EmergencyButton button = findViewById(R.id.emergency_call_button); + if (button != null) { + button.setCallback(this); + } + } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + mSecurityMessageDisplay = KeyguardMessageArea.findSecurityMessageDisplay(this); + } + + @Override + public void onEmergencyButtonClickedWhenInCall() { + mCallback.reset(); } /* @@ -83,14 +130,147 @@ public abstract class KeyguardAbsKeyInputView extends KeyguardInputView { return R.string.kg_wrong_password; } + protected void verifyPasswordAndUnlock() { + if (mDismissing) return; // already verified but haven't been dismissed; don't do it again. + + final LockscreenCredential password = getEnteredCredential(); + setPasswordEntryInputEnabled(false); + if (mPendingLockCheck != null) { + mPendingLockCheck.cancel(false); + } + + final int userId = KeyguardUpdateMonitor.getCurrentUser(); + if (password.size() <= MINIMUM_PASSWORD_LENGTH_BEFORE_REPORT) { + // to avoid accidental lockout, only count attempts that are long enough to be a + // real password. This may require some tweaking. + setPasswordEntryInputEnabled(true); + onPasswordChecked(userId, false /* matched */, 0, false /* not valid - too short */); + password.zeroize(); + return; + } + + if (LatencyTracker.isEnabled(mContext)) { + LatencyTracker.getInstance(mContext).onActionStart(ACTION_CHECK_CREDENTIAL); + LatencyTracker.getInstance(mContext).onActionStart(ACTION_CHECK_CREDENTIAL_UNLOCKED); + } + + mKeyguardUpdateMonitor.setCredentialAttempted(); + mPendingLockCheck = LockPatternChecker.checkCredential( + mLockPatternUtils, + password, + userId, + new LockPatternChecker.OnCheckCallback() { + + @Override + public void onEarlyMatched() { + if (LatencyTracker.isEnabled(mContext)) { + LatencyTracker.getInstance(mContext).onActionEnd( + ACTION_CHECK_CREDENTIAL); + } + onPasswordChecked(userId, true /* matched */, 0 /* timeoutMs */, + true /* isValidPassword */); + password.zeroize(); + } + + @Override + public void onChecked(boolean matched, int timeoutMs) { + if (LatencyTracker.isEnabled(mContext)) { + LatencyTracker.getInstance(mContext).onActionEnd( + ACTION_CHECK_CREDENTIAL_UNLOCKED); + } + setPasswordEntryInputEnabled(true); + mPendingLockCheck = null; + if (!matched) { + onPasswordChecked(userId, false /* matched */, timeoutMs, + true /* isValidPassword */); + } + password.zeroize(); + } + + @Override + public void onCancelled() { + // We already got dismissed with the early matched callback, so we cancelled + // the check. However, we still need to note down the latency. + if (LatencyTracker.isEnabled(mContext)) { + LatencyTracker.getInstance(mContext).onActionEnd( + ACTION_CHECK_CREDENTIAL_UNLOCKED); + } + password.zeroize(); + } + }); + } + + private void onPasswordChecked(int userId, boolean matched, int timeoutMs, + boolean isValidPassword) { + boolean dismissKeyguard = KeyguardUpdateMonitor.getCurrentUser() == userId; + if (matched) { + mCallback.reportUnlockAttempt(userId, true, 0); + if (dismissKeyguard) { + mDismissing = true; + mCallback.dismiss(true, userId); + } + } else { + if (isValidPassword) { + mCallback.reportUnlockAttempt(userId, false, timeoutMs); + if (timeoutMs > 0) { + long deadline = mLockPatternUtils.setLockoutAttemptDeadline( + userId, timeoutMs); + handleAttemptLockout(deadline); + } + } + if (timeoutMs == 0) { + mSecurityMessageDisplay.setMessage(getWrongPasswordStringId()); + } + } + resetPasswordText(true /* animate */, !matched /* announce deletion if no match */); + } + protected abstract void resetPasswordText(boolean animate, boolean announce); protected abstract LockscreenCredential getEnteredCredential(); protected abstract void setPasswordEntryEnabled(boolean enabled); protected abstract void setPasswordEntryInputEnabled(boolean enabled); + // Prevent user from using the PIN/Password entry until scheduled deadline. + protected void handleAttemptLockout(long elapsedRealtimeDeadline) { + setPasswordEntryEnabled(false); + long elapsedRealtime = SystemClock.elapsedRealtime(); + long secondsInFuture = (long) Math.ceil( + (elapsedRealtimeDeadline - elapsedRealtime) / 1000.0); + mCountdownTimer = new CountDownTimer(secondsInFuture * 1000, 1000) { + + @Override + public void onTick(long millisUntilFinished) { + int secondsRemaining = (int) Math.round(millisUntilFinished / 1000.0); + mSecurityMessageDisplay.setMessage(mContext.getResources().getQuantityString( + R.plurals.kg_too_many_failed_attempts_countdown, + secondsRemaining, secondsRemaining)); + } + + @Override + public void onFinish() { + mSecurityMessageDisplay.setMessage(""); + resetState(); + } + }.start(); + } + + protected void onUserInput() { + if (mCallback != null) { + mCallback.userActivity(); + mCallback.onUserInput(); + } + mSecurityMessageDisplay.setMessage(""); + } + @Override public boolean onKeyDown(int keyCode, KeyEvent event) { - return mKeyDownListener != null && mKeyDownListener.onKeyDown(keyCode, event); + // Fingerprint sensor sends a KeyEvent.KEYCODE_UNKNOWN. + // We don't want to consider it valid user input because the UI + // will already respond to the event. + if (keyCode != KeyEvent.KEYCODE_UNKNOWN) { + onUserInput(); + } + return false; } @Override @@ -98,6 +278,26 @@ public abstract class KeyguardAbsKeyInputView extends KeyguardInputView { return false; } + @Override + public void onPause() { + mResumed = false; + + if (mCountdownTimer != null) { + mCountdownTimer.cancel(); + mCountdownTimer = null; + } + if (mPendingLockCheck != null) { + mPendingLockCheck.cancel(false); + mPendingLockCheck = null; + } + reset(); + } + + @Override + public void onResume(int reason) { + mResumed = true; + } + @Override public KeyguardSecurityCallback getCallback() { return mCallback; @@ -105,7 +305,12 @@ public abstract class KeyguardAbsKeyInputView extends KeyguardInputView { @Override public void showPromptReason(int reason) { - + if (reason != PROMPT_REASON_NONE) { + int promtReasonStringRes = getPromptReasonStringRes(reason); + if (promtReasonStringRes != 0) { + mSecurityMessageDisplay.setMessage(promtReasonStringRes); + } + } } @Override @@ -131,13 +336,5 @@ public abstract class KeyguardAbsKeyInputView extends KeyguardInputView { public boolean startDisappearAnimation(Runnable finishRunnable) { return false; } - - public void setKeyDownListener(KeyDownListener keyDownListener) { - mKeyDownListener = keyDownListener; - } - - public interface KeyDownListener { - boolean onKeyDown(int keyCode, KeyEvent keyEvent); - } } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java deleted file mode 100644 index 3108ae3d2354b..0000000000000 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java +++ /dev/null @@ -1,257 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.keyguard; - -import static com.android.internal.util.LatencyTracker.ACTION_CHECK_CREDENTIAL; -import static com.android.internal.util.LatencyTracker.ACTION_CHECK_CREDENTIAL_UNLOCKED; -import static com.android.keyguard.KeyguardAbsKeyInputView.MINIMUM_PASSWORD_LENGTH_BEFORE_REPORT; - -import android.os.AsyncTask; -import android.os.CountDownTimer; -import android.os.SystemClock; -import android.view.KeyEvent; - -import com.android.internal.util.LatencyTracker; -import com.android.internal.widget.LockPatternChecker; -import com.android.internal.widget.LockPatternUtils; -import com.android.internal.widget.LockscreenCredential; -import com.android.keyguard.EmergencyButton.EmergencyButtonCallback; -import com.android.keyguard.KeyguardAbsKeyInputView.KeyDownListener; -import com.android.keyguard.KeyguardSecurityModel.SecurityMode; -import com.android.systemui.R; - -public abstract class KeyguardAbsKeyInputViewController - extends KeyguardInputViewController { - private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; - private final LockPatternUtils mLockPatternUtils; - protected final KeyguardSecurityCallback mKeyguardSecurityCallback; - private final LatencyTracker mLatencyTracker; - private CountDownTimer mCountdownTimer; - protected KeyguardMessageAreaController mMessageAreaController; - private boolean mDismissing; - protected AsyncTask mPendingLockCheck; - protected boolean mResumed; - - private final KeyDownListener mKeyDownListener = (keyCode, keyEvent) -> { - // Fingerprint sensor sends a KeyEvent.KEYCODE_UNKNOWN. - // We don't want to consider it valid user input because the UI - // will already respond to the event. - if (keyCode != KeyEvent.KEYCODE_UNKNOWN) { - onUserInput(); - } - return false; - }; - - private final EmergencyButtonCallback mEmergencyButtonCallback = new EmergencyButtonCallback() { - @Override - public void onEmergencyButtonClickedWhenInCall() { - mKeyguardSecurityCallback.reset(); - } - }; - - protected KeyguardAbsKeyInputViewController(T view, - KeyguardUpdateMonitor keyguardUpdateMonitor, - SecurityMode securityMode, - LockPatternUtils lockPatternUtils, - KeyguardSecurityCallback keyguardSecurityCallback, - KeyguardMessageAreaController.Factory messageAreaControllerFactory, - LatencyTracker latencyTracker) { - super(view, securityMode, keyguardSecurityCallback); - mKeyguardUpdateMonitor = keyguardUpdateMonitor; - mLockPatternUtils = lockPatternUtils; - mKeyguardSecurityCallback = keyguardSecurityCallback; - mLatencyTracker = latencyTracker; - KeyguardMessageArea kma = KeyguardMessageArea.findSecurityMessageDisplay(mView); - mMessageAreaController = messageAreaControllerFactory.create(kma); - } - - abstract void resetState(); - - @Override - protected void onViewAttached() { - mView.setKeyDownListener(mKeyDownListener); - mView.setEnableHaptics(mLockPatternUtils.isTactileFeedbackEnabled()); - EmergencyButton button = mView.findViewById(R.id.emergency_call_button); - if (button != null) { - button.setCallback(mEmergencyButtonCallback); - } - } - - @Override - public void reset() { - // start fresh - mDismissing = false; - mView.resetPasswordText(false /* animate */, false /* announce */); - // if the user is currently locked out, enforce it. - long deadline = mLockPatternUtils.getLockoutAttemptDeadline( - KeyguardUpdateMonitor.getCurrentUser()); - if (shouldLockout(deadline)) { - handleAttemptLockout(deadline); - } else { - mView.resetState(); - } - } - - // Allow subclasses to override this behavior - protected boolean shouldLockout(long deadline) { - return deadline != 0; - } - - // Prevent user from using the PIN/Password entry until scheduled deadline. - protected void handleAttemptLockout(long elapsedRealtimeDeadline) { - mView.setPasswordEntryEnabled(false); - long elapsedRealtime = SystemClock.elapsedRealtime(); - long secondsInFuture = (long) Math.ceil( - (elapsedRealtimeDeadline - elapsedRealtime) / 1000.0); - mCountdownTimer = new CountDownTimer(secondsInFuture * 1000, 1000) { - - @Override - public void onTick(long millisUntilFinished) { - int secondsRemaining = (int) Math.round(millisUntilFinished / 1000.0); - mMessageAreaController.setMessage(mView.getResources().getQuantityString( - R.plurals.kg_too_many_failed_attempts_countdown, - secondsRemaining, secondsRemaining)); - } - - @Override - public void onFinish() { - mMessageAreaController.setMessage(""); - resetState(); - } - }.start(); - } - - void onPasswordChecked(int userId, boolean matched, int timeoutMs, boolean isValidPassword) { - boolean dismissKeyguard = KeyguardUpdateMonitor.getCurrentUser() == userId; - if (matched) { - mKeyguardSecurityCallback.reportUnlockAttempt(userId, true, 0); - if (dismissKeyguard) { - mDismissing = true; - mKeyguardSecurityCallback.dismiss(true, userId); - } - } else { - if (isValidPassword) { - mKeyguardSecurityCallback.reportUnlockAttempt(userId, false, timeoutMs); - if (timeoutMs > 0) { - long deadline = mLockPatternUtils.setLockoutAttemptDeadline( - userId, timeoutMs); - handleAttemptLockout(deadline); - } - } - if (timeoutMs == 0) { - mMessageAreaController.setMessage(mView.getWrongPasswordStringId()); - } - } - mView.resetPasswordText(true /* animate */, !matched /* announce deletion if no match */); - } - - protected void verifyPasswordAndUnlock() { - if (mDismissing) return; // already verified but haven't been dismissed; don't do it again. - - final LockscreenCredential password = mView.getEnteredCredential(); - mView.setPasswordEntryInputEnabled(false); - if (mPendingLockCheck != null) { - mPendingLockCheck.cancel(false); - } - - final int userId = KeyguardUpdateMonitor.getCurrentUser(); - if (password.size() <= MINIMUM_PASSWORD_LENGTH_BEFORE_REPORT) { - // to avoid accidental lockout, only count attempts that are long enough to be a - // real password. This may require some tweaking. - mView.setPasswordEntryInputEnabled(true); - onPasswordChecked(userId, false /* matched */, 0, false /* not valid - too short */); - password.zeroize(); - return; - } - - mLatencyTracker.onActionStart(ACTION_CHECK_CREDENTIAL); - mLatencyTracker.onActionStart(ACTION_CHECK_CREDENTIAL_UNLOCKED); - - mKeyguardUpdateMonitor.setCredentialAttempted(); - mPendingLockCheck = LockPatternChecker.checkCredential( - mLockPatternUtils, - password, - userId, - new LockPatternChecker.OnCheckCallback() { - - @Override - public void onEarlyMatched() { - mLatencyTracker.onActionEnd(ACTION_CHECK_CREDENTIAL); - - onPasswordChecked(userId, true /* matched */, 0 /* timeoutMs */, - true /* isValidPassword */); - password.zeroize(); - } - - @Override - public void onChecked(boolean matched, int timeoutMs) { - mLatencyTracker.onActionEnd(ACTION_CHECK_CREDENTIAL_UNLOCKED); - mView.setPasswordEntryInputEnabled(true); - mPendingLockCheck = null; - if (!matched) { - onPasswordChecked(userId, false /* matched */, timeoutMs, - true /* isValidPassword */); - } - password.zeroize(); - } - - @Override - public void onCancelled() { - // We already got dismissed with the early matched callback, so we cancelled - // the check. However, we still need to note down the latency. - mLatencyTracker.onActionEnd(ACTION_CHECK_CREDENTIAL_UNLOCKED); - password.zeroize(); - } - }); - } - - @Override - public void showPromptReason(int reason) { - if (reason != PROMPT_REASON_NONE) { - int promtReasonStringRes = mView.getPromptReasonStringRes(reason); - if (promtReasonStringRes != 0) { - mMessageAreaController.setMessage(promtReasonStringRes); - } - } - } - - protected void onUserInput() { - mKeyguardSecurityCallback.userActivity(); - mKeyguardSecurityCallback.onUserInput(); - mMessageAreaController.setMessage(""); - } - - @Override - public void onResume(int reason) { - mResumed = true; - } - - @Override - public void onPause() { - mResumed = false; - - if (mCountdownTimer != null) { - mCountdownTimer.cancel(); - mCountdownTimer = null; - } - if (mPendingLockCheck != null) { - mPendingLockCheck.cancel(false); - mPendingLockCheck = null; - } - reset(); - } -} diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java index 9ffa658da0e89..ad6ca8582bf08 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java @@ -178,18 +178,18 @@ public class KeyguardHostViewController extends ViewController /** Initialize the Controller. */ public void init() { super.init(); + mView.setViewMediatorCallback(mViewMediatorCallback); + // Update ViewMediator with the current input method requirements + mViewMediatorCallback.setNeedsInput(mKeyguardSecurityContainerController.needsInput()); mKeyguardSecurityContainerController.init(); + mKeyguardSecurityContainerController.setSecurityCallback(mSecurityCallback); + mKeyguardSecurityContainerController.showPrimarySecurityScreen(false); } @Override protected void onViewAttached() { - mView.setViewMediatorCallback(mViewMediatorCallback); - // Update ViewMediator with the current input method requirements - mViewMediatorCallback.setNeedsInput(mKeyguardSecurityContainerController.needsInput()); mKeyguardUpdateMonitor.registerCallback(mUpdateCallback); mView.setOnKeyListener(mOnKeyListener); - mKeyguardSecurityContainerController.setSecurityCallback(mSecurityCallback); - mKeyguardSecurityContainerController.showPrimarySecurityScreen(false); } @Override diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java index c73149c7a8b97..552e62052e6f0 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java @@ -17,31 +17,32 @@ package com.android.keyguard; import android.content.res.ColorStateList; -import android.content.res.Resources; -import android.telephony.TelephonyManager; import android.view.MotionEvent; -import android.view.inputmethod.InputMethodManager; import com.android.internal.util.LatencyTracker; import com.android.internal.widget.LockPatternUtils; import com.android.keyguard.KeyguardSecurityModel.SecurityMode; -import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.util.ViewController; -import com.android.systemui.util.concurrency.DelayableExecutor; import javax.inject.Inject; /** Controller for a {@link KeyguardSecurityView}. */ -public abstract class KeyguardInputViewController - extends ViewController implements KeyguardSecurityView { +public class KeyguardInputViewController extends ViewController + implements KeyguardSecurityView { private final SecurityMode mSecurityMode; + private final LockPatternUtils mLockPatternUtils; + private final KeyguardSecurityCallback mKeyguardSecurityCallback; + private KeyguardMessageAreaController mMessageAreaController; protected KeyguardInputViewController(T view, SecurityMode securityMode, + LockPatternUtils lockPatternUtils, KeyguardSecurityCallback keyguardSecurityCallback) { super(view); mSecurityMode = securityMode; + mLockPatternUtils = lockPatternUtils; + mKeyguardSecurityCallback = keyguardSecurityCallback; mView.setKeyguardCallback(keyguardSecurityCallback); } @@ -150,29 +151,16 @@ public abstract class KeyguardInputViewController private final LockPatternUtils mLockPatternUtils; private final LatencyTracker mLatencyTracker; private final KeyguardMessageAreaController.Factory mMessageAreaControllerFactory; - private final InputMethodManager mInputMethodManager; - private final DelayableExecutor mMainExecutor; - private final Resources mResources; - private LiftToActivateListener mLiftToActivateListener; - private TelephonyManager mTelephonyManager; @Inject public Factory(KeyguardUpdateMonitor keyguardUpdateMonitor, LockPatternUtils lockPatternUtils, LatencyTracker latencyTracker, - KeyguardMessageAreaController.Factory messageAreaControllerFactory, - InputMethodManager inputMethodManager, @Main DelayableExecutor mainExecutor, - @Main Resources resources, LiftToActivateListener liftToActivateListener, - TelephonyManager telephonyManager) { + KeyguardMessageAreaController.Factory messageAreaControllerFactory) { mKeyguardUpdateMonitor = keyguardUpdateMonitor; mLockPatternUtils = lockPatternUtils; mLatencyTracker = latencyTracker; mMessageAreaControllerFactory = messageAreaControllerFactory; - mInputMethodManager = inputMethodManager; - mMainExecutor = mainExecutor; - mResources = resources; - mLiftToActivateListener = liftToActivateListener; - mTelephonyManager = telephonyManager; } /** Create a new {@link KeyguardInputViewController}. */ @@ -182,29 +170,9 @@ public abstract class KeyguardInputViewController return new KeyguardPatternViewController((KeyguardPatternView) keyguardInputView, mKeyguardUpdateMonitor, securityMode, mLockPatternUtils, keyguardSecurityCallback, mLatencyTracker, mMessageAreaControllerFactory); - } else if (keyguardInputView instanceof KeyguardPasswordView) { - return new KeyguardPasswordViewController((KeyguardPasswordView) keyguardInputView, - mKeyguardUpdateMonitor, securityMode, mLockPatternUtils, - keyguardSecurityCallback, mMessageAreaControllerFactory, mLatencyTracker, - mInputMethodManager, mMainExecutor, mResources); - } else if (keyguardInputView instanceof KeyguardPINView) { - return new KeyguardPinViewController((KeyguardPINView) keyguardInputView, - mKeyguardUpdateMonitor, securityMode, mLockPatternUtils, - keyguardSecurityCallback, mMessageAreaControllerFactory, mLatencyTracker, - mLiftToActivateListener); - } else if (keyguardInputView instanceof KeyguardSimPinView) { - return new KeyguardSimPinViewController((KeyguardSimPinView) keyguardInputView, - mKeyguardUpdateMonitor, securityMode, mLockPatternUtils, - keyguardSecurityCallback, mMessageAreaControllerFactory, mLatencyTracker, - mLiftToActivateListener, mTelephonyManager); - } else if (keyguardInputView instanceof KeyguardSimPukView) { - return new KeyguardSimPukViewController((KeyguardSimPukView) keyguardInputView, - mKeyguardUpdateMonitor, securityMode, mLockPatternUtils, - keyguardSecurityCallback, mMessageAreaControllerFactory, mLatencyTracker, - mLiftToActivateListener, mTelephonyManager); } - - throw new RuntimeException("Unable to find controller for " + keyguardInputView); + return new KeyguardInputViewController<>(keyguardInputView, securityMode, + mLockPatternUtils, keyguardSecurityCallback); } } } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java index bfa187ea099b5..12ea1d586e103 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java @@ -24,6 +24,7 @@ import android.view.animation.AnimationUtils; import com.android.settingslib.animation.AppearAnimationUtils; import com.android.settingslib.animation.DisappearAnimationUtils; +import com.android.systemui.Dependency; import com.android.systemui.R; /** @@ -39,8 +40,10 @@ public class KeyguardPINView extends KeyguardPinBasedInputView { private ViewGroup mRow1; private ViewGroup mRow2; private ViewGroup mRow3; + private View mDivider; private int mDisappearYTranslation; private View[][] mViews; + private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; public KeyguardPINView(Context context) { this(context, null); @@ -60,10 +63,15 @@ public class KeyguardPINView extends KeyguardPinBasedInputView { mContext, android.R.interpolator.fast_out_linear_in)); mDisappearYTranslation = getResources().getDimensionPixelSize( R.dimen.disappear_y_translation); + mKeyguardUpdateMonitor = Dependency.get(KeyguardUpdateMonitor.class); } @Override protected void resetState() { + super.resetState(); + if (mSecurityMessageDisplay != null) { + mSecurityMessageDisplay.setMessage(""); + } } @Override @@ -80,6 +88,7 @@ public class KeyguardPINView extends KeyguardPinBasedInputView { mRow1 = findViewById(R.id.row1); mRow2 = findViewById(R.id.row2); mRow3 = findViewById(R.id.row3); + mDivider = findViewById(R.id.divider); mViews = new View[][]{ new View[]{ mRow0, null, null @@ -113,16 +122,6 @@ public class KeyguardPINView extends KeyguardPinBasedInputView { } } - @Override - public void onPause() { - - } - - @Override - public void onResume(int reason) { - - } - @Override public void showUsabilityHint() { } @@ -150,24 +149,22 @@ public class KeyguardPINView extends KeyguardPinBasedInputView { @Override public boolean startDisappearAnimation(final Runnable finishRunnable) { - return false; - } - - public boolean startDisappearAnimation(boolean needsSlowUnlockTransition, - final Runnable finishRunnable) { - enableClipping(false); setTranslationY(0); AppearAnimationUtils.startTranslationYAnimation(this, 0 /* delay */, 280 /* duration */, mDisappearYTranslation, mDisappearAnimationUtils.getInterpolator()); - DisappearAnimationUtils disappearAnimationUtils = needsSlowUnlockTransition + DisappearAnimationUtils disappearAnimationUtils = mKeyguardUpdateMonitor + .needsSlowUnlockTransition() ? mDisappearAnimationUtilsLocked : mDisappearAnimationUtils; disappearAnimationUtils.startAnimation2d(mViews, - () -> { - enableClipping(true); - if (finishRunnable != null) { - finishRunnable.run(); + new Runnable() { + @Override + public void run() { + enableClipping(true); + if (finishRunnable != null) { + finishRunnable.run(); + } } }); return true; diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java index 3b865922503aa..97317cf5580f7 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java @@ -18,29 +18,48 @@ package com.android.keyguard; import android.content.Context; import android.graphics.Rect; +import android.os.UserHandle; +import android.text.Editable; +import android.text.InputType; +import android.text.TextUtils; +import android.text.TextWatcher; +import android.text.method.TextKeyListener; import android.util.AttributeSet; +import android.view.KeyEvent; +import android.view.View; import android.view.animation.AnimationUtils; import android.view.animation.Interpolator; +import android.view.inputmethod.EditorInfo; +import android.view.inputmethod.InputMethodInfo; +import android.view.inputmethod.InputMethodManager; +import android.view.inputmethod.InputMethodSubtype; import android.widget.TextView; +import android.widget.TextView.OnEditorActionListener; import com.android.internal.widget.LockscreenCredential; import com.android.internal.widget.TextViewInputDisabler; import com.android.systemui.R; + +import java.util.List; /** * Displays an alphanumeric (latin-1) key entry for the user to enter * an unlock password */ public class KeyguardPasswordView extends KeyguardAbsKeyInputView - implements KeyguardSecurityView { + implements KeyguardSecurityView, OnEditorActionListener, TextWatcher { + private final boolean mShowImeAtScreenOn; private final int mDisappearYTranslation; // A delay constant to be used in a workaround for the situation where InputMethodManagerService // is not switched to the new user yet. // TODO: Remove this by ensuring such a race condition never happens. + private static final int DELAY_MILLIS_TO_REEVALUATE_IME_SWITCH_ICON = 500; // 500ms + InputMethodManager mImm; private TextView mPasswordEntry; private TextViewInputDisabler mPasswordEntryDisabler; + private View mSwitchImeButton; private Interpolator mLinearOutSlowInInterpolator; private Interpolator mFastOutLinearInInterpolator; @@ -51,6 +70,8 @@ public class KeyguardPasswordView extends KeyguardAbsKeyInputView public KeyguardPasswordView(Context context, AttributeSet attrs) { super(context, attrs); + mShowImeAtScreenOn = context.getResources(). + getBoolean(R.bool.kg_show_ime_at_screen_on); mDisappearYTranslation = getResources().getDimensionPixelSize( R.dimen.disappear_y_translation); mLinearOutSlowInInterpolator = AnimationUtils.loadInterpolator( @@ -61,6 +82,20 @@ public class KeyguardPasswordView extends KeyguardAbsKeyInputView @Override protected void resetState() { + mPasswordEntry.setTextOperationUser(UserHandle.of(KeyguardUpdateMonitor.getCurrentUser())); + if (mSecurityMessageDisplay != null) { + mSecurityMessageDisplay.setMessage(""); + } + final boolean wasDisabled = mPasswordEntry.isEnabled(); + setPasswordEntryEnabled(true); + setPasswordEntryInputEnabled(true); + // Don't call showSoftInput when PasswordEntry is invisible or in pausing stage. + if (!mResumed || !mPasswordEntry.isVisibleToUser()) { + return; + } + if (wasDisabled) { + mImm.showSoftInput(mPasswordEntry, InputMethodManager.SHOW_IMPLICIT); + } } @Override @@ -68,20 +103,28 @@ public class KeyguardPasswordView extends KeyguardAbsKeyInputView return R.id.passwordEntry; } - @Override - public void onPause() { - } - - @Override - public void onResume(int reason) { - - } - @Override public boolean needsInput() { return true; } + @Override + public void onResume(final int reason) { + super.onResume(reason); + + // Wait a bit to focus the field so the focusable flag on the window is already set then. + post(new Runnable() { + @Override + public void run() { + if (isShown() && mPasswordEntry.isEnabled()) { + mPasswordEntry.requestFocus(); + if (reason != KeyguardSecurityView.SCREEN_ON || mShowImeAtScreenOn) { + mImm.showSoftInput(mPasswordEntry, InputMethodManager.SHOW_IMPLICIT); + } + } + } + }); + } @Override protected int getPromptReasonStringRes(int reason) { @@ -103,13 +146,97 @@ public class KeyguardPasswordView extends KeyguardAbsKeyInputView } } + @Override + public void onPause() { + super.onPause(); + mImm.hideSoftInputFromWindow(getWindowToken(), 0); + } + + @Override + public void onStartingToHide() { + mImm.hideSoftInputFromWindow(getWindowToken(), 0); + } + + private void updateSwitchImeButton() { + // If there's more than one IME, enable the IME switcher button + final boolean wasVisible = mSwitchImeButton.getVisibility() == View.VISIBLE; + final boolean shouldBeVisible = hasMultipleEnabledIMEsOrSubtypes(mImm, false); + if (wasVisible != shouldBeVisible) { + mSwitchImeButton.setVisibility(shouldBeVisible ? View.VISIBLE : View.GONE); + } + + // TODO: Check if we still need this hack. + // If no icon is visible, reset the start margin on the password field so the text is + // still centered. + if (mSwitchImeButton.getVisibility() != View.VISIBLE) { + android.view.ViewGroup.LayoutParams params = mPasswordEntry.getLayoutParams(); + if (params instanceof MarginLayoutParams) { + final MarginLayoutParams mlp = (MarginLayoutParams) params; + mlp.setMarginStart(0); + mPasswordEntry.setLayoutParams(params); + } + } + } @Override protected void onFinishInflate() { super.onFinishInflate(); + mImm = (InputMethodManager) getContext().getSystemService( + Context.INPUT_METHOD_SERVICE); + mPasswordEntry = findViewById(getPasswordTextViewId()); + mPasswordEntry.setTextOperationUser(UserHandle.of(KeyguardUpdateMonitor.getCurrentUser())); mPasswordEntryDisabler = new TextViewInputDisabler(mPasswordEntry); + mPasswordEntry.setKeyListener(TextKeyListener.getInstance()); + mPasswordEntry.setInputType(InputType.TYPE_CLASS_TEXT + | InputType.TYPE_TEXT_VARIATION_PASSWORD); + mPasswordEntry.setOnEditorActionListener(this); + mPasswordEntry.addTextChangedListener(this); + + // Poke the wakelock any time the text is selected or modified + mPasswordEntry.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + mCallback.userActivity(); + } + }); + + // Set selected property on so the view can send accessibility events. + mPasswordEntry.setSelected(true); + + mSwitchImeButton = findViewById(R.id.switch_ime_button); + mSwitchImeButton.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + mCallback.userActivity(); // Leave the screen on a bit longer + // Do not show auxiliary subtypes in password lock screen. + mImm.showInputMethodPickerFromSystem(false /* showAuxiliarySubtypes */, + getContext().getDisplayId()); + } + }); + + View cancelBtn = findViewById(R.id.cancel_button); + if (cancelBtn != null) { + cancelBtn.setOnClickListener(view -> { + mCallback.reset(); + mCallback.onCancelClicked(); + }); + } + + // If there's more than one IME, enable the IME switcher button + updateSwitchImeButton(); + + // When we the current user is switching, InputMethodManagerService sometimes has not + // switched internal state yet here. As a quick workaround, we check the keyboard state + // again. + // TODO: Remove this workaround by ensuring such a race condition never happens. + postDelayed(new Runnable() { + @Override + public void run() { + updateSwitchImeButton(); + } + }, DELAY_MILLIS_TO_REEVALUATE_IME_SWITCH_ICON); } @Override @@ -138,6 +265,55 @@ public class KeyguardPasswordView extends KeyguardAbsKeyInputView mPasswordEntryDisabler.setInputEnabled(enabled); } + /** + * Method adapted from com.android.inputmethod.latin.Utils + * + * @param imm The input method manager + * @param shouldIncludeAuxiliarySubtypes + * @return true if we have multiple IMEs to choose from + */ + private boolean hasMultipleEnabledIMEsOrSubtypes(InputMethodManager imm, + final boolean shouldIncludeAuxiliarySubtypes) { + final List enabledImis = + imm.getEnabledInputMethodListAsUser(KeyguardUpdateMonitor.getCurrentUser()); + + // Number of the filtered IMEs + int filteredImisCount = 0; + + for (InputMethodInfo imi : enabledImis) { + // We can return true immediately after we find two or more filtered IMEs. + if (filteredImisCount > 1) return true; + final List subtypes = + imm.getEnabledInputMethodSubtypeList(imi, true); + // IMEs that have no subtypes should be counted. + if (subtypes.isEmpty()) { + ++filteredImisCount; + continue; + } + + int auxCount = 0; + for (InputMethodSubtype subtype : subtypes) { + if (subtype.isAuxiliary()) { + ++auxCount; + } + } + final int nonAuxCount = subtypes.size() - auxCount; + + // IMEs that have one or more non-auxiliary subtypes should be counted. + // If shouldIncludeAuxiliarySubtypes is true, IMEs that have two or more auxiliary + // subtypes should be counted as well. + if (nonAuxCount > 0 || (shouldIncludeAuxiliarySubtypes && auxCount > 1)) { + ++filteredImisCount; + continue; + } + } + + return filteredImisCount > 1 + // imm.getEnabledInputMethodSubtypeList(null, false) will return the current IME's enabled + // input method subtype (The current IME should be LatinIME.) + || imm.getEnabledInputMethodSubtypeList(null, false).size() > 1; + } + @Override public void showUsabilityHint() { } @@ -169,9 +345,46 @@ public class KeyguardPasswordView extends KeyguardAbsKeyInputView return true; } + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) { + if (mCallback != null) { + mCallback.userActivity(); + } + } + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { + } + + @Override + public void afterTextChanged(Editable s) { + // Poor man's user edit detection, assuming empty text is programmatic and everything else + // is from the user. + if (!TextUtils.isEmpty(s)) { + onUserInput(); + } + } + + @Override + public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { + // Check if this was the result of hitting the enter key + final boolean isSoftImeEvent = event == null + && (actionId == EditorInfo.IME_NULL + || actionId == EditorInfo.IME_ACTION_DONE + || actionId == EditorInfo.IME_ACTION_NEXT); + final boolean isKeyboardEnterKey = event != null + && KeyEvent.isConfirmKey(event.getKeyCode()) + && event.getAction() == KeyEvent.ACTION_DOWN; + if (isSoftImeEvent || isKeyboardEnterKey) { + verifyPasswordAndUnlock(); + return true; + } + return false; + } + @Override public CharSequence getTitle() { - return getResources().getString( + return getContext().getString( com.android.internal.R.string.keyguard_accessibility_password_unlock); } } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java deleted file mode 100644 index 52afdcd1cacb2..0000000000000 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java +++ /dev/null @@ -1,270 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.keyguard; - -import android.content.res.Resources; -import android.os.UserHandle; -import android.text.Editable; -import android.text.InputType; -import android.text.TextUtils; -import android.text.TextWatcher; -import android.text.method.TextKeyListener; -import android.view.KeyEvent; -import android.view.View; -import android.view.ViewGroup.MarginLayoutParams; -import android.view.inputmethod.EditorInfo; -import android.view.inputmethod.InputMethodInfo; -import android.view.inputmethod.InputMethodManager; -import android.view.inputmethod.InputMethodSubtype; -import android.widget.TextView; -import android.widget.TextView.OnEditorActionListener; - -import com.android.internal.util.LatencyTracker; -import com.android.internal.widget.LockPatternUtils; -import com.android.keyguard.KeyguardSecurityModel.SecurityMode; -import com.android.systemui.R; -import com.android.systemui.dagger.qualifiers.Main; -import com.android.systemui.util.concurrency.DelayableExecutor; - -import java.util.List; - -public class KeyguardPasswordViewController - extends KeyguardAbsKeyInputViewController { - - private static final int DELAY_MILLIS_TO_REEVALUATE_IME_SWITCH_ICON = 500; // 500ms - - private final KeyguardSecurityCallback mKeyguardSecurityCallback; - private final InputMethodManager mInputMethodManager; - private final DelayableExecutor mMainExecutor; - private final boolean mShowImeAtScreenOn; - private TextView mPasswordEntry; - private View mSwitchImeButton; - - private final OnEditorActionListener mOnEditorActionListener = (v, actionId, event) -> { - // Check if this was the result of hitting the enter key - final boolean isSoftImeEvent = event == null - && (actionId == EditorInfo.IME_NULL - || actionId == EditorInfo.IME_ACTION_DONE - || actionId == EditorInfo.IME_ACTION_NEXT); - final boolean isKeyboardEnterKey = event != null - && KeyEvent.isConfirmKey(event.getKeyCode()) - && event.getAction() == KeyEvent.ACTION_DOWN; - if (isSoftImeEvent || isKeyboardEnterKey) { - verifyPasswordAndUnlock(); - return true; - } - return false; - }; - - private final TextWatcher mTextWatcher = new TextWatcher() { - @Override - public void beforeTextChanged(CharSequence s, int start, int count, int after) { - mKeyguardSecurityCallback.userActivity(); - } - - @Override - public void onTextChanged(CharSequence s, int start, int before, int count) { - } - - @Override - public void afterTextChanged(Editable s) { - if (!TextUtils.isEmpty(s)) { - onUserInput(); - } - } - }; - - protected KeyguardPasswordViewController(KeyguardPasswordView view, - KeyguardUpdateMonitor keyguardUpdateMonitor, - SecurityMode securityMode, - LockPatternUtils lockPatternUtils, - KeyguardSecurityCallback keyguardSecurityCallback, - KeyguardMessageAreaController.Factory messageAreaControllerFactory, - LatencyTracker latencyTracker, - InputMethodManager inputMethodManager, - @Main DelayableExecutor mainExecutor, - @Main Resources resources) { - super(view, keyguardUpdateMonitor, securityMode, lockPatternUtils, keyguardSecurityCallback, - messageAreaControllerFactory, latencyTracker); - mKeyguardSecurityCallback = keyguardSecurityCallback; - mInputMethodManager = inputMethodManager; - mMainExecutor = mainExecutor; - mShowImeAtScreenOn = resources.getBoolean(R.bool.kg_show_ime_at_screen_on); - mPasswordEntry = mView.findViewById(mView.getPasswordTextViewId()); - mSwitchImeButton = mView.findViewById(R.id.switch_ime_button); - } - - @Override - protected void onViewAttached() { - super.onViewAttached(); - mPasswordEntry.setTextOperationUser(UserHandle.of(KeyguardUpdateMonitor.getCurrentUser())); - mPasswordEntry.setKeyListener(TextKeyListener.getInstance()); - mPasswordEntry.setInputType(InputType.TYPE_CLASS_TEXT - | InputType.TYPE_TEXT_VARIATION_PASSWORD); - - // Set selected property on so the view can send accessibility events. - mPasswordEntry.setSelected(true); - mPasswordEntry.setOnEditorActionListener(mOnEditorActionListener); - mPasswordEntry.addTextChangedListener(mTextWatcher); - // Poke the wakelock any time the text is selected or modified - mPasswordEntry.setOnClickListener(v -> mKeyguardSecurityCallback.userActivity()); - - mSwitchImeButton.setOnClickListener(v -> { - mKeyguardSecurityCallback.userActivity(); // Leave the screen on a bit longer - // Do not show auxiliary subtypes in password lock screen. - mInputMethodManager.showInputMethodPickerFromSystem(false, - mView.getContext().getDisplayId()); - }); - - View cancelBtn = mView.findViewById(R.id.cancel_button); - if (cancelBtn != null) { - cancelBtn.setOnClickListener(view -> { - mKeyguardSecurityCallback.reset(); - mKeyguardSecurityCallback.onCancelClicked(); - }); - } - - // If there's more than one IME, enable the IME switcher button - updateSwitchImeButton(); - - // When we the current user is switching, InputMethodManagerService sometimes has not - // switched internal state yet here. As a quick workaround, we check the keyboard state - // again. - // TODO: Remove this workaround by ensuring such a race condition never happens. - mMainExecutor.executeDelayed( - this::updateSwitchImeButton, DELAY_MILLIS_TO_REEVALUATE_IME_SWITCH_ICON); - } - - @Override - protected void onViewDetached() { - super.onViewDetached(); - mPasswordEntry.setOnEditorActionListener(null); - } - - @Override - void resetState() { - mPasswordEntry.setTextOperationUser(UserHandle.of(KeyguardUpdateMonitor.getCurrentUser())); - mMessageAreaController.setMessage(""); - final boolean wasDisabled = mPasswordEntry.isEnabled(); - mView.setPasswordEntryEnabled(true); - mView.setPasswordEntryInputEnabled(true); - // Don't call showSoftInput when PasswordEntry is invisible or in pausing stage. - if (!mResumed || !mPasswordEntry.isVisibleToUser()) { - return; - } - if (wasDisabled) { - mInputMethodManager.showSoftInput(mPasswordEntry, InputMethodManager.SHOW_IMPLICIT); - } - } - - @Override - public void onResume(int reason) { - super.onResume(reason); - // Wait a bit to focus the field so the focusable flag on the window is already set then. - mMainExecutor.execute(() -> { - if (mView.isShown() && mPasswordEntry.isEnabled()) { - mPasswordEntry.requestFocus(); - if (reason != KeyguardSecurityView.SCREEN_ON || mShowImeAtScreenOn) { - mInputMethodManager.showSoftInput( - mPasswordEntry, InputMethodManager.SHOW_IMPLICIT); - } - } - }); - } - - @Override - public void onPause() { - super.onPause(); - mInputMethodManager.hideSoftInputFromWindow(mView.getWindowToken(), 0); - } - - @Override - public void onStartingToHide() { - mInputMethodManager.hideSoftInputFromWindow(mView.getWindowToken(), 0); - } - - private void updateSwitchImeButton() { - // If there's more than one IME, enable the IME switcher button - final boolean wasVisible = mSwitchImeButton.getVisibility() == View.VISIBLE; - final boolean shouldBeVisible = hasMultipleEnabledIMEsOrSubtypes( - mInputMethodManager, false); - if (wasVisible != shouldBeVisible) { - mSwitchImeButton.setVisibility(shouldBeVisible ? View.VISIBLE : View.GONE); - } - - // TODO: Check if we still need this hack. - // If no icon is visible, reset the start margin on the password field so the text is - // still centered. - if (mSwitchImeButton.getVisibility() != View.VISIBLE) { - android.view.ViewGroup.LayoutParams params = mPasswordEntry.getLayoutParams(); - if (params instanceof MarginLayoutParams) { - final MarginLayoutParams mlp = (MarginLayoutParams) params; - mlp.setMarginStart(0); - mPasswordEntry.setLayoutParams(params); - } - } - } - - /** - * Method adapted from com.android.inputmethod.latin.Utils - * - * @param imm The input method manager - * @param shouldIncludeAuxiliarySubtypes - * @return true if we have multiple IMEs to choose from - */ - private boolean hasMultipleEnabledIMEsOrSubtypes(InputMethodManager imm, - final boolean shouldIncludeAuxiliarySubtypes) { - final List enabledImis = - imm.getEnabledInputMethodListAsUser(KeyguardUpdateMonitor.getCurrentUser()); - - // Number of the filtered IMEs - int filteredImisCount = 0; - - for (InputMethodInfo imi : enabledImis) { - // We can return true immediately after we find two or more filtered IMEs. - if (filteredImisCount > 1) return true; - final List subtypes = - imm.getEnabledInputMethodSubtypeList(imi, true); - // IMEs that have no subtypes should be counted. - if (subtypes.isEmpty()) { - ++filteredImisCount; - continue; - } - - int auxCount = 0; - for (InputMethodSubtype subtype : subtypes) { - if (subtype.isAuxiliary()) { - ++auxCount; - } - } - final int nonAuxCount = subtypes.size() - auxCount; - - // IMEs that have one or more non-auxiliary subtypes should be counted. - // If shouldIncludeAuxiliarySubtypes is true, IMEs that have two or more auxiliary - // subtypes should be counted as well. - if (nonAuxCount > 0 || (shouldIncludeAuxiliarySubtypes && auxCount > 1)) { - ++filteredImisCount; - continue; - } - } - - return filteredImisCount > 1 - // imm.getEnabledInputMethodSubtypeList(null, false) will return the current IME's - //enabled input method subtype (The current IME should be LatinIME.) - || imm.getEnabledInputMethodSubtypeList(null, false).size() > 1; - } -} diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java index 13a16360be040..5e547d3120bfe 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java @@ -180,18 +180,18 @@ public class KeyguardPatternViewController KeyguardSecurityCallback keyguardSecurityCallback, LatencyTracker latencyTracker, KeyguardMessageAreaController.Factory messageAreaControllerFactory) { - super(view, securityMode, keyguardSecurityCallback); + super(view, securityMode, lockPatternUtils, keyguardSecurityCallback); mKeyguardUpdateMonitor = keyguardUpdateMonitor; mLockPatternUtils = lockPatternUtils; mLatencyTracker = latencyTracker; mMessageAreaControllerFactory = messageAreaControllerFactory; - KeyguardMessageArea kma = KeyguardMessageArea.findSecurityMessageDisplay(mView); - mMessageAreaController = mMessageAreaControllerFactory.create(kma); - mLockPatternView = mView.findViewById(R.id.lockPatternView); } @Override protected void onViewAttached() { + KeyguardMessageArea kma = KeyguardMessageArea.findSecurityMessageDisplay(mView); + mMessageAreaController = mMessageAreaControllerFactory.create(kma); + mLockPatternView = mView.findViewById(R.id.lockPatternView); mLockPatternView.setOnPatternListener(new UnlockPatternListener()); mLockPatternView.setSaveEnabled(false); mLockPatternView.setInStealthMode(!mLockPatternUtils.isVisiblePatternEnabled( diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java index e579380fc8d59..c7f27cf8a71a9 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java @@ -20,6 +20,7 @@ import android.content.Context; import android.graphics.Rect; import android.util.AttributeSet; import android.view.KeyEvent; +import android.view.MotionEvent; import android.view.View; import com.android.internal.widget.LockscreenCredential; @@ -28,12 +29,22 @@ import com.android.systemui.R; /** * A Pin based Keyguard input view */ -public abstract class KeyguardPinBasedInputView extends KeyguardAbsKeyInputView { +public abstract class KeyguardPinBasedInputView extends KeyguardAbsKeyInputView + implements View.OnKeyListener, View.OnTouchListener { protected PasswordTextView mPasswordEntry; private View mOkButton; private View mDeleteButton; - private View[] mButtons = new View[10]; + private View mButton0; + private View mButton1; + private View mButton2; + private View mButton3; + private View mButton4; + private View mButton5; + private View mButton6; + private View mButton7; + private View mButton8; + private View mButton9; public KeyguardPinBasedInputView(Context context) { this(context, null); @@ -51,6 +62,7 @@ public abstract class KeyguardPinBasedInputView extends KeyguardAbsKeyInputView @Override protected void resetState() { + setPasswordEntryEnabled(true); } @Override @@ -74,10 +86,10 @@ public abstract class KeyguardPinBasedInputView extends KeyguardAbsKeyInputView @Override public boolean onKeyDown(int keyCode, KeyEvent event) { if (KeyEvent.isConfirmKey(keyCode)) { - mOkButton.performClick(); + performClick(mOkButton); return true; } else if (keyCode == KeyEvent.KEYCODE_DEL) { - mDeleteButton.performClick(); + performClick(mDeleteButton); return true; } if (keyCode >= KeyEvent.KEYCODE_0 && keyCode <= KeyEvent.KEYCODE_9) { @@ -113,9 +125,42 @@ public abstract class KeyguardPinBasedInputView extends KeyguardAbsKeyInputView } } + private void performClick(View view) { + view.performClick(); + } + private void performNumberClick(int number) { - if (number >= 0 && number <= 9) { - mButtons[number].performClick(); + switch (number) { + case 0: + performClick(mButton0); + break; + case 1: + performClick(mButton1); + break; + case 2: + performClick(mButton2); + break; + case 3: + performClick(mButton3); + break; + case 4: + performClick(mButton4); + break; + case 5: + performClick(mButton5); + break; + case 6: + performClick(mButton6); + break; + case 7: + performClick(mButton7); + break; + case 8: + performClick(mButton8); + break; + case 9: + performClick(mButton9); + break; } } @@ -132,30 +177,93 @@ public abstract class KeyguardPinBasedInputView extends KeyguardAbsKeyInputView @Override protected void onFinishInflate() { mPasswordEntry = findViewById(getPasswordTextViewId()); + mPasswordEntry.setOnKeyListener(this); // Set selected property on so the view can send accessibility events. mPasswordEntry.setSelected(true); + mPasswordEntry.setUserActivityListener(new PasswordTextView.UserActivityListener() { + @Override + public void onUserActivity() { + onUserInput(); + } + }); + mOkButton = findViewById(R.id.key_enter); + if (mOkButton != null) { + mOkButton.setOnTouchListener(this); + mOkButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (mPasswordEntry.isEnabled()) { + verifyPasswordAndUnlock(); + } + } + }); + mOkButton.setOnHoverListener(new LiftToActivateListener(getContext())); + } mDeleteButton = findViewById(R.id.delete_button); mDeleteButton.setVisibility(View.VISIBLE); + mDeleteButton.setOnTouchListener(this); + mDeleteButton.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + // check for time-based lockouts + if (mPasswordEntry.isEnabled()) { + mPasswordEntry.deleteLastChar(); + } + } + }); + mDeleteButton.setOnLongClickListener(new View.OnLongClickListener() { + @Override + public boolean onLongClick(View v) { + // check for time-based lockouts + if (mPasswordEntry.isEnabled()) { + resetPasswordText(true /* animate */, true /* announce */); + } + doHapticKeyClick(); + return true; + } + }); - mButtons[0] = findViewById(R.id.key0); - mButtons[1] = findViewById(R.id.key1); - mButtons[2] = findViewById(R.id.key2); - mButtons[3] = findViewById(R.id.key3); - mButtons[4] = findViewById(R.id.key4); - mButtons[5] = findViewById(R.id.key5); - mButtons[6] = findViewById(R.id.key6); - mButtons[7] = findViewById(R.id.key7); - mButtons[8] = findViewById(R.id.key8); - mButtons[9] = findViewById(R.id.key9); + mButton0 = findViewById(R.id.key0); + mButton1 = findViewById(R.id.key1); + mButton2 = findViewById(R.id.key2); + mButton3 = findViewById(R.id.key3); + mButton4 = findViewById(R.id.key4); + mButton5 = findViewById(R.id.key5); + mButton6 = findViewById(R.id.key6); + mButton7 = findViewById(R.id.key7); + mButton8 = findViewById(R.id.key8); + mButton9 = findViewById(R.id.key9); mPasswordEntry.requestFocus(); super.onFinishInflate(); } + @Override + public void onResume(int reason) { + super.onResume(reason); + mPasswordEntry.requestFocus(); + } + + @Override + public boolean onTouch(View v, MotionEvent event) { + if (event.getActionMasked() == MotionEvent.ACTION_DOWN) { + doHapticKeyClick(); + } + return false; + } + + @Override + public boolean onKey(View v, int keyCode, KeyEvent event) { + if (event.getAction() == KeyEvent.ACTION_DOWN) { + return onKeyDown(keyCode, event); + } + return false; + } + @Override public CharSequence getTitle() { return getContext().getString( diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputViewController.java deleted file mode 100644 index 4d0ebfffbe04f..0000000000000 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputViewController.java +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.keyguard; - -import android.view.KeyEvent; -import android.view.MotionEvent; -import android.view.View; -import android.view.View.OnKeyListener; -import android.view.View.OnTouchListener; - -import com.android.internal.util.LatencyTracker; -import com.android.internal.widget.LockPatternUtils; -import com.android.keyguard.KeyguardSecurityModel.SecurityMode; -import com.android.systemui.R; - -public abstract class KeyguardPinBasedInputViewController - extends KeyguardAbsKeyInputViewController { - - private final LiftToActivateListener mLiftToActivateListener; - protected PasswordTextView mPasswordEntry; - - private final OnKeyListener mOnKeyListener = (v, keyCode, event) -> { - if (event.getAction() == KeyEvent.ACTION_DOWN) { - return mView.onKeyDown(keyCode, event); - } - return false; - }; - - private final OnTouchListener mOnTouchListener = (v, event) -> { - if (event.getActionMasked() == MotionEvent.ACTION_DOWN) { - mView.doHapticKeyClick(); - } - return false; - }; - - protected KeyguardPinBasedInputViewController(T view, - KeyguardUpdateMonitor keyguardUpdateMonitor, - SecurityMode securityMode, - LockPatternUtils lockPatternUtils, - KeyguardSecurityCallback keyguardSecurityCallback, - KeyguardMessageAreaController.Factory messageAreaControllerFactory, - LatencyTracker latencyTracker, - LiftToActivateListener liftToActivateListener) { - super(view, keyguardUpdateMonitor, securityMode, lockPatternUtils, keyguardSecurityCallback, - messageAreaControllerFactory, latencyTracker); - mLiftToActivateListener = liftToActivateListener; - mPasswordEntry = mView.findViewById(mView.getPasswordTextViewId()); - } - - @Override - protected void onViewAttached() { - super.onViewAttached(); - - mPasswordEntry.setOnKeyListener(mOnKeyListener); - mPasswordEntry.setUserActivityListener(this::onUserInput); - - View deleteButton = mView.findViewById(R.id.delete_button); - deleteButton.setOnTouchListener(mOnTouchListener); - deleteButton.setOnClickListener(v -> { - // check for time-based lockouts - if (mPasswordEntry.isEnabled()) { - mPasswordEntry.deleteLastChar(); - } - }); - deleteButton.setOnLongClickListener(v -> { - // check for time-based lockouts - if (mPasswordEntry.isEnabled()) { - mView.resetPasswordText(true /* animate */, true /* announce */); - } - mView.doHapticKeyClick(); - return true; - }); - - View okButton = mView.findViewById(R.id.key_enter); - if (okButton != null) { - okButton.setOnTouchListener(mOnTouchListener); - okButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - if (mPasswordEntry.isEnabled()) { - verifyPasswordAndUnlock(); - } - } - }); - okButton.setOnHoverListener(mLiftToActivateListener); - } - } - - @Override - public void onResume(int reason) { - super.onResume(reason); - mPasswordEntry.requestFocus(); - } - - @Override - void resetState() { - mView.setPasswordEntryEnabled(true); - } -} diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java deleted file mode 100644 index 625ab5214da15..0000000000000 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.keyguard; - -import com.android.internal.util.LatencyTracker; -import com.android.internal.widget.LockPatternUtils; -import com.android.keyguard.KeyguardSecurityModel.SecurityMode; - -public class KeyguardPinViewController - extends KeyguardPinBasedInputViewController { - private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; - - protected KeyguardPinViewController(KeyguardPINView view, - KeyguardUpdateMonitor keyguardUpdateMonitor, - SecurityMode securityMode, LockPatternUtils lockPatternUtils, - KeyguardSecurityCallback keyguardSecurityCallback, - KeyguardMessageAreaController.Factory messageAreaControllerFactory, - LatencyTracker latencyTracker, - LiftToActivateListener liftToActivateListener) { - super(view, keyguardUpdateMonitor, securityMode, lockPatternUtils, keyguardSecurityCallback, - messageAreaControllerFactory, latencyTracker, liftToActivateListener); - mKeyguardUpdateMonitor = keyguardUpdateMonitor; - } - - @Override - void resetState() { - super.resetState(); - mMessageAreaController.setMessage(""); - } - - @Override - public boolean startDisappearAnimation(Runnable finishRunnable) { - return mView.startDisappearAnimation( - mKeyguardUpdateMonitor.needsSlowUnlockTransition(), finishRunnable); - } -} diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java index 2c7dce6e43021..342f3de2ede31 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java @@ -197,7 +197,6 @@ public class KeyguardSecurityContainerController extends ViewController= 0) { + return; + } + + // Sending empty PIN here to query the number of remaining PIN attempts + new CheckSimPin("", mSubId) { + void onSimCheckResponse(final PinResult result) { + Log.d(LOG_TAG, "onSimCheckResponse " + " empty One result " + + result.toString()); + if (result.getAttemptsRemaining() >= 0) { + mRemainingAttempts = result.getAttemptsRemaining(); + setLockedSimMessage(); + } + } + }.start(); + } + + private void handleSubInfoChangeIfNeeded() { + KeyguardUpdateMonitor monitor = Dependency.get(KeyguardUpdateMonitor.class); + int subId = monitor.getNextSubIdForState(TelephonyManager.SIM_STATE_PIN_REQUIRED); + if (subId != mSubId && SubscriptionManager.isValidSubscriptionId(subId)) { + mSubId = subId; + mShowDefaultMessage = true; + mRemainingAttempts = -1; + } } @Override @@ -54,6 +173,35 @@ public class KeyguardSimPinView extends KeyguardPinBasedInputView { return 0; } + private String getPinPasswordErrorMessage(int attemptsRemaining, boolean isDefault) { + String displayMessage; + int msgId; + if (attemptsRemaining == 0) { + displayMessage = getContext().getString(R.string.kg_password_wrong_pin_code_pukked); + } else if (attemptsRemaining > 0) { + msgId = isDefault ? R.plurals.kg_password_default_pin_message : + R.plurals.kg_password_wrong_pin_code; + displayMessage = getContext().getResources() + .getQuantityString(msgId, attemptsRemaining, attemptsRemaining); + } else { + msgId = isDefault ? R.string.kg_sim_pin_instructions : R.string.kg_password_pin_failed; + displayMessage = getContext().getString(msgId); + } + if (KeyguardEsimArea.isEsimLocked(mContext, mSubId)) { + displayMessage = getResources() + .getString(R.string.kg_sim_lock_esim_instructions, displayMessage); + } + if (DEBUG) Log.d(LOG_TAG, "getPinPasswordErrorMessage:" + + " attemptsRemaining=" + attemptsRemaining + " displayMessage=" + displayMessage); + return displayMessage; + } + + @Override + protected boolean shouldLockout(long deadline) { + // SIM PIN doesn't have a timed lockout + return false; + } + @Override protected int getPasswordTextViewId() { return R.id.simPinEntry; @@ -66,16 +214,7 @@ public class KeyguardSimPinView extends KeyguardPinBasedInputView { if (mEcaView instanceof EmergencyCarrierArea) { ((EmergencyCarrierArea) mEcaView).setCarrierTextVisible(true); } - } - - @Override - public void onPause() { - - } - - @Override - public void onResume(int reason) { - + mSimImageView = findViewById(R.id.keyguard_sim); } @Override @@ -83,6 +222,167 @@ public class KeyguardSimPinView extends KeyguardPinBasedInputView { } + @Override + public void onResume(int reason) { + super.onResume(reason); + Dependency.get(KeyguardUpdateMonitor.class).registerCallback(mUpdateMonitorCallback); + resetState(); + } + + @Override + public void onPause() { + // dismiss the dialog. + if (mSimUnlockProgressDialog != null) { + mSimUnlockProgressDialog.dismiss(); + mSimUnlockProgressDialog = null; + } + Dependency.get(KeyguardUpdateMonitor.class).removeCallback(mUpdateMonitorCallback); + } + + /** + * Since the IPC can block, we want to run the request in a separate thread + * with a callback. + */ + private abstract class CheckSimPin extends Thread { + private final String mPin; + private int mSubId; + + protected CheckSimPin(String pin, int subId) { + mPin = pin; + mSubId = subId; + } + + abstract void onSimCheckResponse(@NonNull PinResult result); + + @Override + public void run() { + if (DEBUG) { + Log.v(TAG, "call supplyPinReportResultForSubscriber(subid=" + mSubId + ")"); + } + TelephonyManager telephonyManager = + ((TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE)) + .createForSubscriptionId(mSubId); + final PinResult result = telephonyManager.supplyPinReportPinResult(mPin); + if (result == null) { + Log.e(TAG, "Error result for supplyPinReportResult."); + post(new Runnable() { + @Override + public void run() { + onSimCheckResponse(PinResult.getDefaultFailedResult()); + } + }); + } else { + if (DEBUG) { + Log.v(TAG, "supplyPinReportResult returned: " + result.toString()); + } + post(new Runnable() { + @Override + public void run() { + onSimCheckResponse(result); + } + }); + } + } + } + + private Dialog getSimUnlockProgressDialog() { + if (mSimUnlockProgressDialog == null) { + mSimUnlockProgressDialog = new ProgressDialog(mContext); + mSimUnlockProgressDialog.setMessage( + mContext.getString(R.string.kg_sim_unlock_progress_dialog_message)); + mSimUnlockProgressDialog.setIndeterminate(true); + mSimUnlockProgressDialog.setCancelable(false); + mSimUnlockProgressDialog.getWindow().setType( + WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG); + } + return mSimUnlockProgressDialog; + } + + private Dialog getSimRemainingAttemptsDialog(int remaining) { + String msg = getPinPasswordErrorMessage(remaining, false); + if (mRemainingAttemptsDialog == null) { + Builder builder = new AlertDialog.Builder(mContext); + builder.setMessage(msg); + builder.setCancelable(false); + builder.setNeutralButton(R.string.ok, null); + mRemainingAttemptsDialog = builder.create(); + mRemainingAttemptsDialog.getWindow().setType( + WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG); + } else { + mRemainingAttemptsDialog.setMessage(msg); + } + return mRemainingAttemptsDialog; + } + + @Override + protected void verifyPasswordAndUnlock() { + String entry = mPasswordEntry.getText(); + + if (entry.length() < 4) { + // otherwise, display a message to the user, and don't submit. + mSecurityMessageDisplay.setMessage(R.string.kg_invalid_sim_pin_hint); + resetPasswordText(true /* animate */, true /* announce */); + mCallback.userActivity(); + return; + } + + getSimUnlockProgressDialog().show(); + + if (mCheckSimPinThread == null) { + mCheckSimPinThread = new CheckSimPin(mPasswordEntry.getText(), mSubId) { + @Override + void onSimCheckResponse(final PinResult result) { + post(new Runnable() { + @Override + public void run() { + mRemainingAttempts = result.getAttemptsRemaining(); + if (mSimUnlockProgressDialog != null) { + mSimUnlockProgressDialog.hide(); + } + resetPasswordText(true /* animate */, + /* announce */ + result.getType() != PinResult.PIN_RESULT_TYPE_SUCCESS); + if (result.getType() == PinResult.PIN_RESULT_TYPE_SUCCESS) { + Dependency.get(KeyguardUpdateMonitor.class) + .reportSimUnlocked(mSubId); + mRemainingAttempts = -1; + mShowDefaultMessage = true; + if (mCallback != null) { + mCallback.dismiss(true, KeyguardUpdateMonitor.getCurrentUser()); + } + } else { + mShowDefaultMessage = false; + if (result.getType() == PinResult.PIN_RESULT_TYPE_INCORRECT) { + if (result.getAttemptsRemaining() <= 2) { + // this is getting critical - show dialog + getSimRemainingAttemptsDialog( + result.getAttemptsRemaining()).show(); + } else { + // show message + mSecurityMessageDisplay.setMessage( + getPinPasswordErrorMessage( + result.getAttemptsRemaining(), false)); + } + } else { + // "PIN operation failed!" - no idea what this was and no way to + // find out. :/ + mSecurityMessageDisplay.setMessage(getContext().getString( + R.string.kg_password_pin_failed)); + } + if (DEBUG) Log.d(LOG_TAG, "verifyPasswordAndUnlock " + + " CheckSimPin.onSimCheckResponse: " + result + + " attemptsRemaining=" + result.getAttemptsRemaining()); + } + mCallback.userActivity(); + mCheckSimPinThread = null; + } + }); + } + }; + mCheckSimPinThread.start(); + } + } + @Override public void startAppearAnimation() { // noop. diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java deleted file mode 100644 index 3c29058e0afc2..0000000000000 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java +++ /dev/null @@ -1,350 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.keyguard; - -import android.annotation.NonNull; -import android.app.AlertDialog; -import android.app.AlertDialog.Builder; -import android.app.Dialog; -import android.app.ProgressDialog; -import android.content.res.ColorStateList; -import android.content.res.Resources; -import android.content.res.TypedArray; -import android.graphics.Color; -import android.telephony.PinResult; -import android.telephony.SubscriptionInfo; -import android.telephony.SubscriptionManager; -import android.telephony.TelephonyManager; -import android.util.Log; -import android.view.View; -import android.view.WindowManager; -import android.widget.ImageView; - -import com.android.internal.util.LatencyTracker; -import com.android.internal.widget.LockPatternUtils; -import com.android.keyguard.KeyguardSecurityModel.SecurityMode; -import com.android.systemui.R; - -public class KeyguardSimPinViewController - extends KeyguardPinBasedInputViewController { - public static final String TAG = "KeyguardSimPinView"; - private static final String LOG_TAG = "KeyguardSimPinView"; - private static final boolean DEBUG = KeyguardConstants.DEBUG_SIM_STATES; - private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; - private final TelephonyManager mTelephonyManager; - - private ProgressDialog mSimUnlockProgressDialog; - private CheckSimPin mCheckSimPinThread; - private int mRemainingAttempts; - // Below flag is set to true during power-up or when a new SIM card inserted on device. - // When this is true and when SIM card is PIN locked state, on PIN lock screen, message would - // be displayed to inform user about the number of remaining PIN attempts left. - private boolean mShowDefaultMessage; - private int mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; - private AlertDialog mRemainingAttemptsDialog; - private ImageView mSimImageView; - - KeyguardUpdateMonitorCallback mUpdateMonitorCallback = new KeyguardUpdateMonitorCallback() { - @Override - public void onSimStateChanged(int subId, int slotId, int simState) { - if (DEBUG) Log.v(TAG, "onSimStateChanged(subId=" + subId + ",state=" + simState + ")"); - if (simState == TelephonyManager.SIM_STATE_READY) { - mRemainingAttempts = -1; - resetState(); - } else { - resetState(); - } - } - }; - - protected KeyguardSimPinViewController(KeyguardSimPinView view, - KeyguardUpdateMonitor keyguardUpdateMonitor, - SecurityMode securityMode, LockPatternUtils lockPatternUtils, - KeyguardSecurityCallback keyguardSecurityCallback, - KeyguardMessageAreaController.Factory messageAreaControllerFactory, - LatencyTracker latencyTracker, - LiftToActivateListener liftToActivateListener, - TelephonyManager telephonyManager) { - super(view, keyguardUpdateMonitor, securityMode, lockPatternUtils, keyguardSecurityCallback, - messageAreaControllerFactory, latencyTracker, liftToActivateListener); - mKeyguardUpdateMonitor = keyguardUpdateMonitor; - mTelephonyManager = telephonyManager; - mSimImageView = mView.findViewById(R.id.keyguard_sim); - } - - @Override - protected void onViewAttached() { - super.onViewAttached(); - } - - @Override - void resetState() { - super.resetState(); - if (DEBUG) Log.v(TAG, "Resetting state"); - handleSubInfoChangeIfNeeded(); - mMessageAreaController.setMessage(""); - if (mShowDefaultMessage) { - showDefaultMessage(); - } - - mView.setEsimLocked(KeyguardEsimArea.isEsimLocked(mView.getContext(), mSubId)); - } - - @Override - public boolean startDisappearAnimation(Runnable finishRunnable) { - return mView.startDisappearAnimation(finishRunnable); - } - - @Override - public void onResume(int reason) { - super.onResume(reason); - mKeyguardUpdateMonitor.registerCallback(mUpdateMonitorCallback); - mView.resetState(); - } - - @Override - public void onPause() { - super.onPause(); - mKeyguardUpdateMonitor.removeCallback(mUpdateMonitorCallback); - - // dismiss the dialog. - if (mSimUnlockProgressDialog != null) { - mSimUnlockProgressDialog.dismiss(); - mSimUnlockProgressDialog = null; - } - } - - @Override - protected void verifyPasswordAndUnlock() { - String entry = mPasswordEntry.getText(); - - if (entry.length() < 4) { - // otherwise, display a message to the user, and don't submit. - mMessageAreaController.setMessage( - com.android.systemui.R.string.kg_invalid_sim_pin_hint); - mView.resetPasswordText(true /* animate */, true /* announce */); - mKeyguardSecurityCallback.userActivity(); - return; - } - - getSimUnlockProgressDialog().show(); - - if (mCheckSimPinThread == null) { - mCheckSimPinThread = new CheckSimPin(mPasswordEntry.getText(), mSubId) { - @Override - void onSimCheckResponse(final PinResult result) { - mView.post(() -> { - mRemainingAttempts = result.getAttemptsRemaining(); - if (mSimUnlockProgressDialog != null) { - mSimUnlockProgressDialog.hide(); - } - mView.resetPasswordText(true /* animate */, - /* announce */ - result.getType() != PinResult.PIN_RESULT_TYPE_SUCCESS); - if (result.getType() == PinResult.PIN_RESULT_TYPE_SUCCESS) { - mKeyguardUpdateMonitor.reportSimUnlocked(mSubId); - mRemainingAttempts = -1; - mShowDefaultMessage = true; - mKeyguardSecurityCallback.dismiss( - true, KeyguardUpdateMonitor.getCurrentUser()); - } else { - mShowDefaultMessage = false; - if (result.getType() == PinResult.PIN_RESULT_TYPE_INCORRECT) { - if (result.getAttemptsRemaining() <= 2) { - // this is getting critical - show dialog - getSimRemainingAttemptsDialog( - result.getAttemptsRemaining()).show(); - } else { - // show message - mMessageAreaController.setMessage( - getPinPasswordErrorMessage( - result.getAttemptsRemaining(), false)); - } - } else { - // "PIN operation failed!" - no idea what this was and no way to - // find out. :/ - mMessageAreaController.setMessage(mView.getResources().getString( - R.string.kg_password_pin_failed)); - } - if (DEBUG) { - Log.d(LOG_TAG, "verifyPasswordAndUnlock " - + " CheckSimPin.onSimCheckResponse: " + result - + " attemptsRemaining=" + result.getAttemptsRemaining()); - } - } - mKeyguardSecurityCallback.userActivity(); - mCheckSimPinThread = null; - }); - } - }; - mCheckSimPinThread.start(); - } - } - - private Dialog getSimUnlockProgressDialog() { - if (mSimUnlockProgressDialog == null) { - mSimUnlockProgressDialog = new ProgressDialog(mView.getContext()); - mSimUnlockProgressDialog.setMessage( - mView.getResources().getString(R.string.kg_sim_unlock_progress_dialog_message)); - mSimUnlockProgressDialog.setIndeterminate(true); - mSimUnlockProgressDialog.setCancelable(false); - mSimUnlockProgressDialog.getWindow().setType( - WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG); - } - return mSimUnlockProgressDialog; - } - - - private Dialog getSimRemainingAttemptsDialog(int remaining) { - String msg = getPinPasswordErrorMessage(remaining, false); - if (mRemainingAttemptsDialog == null) { - Builder builder = new AlertDialog.Builder(mView.getContext()); - builder.setMessage(msg); - builder.setCancelable(false); - builder.setNeutralButton(R.string.ok, null); - mRemainingAttemptsDialog = builder.create(); - mRemainingAttemptsDialog.getWindow().setType( - WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG); - } else { - mRemainingAttemptsDialog.setMessage(msg); - } - return mRemainingAttemptsDialog; - } - - - private String getPinPasswordErrorMessage(int attemptsRemaining, boolean isDefault) { - String displayMessage; - int msgId; - if (attemptsRemaining == 0) { - displayMessage = mView.getResources().getString( - R.string.kg_password_wrong_pin_code_pukked); - } else if (attemptsRemaining > 0) { - msgId = isDefault ? R.plurals.kg_password_default_pin_message : - R.plurals.kg_password_wrong_pin_code; - displayMessage = mView.getResources() - .getQuantityString(msgId, attemptsRemaining, attemptsRemaining); - } else { - msgId = isDefault ? R.string.kg_sim_pin_instructions : R.string.kg_password_pin_failed; - displayMessage = mView.getResources().getString(msgId); - } - if (KeyguardEsimArea.isEsimLocked(mView.getContext(), mSubId)) { - displayMessage = mView.getResources() - .getString(R.string.kg_sim_lock_esim_instructions, displayMessage); - } - if (DEBUG) { - Log.d(LOG_TAG, "getPinPasswordErrorMessage: attemptsRemaining=" - + attemptsRemaining + " displayMessage=" + displayMessage); - } - return displayMessage; - } - - private void showDefaultMessage() { - setLockedSimMessage(); - if (mRemainingAttempts >= 0) { - return; - } - - // Sending empty PIN here to query the number of remaining PIN attempts - new CheckSimPin("", mSubId) { - void onSimCheckResponse(final PinResult result) { - Log.d(LOG_TAG, "onSimCheckResponse " + " empty One result " - + result.toString()); - if (result.getAttemptsRemaining() >= 0) { - mRemainingAttempts = result.getAttemptsRemaining(); - setLockedSimMessage(); - } - } - }.start(); - } - - /** - * Since the IPC can block, we want to run the request in a separate thread - * with a callback. - */ - private abstract class CheckSimPin extends Thread { - private final String mPin; - private int mSubId; - - protected CheckSimPin(String pin, int subId) { - mPin = pin; - mSubId = subId; - } - - abstract void onSimCheckResponse(@NonNull PinResult result); - - @Override - public void run() { - if (DEBUG) { - Log.v(TAG, "call supplyPinReportResultForSubscriber(subid=" + mSubId + ")"); - } - TelephonyManager telephonyManager = - mTelephonyManager.createForSubscriptionId(mSubId); - final PinResult result = telephonyManager.supplyPinReportPinResult(mPin); - if (result == null) { - Log.e(TAG, "Error result for supplyPinReportResult."); - mView.post(() -> onSimCheckResponse(PinResult.getDefaultFailedResult())); - } else { - if (DEBUG) { - Log.v(TAG, "supplyPinReportResult returned: " + result.toString()); - } - mView.post(() -> onSimCheckResponse(result)); - } - } - } - - private void setLockedSimMessage() { - boolean isEsimLocked = KeyguardEsimArea.isEsimLocked(mView.getContext(), mSubId); - int count = 1; - if (mTelephonyManager != null) { - count = mTelephonyManager.getActiveModemCount(); - } - Resources rez = mView.getResources(); - String msg; - TypedArray array = mView.getContext().obtainStyledAttributes( - new int[] { R.attr.wallpaperTextColor }); - int color = array.getColor(0, Color.WHITE); - array.recycle(); - if (count < 2) { - msg = rez.getString(R.string.kg_sim_pin_instructions); - } else { - SubscriptionInfo info = mKeyguardUpdateMonitor.getSubscriptionInfoForSubId(mSubId); - CharSequence displayName = info != null ? info.getDisplayName() : ""; // don't crash - msg = rez.getString(R.string.kg_sim_pin_instructions_multi, displayName); - if (info != null) { - color = info.getIconTint(); - } - } - if (isEsimLocked) { - msg = rez.getString(R.string.kg_sim_lock_esim_instructions, msg); - } - - if (mView.getVisibility() == View.VISIBLE) { - mMessageAreaController.setMessage(msg); - } - mSimImageView.setImageTintList(ColorStateList.valueOf(color)); - } - - private void handleSubInfoChangeIfNeeded() { - int subId = mKeyguardUpdateMonitor - .getNextSubIdForState(TelephonyManager.SIM_STATE_PIN_REQUIRED); - if (subId != mSubId && SubscriptionManager.isValidSubscriptionId(subId)) { - mSubId = subId; - mShowDefaultMessage = true; - mRemainingAttempts = -1; - } - } -} diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java index 932d9b966fa39..5148dd709026f 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java @@ -16,11 +16,27 @@ package com.android.keyguard; +import android.annotation.NonNull; +import android.app.Activity; +import android.app.AlertDialog; +import android.app.Dialog; import android.app.ProgressDialog; import android.content.Context; +import android.content.res.ColorStateList; +import android.content.res.Resources; +import android.content.res.TypedArray; +import android.graphics.Color; +import android.telephony.PinResult; +import android.telephony.SubscriptionInfo; +import android.telephony.SubscriptionManager; +import android.telephony.TelephonyManager; import android.util.AttributeSet; import android.util.Log; +import android.view.View; +import android.view.WindowManager; +import android.widget.ImageView; +import com.android.systemui.Dependency; import com.android.systemui.R; @@ -28,10 +44,47 @@ import com.android.systemui.R; * Displays a PIN pad for entering a PUK (Pin Unlock Kode) provided by a carrier. */ public class KeyguardSimPukView extends KeyguardPinBasedInputView { + private static final String LOG_TAG = "KeyguardSimPukView"; private static final boolean DEBUG = KeyguardConstants.DEBUG; public static final String TAG = "KeyguardSimPukView"; private ProgressDialog mSimUnlockProgressDialog = null; + private CheckSimPuk mCheckSimPukThread; + + // Below flag is set to true during power-up or when a new SIM card inserted on device. + // When this is true and when SIM card is PUK locked state, on PIN lock screen, message would + // be displayed to inform user about the number of remaining PUK attempts left. + private boolean mShowDefaultMessage = true; + private int mRemainingAttempts = -1; + private String mPukText; + private String mPinText; + private StateMachine mStateMachine = new StateMachine(); + private AlertDialog mRemainingAttemptsDialog; + private int mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; + private ImageView mSimImageView; + + KeyguardUpdateMonitorCallback mUpdateMonitorCallback = new KeyguardUpdateMonitorCallback() { + @Override + public void onSimStateChanged(int subId, int slotId, int simState) { + if (DEBUG) Log.v(TAG, "onSimStateChanged(subId=" + subId + ",state=" + simState + ")"); + switch(simState) { + // If the SIM is unlocked via a key sequence through the emergency dialer, it will + // move into the READY state and the PUK lock keyguard should be removed. + case TelephonyManager.SIM_STATE_READY: { + mRemainingAttempts = -1; + mShowDefaultMessage = true; + // mCallback can be null if onSimStateChanged callback is called when keyguard + // isn't active. + if (mCallback != null) { + mCallback.dismiss(true, KeyguardUpdateMonitor.getCurrentUser()); + } + break; + } + default: + resetState(); + } + } + }; public KeyguardSimPukView(Context context) { this(context, null); @@ -41,14 +94,136 @@ public class KeyguardSimPukView extends KeyguardPinBasedInputView { super(context, attrs); } + private class StateMachine { + final int ENTER_PUK = 0; + final int ENTER_PIN = 1; + final int CONFIRM_PIN = 2; + final int DONE = 3; + private int state = ENTER_PUK; + + public void next() { + int msg = 0; + if (state == ENTER_PUK) { + if (checkPuk()) { + state = ENTER_PIN; + msg = R.string.kg_puk_enter_pin_hint; + } else { + msg = R.string.kg_invalid_sim_puk_hint; + } + } else if (state == ENTER_PIN) { + if (checkPin()) { + state = CONFIRM_PIN; + msg = R.string.kg_enter_confirm_pin_hint; + } else { + msg = R.string.kg_invalid_sim_pin_hint; + } + } else if (state == CONFIRM_PIN) { + if (confirmPin()) { + state = DONE; + msg = R.string.keyguard_sim_unlock_progress_dialog_message; + updateSim(); + } else { + state = ENTER_PIN; // try again? + msg = R.string.kg_invalid_confirm_pin_hint; + } + } + resetPasswordText(true /* animate */, true /* announce */); + if (msg != 0) { + mSecurityMessageDisplay.setMessage(msg); + } + } + + + void reset() { + mPinText=""; + mPukText=""; + state = ENTER_PUK; + handleSubInfoChangeIfNeeded(); + if (mShowDefaultMessage) { + showDefaultMessage(); + } + boolean isEsimLocked = KeyguardEsimArea.isEsimLocked(mContext, mSubId); + + KeyguardEsimArea esimButton = findViewById(R.id.keyguard_esim_area); + esimButton.setVisibility(isEsimLocked ? View.VISIBLE : View.GONE); + mPasswordEntry.requestFocus(); + } + + + } + + private void showDefaultMessage() { + if (mRemainingAttempts >= 0) { + mSecurityMessageDisplay.setMessage(getPukPasswordErrorMessage( + mRemainingAttempts, true)); + return; + } + + boolean isEsimLocked = KeyguardEsimArea.isEsimLocked(mContext, mSubId); + int count = 1; + TelephonyManager telephonyManager = + (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE); + if (telephonyManager != null) { + count = telephonyManager.getActiveModemCount(); + } + Resources rez = getResources(); + String msg; + TypedArray array = mContext.obtainStyledAttributes(new int[] { R.attr.wallpaperTextColor }); + int color = array.getColor(0, Color.WHITE); + array.recycle(); + if (count < 2) { + msg = rez.getString(R.string.kg_puk_enter_puk_hint); + } else { + SubscriptionInfo info = Dependency.get(KeyguardUpdateMonitor.class) + .getSubscriptionInfoForSubId(mSubId); + CharSequence displayName = info != null ? info.getDisplayName() : ""; + msg = rez.getString(R.string.kg_puk_enter_puk_hint_multi, displayName); + if (info != null) { + color = info.getIconTint(); + } + } + if (isEsimLocked) { + msg = rez.getString(R.string.kg_sim_lock_esim_instructions, msg); + } + if (mSecurityMessageDisplay != null) { + mSecurityMessageDisplay.setMessage(msg); + } + mSimImageView.setImageTintList(ColorStateList.valueOf(color)); + + // Sending empty PUK here to query the number of remaining PIN attempts + new CheckSimPuk("", "", mSubId) { + void onSimLockChangedResponse(final PinResult result) { + if (result == null) Log.e(LOG_TAG, "onSimCheckResponse, pin result is NULL"); + else { + Log.d(LOG_TAG, "onSimCheckResponse " + " empty One result " + + result.toString()); + if (result.getAttemptsRemaining() >= 0) { + mRemainingAttempts = result.getAttemptsRemaining(); + mSecurityMessageDisplay.setMessage( + getPukPasswordErrorMessage(result.getAttemptsRemaining(), true)); + } + } + } + }.start(); + } + + private void handleSubInfoChangeIfNeeded() { + KeyguardUpdateMonitor monitor = Dependency.get(KeyguardUpdateMonitor.class); + int subId = monitor.getNextSubIdForState(TelephonyManager.SIM_STATE_PUK_REQUIRED); + if (subId != mSubId && SubscriptionManager.isValidSubscriptionId(subId)) { + mSubId = subId; + mShowDefaultMessage = true; + mRemainingAttempts = -1; + } + } + @Override protected int getPromptReasonStringRes(int reason) { // No message on SIM Puk return 0; } - String getPukPasswordErrorMessage( - int attemptsRemaining, boolean isDefault, boolean isEsimLocked) { + private String getPukPasswordErrorMessage(int attemptsRemaining, boolean isDefault) { String displayMessage; if (attemptsRemaining == 0) { @@ -63,18 +238,27 @@ public class KeyguardSimPukView extends KeyguardPinBasedInputView { R.string.kg_password_puk_failed; displayMessage = getContext().getString(msgId); } - if (isEsimLocked) { + if (KeyguardEsimArea.isEsimLocked(mContext, mSubId)) { displayMessage = getResources() .getString(R.string.kg_sim_lock_esim_instructions, displayMessage); } - if (DEBUG) { - Log.d(TAG, "getPukPasswordErrorMessage:" - + " attemptsRemaining=" + attemptsRemaining - + " displayMessage=" + displayMessage); - } + if (DEBUG) Log.d(LOG_TAG, "getPukPasswordErrorMessage:" + + " attemptsRemaining=" + attemptsRemaining + " displayMessage=" + displayMessage); return displayMessage; } + @Override + public void resetState() { + super.resetState(); + mStateMachine.reset(); + } + + @Override + protected boolean shouldLockout(long deadline) { + // SIM PUK doesn't have a timed lockout + return false; + } + @Override protected int getPasswordTextViewId() { return R.id.pukEntry; @@ -87,6 +271,20 @@ public class KeyguardSimPukView extends KeyguardPinBasedInputView { if (mEcaView instanceof EmergencyCarrierArea) { ((EmergencyCarrierArea) mEcaView).setCarrierTextVisible(true); } + mSimImageView = findViewById(R.id.keyguard_sim); + } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + Dependency.get(KeyguardUpdateMonitor.class).registerCallback(mUpdateMonitorCallback); + resetState(); + } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + Dependency.get(KeyguardUpdateMonitor.class).removeCallback(mUpdateMonitorCallback); } @Override @@ -102,8 +300,168 @@ public class KeyguardSimPukView extends KeyguardPinBasedInputView { } } + /** + * Since the IPC can block, we want to run the request in a separate thread + * with a callback. + */ + private abstract class CheckSimPuk extends Thread { + + private final String mPin, mPuk; + private final int mSubId; + + protected CheckSimPuk(String puk, String pin, int subId) { + mPuk = puk; + mPin = pin; + mSubId = subId; + } + + abstract void onSimLockChangedResponse(@NonNull PinResult result); + + @Override + public void run() { + if (DEBUG) Log.v(TAG, "call supplyPukReportResult()"); + TelephonyManager telephonyManager = + ((TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE)) + .createForSubscriptionId(mSubId); + final PinResult result = telephonyManager.supplyPukReportPinResult(mPuk, mPin); + if (result == null) { + Log.e(TAG, "Error result for supplyPukReportResult."); + post(new Runnable() { + @Override + public void run() { + onSimLockChangedResponse(PinResult.getDefaultFailedResult()); + } + }); + } else { + if (DEBUG) { + Log.v(TAG, "supplyPukReportResult returned: " + result.toString()); + } + post(new Runnable() { + @Override + public void run() { + onSimLockChangedResponse(result); + } + }); + } + } + } + + private Dialog getSimUnlockProgressDialog() { + if (mSimUnlockProgressDialog == null) { + mSimUnlockProgressDialog = new ProgressDialog(mContext); + mSimUnlockProgressDialog.setMessage( + mContext.getString(R.string.kg_sim_unlock_progress_dialog_message)); + mSimUnlockProgressDialog.setIndeterminate(true); + mSimUnlockProgressDialog.setCancelable(false); + if (!(mContext instanceof Activity)) { + mSimUnlockProgressDialog.getWindow().setType( + WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG); + } + } + return mSimUnlockProgressDialog; + } + + private Dialog getPukRemainingAttemptsDialog(int remaining) { + String msg = getPukPasswordErrorMessage(remaining, false); + if (mRemainingAttemptsDialog == null) { + AlertDialog.Builder builder = new AlertDialog.Builder(mContext); + builder.setMessage(msg); + builder.setCancelable(false); + builder.setNeutralButton(R.string.ok, null); + mRemainingAttemptsDialog = builder.create(); + mRemainingAttemptsDialog.getWindow().setType( + WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG); + } else { + mRemainingAttemptsDialog.setMessage(msg); + } + return mRemainingAttemptsDialog; + } + + private boolean checkPuk() { + // make sure the puk is at least 8 digits long. + if (mPasswordEntry.getText().length() == 8) { + mPukText = mPasswordEntry.getText(); + return true; + } + return false; + } + + private boolean checkPin() { + // make sure the PIN is between 4 and 8 digits + int length = mPasswordEntry.getText().length(); + if (length >= 4 && length <= 8) { + mPinText = mPasswordEntry.getText(); + return true; + } + return false; + } + + public boolean confirmPin() { + return mPinText.equals(mPasswordEntry.getText()); + } + + private void updateSim() { + getSimUnlockProgressDialog().show(); + + if (mCheckSimPukThread == null) { + mCheckSimPukThread = new CheckSimPuk(mPukText, mPinText, mSubId) { + @Override + void onSimLockChangedResponse(final PinResult result) { + post(new Runnable() { + @Override + public void run() { + if (mSimUnlockProgressDialog != null) { + mSimUnlockProgressDialog.hide(); + } + resetPasswordText(true /* animate */, + /* announce */ + result.getType() != PinResult.PIN_RESULT_TYPE_SUCCESS); + if (result.getType() == PinResult.PIN_RESULT_TYPE_SUCCESS) { + Dependency.get(KeyguardUpdateMonitor.class) + .reportSimUnlocked(mSubId); + mRemainingAttempts = -1; + mShowDefaultMessage = true; + if (mCallback != null) { + mCallback.dismiss(true, + KeyguardUpdateMonitor.getCurrentUser()); + } + } else { + mShowDefaultMessage = false; + if (result.getType() == PinResult.PIN_RESULT_TYPE_INCORRECT) { + // show message + mSecurityMessageDisplay.setMessage(getPukPasswordErrorMessage( + result.getAttemptsRemaining(), false)); + if (result.getAttemptsRemaining() <= 2) { + // this is getting critical - show dialog + getPukRemainingAttemptsDialog( + result.getAttemptsRemaining()).show(); + } else { + // show message + mSecurityMessageDisplay.setMessage( + getPukPasswordErrorMessage( + result.getAttemptsRemaining(), false)); + } + } else { + mSecurityMessageDisplay.setMessage(getContext().getString( + R.string.kg_password_puk_failed)); + } + if (DEBUG) Log.d(LOG_TAG, "verifyPasswordAndUnlock " + + " UpdateSim.onSimCheckResponse: " + + " attemptsRemaining=" + result.getAttemptsRemaining()); + } + mStateMachine.reset(); + mCheckSimPukThread = null; + } + }); + } + }; + mCheckSimPukThread.start(); + } + } + @Override - public void onResume(int reason) { + protected void verifyPasswordAndUnlock() { + mStateMachine.next(); } @Override diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java deleted file mode 100644 index 9b2166b044155..0000000000000 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java +++ /dev/null @@ -1,404 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.keyguard; - -import android.annotation.NonNull; -import android.app.Activity; -import android.app.AlertDialog; -import android.app.Dialog; -import android.app.ProgressDialog; -import android.content.res.ColorStateList; -import android.content.res.Resources; -import android.content.res.TypedArray; -import android.graphics.Color; -import android.telephony.PinResult; -import android.telephony.SubscriptionInfo; -import android.telephony.SubscriptionManager; -import android.telephony.TelephonyManager; -import android.util.Log; -import android.view.View; -import android.view.WindowManager; -import android.widget.ImageView; - -import com.android.internal.util.LatencyTracker; -import com.android.internal.widget.LockPatternUtils; -import com.android.keyguard.KeyguardSecurityModel.SecurityMode; -import com.android.systemui.Dependency; -import com.android.systemui.R; - -public class KeyguardSimPukViewController - extends KeyguardPinBasedInputViewController { - private static final boolean DEBUG = KeyguardConstants.DEBUG; - public static final String TAG = "KeyguardSimPukView"; - - private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; - private final TelephonyManager mTelephonyManager; - - private String mPukText; - private String mPinText; - private int mRemainingAttempts; - // Below flag is set to true during power-up or when a new SIM card inserted on device. - // When this is true and when SIM card is PUK locked state, on PIN lock screen, message would - // be displayed to inform user about the number of remaining PUK attempts left. - private boolean mShowDefaultMessage; - private StateMachine mStateMachine = new StateMachine(); - private int mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; - private CheckSimPuk mCheckSimPukThread; - private ProgressDialog mSimUnlockProgressDialog; - - KeyguardUpdateMonitorCallback mUpdateMonitorCallback = new KeyguardUpdateMonitorCallback() { - @Override - public void onSimStateChanged(int subId, int slotId, int simState) { - if (DEBUG) Log.v(TAG, "onSimStateChanged(subId=" + subId + ",state=" + simState + ")"); - // If the SIM is unlocked via a key sequence through the emergency dialer, it will - // move into the READY state and the PUK lock keyguard should be removed. - if (simState == TelephonyManager.SIM_STATE_READY) { - mRemainingAttempts = -1; - mShowDefaultMessage = true; - mKeyguardSecurityCallback.dismiss(true, KeyguardUpdateMonitor.getCurrentUser()); - } else { - resetState(); - } - } - }; - private ImageView mSimImageView; - private AlertDialog mRemainingAttemptsDialog; - - protected KeyguardSimPukViewController(KeyguardSimPukView view, - KeyguardUpdateMonitor keyguardUpdateMonitor, - SecurityMode securityMode, LockPatternUtils lockPatternUtils, - KeyguardSecurityCallback keyguardSecurityCallback, - KeyguardMessageAreaController.Factory messageAreaControllerFactory, - LatencyTracker latencyTracker, - LiftToActivateListener liftToActivateListener, - TelephonyManager telephonyManager) { - super(view, keyguardUpdateMonitor, securityMode, lockPatternUtils, keyguardSecurityCallback, - messageAreaControllerFactory, latencyTracker, liftToActivateListener); - mKeyguardUpdateMonitor = keyguardUpdateMonitor; - mTelephonyManager = telephonyManager; - mSimImageView = mView.findViewById(R.id.keyguard_sim); - } - - @Override - protected void onViewAttached() { - super.onViewAttached(); - mKeyguardUpdateMonitor.registerCallback(mUpdateMonitorCallback); - } - - @Override - protected void onViewDetached() { - super.onViewDetached(); - mKeyguardUpdateMonitor.removeCallback(mUpdateMonitorCallback); - } - - @Override - void resetState() { - super.resetState(); - mStateMachine.reset(); - } - - @Override - protected void verifyPasswordAndUnlock() { - mStateMachine.next(); - } - - private class StateMachine { - static final int ENTER_PUK = 0; - static final int ENTER_PIN = 1; - static final int CONFIRM_PIN = 2; - static final int DONE = 3; - - private int mState = ENTER_PUK; - - public void next() { - int msg = 0; - if (mState == ENTER_PUK) { - if (checkPuk()) { - mState = ENTER_PIN; - msg = com.android.systemui.R.string.kg_puk_enter_pin_hint; - } else { - msg = com.android.systemui.R.string.kg_invalid_sim_puk_hint; - } - } else if (mState == ENTER_PIN) { - if (checkPin()) { - mState = CONFIRM_PIN; - msg = com.android.systemui.R.string.kg_enter_confirm_pin_hint; - } else { - msg = com.android.systemui.R.string.kg_invalid_sim_pin_hint; - } - } else if (mState == CONFIRM_PIN) { - if (confirmPin()) { - mState = DONE; - msg = com.android.systemui.R.string.keyguard_sim_unlock_progress_dialog_message; - updateSim(); - } else { - mState = ENTER_PIN; // try again? - msg = com.android.systemui.R.string.kg_invalid_confirm_pin_hint; - } - } - mView.resetPasswordText(true /* animate */, true /* announce */); - if (msg != 0) { - mMessageAreaController.setMessage(msg); - } - } - - - void reset() { - mPinText = ""; - mPukText = ""; - mState = ENTER_PUK; - handleSubInfoChangeIfNeeded(); - if (mShowDefaultMessage) { - showDefaultMessage(); - } - boolean isEsimLocked = KeyguardEsimArea.isEsimLocked(mView.getContext(), mSubId); - - KeyguardEsimArea esimButton = mView.findViewById(R.id.keyguard_esim_area); - esimButton.setVisibility(isEsimLocked ? View.VISIBLE : View.GONE); - mPasswordEntry.requestFocus(); - } - } - - private void showDefaultMessage() { - if (mRemainingAttempts >= 0) { - mMessageAreaController.setMessage(mView.getPukPasswordErrorMessage( - mRemainingAttempts, true, - KeyguardEsimArea.isEsimLocked(mView.getContext(), mSubId))); - return; - } - - boolean isEsimLocked = KeyguardEsimArea.isEsimLocked(mView.getContext(), mSubId); - int count = 1; - if (mTelephonyManager != null) { - count = mTelephonyManager.getActiveModemCount(); - } - Resources rez = mView.getResources(); - String msg; - TypedArray array = mView.getContext().obtainStyledAttributes( - new int[] { R.attr.wallpaperTextColor }); - int color = array.getColor(0, Color.WHITE); - array.recycle(); - if (count < 2) { - msg = rez.getString(R.string.kg_puk_enter_puk_hint); - } else { - SubscriptionInfo info = Dependency.get(KeyguardUpdateMonitor.class) - .getSubscriptionInfoForSubId(mSubId); - CharSequence displayName = info != null ? info.getDisplayName() : ""; - msg = rez.getString(R.string.kg_puk_enter_puk_hint_multi, displayName); - if (info != null) { - color = info.getIconTint(); - } - } - if (isEsimLocked) { - msg = rez.getString(R.string.kg_sim_lock_esim_instructions, msg); - } - mMessageAreaController.setMessage(msg); - mSimImageView.setImageTintList(ColorStateList.valueOf(color)); - - // Sending empty PUK here to query the number of remaining PIN attempts - new CheckSimPuk("", "", mSubId) { - void onSimLockChangedResponse(final PinResult result) { - if (result == null) Log.e(TAG, "onSimCheckResponse, pin result is NULL"); - else { - Log.d(TAG, "onSimCheckResponse " + " empty One result " - + result.toString()); - if (result.getAttemptsRemaining() >= 0) { - mRemainingAttempts = result.getAttemptsRemaining(); - mMessageAreaController.setMessage( - mView.getPukPasswordErrorMessage( - result.getAttemptsRemaining(), true, - KeyguardEsimArea.isEsimLocked(mView.getContext(), mSubId))); - } - } - } - }.start(); - } - - private boolean checkPuk() { - // make sure the puk is at least 8 digits long. - if (mPasswordEntry.getText().length() == 8) { - mPukText = mPasswordEntry.getText(); - return true; - } - return false; - } - - private boolean checkPin() { - // make sure the PIN is between 4 and 8 digits - int length = mPasswordEntry.getText().length(); - if (length >= 4 && length <= 8) { - mPinText = mPasswordEntry.getText(); - return true; - } - return false; - } - - public boolean confirmPin() { - return mPinText.equals(mPasswordEntry.getText()); - } - - - - - private void updateSim() { - getSimUnlockProgressDialog().show(); - - if (mCheckSimPukThread == null) { - mCheckSimPukThread = new CheckSimPuk(mPukText, mPinText, mSubId) { - @Override - void onSimLockChangedResponse(final PinResult result) { - mView.post(() -> { - if (mSimUnlockProgressDialog != null) { - mSimUnlockProgressDialog.hide(); - } - mView.resetPasswordText(true /* animate */, - /* announce */ - result.getType() != PinResult.PIN_RESULT_TYPE_SUCCESS); - if (result.getType() == PinResult.PIN_RESULT_TYPE_SUCCESS) { - mKeyguardUpdateMonitor.reportSimUnlocked(mSubId); - mRemainingAttempts = -1; - mShowDefaultMessage = true; - - mKeyguardSecurityCallback.dismiss( - true, KeyguardUpdateMonitor.getCurrentUser()); - } else { - mShowDefaultMessage = false; - if (result.getType() == PinResult.PIN_RESULT_TYPE_INCORRECT) { - // show message - mMessageAreaController.setMessage(mView.getPukPasswordErrorMessage( - result.getAttemptsRemaining(), false, - KeyguardEsimArea.isEsimLocked(mView.getContext(), mSubId))); - if (result.getAttemptsRemaining() <= 2) { - // this is getting critical - show dialog - getPukRemainingAttemptsDialog( - result.getAttemptsRemaining()).show(); - } else { - // show message - mMessageAreaController.setMessage( - mView.getPukPasswordErrorMessage( - result.getAttemptsRemaining(), false, - KeyguardEsimArea.isEsimLocked( - mView.getContext(), mSubId))); - } - } else { - mMessageAreaController.setMessage(mView.getResources().getString( - R.string.kg_password_puk_failed)); - } - if (DEBUG) { - Log.d(TAG, "verifyPasswordAndUnlock " - + " UpdateSim.onSimCheckResponse: " - + " attemptsRemaining=" + result.getAttemptsRemaining()); - } - } - mStateMachine.reset(); - mCheckSimPukThread = null; - }); - } - }; - mCheckSimPukThread.start(); - } - } - - @Override - protected boolean shouldLockout(long deadline) { - // SIM PUK doesn't have a timed lockout - return false; - } - - private Dialog getSimUnlockProgressDialog() { - if (mSimUnlockProgressDialog == null) { - mSimUnlockProgressDialog = new ProgressDialog(mView.getContext()); - mSimUnlockProgressDialog.setMessage( - mView.getResources().getString(R.string.kg_sim_unlock_progress_dialog_message)); - mSimUnlockProgressDialog.setIndeterminate(true); - mSimUnlockProgressDialog.setCancelable(false); - if (!(mView.getContext() instanceof Activity)) { - mSimUnlockProgressDialog.getWindow().setType( - WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG); - } - } - return mSimUnlockProgressDialog; - } - - private void handleSubInfoChangeIfNeeded() { - int subId = mKeyguardUpdateMonitor.getNextSubIdForState( - TelephonyManager.SIM_STATE_PUK_REQUIRED); - if (subId != mSubId && SubscriptionManager.isValidSubscriptionId(subId)) { - mSubId = subId; - mShowDefaultMessage = true; - mRemainingAttempts = -1; - } - } - - - private Dialog getPukRemainingAttemptsDialog(int remaining) { - String msg = mView.getPukPasswordErrorMessage(remaining, false, - KeyguardEsimArea.isEsimLocked(mView.getContext(), mSubId)); - if (mRemainingAttemptsDialog == null) { - AlertDialog.Builder builder = new AlertDialog.Builder(mView.getContext()); - builder.setMessage(msg); - builder.setCancelable(false); - builder.setNeutralButton(R.string.ok, null); - mRemainingAttemptsDialog = builder.create(); - mRemainingAttemptsDialog.getWindow().setType( - WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG); - } else { - mRemainingAttemptsDialog.setMessage(msg); - } - return mRemainingAttemptsDialog; - } - - /** - * Since the IPC can block, we want to run the request in a separate thread - * with a callback. - */ - private abstract class CheckSimPuk extends Thread { - - private final String mPin, mPuk; - private final int mSubId; - - protected CheckSimPuk(String puk, String pin, int subId) { - mPuk = puk; - mPin = pin; - mSubId = subId; - } - - abstract void onSimLockChangedResponse(@NonNull PinResult result); - - @Override - public void run() { - if (DEBUG) Log.v(TAG, "call supplyPukReportResult()"); - TelephonyManager telephonyManager = mTelephonyManager.createForSubscriptionId(mSubId); - final PinResult result = telephonyManager.supplyPukReportPinResult(mPuk, mPin); - if (result == null) { - Log.e(TAG, "Error result for supplyPukReportResult."); - mView.post(() -> onSimLockChangedResponse(PinResult.getDefaultFailedResult())); - } else { - if (DEBUG) { - Log.v(TAG, "supplyPukReportResult returned: " + result.toString()); - } - mView.post(new Runnable() { - @Override - public void run() { - onSimLockChangedResponse(result); - } - }); - } - } - } - -} diff --git a/packages/SystemUI/src/com/android/keyguard/LiftToActivateListener.java b/packages/SystemUI/src/com/android/keyguard/LiftToActivateListener.java index 425e50ed63975..e59602b1cfffc 100644 --- a/packages/SystemUI/src/com/android/keyguard/LiftToActivateListener.java +++ b/packages/SystemUI/src/com/android/keyguard/LiftToActivateListener.java @@ -16,12 +16,11 @@ package com.android.keyguard; +import android.content.Context; import android.view.MotionEvent; import android.view.View; import android.view.accessibility.AccessibilityManager; -import javax.inject.Inject; - /** * Hover listener that implements lift-to-activate interaction for * accessibility. May be added to multiple views. @@ -32,9 +31,9 @@ class LiftToActivateListener implements View.OnHoverListener { private boolean mCachedClickableState; - @Inject - LiftToActivateListener(AccessibilityManager accessibilityManager) { - mAccessibilityManager = accessibilityManager; + public LiftToActivateListener(Context context) { + mAccessibilityManager = (AccessibilityManager) context.getSystemService( + Context.ACCESSIBILITY_SERVICE); } @Override diff --git a/packages/SystemUI/src/com/android/keyguard/NumPadKey.java b/packages/SystemUI/src/com/android/keyguard/NumPadKey.java index 2205fdd4267d5..b0457fce6a1a0 100644 --- a/packages/SystemUI/src/com/android/keyguard/NumPadKey.java +++ b/packages/SystemUI/src/com/android/keyguard/NumPadKey.java @@ -26,7 +26,6 @@ import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; -import android.view.accessibility.AccessibilityManager; import android.widget.TextView; import com.android.internal.widget.LockPatternUtils; @@ -91,8 +90,7 @@ public class NumPadKey extends ViewGroup { } setOnClickListener(mListener); - setOnHoverListener(new LiftToActivateListener( - (AccessibilityManager) context.getSystemService(Context.ACCESSIBILITY_SERVICE))); + setOnHoverListener(new LiftToActivateListener(context)); mLockPatternUtils = new LockPatternUtils(context); mPM = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); diff --git a/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java b/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java index 79925bad3cc78..b35579d3624ba 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java @@ -62,7 +62,6 @@ import android.view.ViewConfiguration; import android.view.WindowManager; import android.view.WindowManagerGlobal; import android.view.accessibility.AccessibilityManager; -import android.view.inputmethod.InputMethodManager; import com.android.internal.app.IBatteryStats; import com.android.internal.statusbar.IStatusBarService; @@ -182,12 +181,6 @@ public class FrameworkServicesModule { return context.getSystemService(FingerprintManager.class); } - @Provides - @Singleton - static InputMethodManager provideInputMethodManager(Context context) { - return context.getSystemService(InputMethodManager.class); - } - @Provides @Singleton static IPackageManager provideIPackageManager() { diff --git a/packages/SystemUI/src/com/android/systemui/util/ViewController.java b/packages/SystemUI/src/com/android/systemui/util/ViewController.java index f30472f75d298..64f8dbbb9e34d 100644 --- a/packages/SystemUI/src/com/android/systemui/util/ViewController.java +++ b/packages/SystemUI/src/com/android/systemui/util/ViewController.java @@ -23,20 +23,7 @@ import android.view.View.OnAttachStateChangeListener; * Utility class that handles view lifecycle events for View Controllers. * * Implementations should handle setup and teardown related activities inside of - * {@link #onViewAttached()} and {@link #onViewDetached()}. Be sure to call {@link #init()} on - * any child controllers that this uses. This can be done in {@link init()} if the controllers - * are injected, or right after creation time of the child controller. - * - * Tip: View "attachment" happens top down - parents are notified that they are attached before - * any children. That means that if you call a method on a child controller in - * {@link #onViewAttached()}, the child controller may not have had its onViewAttach method - * called, so it may not be fully set up. - * - * As such, make sure that methods on your controller are safe to call _before_ its {@link #init()} - * and {@link #onViewAttached()} methods are called. Specifically, if your controller must call - * {@link View#findViewById(int)} on its root view to setup member variables, do so in its - * constructor. Save {@link #onViewAttached()} for things that can happen post-construction - adding - * listeners, dynamically changing content, or other runtime decisions. + * {@link #onViewAttached()} and {@link #onViewDetached()}. * * @param View class that this ViewController is for. */ diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java deleted file mode 100644 index 2d6ecb6f877e4..0000000000000 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.keyguard; - -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyZeroInteractions; -import static org.mockito.Mockito.when; - -import android.testing.AndroidTestingRunner; -import android.testing.TestableLooper.RunWithLooper; -import android.view.KeyEvent; - -import androidx.test.filters.SmallTest; - -import com.android.internal.util.LatencyTracker; -import com.android.internal.widget.LockPatternUtils; -import com.android.keyguard.KeyguardAbsKeyInputView.KeyDownListener; -import com.android.keyguard.KeyguardSecurityModel.SecurityMode; -import com.android.systemui.R; -import com.android.systemui.SysuiTestCase; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.ArgumentCaptor; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -@SmallTest -@RunWith(AndroidTestingRunner.class) -@RunWithLooper -public class KeyguardAbsKeyInputViewControllerTest extends SysuiTestCase { - - @Mock - private KeyguardAbsKeyInputView mAbsKeyInputView; - @Mock - private PasswordTextView mPasswordEntry; - @Mock - private KeyguardMessageArea mKeyguardMessageArea; - @Mock - private KeyguardUpdateMonitor mKeyguardUpdateMonitor; - @Mock - private SecurityMode mSecurityMode; - @Mock - private LockPatternUtils mLockPatternUtils; - @Mock - private KeyguardSecurityCallback mKeyguardSecurityCallback; - @Mock - private KeyguardMessageAreaController.Factory mKeyguardMessageAreaControllerFactory; - @Mock - private KeyguardMessageAreaController mKeyguardMessageAreaController; - @Mock - private LatencyTracker mLatencyTracker; - - private KeyguardAbsKeyInputViewController mKeyguardAbsKeyInputViewController; - - @Before - public void setup() { - MockitoAnnotations.initMocks(this); - when(mKeyguardMessageAreaControllerFactory.create(any(KeyguardMessageArea.class))) - .thenReturn(mKeyguardMessageAreaController); - when(mAbsKeyInputView.getPasswordTextViewId()).thenReturn(1); - when(mAbsKeyInputView.findViewById(1)).thenReturn(mPasswordEntry); - when(mAbsKeyInputView.isAttachedToWindow()).thenReturn(true); - when(mAbsKeyInputView.findViewById(R.id.keyguard_message_area)) - .thenReturn(mKeyguardMessageArea); - mKeyguardAbsKeyInputViewController = new KeyguardAbsKeyInputViewController(mAbsKeyInputView, - mKeyguardUpdateMonitor, mSecurityMode, mLockPatternUtils, mKeyguardSecurityCallback, - mKeyguardMessageAreaControllerFactory, mLatencyTracker) { - @Override - void resetState() { - } - - @Override - public void onResume(int reason) { - super.onResume(reason); - } - }; - mKeyguardAbsKeyInputViewController.init(); - } - - @Test - public void onKeyDown_clearsSecurityMessage() { - ArgumentCaptor onKeyDownListenerArgumentCaptor = - ArgumentCaptor.forClass(KeyDownListener.class); - verify(mAbsKeyInputView).setKeyDownListener(onKeyDownListenerArgumentCaptor.capture()); - onKeyDownListenerArgumentCaptor.getValue().onKeyDown( - KeyEvent.KEYCODE_0, mock(KeyEvent.class)); - verify(mKeyguardSecurityCallback).userActivity(); - verify(mKeyguardMessageAreaController).setMessage(eq("")); - } - - @Test - public void onKeyDown_noSecurityMessageInteraction() { - ArgumentCaptor onKeyDownListenerArgumentCaptor = - ArgumentCaptor.forClass(KeyDownListener.class); - verify(mAbsKeyInputView).setKeyDownListener(onKeyDownListenerArgumentCaptor.capture()); - onKeyDownListenerArgumentCaptor.getValue().onKeyDown( - KeyEvent.KEYCODE_UNKNOWN, mock(KeyEvent.class)); - verifyZeroInteractions(mKeyguardSecurityCallback); - verifyZeroInteractions(mKeyguardMessageAreaController); - } -} diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java deleted file mode 100644 index 4944284698a05..0000000000000 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.keyguard; - -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import android.testing.AndroidTestingRunner; -import android.testing.TestableLooper.RunWithLooper; -import android.view.View; - -import androidx.test.filters.SmallTest; - -import com.android.internal.util.LatencyTracker; -import com.android.internal.widget.LockPatternUtils; -import com.android.keyguard.KeyguardSecurityModel.SecurityMode; -import com.android.systemui.R; -import com.android.systemui.SysuiTestCase; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -@SmallTest -@RunWith(AndroidTestingRunner.class) -@RunWithLooper -public class KeyguardPinBasedInputViewControllerTest extends SysuiTestCase { - - @Mock - private KeyguardPinBasedInputView mPinBasedInputView; - @Mock - private PasswordTextView mPasswordEntry; - @Mock - private KeyguardMessageArea mKeyguardMessageArea; - @Mock - private KeyguardUpdateMonitor mKeyguardUpdateMonitor; - @Mock - private SecurityMode mSecurityMode; - @Mock - private LockPatternUtils mLockPatternUtils; - @Mock - private KeyguardSecurityCallback mKeyguardSecurityCallback; - @Mock - private KeyguardMessageAreaController.Factory mKeyguardMessageAreaControllerFactory; - @Mock - private KeyguardMessageAreaController mKeyguardMessageAreaController; - @Mock - private LatencyTracker mLatencyTracker; - @Mock - private LiftToActivateListener mLiftToactivateListener; - @Mock - private View mDeleteButton; - @Mock - private View mOkButton; - - private KeyguardPinBasedInputViewController mKeyguardPinViewController; - - @Before - public void setup() { - MockitoAnnotations.initMocks(this); - when(mKeyguardMessageAreaControllerFactory.create(any(KeyguardMessageArea.class))) - .thenReturn(mKeyguardMessageAreaController); - when(mPinBasedInputView.getPasswordTextViewId()).thenReturn(1); - when(mPinBasedInputView.findViewById(1)).thenReturn(mPasswordEntry); - when(mPinBasedInputView.isAttachedToWindow()).thenReturn(true); - when(mPinBasedInputView.findViewById(R.id.keyguard_message_area)) - .thenReturn(mKeyguardMessageArea); - when(mPinBasedInputView.findViewById(R.id.delete_button)) - .thenReturn(mDeleteButton); - when(mPinBasedInputView.findViewById(R.id.key_enter)) - .thenReturn(mOkButton); - mKeyguardPinViewController = new KeyguardPinBasedInputViewController(mPinBasedInputView, - mKeyguardUpdateMonitor, mSecurityMode, mLockPatternUtils, mKeyguardSecurityCallback, - mKeyguardMessageAreaControllerFactory, mLatencyTracker, mLiftToactivateListener) { - @Override - public void onResume(int reason) { - super.onResume(reason); - } - }; - mKeyguardPinViewController.init(); - } - - @Test - public void onResume_requestsFocus() { - mKeyguardPinViewController.onResume(KeyguardSecurityView.SCREEN_ON); - verify(mPasswordEntry).requestFocus(); - } -} - diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewTest.java new file mode 100644 index 0000000000000..6666a926c68b7 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewTest.java @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.keyguard; + +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyZeroInteractions; + +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper.RunWithLooper; +import android.view.KeyEvent; +import android.view.LayoutInflater; + +import androidx.test.filters.SmallTest; + +import com.android.systemui.R; +import com.android.systemui.SysuiTestCase; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +@SmallTest +@RunWith(AndroidTestingRunner.class) +@RunWithLooper +public class KeyguardPinBasedInputViewTest extends SysuiTestCase { + + @Mock + private PasswordTextView mPasswordEntry; + @Mock + private SecurityMessageDisplay mSecurityMessageDisplay; + @InjectMocks + private KeyguardPinBasedInputView mKeyguardPinView; + + @Before + public void setup() { + LayoutInflater inflater = LayoutInflater.from(getContext()); + mDependency.injectMockDependency(KeyguardUpdateMonitor.class); + mKeyguardPinView = + (KeyguardPinBasedInputView) inflater.inflate(R.layout.keyguard_pin_view, null); + MockitoAnnotations.initMocks(this); + } + + @Test + public void onResume_requestsFocus() { + mKeyguardPinView.onResume(KeyguardSecurityView.SCREEN_ON); + verify(mPasswordEntry).requestFocus(); + } + + @Test + public void onKeyDown_clearsSecurityMessage() { + mKeyguardPinView.onKeyDown(KeyEvent.KEYCODE_0, mock(KeyEvent.class)); + verify(mSecurityMessageDisplay).setMessage(eq("")); + } + + @Test + public void onKeyDown_noSecurityMessageInteraction() { + mKeyguardPinView.onKeyDown(KeyEvent.KEYCODE_UNKNOWN, mock(KeyEvent.class)); + verifyZeroInteractions(mSecurityMessageDisplay); + } +}