Revert^2 "7/N controllers for remaining Keyguard Password ..."
Revert submission 12656261-revert-12585643-b166448040-keyguard-message-area-NUUJOVRYJS This also fixes the original issue introduced in this CL. KeyguardAbsKeyInputViewController#reset would call mView#resetState instead of its own internal resetState method. However, all the logic from the various resetState methods had been moved from the views to their controllers. Simply calling resetState direclty resolves the issue (line 105 of KeyguardAbsKeyInputViewController). Reason for revert: Fixing http://b/169231892 and http://b/169145796 Reverted Changes: I7683b2234:Revert "4/N Setup Controller fo KeyguardSecurityCo... I5cbe04c0c:Revert "5/N Add KeyguardSecurityViewFlipperControl... I11dff38ad:Revert "6/N Add Controller for KeyguardPatternView... I55c250121:Revert "7/N controllers for remaining Keyguard Pas... Ie84234cb8:Revert "8/N Remove View Injection from KeyguardMes... Ic62f199a5:Revert "9/N Clean Up Keyguard Class Structure" Change-Id: Ie3669e0c9a23ffd4443ced0fb08ec754f1df55db Fixes: 169145796
This commit is contained in:
@@ -16,45 +16,30 @@
|
||||
|
||||
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
|
||||
implements EmergencyButton.EmergencyButtonCallback {
|
||||
public abstract class KeyguardAbsKeyInputView extends KeyguardInputView {
|
||||
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);
|
||||
@@ -62,7 +47,10 @@ public abstract class KeyguardAbsKeyInputView extends KeyguardInputView
|
||||
|
||||
public KeyguardAbsKeyInputView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
mKeyguardUpdateMonitor = Dependency.get(KeyguardUpdateMonitor.class);
|
||||
}
|
||||
|
||||
void setEnableHaptics(boolean enableHaptics) {
|
||||
mEnableHaptics = enableHaptics;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -72,28 +60,10 @@ 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();
|
||||
@@ -101,24 +71,7 @@ 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();
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -130,147 +83,14 @@ 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) {
|
||||
// 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;
|
||||
return mKeyDownListener != null && mKeyDownListener.onKeyDown(keyCode, event);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -278,26 +98,6 @@ 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;
|
||||
@@ -305,12 +105,7 @@ 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
|
||||
@@ -336,5 +131,13 @@ 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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,257 @@
|
||||
/*
|
||||
* 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<T extends KeyguardAbsKeyInputView>
|
||||
extends KeyguardInputViewController<T> {
|
||||
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 {
|
||||
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();
|
||||
}
|
||||
}
|
||||
@@ -178,18 +178,18 @@ public class KeyguardHostViewController extends ViewController<KeyguardHostView>
|
||||
/** 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
|
||||
|
||||
@@ -17,32 +17,31 @@
|
||||
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 class KeyguardInputViewController<T extends KeyguardInputView> extends ViewController<T>
|
||||
implements KeyguardSecurityView {
|
||||
public abstract class KeyguardInputViewController<T extends KeyguardInputView>
|
||||
extends ViewController<T> 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);
|
||||
}
|
||||
|
||||
@@ -151,16 +150,29 @@ public class KeyguardInputViewController<T extends KeyguardInputView> extends Vi
|
||||
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) {
|
||||
KeyguardMessageAreaController.Factory messageAreaControllerFactory,
|
||||
InputMethodManager inputMethodManager, @Main DelayableExecutor mainExecutor,
|
||||
@Main Resources resources, LiftToActivateListener liftToActivateListener,
|
||||
TelephonyManager telephonyManager) {
|
||||
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
|
||||
mLockPatternUtils = lockPatternUtils;
|
||||
mLatencyTracker = latencyTracker;
|
||||
mMessageAreaControllerFactory = messageAreaControllerFactory;
|
||||
mInputMethodManager = inputMethodManager;
|
||||
mMainExecutor = mainExecutor;
|
||||
mResources = resources;
|
||||
mLiftToActivateListener = liftToActivateListener;
|
||||
mTelephonyManager = telephonyManager;
|
||||
}
|
||||
|
||||
/** Create a new {@link KeyguardInputViewController}. */
|
||||
@@ -170,9 +182,29 @@ public class KeyguardInputViewController<T extends KeyguardInputView> extends Vi
|
||||
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);
|
||||
}
|
||||
return new KeyguardInputViewController<>(keyguardInputView, securityMode,
|
||||
mLockPatternUtils, keyguardSecurityCallback);
|
||||
|
||||
throw new RuntimeException("Unable to find controller for " + keyguardInputView);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,6 @@ 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;
|
||||
|
||||
/**
|
||||
@@ -40,10 +39,8 @@ 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);
|
||||
@@ -63,15 +60,10 @@ 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
|
||||
@@ -88,7 +80,6 @@ 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
|
||||
@@ -122,6 +113,16 @@ public class KeyguardPINView extends KeyguardPinBasedInputView {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume(int reason) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showUsabilityHint() {
|
||||
}
|
||||
@@ -149,22 +150,24 @@ 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 = mKeyguardUpdateMonitor
|
||||
.needsSlowUnlockTransition()
|
||||
DisappearAnimationUtils disappearAnimationUtils = needsSlowUnlockTransition
|
||||
? mDisappearAnimationUtilsLocked
|
||||
: mDisappearAnimationUtils;
|
||||
disappearAnimationUtils.startAnimation2d(mViews,
|
||||
new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
enableClipping(true);
|
||||
if (finishRunnable != null) {
|
||||
finishRunnable.run();
|
||||
}
|
||||
() -> {
|
||||
enableClipping(true);
|
||||
if (finishRunnable != null) {
|
||||
finishRunnable.run();
|
||||
}
|
||||
});
|
||||
return true;
|
||||
|
||||
@@ -18,48 +18,29 @@ 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, OnEditorActionListener, TextWatcher {
|
||||
implements KeyguardSecurityView {
|
||||
|
||||
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;
|
||||
@@ -70,8 +51,6 @@ 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(
|
||||
@@ -82,20 +61,6 @@ 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
|
||||
@@ -103,28 +68,20 @@ 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) {
|
||||
@@ -146,97 +103,13 @@ 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
|
||||
@@ -265,55 +138,6 @@ 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<InputMethodInfo> 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<InputMethodSubtype> 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() {
|
||||
}
|
||||
@@ -345,46 +169,9 @@ 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 getContext().getString(
|
||||
return getResources().getString(
|
||||
com.android.internal.R.string.keyguard_accessibility_password_unlock);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,270 @@
|
||||
/*
|
||||
* 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<KeyguardPasswordView> {
|
||||
|
||||
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<InputMethodInfo> 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<InputMethodSubtype> 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;
|
||||
}
|
||||
}
|
||||
@@ -180,18 +180,18 @@ public class KeyguardPatternViewController
|
||||
KeyguardSecurityCallback keyguardSecurityCallback,
|
||||
LatencyTracker latencyTracker,
|
||||
KeyguardMessageAreaController.Factory messageAreaControllerFactory) {
|
||||
super(view, securityMode, lockPatternUtils, keyguardSecurityCallback);
|
||||
super(view, securityMode, 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(
|
||||
|
||||
@@ -20,7 +20,6 @@ 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;
|
||||
@@ -29,22 +28,12 @@ import com.android.systemui.R;
|
||||
/**
|
||||
* A Pin based Keyguard input view
|
||||
*/
|
||||
public abstract class KeyguardPinBasedInputView extends KeyguardAbsKeyInputView
|
||||
implements View.OnKeyListener, View.OnTouchListener {
|
||||
public abstract class KeyguardPinBasedInputView extends KeyguardAbsKeyInputView {
|
||||
|
||||
protected PasswordTextView mPasswordEntry;
|
||||
private View mOkButton;
|
||||
private View mDeleteButton;
|
||||
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;
|
||||
private View[] mButtons = new View[10];
|
||||
|
||||
public KeyguardPinBasedInputView(Context context) {
|
||||
this(context, null);
|
||||
@@ -62,7 +51,6 @@ public abstract class KeyguardPinBasedInputView extends KeyguardAbsKeyInputView
|
||||
|
||||
@Override
|
||||
protected void resetState() {
|
||||
setPasswordEntryEnabled(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -86,10 +74,10 @@ public abstract class KeyguardPinBasedInputView extends KeyguardAbsKeyInputView
|
||||
@Override
|
||||
public boolean onKeyDown(int keyCode, KeyEvent event) {
|
||||
if (KeyEvent.isConfirmKey(keyCode)) {
|
||||
performClick(mOkButton);
|
||||
mOkButton.performClick();
|
||||
return true;
|
||||
} else if (keyCode == KeyEvent.KEYCODE_DEL) {
|
||||
performClick(mDeleteButton);
|
||||
mDeleteButton.performClick();
|
||||
return true;
|
||||
}
|
||||
if (keyCode >= KeyEvent.KEYCODE_0 && keyCode <= KeyEvent.KEYCODE_9) {
|
||||
@@ -125,42 +113,9 @@ public abstract class KeyguardPinBasedInputView extends KeyguardAbsKeyInputView
|
||||
}
|
||||
}
|
||||
|
||||
private void performClick(View view) {
|
||||
view.performClick();
|
||||
}
|
||||
|
||||
private void performNumberClick(int number) {
|
||||
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;
|
||||
if (number >= 0 && number <= 9) {
|
||||
mButtons[number].performClick();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -177,93 +132,30 @@ 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;
|
||||
}
|
||||
});
|
||||
|
||||
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);
|
||||
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);
|
||||
|
||||
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(
|
||||
|
||||
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
* 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<T extends KeyguardPinBasedInputView>
|
||||
extends KeyguardAbsKeyInputViewController<T> {
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* 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<KeyguardPINView> {
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -197,6 +197,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard
|
||||
@Override
|
||||
public void init() {
|
||||
super.init();
|
||||
mSecurityViewFlipperController.init();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -16,66 +16,19 @@
|
||||
|
||||
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.Context;
|
||||
import android.content.res.ColorStateList;
|
||||
import android.content.res.Configuration;
|
||||
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;
|
||||
|
||||
/**
|
||||
* Displays a PIN pad for unlocking.
|
||||
*/
|
||||
public class KeyguardSimPinView extends KeyguardPinBasedInputView {
|
||||
private static final String LOG_TAG = "KeyguardSimPinView";
|
||||
private static final boolean DEBUG = KeyguardConstants.DEBUG_SIM_STATES;
|
||||
public static final String TAG = "KeyguardSimPinView";
|
||||
|
||||
private ProgressDialog mSimUnlockProgressDialog = null;
|
||||
private CheckSimPin mCheckSimPinThread;
|
||||
|
||||
// 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 = true;
|
||||
private int mRemainingAttempts = -1;
|
||||
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) {
|
||||
case TelephonyManager.SIM_STATE_READY: {
|
||||
mRemainingAttempts = -1;
|
||||
resetState();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
resetState();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
public KeyguardSimPinView(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
@@ -84,81 +37,9 @@ public class KeyguardSimPinView extends KeyguardPinBasedInputView {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resetState() {
|
||||
super.resetState();
|
||||
if (DEBUG) Log.v(TAG, "Resetting state");
|
||||
handleSubInfoChangeIfNeeded();
|
||||
if (mShowDefaultMessage) {
|
||||
showDefaultMessage();
|
||||
}
|
||||
boolean isEsimLocked = KeyguardEsimArea.isEsimLocked(mContext, mSubId);
|
||||
|
||||
public void setEsimLocked(boolean locked) {
|
||||
KeyguardEsimArea esimButton = findViewById(R.id.keyguard_esim_area);
|
||||
esimButton.setVisibility(isEsimLocked ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
|
||||
private void setLockedSimMessage() {
|
||||
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_sim_pin_instructions);
|
||||
} else {
|
||||
SubscriptionInfo info = Dependency.get(KeyguardUpdateMonitor.class)
|
||||
.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 (mSecurityMessageDisplay != null && getVisibility() == VISIBLE) {
|
||||
mSecurityMessageDisplay.setMessage(msg);
|
||||
}
|
||||
mSimImageView.setImageTintList(ColorStateList.valueOf(color));
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
esimButton.setVisibility(locked ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -173,35 +54,6 @@ 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;
|
||||
@@ -214,173 +66,21 @@ public class KeyguardSimPinView extends KeyguardPinBasedInputView {
|
||||
if (mEcaView instanceof EmergencyCarrierArea) {
|
||||
((EmergencyCarrierArea) mEcaView).setCarrierTextVisible(true);
|
||||
}
|
||||
mSimImageView = findViewById(R.id.keyguard_sim);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showUsabilityHint() {
|
||||
public void onPause() {
|
||||
|
||||
}
|
||||
|
||||
@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);
|
||||
}
|
||||
public void showUsabilityHint() {
|
||||
|
||||
/**
|
||||
* 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
|
||||
|
||||
@@ -0,0 +1,350 @@
|
||||
/*
|
||||
* 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<KeyguardSimPinView> {
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,27 +16,11 @@
|
||||
|
||||
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;
|
||||
|
||||
|
||||
@@ -44,47 +28,10 @@ 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);
|
||||
@@ -94,136 +41,14 @@ 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;
|
||||
}
|
||||
|
||||
private String getPukPasswordErrorMessage(int attemptsRemaining, boolean isDefault) {
|
||||
String getPukPasswordErrorMessage(
|
||||
int attemptsRemaining, boolean isDefault, boolean isEsimLocked) {
|
||||
String displayMessage;
|
||||
|
||||
if (attemptsRemaining == 0) {
|
||||
@@ -238,27 +63,18 @@ public class KeyguardSimPukView extends KeyguardPinBasedInputView {
|
||||
R.string.kg_password_puk_failed;
|
||||
displayMessage = getContext().getString(msgId);
|
||||
}
|
||||
if (KeyguardEsimArea.isEsimLocked(mContext, mSubId)) {
|
||||
if (isEsimLocked) {
|
||||
displayMessage = getResources()
|
||||
.getString(R.string.kg_sim_lock_esim_instructions, displayMessage);
|
||||
}
|
||||
if (DEBUG) Log.d(LOG_TAG, "getPukPasswordErrorMessage:"
|
||||
+ " attemptsRemaining=" + attemptsRemaining + " displayMessage=" + displayMessage);
|
||||
if (DEBUG) {
|
||||
Log.d(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;
|
||||
@@ -271,20 +87,6 @@ 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
|
||||
@@ -300,168 +102,8 @@ 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
|
||||
protected void verifyPasswordAndUnlock() {
|
||||
mStateMachine.next();
|
||||
public void onResume(int reason) {
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,404 @@
|
||||
/*
|
||||
* 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<KeyguardSimPukView> {
|
||||
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);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -16,11 +16,12 @@
|
||||
|
||||
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.
|
||||
@@ -31,9 +32,9 @@ class LiftToActivateListener implements View.OnHoverListener {
|
||||
|
||||
private boolean mCachedClickableState;
|
||||
|
||||
public LiftToActivateListener(Context context) {
|
||||
mAccessibilityManager = (AccessibilityManager) context.getSystemService(
|
||||
Context.ACCESSIBILITY_SERVICE);
|
||||
@Inject
|
||||
LiftToActivateListener(AccessibilityManager accessibilityManager) {
|
||||
mAccessibilityManager = accessibilityManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -26,6 +26,7 @@ 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;
|
||||
@@ -90,7 +91,8 @@ public class NumPadKey extends ViewGroup {
|
||||
}
|
||||
|
||||
setOnClickListener(mListener);
|
||||
setOnHoverListener(new LiftToActivateListener(context));
|
||||
setOnHoverListener(new LiftToActivateListener(
|
||||
(AccessibilityManager) context.getSystemService(Context.ACCESSIBILITY_SERVICE)));
|
||||
|
||||
mLockPatternUtils = new LockPatternUtils(context);
|
||||
mPM = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
|
||||
|
||||
@@ -62,6 +62,7 @@ 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;
|
||||
@@ -181,6 +182,12 @@ 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() {
|
||||
|
||||
@@ -23,7 +23,20 @@ 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()}.
|
||||
* {@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.
|
||||
*
|
||||
* @param <T> View class that this ViewController is for.
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
* 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<KeyDownListener> 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<KeyDownListener> onKeyDownListenerArgumentCaptor =
|
||||
ArgumentCaptor.forClass(KeyDownListener.class);
|
||||
verify(mAbsKeyInputView).setKeyDownListener(onKeyDownListenerArgumentCaptor.capture());
|
||||
onKeyDownListenerArgumentCaptor.getValue().onKeyDown(
|
||||
KeyEvent.KEYCODE_UNKNOWN, mock(KeyEvent.class));
|
||||
verifyZeroInteractions(mKeyguardSecurityCallback);
|
||||
verifyZeroInteractions(mKeyguardMessageAreaController);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* 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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,79 +0,0 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user