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