Revert^2 "7/N controllers for remaining Keyguard Password ..."

Revert submission 12656261-revert-12585643-b166448040-keyguard-message-area-NUUJOVRYJS

This also fixes the original issue introduced in this CL.
KeyguardAbsKeyInputViewController#reset would call mView#resetState
instead of its own internal resetState method. However, all the logic
from the various resetState methods had been moved from the views to
their controllers. Simply calling resetState direclty resolves the
issue (line 105 of KeyguardAbsKeyInputViewController).

Reason for revert: Fixing http://b/169231892 and http://b/169145796
Reverted Changes:
I7683b2234:Revert "4/N Setup Controller fo KeyguardSecurityCo...
I5cbe04c0c:Revert "5/N Add KeyguardSecurityViewFlipperControl...
I11dff38ad:Revert "6/N Add Controller for KeyguardPatternView...
I55c250121:Revert "7/N controllers for remaining Keyguard Pas...
Ie84234cb8:Revert "8/N Remove View Injection from KeyguardMes...
Ic62f199a5:Revert "9/N Clean Up Keyguard Class Structure"

Change-Id: Ie3669e0c9a23ffd4443ced0fb08ec754f1df55db
Fixes: 169145796
This commit is contained in:
Dave Mankoff
2020-09-23 14:01:33 +00:00
parent 6350a9f10b
commit dc01930044
23 changed files with 1830 additions and 1356 deletions

View File

@@ -16,45 +16,30 @@
package com.android.keyguard;
import static com.android.internal.util.LatencyTracker.ACTION_CHECK_CREDENTIAL;
import static com.android.internal.util.LatencyTracker.ACTION_CHECK_CREDENTIAL_UNLOCKED;
import android.content.Context;
import android.content.res.ColorStateList;
import android.os.AsyncTask;
import android.os.CountDownTimer;
import android.os.SystemClock;
import android.util.AttributeSet;
import android.view.HapticFeedbackConstants;
import android.view.KeyEvent;
import android.view.View;
import com.android.internal.util.LatencyTracker;
import com.android.internal.widget.LockPatternChecker;
import com.android.internal.widget.LockPatternUtils;
import com.android.internal.widget.LockscreenCredential;
import com.android.systemui.Dependency;
import com.android.systemui.R;
/**
* Base class for PIN and password unlock screens.
*/
public abstract class KeyguardAbsKeyInputView extends KeyguardInputView
implements EmergencyButton.EmergencyButtonCallback {
public abstract class KeyguardAbsKeyInputView extends KeyguardInputView {
protected KeyguardSecurityCallback mCallback;
protected LockPatternUtils mLockPatternUtils;
protected AsyncTask<?, ?, ?> mPendingLockCheck;
protected SecurityMessageDisplay mSecurityMessageDisplay;
protected View mEcaView;
protected boolean mEnableHaptics;
private boolean mDismissing;
protected boolean mResumed;
private CountDownTimer mCountdownTimer = null;
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
// To avoid accidental lockout due to events while the device in in the pocket, ignore
// any passwords with length less than or equal to this length.
protected static final int MINIMUM_PASSWORD_LENGTH_BEFORE_REPORT = 3;
private KeyDownListener mKeyDownListener;
public KeyguardAbsKeyInputView(Context context) {
this(context, null);
@@ -62,7 +47,10 @@ public abstract class KeyguardAbsKeyInputView extends KeyguardInputView
public KeyguardAbsKeyInputView(Context context, AttributeSet attrs) {
super(context, attrs);
mKeyguardUpdateMonitor = Dependency.get(KeyguardUpdateMonitor.class);
}
void setEnableHaptics(boolean enableHaptics) {
mEnableHaptics = enableHaptics;
}
@Override
@@ -72,28 +60,10 @@ public abstract class KeyguardAbsKeyInputView extends KeyguardInputView
@Override
public void setLockPatternUtils(LockPatternUtils utils) {
mLockPatternUtils = utils;
mEnableHaptics = mLockPatternUtils.isTactileFeedbackEnabled();
}
@Override
public void reset() {
// start fresh
mDismissing = false;
resetPasswordText(false /* animate */, false /* announce */);
// if the user is currently locked out, enforce it.
long deadline = mLockPatternUtils.getLockoutAttemptDeadline(
KeyguardUpdateMonitor.getCurrentUser());
if (shouldLockout(deadline)) {
handleAttemptLockout(deadline);
} else {
resetState();
}
}
// Allow subclasses to override this behavior
protected boolean shouldLockout(long deadline) {
return deadline != 0;
}
protected abstract int getPasswordTextViewId();
@@ -101,24 +71,7 @@ public abstract class KeyguardAbsKeyInputView extends KeyguardInputView
@Override
protected void onFinishInflate() {
mLockPatternUtils = new LockPatternUtils(mContext);
mEcaView = findViewById(R.id.keyguard_selector_fade_container);
EmergencyButton button = findViewById(R.id.emergency_call_button);
if (button != null) {
button.setCallback(this);
}
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
mSecurityMessageDisplay = KeyguardMessageArea.findSecurityMessageDisplay(this);
}
@Override
public void onEmergencyButtonClickedWhenInCall() {
mCallback.reset();
}
/*
@@ -130,147 +83,14 @@ public abstract class KeyguardAbsKeyInputView extends KeyguardInputView
return R.string.kg_wrong_password;
}
protected void verifyPasswordAndUnlock() {
if (mDismissing) return; // already verified but haven't been dismissed; don't do it again.
final LockscreenCredential password = getEnteredCredential();
setPasswordEntryInputEnabled(false);
if (mPendingLockCheck != null) {
mPendingLockCheck.cancel(false);
}
final int userId = KeyguardUpdateMonitor.getCurrentUser();
if (password.size() <= MINIMUM_PASSWORD_LENGTH_BEFORE_REPORT) {
// to avoid accidental lockout, only count attempts that are long enough to be a
// real password. This may require some tweaking.
setPasswordEntryInputEnabled(true);
onPasswordChecked(userId, false /* matched */, 0, false /* not valid - too short */);
password.zeroize();
return;
}
if (LatencyTracker.isEnabled(mContext)) {
LatencyTracker.getInstance(mContext).onActionStart(ACTION_CHECK_CREDENTIAL);
LatencyTracker.getInstance(mContext).onActionStart(ACTION_CHECK_CREDENTIAL_UNLOCKED);
}
mKeyguardUpdateMonitor.setCredentialAttempted();
mPendingLockCheck = LockPatternChecker.checkCredential(
mLockPatternUtils,
password,
userId,
new LockPatternChecker.OnCheckCallback() {
@Override
public void onEarlyMatched() {
if (LatencyTracker.isEnabled(mContext)) {
LatencyTracker.getInstance(mContext).onActionEnd(
ACTION_CHECK_CREDENTIAL);
}
onPasswordChecked(userId, true /* matched */, 0 /* timeoutMs */,
true /* isValidPassword */);
password.zeroize();
}
@Override
public void onChecked(boolean matched, int timeoutMs) {
if (LatencyTracker.isEnabled(mContext)) {
LatencyTracker.getInstance(mContext).onActionEnd(
ACTION_CHECK_CREDENTIAL_UNLOCKED);
}
setPasswordEntryInputEnabled(true);
mPendingLockCheck = null;
if (!matched) {
onPasswordChecked(userId, false /* matched */, timeoutMs,
true /* isValidPassword */);
}
password.zeroize();
}
@Override
public void onCancelled() {
// We already got dismissed with the early matched callback, so we cancelled
// the check. However, we still need to note down the latency.
if (LatencyTracker.isEnabled(mContext)) {
LatencyTracker.getInstance(mContext).onActionEnd(
ACTION_CHECK_CREDENTIAL_UNLOCKED);
}
password.zeroize();
}
});
}
private void onPasswordChecked(int userId, boolean matched, int timeoutMs,
boolean isValidPassword) {
boolean dismissKeyguard = KeyguardUpdateMonitor.getCurrentUser() == userId;
if (matched) {
mCallback.reportUnlockAttempt(userId, true, 0);
if (dismissKeyguard) {
mDismissing = true;
mCallback.dismiss(true, userId);
}
} else {
if (isValidPassword) {
mCallback.reportUnlockAttempt(userId, false, timeoutMs);
if (timeoutMs > 0) {
long deadline = mLockPatternUtils.setLockoutAttemptDeadline(
userId, timeoutMs);
handleAttemptLockout(deadline);
}
}
if (timeoutMs == 0) {
mSecurityMessageDisplay.setMessage(getWrongPasswordStringId());
}
}
resetPasswordText(true /* animate */, !matched /* announce deletion if no match */);
}
protected abstract void resetPasswordText(boolean animate, boolean announce);
protected abstract LockscreenCredential getEnteredCredential();
protected abstract void setPasswordEntryEnabled(boolean enabled);
protected abstract void setPasswordEntryInputEnabled(boolean enabled);
// Prevent user from using the PIN/Password entry until scheduled deadline.
protected void handleAttemptLockout(long elapsedRealtimeDeadline) {
setPasswordEntryEnabled(false);
long elapsedRealtime = SystemClock.elapsedRealtime();
long secondsInFuture = (long) Math.ceil(
(elapsedRealtimeDeadline - elapsedRealtime) / 1000.0);
mCountdownTimer = new CountDownTimer(secondsInFuture * 1000, 1000) {
@Override
public void onTick(long millisUntilFinished) {
int secondsRemaining = (int) Math.round(millisUntilFinished / 1000.0);
mSecurityMessageDisplay.setMessage(mContext.getResources().getQuantityString(
R.plurals.kg_too_many_failed_attempts_countdown,
secondsRemaining, secondsRemaining));
}
@Override
public void onFinish() {
mSecurityMessageDisplay.setMessage("");
resetState();
}
}.start();
}
protected void onUserInput() {
if (mCallback != null) {
mCallback.userActivity();
mCallback.onUserInput();
}
mSecurityMessageDisplay.setMessage("");
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
// Fingerprint sensor sends a KeyEvent.KEYCODE_UNKNOWN.
// We don't want to consider it valid user input because the UI
// will already respond to the event.
if (keyCode != KeyEvent.KEYCODE_UNKNOWN) {
onUserInput();
}
return false;
return mKeyDownListener != null && mKeyDownListener.onKeyDown(keyCode, event);
}
@Override
@@ -278,26 +98,6 @@ public abstract class KeyguardAbsKeyInputView extends KeyguardInputView
return false;
}
@Override
public void onPause() {
mResumed = false;
if (mCountdownTimer != null) {
mCountdownTimer.cancel();
mCountdownTimer = null;
}
if (mPendingLockCheck != null) {
mPendingLockCheck.cancel(false);
mPendingLockCheck = null;
}
reset();
}
@Override
public void onResume(int reason) {
mResumed = true;
}
@Override
public KeyguardSecurityCallback getCallback() {
return mCallback;
@@ -305,12 +105,7 @@ public abstract class KeyguardAbsKeyInputView extends KeyguardInputView
@Override
public void showPromptReason(int reason) {
if (reason != PROMPT_REASON_NONE) {
int promtReasonStringRes = getPromptReasonStringRes(reason);
if (promtReasonStringRes != 0) {
mSecurityMessageDisplay.setMessage(promtReasonStringRes);
}
}
}
@Override
@@ -336,5 +131,13 @@ public abstract class KeyguardAbsKeyInputView extends KeyguardInputView
public boolean startDisappearAnimation(Runnable finishRunnable) {
return false;
}
public void setKeyDownListener(KeyDownListener keyDownListener) {
mKeyDownListener = keyDownListener;
}
public interface KeyDownListener {
boolean onKeyDown(int keyCode, KeyEvent keyEvent);
}
}

View File

@@ -0,0 +1,257 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.keyguard;
import static com.android.internal.util.LatencyTracker.ACTION_CHECK_CREDENTIAL;
import static com.android.internal.util.LatencyTracker.ACTION_CHECK_CREDENTIAL_UNLOCKED;
import static com.android.keyguard.KeyguardAbsKeyInputView.MINIMUM_PASSWORD_LENGTH_BEFORE_REPORT;
import android.os.AsyncTask;
import android.os.CountDownTimer;
import android.os.SystemClock;
import android.view.KeyEvent;
import com.android.internal.util.LatencyTracker;
import com.android.internal.widget.LockPatternChecker;
import com.android.internal.widget.LockPatternUtils;
import com.android.internal.widget.LockscreenCredential;
import com.android.keyguard.EmergencyButton.EmergencyButtonCallback;
import com.android.keyguard.KeyguardAbsKeyInputView.KeyDownListener;
import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
import com.android.systemui.R;
public abstract class KeyguardAbsKeyInputViewController<T extends KeyguardAbsKeyInputView>
extends KeyguardInputViewController<T> {
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
private final LockPatternUtils mLockPatternUtils;
protected final KeyguardSecurityCallback mKeyguardSecurityCallback;
private final LatencyTracker mLatencyTracker;
private CountDownTimer mCountdownTimer;
protected KeyguardMessageAreaController mMessageAreaController;
private boolean mDismissing;
protected AsyncTask<?, ?, ?> mPendingLockCheck;
protected boolean mResumed;
private final KeyDownListener mKeyDownListener = (keyCode, keyEvent) -> {
// Fingerprint sensor sends a KeyEvent.KEYCODE_UNKNOWN.
// We don't want to consider it valid user input because the UI
// will already respond to the event.
if (keyCode != KeyEvent.KEYCODE_UNKNOWN) {
onUserInput();
}
return false;
};
private final EmergencyButtonCallback mEmergencyButtonCallback = new EmergencyButtonCallback() {
@Override
public void onEmergencyButtonClickedWhenInCall() {
mKeyguardSecurityCallback.reset();
}
};
protected KeyguardAbsKeyInputViewController(T view,
KeyguardUpdateMonitor keyguardUpdateMonitor,
SecurityMode securityMode,
LockPatternUtils lockPatternUtils,
KeyguardSecurityCallback keyguardSecurityCallback,
KeyguardMessageAreaController.Factory messageAreaControllerFactory,
LatencyTracker latencyTracker) {
super(view, securityMode, keyguardSecurityCallback);
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mLockPatternUtils = lockPatternUtils;
mKeyguardSecurityCallback = keyguardSecurityCallback;
mLatencyTracker = latencyTracker;
KeyguardMessageArea kma = KeyguardMessageArea.findSecurityMessageDisplay(mView);
mMessageAreaController = messageAreaControllerFactory.create(kma);
}
abstract void resetState();
@Override
protected void onViewAttached() {
mView.setKeyDownListener(mKeyDownListener);
mView.setEnableHaptics(mLockPatternUtils.isTactileFeedbackEnabled());
EmergencyButton button = mView.findViewById(R.id.emergency_call_button);
if (button != null) {
button.setCallback(mEmergencyButtonCallback);
}
}
@Override
public void reset() {
// start fresh
mDismissing = false;
mView.resetPasswordText(false /* animate */, false /* announce */);
// if the user is currently locked out, enforce it.
long deadline = mLockPatternUtils.getLockoutAttemptDeadline(
KeyguardUpdateMonitor.getCurrentUser());
if (shouldLockout(deadline)) {
handleAttemptLockout(deadline);
} else {
resetState();
}
}
// Allow subclasses to override this behavior
protected boolean shouldLockout(long deadline) {
return deadline != 0;
}
// Prevent user from using the PIN/Password entry until scheduled deadline.
protected void handleAttemptLockout(long elapsedRealtimeDeadline) {
mView.setPasswordEntryEnabled(false);
long elapsedRealtime = SystemClock.elapsedRealtime();
long secondsInFuture = (long) Math.ceil(
(elapsedRealtimeDeadline - elapsedRealtime) / 1000.0);
mCountdownTimer = new CountDownTimer(secondsInFuture * 1000, 1000) {
@Override
public void onTick(long millisUntilFinished) {
int secondsRemaining = (int) Math.round(millisUntilFinished / 1000.0);
mMessageAreaController.setMessage(mView.getResources().getQuantityString(
R.plurals.kg_too_many_failed_attempts_countdown,
secondsRemaining, secondsRemaining));
}
@Override
public void onFinish() {
mMessageAreaController.setMessage("");
resetState();
}
}.start();
}
void onPasswordChecked(int userId, boolean matched, int timeoutMs, boolean isValidPassword) {
boolean dismissKeyguard = KeyguardUpdateMonitor.getCurrentUser() == userId;
if (matched) {
mKeyguardSecurityCallback.reportUnlockAttempt(userId, true, 0);
if (dismissKeyguard) {
mDismissing = true;
mKeyguardSecurityCallback.dismiss(true, userId);
}
} else {
if (isValidPassword) {
mKeyguardSecurityCallback.reportUnlockAttempt(userId, false, timeoutMs);
if (timeoutMs > 0) {
long deadline = mLockPatternUtils.setLockoutAttemptDeadline(
userId, timeoutMs);
handleAttemptLockout(deadline);
}
}
if (timeoutMs == 0) {
mMessageAreaController.setMessage(mView.getWrongPasswordStringId());
}
}
mView.resetPasswordText(true /* animate */, !matched /* announce deletion if no match */);
}
protected void verifyPasswordAndUnlock() {
if (mDismissing) return; // already verified but haven't been dismissed; don't do it again.
final LockscreenCredential password = mView.getEnteredCredential();
mView.setPasswordEntryInputEnabled(false);
if (mPendingLockCheck != null) {
mPendingLockCheck.cancel(false);
}
final int userId = KeyguardUpdateMonitor.getCurrentUser();
if (password.size() <= MINIMUM_PASSWORD_LENGTH_BEFORE_REPORT) {
// to avoid accidental lockout, only count attempts that are long enough to be a
// real password. This may require some tweaking.
mView.setPasswordEntryInputEnabled(true);
onPasswordChecked(userId, false /* matched */, 0, false /* not valid - too short */);
password.zeroize();
return;
}
mLatencyTracker.onActionStart(ACTION_CHECK_CREDENTIAL);
mLatencyTracker.onActionStart(ACTION_CHECK_CREDENTIAL_UNLOCKED);
mKeyguardUpdateMonitor.setCredentialAttempted();
mPendingLockCheck = LockPatternChecker.checkCredential(
mLockPatternUtils,
password,
userId,
new LockPatternChecker.OnCheckCallback() {
@Override
public void onEarlyMatched() {
mLatencyTracker.onActionEnd(ACTION_CHECK_CREDENTIAL);
onPasswordChecked(userId, true /* matched */, 0 /* timeoutMs */,
true /* isValidPassword */);
password.zeroize();
}
@Override
public void onChecked(boolean matched, int timeoutMs) {
mLatencyTracker.onActionEnd(ACTION_CHECK_CREDENTIAL_UNLOCKED);
mView.setPasswordEntryInputEnabled(true);
mPendingLockCheck = null;
if (!matched) {
onPasswordChecked(userId, false /* matched */, timeoutMs,
true /* isValidPassword */);
}
password.zeroize();
}
@Override
public void onCancelled() {
// We already got dismissed with the early matched callback, so we cancelled
// the check. However, we still need to note down the latency.
mLatencyTracker.onActionEnd(ACTION_CHECK_CREDENTIAL_UNLOCKED);
password.zeroize();
}
});
}
@Override
public void showPromptReason(int reason) {
if (reason != PROMPT_REASON_NONE) {
int promtReasonStringRes = mView.getPromptReasonStringRes(reason);
if (promtReasonStringRes != 0) {
mMessageAreaController.setMessage(promtReasonStringRes);
}
}
}
protected void onUserInput() {
mKeyguardSecurityCallback.userActivity();
mKeyguardSecurityCallback.onUserInput();
mMessageAreaController.setMessage("");
}
@Override
public void onResume(int reason) {
mResumed = true;
}
@Override
public void onPause() {
mResumed = false;
if (mCountdownTimer != null) {
mCountdownTimer.cancel();
mCountdownTimer = null;
}
if (mPendingLockCheck != null) {
mPendingLockCheck.cancel(false);
mPendingLockCheck = null;
}
reset();
}
}

View File

@@ -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

View File

@@ -17,32 +17,31 @@
package com.android.keyguard;
import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.telephony.TelephonyManager;
import android.view.MotionEvent;
import android.view.inputmethod.InputMethodManager;
import com.android.internal.util.LatencyTracker;
import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.util.ViewController;
import com.android.systemui.util.concurrency.DelayableExecutor;
import javax.inject.Inject;
/** Controller for a {@link KeyguardSecurityView}. */
public class KeyguardInputViewController<T extends KeyguardInputView> extends ViewController<T>
implements KeyguardSecurityView {
public abstract class KeyguardInputViewController<T extends KeyguardInputView>
extends ViewController<T> implements KeyguardSecurityView {
private final SecurityMode mSecurityMode;
private final LockPatternUtils mLockPatternUtils;
private final KeyguardSecurityCallback mKeyguardSecurityCallback;
private KeyguardMessageAreaController mMessageAreaController;
protected KeyguardInputViewController(T view, SecurityMode securityMode,
LockPatternUtils lockPatternUtils,
KeyguardSecurityCallback keyguardSecurityCallback) {
super(view);
mSecurityMode = securityMode;
mLockPatternUtils = lockPatternUtils;
mKeyguardSecurityCallback = keyguardSecurityCallback;
mView.setKeyguardCallback(keyguardSecurityCallback);
}
@@ -151,16 +150,29 @@ public class KeyguardInputViewController<T extends KeyguardInputView> extends Vi
private final LockPatternUtils mLockPatternUtils;
private final LatencyTracker mLatencyTracker;
private final KeyguardMessageAreaController.Factory mMessageAreaControllerFactory;
private final InputMethodManager mInputMethodManager;
private final DelayableExecutor mMainExecutor;
private final Resources mResources;
private LiftToActivateListener mLiftToActivateListener;
private TelephonyManager mTelephonyManager;
@Inject
public Factory(KeyguardUpdateMonitor keyguardUpdateMonitor,
LockPatternUtils lockPatternUtils,
LatencyTracker latencyTracker,
KeyguardMessageAreaController.Factory messageAreaControllerFactory) {
KeyguardMessageAreaController.Factory messageAreaControllerFactory,
InputMethodManager inputMethodManager, @Main DelayableExecutor mainExecutor,
@Main Resources resources, LiftToActivateListener liftToActivateListener,
TelephonyManager telephonyManager) {
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mLockPatternUtils = lockPatternUtils;
mLatencyTracker = latencyTracker;
mMessageAreaControllerFactory = messageAreaControllerFactory;
mInputMethodManager = inputMethodManager;
mMainExecutor = mainExecutor;
mResources = resources;
mLiftToActivateListener = liftToActivateListener;
mTelephonyManager = telephonyManager;
}
/** Create a new {@link KeyguardInputViewController}. */
@@ -170,9 +182,29 @@ public class KeyguardInputViewController<T extends KeyguardInputView> extends Vi
return new KeyguardPatternViewController((KeyguardPatternView) keyguardInputView,
mKeyguardUpdateMonitor, securityMode, mLockPatternUtils,
keyguardSecurityCallback, mLatencyTracker, mMessageAreaControllerFactory);
} else if (keyguardInputView instanceof KeyguardPasswordView) {
return new KeyguardPasswordViewController((KeyguardPasswordView) keyguardInputView,
mKeyguardUpdateMonitor, securityMode, mLockPatternUtils,
keyguardSecurityCallback, mMessageAreaControllerFactory, mLatencyTracker,
mInputMethodManager, mMainExecutor, mResources);
} else if (keyguardInputView instanceof KeyguardPINView) {
return new KeyguardPinViewController((KeyguardPINView) keyguardInputView,
mKeyguardUpdateMonitor, securityMode, mLockPatternUtils,
keyguardSecurityCallback, mMessageAreaControllerFactory, mLatencyTracker,
mLiftToActivateListener);
} else if (keyguardInputView instanceof KeyguardSimPinView) {
return new KeyguardSimPinViewController((KeyguardSimPinView) keyguardInputView,
mKeyguardUpdateMonitor, securityMode, mLockPatternUtils,
keyguardSecurityCallback, mMessageAreaControllerFactory, mLatencyTracker,
mLiftToActivateListener, mTelephonyManager);
} else if (keyguardInputView instanceof KeyguardSimPukView) {
return new KeyguardSimPukViewController((KeyguardSimPukView) keyguardInputView,
mKeyguardUpdateMonitor, securityMode, mLockPatternUtils,
keyguardSecurityCallback, mMessageAreaControllerFactory, mLatencyTracker,
mLiftToActivateListener, mTelephonyManager);
}
return new KeyguardInputViewController<>(keyguardInputView, securityMode,
mLockPatternUtils, keyguardSecurityCallback);
throw new RuntimeException("Unable to find controller for " + keyguardInputView);
}
}
}

View File

@@ -24,7 +24,6 @@ import android.view.animation.AnimationUtils;
import com.android.settingslib.animation.AppearAnimationUtils;
import com.android.settingslib.animation.DisappearAnimationUtils;
import com.android.systemui.Dependency;
import com.android.systemui.R;
/**
@@ -40,10 +39,8 @@ public class KeyguardPINView extends KeyguardPinBasedInputView {
private ViewGroup mRow1;
private ViewGroup mRow2;
private ViewGroup mRow3;
private View mDivider;
private int mDisappearYTranslation;
private View[][] mViews;
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
public KeyguardPINView(Context context) {
this(context, null);
@@ -63,15 +60,10 @@ public class KeyguardPINView extends KeyguardPinBasedInputView {
mContext, android.R.interpolator.fast_out_linear_in));
mDisappearYTranslation = getResources().getDimensionPixelSize(
R.dimen.disappear_y_translation);
mKeyguardUpdateMonitor = Dependency.get(KeyguardUpdateMonitor.class);
}
@Override
protected void resetState() {
super.resetState();
if (mSecurityMessageDisplay != null) {
mSecurityMessageDisplay.setMessage("");
}
}
@Override
@@ -88,7 +80,6 @@ public class KeyguardPINView extends KeyguardPinBasedInputView {
mRow1 = findViewById(R.id.row1);
mRow2 = findViewById(R.id.row2);
mRow3 = findViewById(R.id.row3);
mDivider = findViewById(R.id.divider);
mViews = new View[][]{
new View[]{
mRow0, null, null
@@ -122,6 +113,16 @@ public class KeyguardPINView extends KeyguardPinBasedInputView {
}
}
@Override
public void onPause() {
}
@Override
public void onResume(int reason) {
}
@Override
public void showUsabilityHint() {
}
@@ -149,22 +150,24 @@ public class KeyguardPINView extends KeyguardPinBasedInputView {
@Override
public boolean startDisappearAnimation(final Runnable finishRunnable) {
return false;
}
public boolean startDisappearAnimation(boolean needsSlowUnlockTransition,
final Runnable finishRunnable) {
enableClipping(false);
setTranslationY(0);
AppearAnimationUtils.startTranslationYAnimation(this, 0 /* delay */, 280 /* duration */,
mDisappearYTranslation, mDisappearAnimationUtils.getInterpolator());
DisappearAnimationUtils disappearAnimationUtils = mKeyguardUpdateMonitor
.needsSlowUnlockTransition()
DisappearAnimationUtils disappearAnimationUtils = needsSlowUnlockTransition
? mDisappearAnimationUtilsLocked
: mDisappearAnimationUtils;
disappearAnimationUtils.startAnimation2d(mViews,
new Runnable() {
@Override
public void run() {
enableClipping(true);
if (finishRunnable != null) {
finishRunnable.run();
}
() -> {
enableClipping(true);
if (finishRunnable != null) {
finishRunnable.run();
}
});
return true;

View File

@@ -18,48 +18,29 @@ package com.android.keyguard;
import android.content.Context;
import android.graphics.Rect;
import android.os.UserHandle;
import android.text.Editable;
import android.text.InputType;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.text.method.TextKeyListener;
import android.util.AttributeSet;
import android.view.KeyEvent;
import android.view.View;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodInfo;
import android.view.inputmethod.InputMethodManager;
import android.view.inputmethod.InputMethodSubtype;
import android.widget.TextView;
import android.widget.TextView.OnEditorActionListener;
import com.android.internal.widget.LockscreenCredential;
import com.android.internal.widget.TextViewInputDisabler;
import com.android.systemui.R;
import java.util.List;
/**
* Displays an alphanumeric (latin-1) key entry for the user to enter
* an unlock password
*/
public class KeyguardPasswordView extends KeyguardAbsKeyInputView
implements KeyguardSecurityView, OnEditorActionListener, TextWatcher {
implements KeyguardSecurityView {
private final boolean mShowImeAtScreenOn;
private final int mDisappearYTranslation;
// A delay constant to be used in a workaround for the situation where InputMethodManagerService
// is not switched to the new user yet.
// TODO: Remove this by ensuring such a race condition never happens.
private static final int DELAY_MILLIS_TO_REEVALUATE_IME_SWITCH_ICON = 500; // 500ms
InputMethodManager mImm;
private TextView mPasswordEntry;
private TextViewInputDisabler mPasswordEntryDisabler;
private View mSwitchImeButton;
private Interpolator mLinearOutSlowInInterpolator;
private Interpolator mFastOutLinearInInterpolator;
@@ -70,8 +51,6 @@ public class KeyguardPasswordView extends KeyguardAbsKeyInputView
public KeyguardPasswordView(Context context, AttributeSet attrs) {
super(context, attrs);
mShowImeAtScreenOn = context.getResources().
getBoolean(R.bool.kg_show_ime_at_screen_on);
mDisappearYTranslation = getResources().getDimensionPixelSize(
R.dimen.disappear_y_translation);
mLinearOutSlowInInterpolator = AnimationUtils.loadInterpolator(
@@ -82,20 +61,6 @@ public class KeyguardPasswordView extends KeyguardAbsKeyInputView
@Override
protected void resetState() {
mPasswordEntry.setTextOperationUser(UserHandle.of(KeyguardUpdateMonitor.getCurrentUser()));
if (mSecurityMessageDisplay != null) {
mSecurityMessageDisplay.setMessage("");
}
final boolean wasDisabled = mPasswordEntry.isEnabled();
setPasswordEntryEnabled(true);
setPasswordEntryInputEnabled(true);
// Don't call showSoftInput when PasswordEntry is invisible or in pausing stage.
if (!mResumed || !mPasswordEntry.isVisibleToUser()) {
return;
}
if (wasDisabled) {
mImm.showSoftInput(mPasswordEntry, InputMethodManager.SHOW_IMPLICIT);
}
}
@Override
@@ -103,28 +68,20 @@ public class KeyguardPasswordView extends KeyguardAbsKeyInputView
return R.id.passwordEntry;
}
@Override
public void onPause() {
}
@Override
public void onResume(int reason) {
}
@Override
public boolean needsInput() {
return true;
}
@Override
public void onResume(final int reason) {
super.onResume(reason);
// Wait a bit to focus the field so the focusable flag on the window is already set then.
post(new Runnable() {
@Override
public void run() {
if (isShown() && mPasswordEntry.isEnabled()) {
mPasswordEntry.requestFocus();
if (reason != KeyguardSecurityView.SCREEN_ON || mShowImeAtScreenOn) {
mImm.showSoftInput(mPasswordEntry, InputMethodManager.SHOW_IMPLICIT);
}
}
}
});
}
@Override
protected int getPromptReasonStringRes(int reason) {
@@ -146,97 +103,13 @@ public class KeyguardPasswordView extends KeyguardAbsKeyInputView
}
}
@Override
public void onPause() {
super.onPause();
mImm.hideSoftInputFromWindow(getWindowToken(), 0);
}
@Override
public void onStartingToHide() {
mImm.hideSoftInputFromWindow(getWindowToken(), 0);
}
private void updateSwitchImeButton() {
// If there's more than one IME, enable the IME switcher button
final boolean wasVisible = mSwitchImeButton.getVisibility() == View.VISIBLE;
final boolean shouldBeVisible = hasMultipleEnabledIMEsOrSubtypes(mImm, false);
if (wasVisible != shouldBeVisible) {
mSwitchImeButton.setVisibility(shouldBeVisible ? View.VISIBLE : View.GONE);
}
// TODO: Check if we still need this hack.
// If no icon is visible, reset the start margin on the password field so the text is
// still centered.
if (mSwitchImeButton.getVisibility() != View.VISIBLE) {
android.view.ViewGroup.LayoutParams params = mPasswordEntry.getLayoutParams();
if (params instanceof MarginLayoutParams) {
final MarginLayoutParams mlp = (MarginLayoutParams) params;
mlp.setMarginStart(0);
mPasswordEntry.setLayoutParams(params);
}
}
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
mImm = (InputMethodManager) getContext().getSystemService(
Context.INPUT_METHOD_SERVICE);
mPasswordEntry = findViewById(getPasswordTextViewId());
mPasswordEntry.setTextOperationUser(UserHandle.of(KeyguardUpdateMonitor.getCurrentUser()));
mPasswordEntryDisabler = new TextViewInputDisabler(mPasswordEntry);
mPasswordEntry.setKeyListener(TextKeyListener.getInstance());
mPasswordEntry.setInputType(InputType.TYPE_CLASS_TEXT
| InputType.TYPE_TEXT_VARIATION_PASSWORD);
mPasswordEntry.setOnEditorActionListener(this);
mPasswordEntry.addTextChangedListener(this);
// Poke the wakelock any time the text is selected or modified
mPasswordEntry.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
mCallback.userActivity();
}
});
// Set selected property on so the view can send accessibility events.
mPasswordEntry.setSelected(true);
mSwitchImeButton = findViewById(R.id.switch_ime_button);
mSwitchImeButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
mCallback.userActivity(); // Leave the screen on a bit longer
// Do not show auxiliary subtypes in password lock screen.
mImm.showInputMethodPickerFromSystem(false /* showAuxiliarySubtypes */,
getContext().getDisplayId());
}
});
View cancelBtn = findViewById(R.id.cancel_button);
if (cancelBtn != null) {
cancelBtn.setOnClickListener(view -> {
mCallback.reset();
mCallback.onCancelClicked();
});
}
// If there's more than one IME, enable the IME switcher button
updateSwitchImeButton();
// When we the current user is switching, InputMethodManagerService sometimes has not
// switched internal state yet here. As a quick workaround, we check the keyboard state
// again.
// TODO: Remove this workaround by ensuring such a race condition never happens.
postDelayed(new Runnable() {
@Override
public void run() {
updateSwitchImeButton();
}
}, DELAY_MILLIS_TO_REEVALUATE_IME_SWITCH_ICON);
}
@Override
@@ -265,55 +138,6 @@ public class KeyguardPasswordView extends KeyguardAbsKeyInputView
mPasswordEntryDisabler.setInputEnabled(enabled);
}
/**
* Method adapted from com.android.inputmethod.latin.Utils
*
* @param imm The input method manager
* @param shouldIncludeAuxiliarySubtypes
* @return true if we have multiple IMEs to choose from
*/
private boolean hasMultipleEnabledIMEsOrSubtypes(InputMethodManager imm,
final boolean shouldIncludeAuxiliarySubtypes) {
final List<InputMethodInfo> enabledImis =
imm.getEnabledInputMethodListAsUser(KeyguardUpdateMonitor.getCurrentUser());
// Number of the filtered IMEs
int filteredImisCount = 0;
for (InputMethodInfo imi : enabledImis) {
// We can return true immediately after we find two or more filtered IMEs.
if (filteredImisCount > 1) return true;
final List<InputMethodSubtype> subtypes =
imm.getEnabledInputMethodSubtypeList(imi, true);
// IMEs that have no subtypes should be counted.
if (subtypes.isEmpty()) {
++filteredImisCount;
continue;
}
int auxCount = 0;
for (InputMethodSubtype subtype : subtypes) {
if (subtype.isAuxiliary()) {
++auxCount;
}
}
final int nonAuxCount = subtypes.size() - auxCount;
// IMEs that have one or more non-auxiliary subtypes should be counted.
// If shouldIncludeAuxiliarySubtypes is true, IMEs that have two or more auxiliary
// subtypes should be counted as well.
if (nonAuxCount > 0 || (shouldIncludeAuxiliarySubtypes && auxCount > 1)) {
++filteredImisCount;
continue;
}
}
return filteredImisCount > 1
// imm.getEnabledInputMethodSubtypeList(null, false) will return the current IME's enabled
// input method subtype (The current IME should be LatinIME.)
|| imm.getEnabledInputMethodSubtypeList(null, false).size() > 1;
}
@Override
public void showUsabilityHint() {
}
@@ -345,46 +169,9 @@ public class KeyguardPasswordView extends KeyguardAbsKeyInputView
return true;
}
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
if (mCallback != null) {
mCallback.userActivity();
}
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
@Override
public void afterTextChanged(Editable s) {
// Poor man's user edit detection, assuming empty text is programmatic and everything else
// is from the user.
if (!TextUtils.isEmpty(s)) {
onUserInput();
}
}
@Override
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
// Check if this was the result of hitting the enter key
final boolean isSoftImeEvent = event == null
&& (actionId == EditorInfo.IME_NULL
|| actionId == EditorInfo.IME_ACTION_DONE
|| actionId == EditorInfo.IME_ACTION_NEXT);
final boolean isKeyboardEnterKey = event != null
&& KeyEvent.isConfirmKey(event.getKeyCode())
&& event.getAction() == KeyEvent.ACTION_DOWN;
if (isSoftImeEvent || isKeyboardEnterKey) {
verifyPasswordAndUnlock();
return true;
}
return false;
}
@Override
public CharSequence getTitle() {
return getContext().getString(
return getResources().getString(
com.android.internal.R.string.keyguard_accessibility_password_unlock);
}
}

View File

@@ -0,0 +1,270 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.keyguard;
import android.content.res.Resources;
import android.os.UserHandle;
import android.text.Editable;
import android.text.InputType;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.text.method.TextKeyListener;
import android.view.KeyEvent;
import android.view.View;
import android.view.ViewGroup.MarginLayoutParams;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodInfo;
import android.view.inputmethod.InputMethodManager;
import android.view.inputmethod.InputMethodSubtype;
import android.widget.TextView;
import android.widget.TextView.OnEditorActionListener;
import com.android.internal.util.LatencyTracker;
import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
import com.android.systemui.R;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.util.concurrency.DelayableExecutor;
import java.util.List;
public class KeyguardPasswordViewController
extends KeyguardAbsKeyInputViewController<KeyguardPasswordView> {
private static final int DELAY_MILLIS_TO_REEVALUATE_IME_SWITCH_ICON = 500; // 500ms
private final KeyguardSecurityCallback mKeyguardSecurityCallback;
private final InputMethodManager mInputMethodManager;
private final DelayableExecutor mMainExecutor;
private final boolean mShowImeAtScreenOn;
private TextView mPasswordEntry;
private View mSwitchImeButton;
private final OnEditorActionListener mOnEditorActionListener = (v, actionId, event) -> {
// Check if this was the result of hitting the enter key
final boolean isSoftImeEvent = event == null
&& (actionId == EditorInfo.IME_NULL
|| actionId == EditorInfo.IME_ACTION_DONE
|| actionId == EditorInfo.IME_ACTION_NEXT);
final boolean isKeyboardEnterKey = event != null
&& KeyEvent.isConfirmKey(event.getKeyCode())
&& event.getAction() == KeyEvent.ACTION_DOWN;
if (isSoftImeEvent || isKeyboardEnterKey) {
verifyPasswordAndUnlock();
return true;
}
return false;
};
private final TextWatcher mTextWatcher = new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
mKeyguardSecurityCallback.userActivity();
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
@Override
public void afterTextChanged(Editable s) {
if (!TextUtils.isEmpty(s)) {
onUserInput();
}
}
};
protected KeyguardPasswordViewController(KeyguardPasswordView view,
KeyguardUpdateMonitor keyguardUpdateMonitor,
SecurityMode securityMode,
LockPatternUtils lockPatternUtils,
KeyguardSecurityCallback keyguardSecurityCallback,
KeyguardMessageAreaController.Factory messageAreaControllerFactory,
LatencyTracker latencyTracker,
InputMethodManager inputMethodManager,
@Main DelayableExecutor mainExecutor,
@Main Resources resources) {
super(view, keyguardUpdateMonitor, securityMode, lockPatternUtils, keyguardSecurityCallback,
messageAreaControllerFactory, latencyTracker);
mKeyguardSecurityCallback = keyguardSecurityCallback;
mInputMethodManager = inputMethodManager;
mMainExecutor = mainExecutor;
mShowImeAtScreenOn = resources.getBoolean(R.bool.kg_show_ime_at_screen_on);
mPasswordEntry = mView.findViewById(mView.getPasswordTextViewId());
mSwitchImeButton = mView.findViewById(R.id.switch_ime_button);
}
@Override
protected void onViewAttached() {
super.onViewAttached();
mPasswordEntry.setTextOperationUser(UserHandle.of(KeyguardUpdateMonitor.getCurrentUser()));
mPasswordEntry.setKeyListener(TextKeyListener.getInstance());
mPasswordEntry.setInputType(InputType.TYPE_CLASS_TEXT
| InputType.TYPE_TEXT_VARIATION_PASSWORD);
// Set selected property on so the view can send accessibility events.
mPasswordEntry.setSelected(true);
mPasswordEntry.setOnEditorActionListener(mOnEditorActionListener);
mPasswordEntry.addTextChangedListener(mTextWatcher);
// Poke the wakelock any time the text is selected or modified
mPasswordEntry.setOnClickListener(v -> mKeyguardSecurityCallback.userActivity());
mSwitchImeButton.setOnClickListener(v -> {
mKeyguardSecurityCallback.userActivity(); // Leave the screen on a bit longer
// Do not show auxiliary subtypes in password lock screen.
mInputMethodManager.showInputMethodPickerFromSystem(false,
mView.getContext().getDisplayId());
});
View cancelBtn = mView.findViewById(R.id.cancel_button);
if (cancelBtn != null) {
cancelBtn.setOnClickListener(view -> {
mKeyguardSecurityCallback.reset();
mKeyguardSecurityCallback.onCancelClicked();
});
}
// If there's more than one IME, enable the IME switcher button
updateSwitchImeButton();
// When we the current user is switching, InputMethodManagerService sometimes has not
// switched internal state yet here. As a quick workaround, we check the keyboard state
// again.
// TODO: Remove this workaround by ensuring such a race condition never happens.
mMainExecutor.executeDelayed(
this::updateSwitchImeButton, DELAY_MILLIS_TO_REEVALUATE_IME_SWITCH_ICON);
}
@Override
protected void onViewDetached() {
super.onViewDetached();
mPasswordEntry.setOnEditorActionListener(null);
}
@Override
void resetState() {
mPasswordEntry.setTextOperationUser(UserHandle.of(KeyguardUpdateMonitor.getCurrentUser()));
mMessageAreaController.setMessage("");
final boolean wasDisabled = mPasswordEntry.isEnabled();
mView.setPasswordEntryEnabled(true);
mView.setPasswordEntryInputEnabled(true);
// Don't call showSoftInput when PasswordEntry is invisible or in pausing stage.
if (!mResumed || !mPasswordEntry.isVisibleToUser()) {
return;
}
if (wasDisabled) {
mInputMethodManager.showSoftInput(mPasswordEntry, InputMethodManager.SHOW_IMPLICIT);
}
}
@Override
public void onResume(int reason) {
super.onResume(reason);
// Wait a bit to focus the field so the focusable flag on the window is already set then.
mMainExecutor.execute(() -> {
if (mView.isShown() && mPasswordEntry.isEnabled()) {
mPasswordEntry.requestFocus();
if (reason != KeyguardSecurityView.SCREEN_ON || mShowImeAtScreenOn) {
mInputMethodManager.showSoftInput(
mPasswordEntry, InputMethodManager.SHOW_IMPLICIT);
}
}
});
}
@Override
public void onPause() {
super.onPause();
mInputMethodManager.hideSoftInputFromWindow(mView.getWindowToken(), 0);
}
@Override
public void onStartingToHide() {
mInputMethodManager.hideSoftInputFromWindow(mView.getWindowToken(), 0);
}
private void updateSwitchImeButton() {
// If there's more than one IME, enable the IME switcher button
final boolean wasVisible = mSwitchImeButton.getVisibility() == View.VISIBLE;
final boolean shouldBeVisible = hasMultipleEnabledIMEsOrSubtypes(
mInputMethodManager, false);
if (wasVisible != shouldBeVisible) {
mSwitchImeButton.setVisibility(shouldBeVisible ? View.VISIBLE : View.GONE);
}
// TODO: Check if we still need this hack.
// If no icon is visible, reset the start margin on the password field so the text is
// still centered.
if (mSwitchImeButton.getVisibility() != View.VISIBLE) {
android.view.ViewGroup.LayoutParams params = mPasswordEntry.getLayoutParams();
if (params instanceof MarginLayoutParams) {
final MarginLayoutParams mlp = (MarginLayoutParams) params;
mlp.setMarginStart(0);
mPasswordEntry.setLayoutParams(params);
}
}
}
/**
* Method adapted from com.android.inputmethod.latin.Utils
*
* @param imm The input method manager
* @param shouldIncludeAuxiliarySubtypes
* @return true if we have multiple IMEs to choose from
*/
private boolean hasMultipleEnabledIMEsOrSubtypes(InputMethodManager imm,
final boolean shouldIncludeAuxiliarySubtypes) {
final List<InputMethodInfo> enabledImis =
imm.getEnabledInputMethodListAsUser(KeyguardUpdateMonitor.getCurrentUser());
// Number of the filtered IMEs
int filteredImisCount = 0;
for (InputMethodInfo imi : enabledImis) {
// We can return true immediately after we find two or more filtered IMEs.
if (filteredImisCount > 1) return true;
final List<InputMethodSubtype> subtypes =
imm.getEnabledInputMethodSubtypeList(imi, true);
// IMEs that have no subtypes should be counted.
if (subtypes.isEmpty()) {
++filteredImisCount;
continue;
}
int auxCount = 0;
for (InputMethodSubtype subtype : subtypes) {
if (subtype.isAuxiliary()) {
++auxCount;
}
}
final int nonAuxCount = subtypes.size() - auxCount;
// IMEs that have one or more non-auxiliary subtypes should be counted.
// If shouldIncludeAuxiliarySubtypes is true, IMEs that have two or more auxiliary
// subtypes should be counted as well.
if (nonAuxCount > 0 || (shouldIncludeAuxiliarySubtypes && auxCount > 1)) {
++filteredImisCount;
continue;
}
}
return filteredImisCount > 1
// imm.getEnabledInputMethodSubtypeList(null, false) will return the current IME's
//enabled input method subtype (The current IME should be LatinIME.)
|| imm.getEnabledInputMethodSubtypeList(null, false).size() > 1;
}
}

View File

@@ -180,18 +180,18 @@ public class KeyguardPatternViewController
KeyguardSecurityCallback keyguardSecurityCallback,
LatencyTracker latencyTracker,
KeyguardMessageAreaController.Factory messageAreaControllerFactory) {
super(view, securityMode, lockPatternUtils, keyguardSecurityCallback);
super(view, securityMode, keyguardSecurityCallback);
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mLockPatternUtils = lockPatternUtils;
mLatencyTracker = latencyTracker;
mMessageAreaControllerFactory = messageAreaControllerFactory;
KeyguardMessageArea kma = KeyguardMessageArea.findSecurityMessageDisplay(mView);
mMessageAreaController = mMessageAreaControllerFactory.create(kma);
mLockPatternView = mView.findViewById(R.id.lockPatternView);
}
@Override
protected void onViewAttached() {
KeyguardMessageArea kma = KeyguardMessageArea.findSecurityMessageDisplay(mView);
mMessageAreaController = mMessageAreaControllerFactory.create(kma);
mLockPatternView = mView.findViewById(R.id.lockPatternView);
mLockPatternView.setOnPatternListener(new UnlockPatternListener());
mLockPatternView.setSaveEnabled(false);
mLockPatternView.setInStealthMode(!mLockPatternUtils.isVisiblePatternEnabled(

View File

@@ -20,7 +20,6 @@ import android.content.Context;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import com.android.internal.widget.LockscreenCredential;
@@ -29,22 +28,12 @@ import com.android.systemui.R;
/**
* A Pin based Keyguard input view
*/
public abstract class KeyguardPinBasedInputView extends KeyguardAbsKeyInputView
implements View.OnKeyListener, View.OnTouchListener {
public abstract class KeyguardPinBasedInputView extends KeyguardAbsKeyInputView {
protected PasswordTextView mPasswordEntry;
private View mOkButton;
private View mDeleteButton;
private View mButton0;
private View mButton1;
private View mButton2;
private View mButton3;
private View mButton4;
private View mButton5;
private View mButton6;
private View mButton7;
private View mButton8;
private View mButton9;
private View[] mButtons = new View[10];
public KeyguardPinBasedInputView(Context context) {
this(context, null);
@@ -62,7 +51,6 @@ public abstract class KeyguardPinBasedInputView extends KeyguardAbsKeyInputView
@Override
protected void resetState() {
setPasswordEntryEnabled(true);
}
@Override
@@ -86,10 +74,10 @@ public abstract class KeyguardPinBasedInputView extends KeyguardAbsKeyInputView
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (KeyEvent.isConfirmKey(keyCode)) {
performClick(mOkButton);
mOkButton.performClick();
return true;
} else if (keyCode == KeyEvent.KEYCODE_DEL) {
performClick(mDeleteButton);
mDeleteButton.performClick();
return true;
}
if (keyCode >= KeyEvent.KEYCODE_0 && keyCode <= KeyEvent.KEYCODE_9) {
@@ -125,42 +113,9 @@ public abstract class KeyguardPinBasedInputView extends KeyguardAbsKeyInputView
}
}
private void performClick(View view) {
view.performClick();
}
private void performNumberClick(int number) {
switch (number) {
case 0:
performClick(mButton0);
break;
case 1:
performClick(mButton1);
break;
case 2:
performClick(mButton2);
break;
case 3:
performClick(mButton3);
break;
case 4:
performClick(mButton4);
break;
case 5:
performClick(mButton5);
break;
case 6:
performClick(mButton6);
break;
case 7:
performClick(mButton7);
break;
case 8:
performClick(mButton8);
break;
case 9:
performClick(mButton9);
break;
if (number >= 0 && number <= 9) {
mButtons[number].performClick();
}
}
@@ -177,93 +132,30 @@ public abstract class KeyguardPinBasedInputView extends KeyguardAbsKeyInputView
@Override
protected void onFinishInflate() {
mPasswordEntry = findViewById(getPasswordTextViewId());
mPasswordEntry.setOnKeyListener(this);
// Set selected property on so the view can send accessibility events.
mPasswordEntry.setSelected(true);
mPasswordEntry.setUserActivityListener(new PasswordTextView.UserActivityListener() {
@Override
public void onUserActivity() {
onUserInput();
}
});
mOkButton = findViewById(R.id.key_enter);
if (mOkButton != null) {
mOkButton.setOnTouchListener(this);
mOkButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (mPasswordEntry.isEnabled()) {
verifyPasswordAndUnlock();
}
}
});
mOkButton.setOnHoverListener(new LiftToActivateListener(getContext()));
}
mDeleteButton = findViewById(R.id.delete_button);
mDeleteButton.setVisibility(View.VISIBLE);
mDeleteButton.setOnTouchListener(this);
mDeleteButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// check for time-based lockouts
if (mPasswordEntry.isEnabled()) {
mPasswordEntry.deleteLastChar();
}
}
});
mDeleteButton.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
// check for time-based lockouts
if (mPasswordEntry.isEnabled()) {
resetPasswordText(true /* animate */, true /* announce */);
}
doHapticKeyClick();
return true;
}
});
mButton0 = findViewById(R.id.key0);
mButton1 = findViewById(R.id.key1);
mButton2 = findViewById(R.id.key2);
mButton3 = findViewById(R.id.key3);
mButton4 = findViewById(R.id.key4);
mButton5 = findViewById(R.id.key5);
mButton6 = findViewById(R.id.key6);
mButton7 = findViewById(R.id.key7);
mButton8 = findViewById(R.id.key8);
mButton9 = findViewById(R.id.key9);
mButtons[0] = findViewById(R.id.key0);
mButtons[1] = findViewById(R.id.key1);
mButtons[2] = findViewById(R.id.key2);
mButtons[3] = findViewById(R.id.key3);
mButtons[4] = findViewById(R.id.key4);
mButtons[5] = findViewById(R.id.key5);
mButtons[6] = findViewById(R.id.key6);
mButtons[7] = findViewById(R.id.key7);
mButtons[8] = findViewById(R.id.key8);
mButtons[9] = findViewById(R.id.key9);
mPasswordEntry.requestFocus();
super.onFinishInflate();
}
@Override
public void onResume(int reason) {
super.onResume(reason);
mPasswordEntry.requestFocus();
}
@Override
public boolean onTouch(View v, MotionEvent event) {
if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
doHapticKeyClick();
}
return false;
}
@Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
if (event.getAction() == KeyEvent.ACTION_DOWN) {
return onKeyDown(keyCode, event);
}
return false;
}
@Override
public CharSequence getTitle() {
return getContext().getString(

View File

@@ -0,0 +1,113 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.keyguard;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnKeyListener;
import android.view.View.OnTouchListener;
import com.android.internal.util.LatencyTracker;
import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
import com.android.systemui.R;
public abstract class KeyguardPinBasedInputViewController<T extends KeyguardPinBasedInputView>
extends KeyguardAbsKeyInputViewController<T> {
private final LiftToActivateListener mLiftToActivateListener;
protected PasswordTextView mPasswordEntry;
private final OnKeyListener mOnKeyListener = (v, keyCode, event) -> {
if (event.getAction() == KeyEvent.ACTION_DOWN) {
return mView.onKeyDown(keyCode, event);
}
return false;
};
private final OnTouchListener mOnTouchListener = (v, event) -> {
if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
mView.doHapticKeyClick();
}
return false;
};
protected KeyguardPinBasedInputViewController(T view,
KeyguardUpdateMonitor keyguardUpdateMonitor,
SecurityMode securityMode,
LockPatternUtils lockPatternUtils,
KeyguardSecurityCallback keyguardSecurityCallback,
KeyguardMessageAreaController.Factory messageAreaControllerFactory,
LatencyTracker latencyTracker,
LiftToActivateListener liftToActivateListener) {
super(view, keyguardUpdateMonitor, securityMode, lockPatternUtils, keyguardSecurityCallback,
messageAreaControllerFactory, latencyTracker);
mLiftToActivateListener = liftToActivateListener;
mPasswordEntry = mView.findViewById(mView.getPasswordTextViewId());
}
@Override
protected void onViewAttached() {
super.onViewAttached();
mPasswordEntry.setOnKeyListener(mOnKeyListener);
mPasswordEntry.setUserActivityListener(this::onUserInput);
View deleteButton = mView.findViewById(R.id.delete_button);
deleteButton.setOnTouchListener(mOnTouchListener);
deleteButton.setOnClickListener(v -> {
// check for time-based lockouts
if (mPasswordEntry.isEnabled()) {
mPasswordEntry.deleteLastChar();
}
});
deleteButton.setOnLongClickListener(v -> {
// check for time-based lockouts
if (mPasswordEntry.isEnabled()) {
mView.resetPasswordText(true /* animate */, true /* announce */);
}
mView.doHapticKeyClick();
return true;
});
View okButton = mView.findViewById(R.id.key_enter);
if (okButton != null) {
okButton.setOnTouchListener(mOnTouchListener);
okButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (mPasswordEntry.isEnabled()) {
verifyPasswordAndUnlock();
}
}
});
okButton.setOnHoverListener(mLiftToActivateListener);
}
}
@Override
public void onResume(int reason) {
super.onResume(reason);
mPasswordEntry.requestFocus();
}
@Override
void resetState() {
mView.setPasswordEntryEnabled(true);
}
}

View File

@@ -0,0 +1,50 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.keyguard;
import com.android.internal.util.LatencyTracker;
import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
public class KeyguardPinViewController
extends KeyguardPinBasedInputViewController<KeyguardPINView> {
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
protected KeyguardPinViewController(KeyguardPINView view,
KeyguardUpdateMonitor keyguardUpdateMonitor,
SecurityMode securityMode, LockPatternUtils lockPatternUtils,
KeyguardSecurityCallback keyguardSecurityCallback,
KeyguardMessageAreaController.Factory messageAreaControllerFactory,
LatencyTracker latencyTracker,
LiftToActivateListener liftToActivateListener) {
super(view, keyguardUpdateMonitor, securityMode, lockPatternUtils, keyguardSecurityCallback,
messageAreaControllerFactory, latencyTracker, liftToActivateListener);
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
}
@Override
void resetState() {
super.resetState();
mMessageAreaController.setMessage("");
}
@Override
public boolean startDisappearAnimation(Runnable finishRunnable) {
return mView.startDisappearAnimation(
mKeyguardUpdateMonitor.needsSlowUnlockTransition(), finishRunnable);
}
}

View File

@@ -197,6 +197,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard
@Override
public void init() {
super.init();
mSecurityViewFlipperController.init();
}
@Override

View File

@@ -16,66 +16,19 @@
package com.android.keyguard;
import android.annotation.NonNull;
import android.app.AlertDialog;
import android.app.AlertDialog.Builder;
import android.app.Dialog;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.telephony.PinResult;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.WindowManager;
import android.widget.ImageView;
import com.android.systemui.Dependency;
import com.android.systemui.R;
/**
* Displays a PIN pad for unlocking.
*/
public class KeyguardSimPinView extends KeyguardPinBasedInputView {
private static final String LOG_TAG = "KeyguardSimPinView";
private static final boolean DEBUG = KeyguardConstants.DEBUG_SIM_STATES;
public static final String TAG = "KeyguardSimPinView";
private ProgressDialog mSimUnlockProgressDialog = null;
private CheckSimPin mCheckSimPinThread;
// Below flag is set to true during power-up or when a new SIM card inserted on device.
// When this is true and when SIM card is PIN locked state, on PIN lock screen, message would
// be displayed to inform user about the number of remaining PIN attempts left.
private boolean mShowDefaultMessage = true;
private int mRemainingAttempts = -1;
private AlertDialog mRemainingAttemptsDialog;
private int mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
private ImageView mSimImageView;
KeyguardUpdateMonitorCallback mUpdateMonitorCallback = new KeyguardUpdateMonitorCallback() {
@Override
public void onSimStateChanged(int subId, int slotId, int simState) {
if (DEBUG) Log.v(TAG, "onSimStateChanged(subId=" + subId + ",state=" + simState + ")");
switch(simState) {
case TelephonyManager.SIM_STATE_READY: {
mRemainingAttempts = -1;
resetState();
break;
}
default:
resetState();
}
}
};
public KeyguardSimPinView(Context context) {
this(context, null);
}
@@ -84,81 +37,9 @@ public class KeyguardSimPinView extends KeyguardPinBasedInputView {
super(context, attrs);
}
@Override
public void resetState() {
super.resetState();
if (DEBUG) Log.v(TAG, "Resetting state");
handleSubInfoChangeIfNeeded();
if (mShowDefaultMessage) {
showDefaultMessage();
}
boolean isEsimLocked = KeyguardEsimArea.isEsimLocked(mContext, mSubId);
public void setEsimLocked(boolean locked) {
KeyguardEsimArea esimButton = findViewById(R.id.keyguard_esim_area);
esimButton.setVisibility(isEsimLocked ? View.VISIBLE : View.GONE);
}
private void setLockedSimMessage() {
boolean isEsimLocked = KeyguardEsimArea.isEsimLocked(mContext, mSubId);
int count = 1;
TelephonyManager telephonyManager =
(TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
if (telephonyManager != null) {
count = telephonyManager.getActiveModemCount();
}
Resources rez = getResources();
String msg;
TypedArray array = mContext.obtainStyledAttributes(new int[] { R.attr.wallpaperTextColor });
int color = array.getColor(0, Color.WHITE);
array.recycle();
if (count < 2) {
msg = rez.getString(R.string.kg_sim_pin_instructions);
} else {
SubscriptionInfo info = Dependency.get(KeyguardUpdateMonitor.class)
.getSubscriptionInfoForSubId(mSubId);
CharSequence displayName = info != null ? info.getDisplayName() : ""; // don't crash
msg = rez.getString(R.string.kg_sim_pin_instructions_multi, displayName);
if (info != null) {
color = info.getIconTint();
}
}
if (isEsimLocked) {
msg = rez.getString(R.string.kg_sim_lock_esim_instructions, msg);
}
if (mSecurityMessageDisplay != null && getVisibility() == VISIBLE) {
mSecurityMessageDisplay.setMessage(msg);
}
mSimImageView.setImageTintList(ColorStateList.valueOf(color));
}
private void showDefaultMessage() {
setLockedSimMessage();
if (mRemainingAttempts >= 0) {
return;
}
// Sending empty PIN here to query the number of remaining PIN attempts
new CheckSimPin("", mSubId) {
void onSimCheckResponse(final PinResult result) {
Log.d(LOG_TAG, "onSimCheckResponse " + " empty One result "
+ result.toString());
if (result.getAttemptsRemaining() >= 0) {
mRemainingAttempts = result.getAttemptsRemaining();
setLockedSimMessage();
}
}
}.start();
}
private void handleSubInfoChangeIfNeeded() {
KeyguardUpdateMonitor monitor = Dependency.get(KeyguardUpdateMonitor.class);
int subId = monitor.getNextSubIdForState(TelephonyManager.SIM_STATE_PIN_REQUIRED);
if (subId != mSubId && SubscriptionManager.isValidSubscriptionId(subId)) {
mSubId = subId;
mShowDefaultMessage = true;
mRemainingAttempts = -1;
}
esimButton.setVisibility(locked ? View.VISIBLE : View.GONE);
}
@Override
@@ -173,35 +54,6 @@ public class KeyguardSimPinView extends KeyguardPinBasedInputView {
return 0;
}
private String getPinPasswordErrorMessage(int attemptsRemaining, boolean isDefault) {
String displayMessage;
int msgId;
if (attemptsRemaining == 0) {
displayMessage = getContext().getString(R.string.kg_password_wrong_pin_code_pukked);
} else if (attemptsRemaining > 0) {
msgId = isDefault ? R.plurals.kg_password_default_pin_message :
R.plurals.kg_password_wrong_pin_code;
displayMessage = getContext().getResources()
.getQuantityString(msgId, attemptsRemaining, attemptsRemaining);
} else {
msgId = isDefault ? R.string.kg_sim_pin_instructions : R.string.kg_password_pin_failed;
displayMessage = getContext().getString(msgId);
}
if (KeyguardEsimArea.isEsimLocked(mContext, mSubId)) {
displayMessage = getResources()
.getString(R.string.kg_sim_lock_esim_instructions, displayMessage);
}
if (DEBUG) Log.d(LOG_TAG, "getPinPasswordErrorMessage:"
+ " attemptsRemaining=" + attemptsRemaining + " displayMessage=" + displayMessage);
return displayMessage;
}
@Override
protected boolean shouldLockout(long deadline) {
// SIM PIN doesn't have a timed lockout
return false;
}
@Override
protected int getPasswordTextViewId() {
return R.id.simPinEntry;
@@ -214,173 +66,21 @@ public class KeyguardSimPinView extends KeyguardPinBasedInputView {
if (mEcaView instanceof EmergencyCarrierArea) {
((EmergencyCarrierArea) mEcaView).setCarrierTextVisible(true);
}
mSimImageView = findViewById(R.id.keyguard_sim);
}
@Override
public void showUsabilityHint() {
public void onPause() {
}
@Override
public void onResume(int reason) {
super.onResume(reason);
Dependency.get(KeyguardUpdateMonitor.class).registerCallback(mUpdateMonitorCallback);
resetState();
}
@Override
public void onPause() {
// dismiss the dialog.
if (mSimUnlockProgressDialog != null) {
mSimUnlockProgressDialog.dismiss();
mSimUnlockProgressDialog = null;
}
Dependency.get(KeyguardUpdateMonitor.class).removeCallback(mUpdateMonitorCallback);
}
public void showUsabilityHint() {
/**
* Since the IPC can block, we want to run the request in a separate thread
* with a callback.
*/
private abstract class CheckSimPin extends Thread {
private final String mPin;
private int mSubId;
protected CheckSimPin(String pin, int subId) {
mPin = pin;
mSubId = subId;
}
abstract void onSimCheckResponse(@NonNull PinResult result);
@Override
public void run() {
if (DEBUG) {
Log.v(TAG, "call supplyPinReportResultForSubscriber(subid=" + mSubId + ")");
}
TelephonyManager telephonyManager =
((TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE))
.createForSubscriptionId(mSubId);
final PinResult result = telephonyManager.supplyPinReportPinResult(mPin);
if (result == null) {
Log.e(TAG, "Error result for supplyPinReportResult.");
post(new Runnable() {
@Override
public void run() {
onSimCheckResponse(PinResult.getDefaultFailedResult());
}
});
} else {
if (DEBUG) {
Log.v(TAG, "supplyPinReportResult returned: " + result.toString());
}
post(new Runnable() {
@Override
public void run() {
onSimCheckResponse(result);
}
});
}
}
}
private Dialog getSimUnlockProgressDialog() {
if (mSimUnlockProgressDialog == null) {
mSimUnlockProgressDialog = new ProgressDialog(mContext);
mSimUnlockProgressDialog.setMessage(
mContext.getString(R.string.kg_sim_unlock_progress_dialog_message));
mSimUnlockProgressDialog.setIndeterminate(true);
mSimUnlockProgressDialog.setCancelable(false);
mSimUnlockProgressDialog.getWindow().setType(
WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
}
return mSimUnlockProgressDialog;
}
private Dialog getSimRemainingAttemptsDialog(int remaining) {
String msg = getPinPasswordErrorMessage(remaining, false);
if (mRemainingAttemptsDialog == null) {
Builder builder = new AlertDialog.Builder(mContext);
builder.setMessage(msg);
builder.setCancelable(false);
builder.setNeutralButton(R.string.ok, null);
mRemainingAttemptsDialog = builder.create();
mRemainingAttemptsDialog.getWindow().setType(
WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
} else {
mRemainingAttemptsDialog.setMessage(msg);
}
return mRemainingAttemptsDialog;
}
@Override
protected void verifyPasswordAndUnlock() {
String entry = mPasswordEntry.getText();
if (entry.length() < 4) {
// otherwise, display a message to the user, and don't submit.
mSecurityMessageDisplay.setMessage(R.string.kg_invalid_sim_pin_hint);
resetPasswordText(true /* animate */, true /* announce */);
mCallback.userActivity();
return;
}
getSimUnlockProgressDialog().show();
if (mCheckSimPinThread == null) {
mCheckSimPinThread = new CheckSimPin(mPasswordEntry.getText(), mSubId) {
@Override
void onSimCheckResponse(final PinResult result) {
post(new Runnable() {
@Override
public void run() {
mRemainingAttempts = result.getAttemptsRemaining();
if (mSimUnlockProgressDialog != null) {
mSimUnlockProgressDialog.hide();
}
resetPasswordText(true /* animate */,
/* announce */
result.getType() != PinResult.PIN_RESULT_TYPE_SUCCESS);
if (result.getType() == PinResult.PIN_RESULT_TYPE_SUCCESS) {
Dependency.get(KeyguardUpdateMonitor.class)
.reportSimUnlocked(mSubId);
mRemainingAttempts = -1;
mShowDefaultMessage = true;
if (mCallback != null) {
mCallback.dismiss(true, KeyguardUpdateMonitor.getCurrentUser());
}
} else {
mShowDefaultMessage = false;
if (result.getType() == PinResult.PIN_RESULT_TYPE_INCORRECT) {
if (result.getAttemptsRemaining() <= 2) {
// this is getting critical - show dialog
getSimRemainingAttemptsDialog(
result.getAttemptsRemaining()).show();
} else {
// show message
mSecurityMessageDisplay.setMessage(
getPinPasswordErrorMessage(
result.getAttemptsRemaining(), false));
}
} else {
// "PIN operation failed!" - no idea what this was and no way to
// find out. :/
mSecurityMessageDisplay.setMessage(getContext().getString(
R.string.kg_password_pin_failed));
}
if (DEBUG) Log.d(LOG_TAG, "verifyPasswordAndUnlock "
+ " CheckSimPin.onSimCheckResponse: " + result
+ " attemptsRemaining=" + result.getAttemptsRemaining());
}
mCallback.userActivity();
mCheckSimPinThread = null;
}
});
}
};
mCheckSimPinThread.start();
}
}
@Override

View File

@@ -0,0 +1,350 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.keyguard;
import android.annotation.NonNull;
import android.app.AlertDialog;
import android.app.AlertDialog.Builder;
import android.app.Dialog;
import android.app.ProgressDialog;
import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.telephony.PinResult;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.util.Log;
import android.view.View;
import android.view.WindowManager;
import android.widget.ImageView;
import com.android.internal.util.LatencyTracker;
import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
import com.android.systemui.R;
public class KeyguardSimPinViewController
extends KeyguardPinBasedInputViewController<KeyguardSimPinView> {
public static final String TAG = "KeyguardSimPinView";
private static final String LOG_TAG = "KeyguardSimPinView";
private static final boolean DEBUG = KeyguardConstants.DEBUG_SIM_STATES;
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
private final TelephonyManager mTelephonyManager;
private ProgressDialog mSimUnlockProgressDialog;
private CheckSimPin mCheckSimPinThread;
private int mRemainingAttempts;
// Below flag is set to true during power-up or when a new SIM card inserted on device.
// When this is true and when SIM card is PIN locked state, on PIN lock screen, message would
// be displayed to inform user about the number of remaining PIN attempts left.
private boolean mShowDefaultMessage;
private int mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
private AlertDialog mRemainingAttemptsDialog;
private ImageView mSimImageView;
KeyguardUpdateMonitorCallback mUpdateMonitorCallback = new KeyguardUpdateMonitorCallback() {
@Override
public void onSimStateChanged(int subId, int slotId, int simState) {
if (DEBUG) Log.v(TAG, "onSimStateChanged(subId=" + subId + ",state=" + simState + ")");
if (simState == TelephonyManager.SIM_STATE_READY) {
mRemainingAttempts = -1;
resetState();
} else {
resetState();
}
}
};
protected KeyguardSimPinViewController(KeyguardSimPinView view,
KeyguardUpdateMonitor keyguardUpdateMonitor,
SecurityMode securityMode, LockPatternUtils lockPatternUtils,
KeyguardSecurityCallback keyguardSecurityCallback,
KeyguardMessageAreaController.Factory messageAreaControllerFactory,
LatencyTracker latencyTracker,
LiftToActivateListener liftToActivateListener,
TelephonyManager telephonyManager) {
super(view, keyguardUpdateMonitor, securityMode, lockPatternUtils, keyguardSecurityCallback,
messageAreaControllerFactory, latencyTracker, liftToActivateListener);
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mTelephonyManager = telephonyManager;
mSimImageView = mView.findViewById(R.id.keyguard_sim);
}
@Override
protected void onViewAttached() {
super.onViewAttached();
}
@Override
void resetState() {
super.resetState();
if (DEBUG) Log.v(TAG, "Resetting state");
handleSubInfoChangeIfNeeded();
mMessageAreaController.setMessage("");
if (mShowDefaultMessage) {
showDefaultMessage();
}
mView.setEsimLocked(KeyguardEsimArea.isEsimLocked(mView.getContext(), mSubId));
}
@Override
public boolean startDisappearAnimation(Runnable finishRunnable) {
return mView.startDisappearAnimation(finishRunnable);
}
@Override
public void onResume(int reason) {
super.onResume(reason);
mKeyguardUpdateMonitor.registerCallback(mUpdateMonitorCallback);
mView.resetState();
}
@Override
public void onPause() {
super.onPause();
mKeyguardUpdateMonitor.removeCallback(mUpdateMonitorCallback);
// dismiss the dialog.
if (mSimUnlockProgressDialog != null) {
mSimUnlockProgressDialog.dismiss();
mSimUnlockProgressDialog = null;
}
}
@Override
protected void verifyPasswordAndUnlock() {
String entry = mPasswordEntry.getText();
if (entry.length() < 4) {
// otherwise, display a message to the user, and don't submit.
mMessageAreaController.setMessage(
com.android.systemui.R.string.kg_invalid_sim_pin_hint);
mView.resetPasswordText(true /* animate */, true /* announce */);
mKeyguardSecurityCallback.userActivity();
return;
}
getSimUnlockProgressDialog().show();
if (mCheckSimPinThread == null) {
mCheckSimPinThread = new CheckSimPin(mPasswordEntry.getText(), mSubId) {
@Override
void onSimCheckResponse(final PinResult result) {
mView.post(() -> {
mRemainingAttempts = result.getAttemptsRemaining();
if (mSimUnlockProgressDialog != null) {
mSimUnlockProgressDialog.hide();
}
mView.resetPasswordText(true /* animate */,
/* announce */
result.getType() != PinResult.PIN_RESULT_TYPE_SUCCESS);
if (result.getType() == PinResult.PIN_RESULT_TYPE_SUCCESS) {
mKeyguardUpdateMonitor.reportSimUnlocked(mSubId);
mRemainingAttempts = -1;
mShowDefaultMessage = true;
mKeyguardSecurityCallback.dismiss(
true, KeyguardUpdateMonitor.getCurrentUser());
} else {
mShowDefaultMessage = false;
if (result.getType() == PinResult.PIN_RESULT_TYPE_INCORRECT) {
if (result.getAttemptsRemaining() <= 2) {
// this is getting critical - show dialog
getSimRemainingAttemptsDialog(
result.getAttemptsRemaining()).show();
} else {
// show message
mMessageAreaController.setMessage(
getPinPasswordErrorMessage(
result.getAttemptsRemaining(), false));
}
} else {
// "PIN operation failed!" - no idea what this was and no way to
// find out. :/
mMessageAreaController.setMessage(mView.getResources().getString(
R.string.kg_password_pin_failed));
}
if (DEBUG) {
Log.d(LOG_TAG, "verifyPasswordAndUnlock "
+ " CheckSimPin.onSimCheckResponse: " + result
+ " attemptsRemaining=" + result.getAttemptsRemaining());
}
}
mKeyguardSecurityCallback.userActivity();
mCheckSimPinThread = null;
});
}
};
mCheckSimPinThread.start();
}
}
private Dialog getSimUnlockProgressDialog() {
if (mSimUnlockProgressDialog == null) {
mSimUnlockProgressDialog = new ProgressDialog(mView.getContext());
mSimUnlockProgressDialog.setMessage(
mView.getResources().getString(R.string.kg_sim_unlock_progress_dialog_message));
mSimUnlockProgressDialog.setIndeterminate(true);
mSimUnlockProgressDialog.setCancelable(false);
mSimUnlockProgressDialog.getWindow().setType(
WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
}
return mSimUnlockProgressDialog;
}
private Dialog getSimRemainingAttemptsDialog(int remaining) {
String msg = getPinPasswordErrorMessage(remaining, false);
if (mRemainingAttemptsDialog == null) {
Builder builder = new AlertDialog.Builder(mView.getContext());
builder.setMessage(msg);
builder.setCancelable(false);
builder.setNeutralButton(R.string.ok, null);
mRemainingAttemptsDialog = builder.create();
mRemainingAttemptsDialog.getWindow().setType(
WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
} else {
mRemainingAttemptsDialog.setMessage(msg);
}
return mRemainingAttemptsDialog;
}
private String getPinPasswordErrorMessage(int attemptsRemaining, boolean isDefault) {
String displayMessage;
int msgId;
if (attemptsRemaining == 0) {
displayMessage = mView.getResources().getString(
R.string.kg_password_wrong_pin_code_pukked);
} else if (attemptsRemaining > 0) {
msgId = isDefault ? R.plurals.kg_password_default_pin_message :
R.plurals.kg_password_wrong_pin_code;
displayMessage = mView.getResources()
.getQuantityString(msgId, attemptsRemaining, attemptsRemaining);
} else {
msgId = isDefault ? R.string.kg_sim_pin_instructions : R.string.kg_password_pin_failed;
displayMessage = mView.getResources().getString(msgId);
}
if (KeyguardEsimArea.isEsimLocked(mView.getContext(), mSubId)) {
displayMessage = mView.getResources()
.getString(R.string.kg_sim_lock_esim_instructions, displayMessage);
}
if (DEBUG) {
Log.d(LOG_TAG, "getPinPasswordErrorMessage: attemptsRemaining="
+ attemptsRemaining + " displayMessage=" + displayMessage);
}
return displayMessage;
}
private void showDefaultMessage() {
setLockedSimMessage();
if (mRemainingAttempts >= 0) {
return;
}
// Sending empty PIN here to query the number of remaining PIN attempts
new CheckSimPin("", mSubId) {
void onSimCheckResponse(final PinResult result) {
Log.d(LOG_TAG, "onSimCheckResponse " + " empty One result "
+ result.toString());
if (result.getAttemptsRemaining() >= 0) {
mRemainingAttempts = result.getAttemptsRemaining();
setLockedSimMessage();
}
}
}.start();
}
/**
* Since the IPC can block, we want to run the request in a separate thread
* with a callback.
*/
private abstract class CheckSimPin extends Thread {
private final String mPin;
private int mSubId;
protected CheckSimPin(String pin, int subId) {
mPin = pin;
mSubId = subId;
}
abstract void onSimCheckResponse(@NonNull PinResult result);
@Override
public void run() {
if (DEBUG) {
Log.v(TAG, "call supplyPinReportResultForSubscriber(subid=" + mSubId + ")");
}
TelephonyManager telephonyManager =
mTelephonyManager.createForSubscriptionId(mSubId);
final PinResult result = telephonyManager.supplyPinReportPinResult(mPin);
if (result == null) {
Log.e(TAG, "Error result for supplyPinReportResult.");
mView.post(() -> onSimCheckResponse(PinResult.getDefaultFailedResult()));
} else {
if (DEBUG) {
Log.v(TAG, "supplyPinReportResult returned: " + result.toString());
}
mView.post(() -> onSimCheckResponse(result));
}
}
}
private void setLockedSimMessage() {
boolean isEsimLocked = KeyguardEsimArea.isEsimLocked(mView.getContext(), mSubId);
int count = 1;
if (mTelephonyManager != null) {
count = mTelephonyManager.getActiveModemCount();
}
Resources rez = mView.getResources();
String msg;
TypedArray array = mView.getContext().obtainStyledAttributes(
new int[] { R.attr.wallpaperTextColor });
int color = array.getColor(0, Color.WHITE);
array.recycle();
if (count < 2) {
msg = rez.getString(R.string.kg_sim_pin_instructions);
} else {
SubscriptionInfo info = mKeyguardUpdateMonitor.getSubscriptionInfoForSubId(mSubId);
CharSequence displayName = info != null ? info.getDisplayName() : ""; // don't crash
msg = rez.getString(R.string.kg_sim_pin_instructions_multi, displayName);
if (info != null) {
color = info.getIconTint();
}
}
if (isEsimLocked) {
msg = rez.getString(R.string.kg_sim_lock_esim_instructions, msg);
}
if (mView.getVisibility() == View.VISIBLE) {
mMessageAreaController.setMessage(msg);
}
mSimImageView.setImageTintList(ColorStateList.valueOf(color));
}
private void handleSubInfoChangeIfNeeded() {
int subId = mKeyguardUpdateMonitor
.getNextSubIdForState(TelephonyManager.SIM_STATE_PIN_REQUIRED);
if (subId != mSubId && SubscriptionManager.isValidSubscriptionId(subId)) {
mSubId = subId;
mShowDefaultMessage = true;
mRemainingAttempts = -1;
}
}
}

View File

@@ -16,27 +16,11 @@
package com.android.keyguard;
import android.annotation.NonNull;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.telephony.PinResult;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.WindowManager;
import android.widget.ImageView;
import com.android.systemui.Dependency;
import com.android.systemui.R;
@@ -44,47 +28,10 @@ import com.android.systemui.R;
* Displays a PIN pad for entering a PUK (Pin Unlock Kode) provided by a carrier.
*/
public class KeyguardSimPukView extends KeyguardPinBasedInputView {
private static final String LOG_TAG = "KeyguardSimPukView";
private static final boolean DEBUG = KeyguardConstants.DEBUG;
public static final String TAG = "KeyguardSimPukView";
private ProgressDialog mSimUnlockProgressDialog = null;
private CheckSimPuk mCheckSimPukThread;
// Below flag is set to true during power-up or when a new SIM card inserted on device.
// When this is true and when SIM card is PUK locked state, on PIN lock screen, message would
// be displayed to inform user about the number of remaining PUK attempts left.
private boolean mShowDefaultMessage = true;
private int mRemainingAttempts = -1;
private String mPukText;
private String mPinText;
private StateMachine mStateMachine = new StateMachine();
private AlertDialog mRemainingAttemptsDialog;
private int mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
private ImageView mSimImageView;
KeyguardUpdateMonitorCallback mUpdateMonitorCallback = new KeyguardUpdateMonitorCallback() {
@Override
public void onSimStateChanged(int subId, int slotId, int simState) {
if (DEBUG) Log.v(TAG, "onSimStateChanged(subId=" + subId + ",state=" + simState + ")");
switch(simState) {
// If the SIM is unlocked via a key sequence through the emergency dialer, it will
// move into the READY state and the PUK lock keyguard should be removed.
case TelephonyManager.SIM_STATE_READY: {
mRemainingAttempts = -1;
mShowDefaultMessage = true;
// mCallback can be null if onSimStateChanged callback is called when keyguard
// isn't active.
if (mCallback != null) {
mCallback.dismiss(true, KeyguardUpdateMonitor.getCurrentUser());
}
break;
}
default:
resetState();
}
}
};
public KeyguardSimPukView(Context context) {
this(context, null);
@@ -94,136 +41,14 @@ public class KeyguardSimPukView extends KeyguardPinBasedInputView {
super(context, attrs);
}
private class StateMachine {
final int ENTER_PUK = 0;
final int ENTER_PIN = 1;
final int CONFIRM_PIN = 2;
final int DONE = 3;
private int state = ENTER_PUK;
public void next() {
int msg = 0;
if (state == ENTER_PUK) {
if (checkPuk()) {
state = ENTER_PIN;
msg = R.string.kg_puk_enter_pin_hint;
} else {
msg = R.string.kg_invalid_sim_puk_hint;
}
} else if (state == ENTER_PIN) {
if (checkPin()) {
state = CONFIRM_PIN;
msg = R.string.kg_enter_confirm_pin_hint;
} else {
msg = R.string.kg_invalid_sim_pin_hint;
}
} else if (state == CONFIRM_PIN) {
if (confirmPin()) {
state = DONE;
msg = R.string.keyguard_sim_unlock_progress_dialog_message;
updateSim();
} else {
state = ENTER_PIN; // try again?
msg = R.string.kg_invalid_confirm_pin_hint;
}
}
resetPasswordText(true /* animate */, true /* announce */);
if (msg != 0) {
mSecurityMessageDisplay.setMessage(msg);
}
}
void reset() {
mPinText="";
mPukText="";
state = ENTER_PUK;
handleSubInfoChangeIfNeeded();
if (mShowDefaultMessage) {
showDefaultMessage();
}
boolean isEsimLocked = KeyguardEsimArea.isEsimLocked(mContext, mSubId);
KeyguardEsimArea esimButton = findViewById(R.id.keyguard_esim_area);
esimButton.setVisibility(isEsimLocked ? View.VISIBLE : View.GONE);
mPasswordEntry.requestFocus();
}
}
private void showDefaultMessage() {
if (mRemainingAttempts >= 0) {
mSecurityMessageDisplay.setMessage(getPukPasswordErrorMessage(
mRemainingAttempts, true));
return;
}
boolean isEsimLocked = KeyguardEsimArea.isEsimLocked(mContext, mSubId);
int count = 1;
TelephonyManager telephonyManager =
(TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
if (telephonyManager != null) {
count = telephonyManager.getActiveModemCount();
}
Resources rez = getResources();
String msg;
TypedArray array = mContext.obtainStyledAttributes(new int[] { R.attr.wallpaperTextColor });
int color = array.getColor(0, Color.WHITE);
array.recycle();
if (count < 2) {
msg = rez.getString(R.string.kg_puk_enter_puk_hint);
} else {
SubscriptionInfo info = Dependency.get(KeyguardUpdateMonitor.class)
.getSubscriptionInfoForSubId(mSubId);
CharSequence displayName = info != null ? info.getDisplayName() : "";
msg = rez.getString(R.string.kg_puk_enter_puk_hint_multi, displayName);
if (info != null) {
color = info.getIconTint();
}
}
if (isEsimLocked) {
msg = rez.getString(R.string.kg_sim_lock_esim_instructions, msg);
}
if (mSecurityMessageDisplay != null) {
mSecurityMessageDisplay.setMessage(msg);
}
mSimImageView.setImageTintList(ColorStateList.valueOf(color));
// Sending empty PUK here to query the number of remaining PIN attempts
new CheckSimPuk("", "", mSubId) {
void onSimLockChangedResponse(final PinResult result) {
if (result == null) Log.e(LOG_TAG, "onSimCheckResponse, pin result is NULL");
else {
Log.d(LOG_TAG, "onSimCheckResponse " + " empty One result "
+ result.toString());
if (result.getAttemptsRemaining() >= 0) {
mRemainingAttempts = result.getAttemptsRemaining();
mSecurityMessageDisplay.setMessage(
getPukPasswordErrorMessage(result.getAttemptsRemaining(), true));
}
}
}
}.start();
}
private void handleSubInfoChangeIfNeeded() {
KeyguardUpdateMonitor monitor = Dependency.get(KeyguardUpdateMonitor.class);
int subId = monitor.getNextSubIdForState(TelephonyManager.SIM_STATE_PUK_REQUIRED);
if (subId != mSubId && SubscriptionManager.isValidSubscriptionId(subId)) {
mSubId = subId;
mShowDefaultMessage = true;
mRemainingAttempts = -1;
}
}
@Override
protected int getPromptReasonStringRes(int reason) {
// No message on SIM Puk
return 0;
}
private String getPukPasswordErrorMessage(int attemptsRemaining, boolean isDefault) {
String getPukPasswordErrorMessage(
int attemptsRemaining, boolean isDefault, boolean isEsimLocked) {
String displayMessage;
if (attemptsRemaining == 0) {
@@ -238,27 +63,18 @@ public class KeyguardSimPukView extends KeyguardPinBasedInputView {
R.string.kg_password_puk_failed;
displayMessage = getContext().getString(msgId);
}
if (KeyguardEsimArea.isEsimLocked(mContext, mSubId)) {
if (isEsimLocked) {
displayMessage = getResources()
.getString(R.string.kg_sim_lock_esim_instructions, displayMessage);
}
if (DEBUG) Log.d(LOG_TAG, "getPukPasswordErrorMessage:"
+ " attemptsRemaining=" + attemptsRemaining + " displayMessage=" + displayMessage);
if (DEBUG) {
Log.d(TAG, "getPukPasswordErrorMessage:"
+ " attemptsRemaining=" + attemptsRemaining
+ " displayMessage=" + displayMessage);
}
return displayMessage;
}
@Override
public void resetState() {
super.resetState();
mStateMachine.reset();
}
@Override
protected boolean shouldLockout(long deadline) {
// SIM PUK doesn't have a timed lockout
return false;
}
@Override
protected int getPasswordTextViewId() {
return R.id.pukEntry;
@@ -271,20 +87,6 @@ public class KeyguardSimPukView extends KeyguardPinBasedInputView {
if (mEcaView instanceof EmergencyCarrierArea) {
((EmergencyCarrierArea) mEcaView).setCarrierTextVisible(true);
}
mSimImageView = findViewById(R.id.keyguard_sim);
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
Dependency.get(KeyguardUpdateMonitor.class).registerCallback(mUpdateMonitorCallback);
resetState();
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
Dependency.get(KeyguardUpdateMonitor.class).removeCallback(mUpdateMonitorCallback);
}
@Override
@@ -300,168 +102,8 @@ public class KeyguardSimPukView extends KeyguardPinBasedInputView {
}
}
/**
* Since the IPC can block, we want to run the request in a separate thread
* with a callback.
*/
private abstract class CheckSimPuk extends Thread {
private final String mPin, mPuk;
private final int mSubId;
protected CheckSimPuk(String puk, String pin, int subId) {
mPuk = puk;
mPin = pin;
mSubId = subId;
}
abstract void onSimLockChangedResponse(@NonNull PinResult result);
@Override
public void run() {
if (DEBUG) Log.v(TAG, "call supplyPukReportResult()");
TelephonyManager telephonyManager =
((TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE))
.createForSubscriptionId(mSubId);
final PinResult result = telephonyManager.supplyPukReportPinResult(mPuk, mPin);
if (result == null) {
Log.e(TAG, "Error result for supplyPukReportResult.");
post(new Runnable() {
@Override
public void run() {
onSimLockChangedResponse(PinResult.getDefaultFailedResult());
}
});
} else {
if (DEBUG) {
Log.v(TAG, "supplyPukReportResult returned: " + result.toString());
}
post(new Runnable() {
@Override
public void run() {
onSimLockChangedResponse(result);
}
});
}
}
}
private Dialog getSimUnlockProgressDialog() {
if (mSimUnlockProgressDialog == null) {
mSimUnlockProgressDialog = new ProgressDialog(mContext);
mSimUnlockProgressDialog.setMessage(
mContext.getString(R.string.kg_sim_unlock_progress_dialog_message));
mSimUnlockProgressDialog.setIndeterminate(true);
mSimUnlockProgressDialog.setCancelable(false);
if (!(mContext instanceof Activity)) {
mSimUnlockProgressDialog.getWindow().setType(
WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
}
}
return mSimUnlockProgressDialog;
}
private Dialog getPukRemainingAttemptsDialog(int remaining) {
String msg = getPukPasswordErrorMessage(remaining, false);
if (mRemainingAttemptsDialog == null) {
AlertDialog.Builder builder = new AlertDialog.Builder(mContext);
builder.setMessage(msg);
builder.setCancelable(false);
builder.setNeutralButton(R.string.ok, null);
mRemainingAttemptsDialog = builder.create();
mRemainingAttemptsDialog.getWindow().setType(
WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
} else {
mRemainingAttemptsDialog.setMessage(msg);
}
return mRemainingAttemptsDialog;
}
private boolean checkPuk() {
// make sure the puk is at least 8 digits long.
if (mPasswordEntry.getText().length() == 8) {
mPukText = mPasswordEntry.getText();
return true;
}
return false;
}
private boolean checkPin() {
// make sure the PIN is between 4 and 8 digits
int length = mPasswordEntry.getText().length();
if (length >= 4 && length <= 8) {
mPinText = mPasswordEntry.getText();
return true;
}
return false;
}
public boolean confirmPin() {
return mPinText.equals(mPasswordEntry.getText());
}
private void updateSim() {
getSimUnlockProgressDialog().show();
if (mCheckSimPukThread == null) {
mCheckSimPukThread = new CheckSimPuk(mPukText, mPinText, mSubId) {
@Override
void onSimLockChangedResponse(final PinResult result) {
post(new Runnable() {
@Override
public void run() {
if (mSimUnlockProgressDialog != null) {
mSimUnlockProgressDialog.hide();
}
resetPasswordText(true /* animate */,
/* announce */
result.getType() != PinResult.PIN_RESULT_TYPE_SUCCESS);
if (result.getType() == PinResult.PIN_RESULT_TYPE_SUCCESS) {
Dependency.get(KeyguardUpdateMonitor.class)
.reportSimUnlocked(mSubId);
mRemainingAttempts = -1;
mShowDefaultMessage = true;
if (mCallback != null) {
mCallback.dismiss(true,
KeyguardUpdateMonitor.getCurrentUser());
}
} else {
mShowDefaultMessage = false;
if (result.getType() == PinResult.PIN_RESULT_TYPE_INCORRECT) {
// show message
mSecurityMessageDisplay.setMessage(getPukPasswordErrorMessage(
result.getAttemptsRemaining(), false));
if (result.getAttemptsRemaining() <= 2) {
// this is getting critical - show dialog
getPukRemainingAttemptsDialog(
result.getAttemptsRemaining()).show();
} else {
// show message
mSecurityMessageDisplay.setMessage(
getPukPasswordErrorMessage(
result.getAttemptsRemaining(), false));
}
} else {
mSecurityMessageDisplay.setMessage(getContext().getString(
R.string.kg_password_puk_failed));
}
if (DEBUG) Log.d(LOG_TAG, "verifyPasswordAndUnlock "
+ " UpdateSim.onSimCheckResponse: "
+ " attemptsRemaining=" + result.getAttemptsRemaining());
}
mStateMachine.reset();
mCheckSimPukThread = null;
}
});
}
};
mCheckSimPukThread.start();
}
}
@Override
protected void verifyPasswordAndUnlock() {
mStateMachine.next();
public void onResume(int reason) {
}
@Override

View File

@@ -0,0 +1,404 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.keyguard;
import android.annotation.NonNull;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.ProgressDialog;
import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.telephony.PinResult;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.util.Log;
import android.view.View;
import android.view.WindowManager;
import android.widget.ImageView;
import com.android.internal.util.LatencyTracker;
import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
import com.android.systemui.Dependency;
import com.android.systemui.R;
public class KeyguardSimPukViewController
extends KeyguardPinBasedInputViewController<KeyguardSimPukView> {
private static final boolean DEBUG = KeyguardConstants.DEBUG;
public static final String TAG = "KeyguardSimPukView";
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
private final TelephonyManager mTelephonyManager;
private String mPukText;
private String mPinText;
private int mRemainingAttempts;
// Below flag is set to true during power-up or when a new SIM card inserted on device.
// When this is true and when SIM card is PUK locked state, on PIN lock screen, message would
// be displayed to inform user about the number of remaining PUK attempts left.
private boolean mShowDefaultMessage;
private StateMachine mStateMachine = new StateMachine();
private int mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
private CheckSimPuk mCheckSimPukThread;
private ProgressDialog mSimUnlockProgressDialog;
KeyguardUpdateMonitorCallback mUpdateMonitorCallback = new KeyguardUpdateMonitorCallback() {
@Override
public void onSimStateChanged(int subId, int slotId, int simState) {
if (DEBUG) Log.v(TAG, "onSimStateChanged(subId=" + subId + ",state=" + simState + ")");
// If the SIM is unlocked via a key sequence through the emergency dialer, it will
// move into the READY state and the PUK lock keyguard should be removed.
if (simState == TelephonyManager.SIM_STATE_READY) {
mRemainingAttempts = -1;
mShowDefaultMessage = true;
mKeyguardSecurityCallback.dismiss(true, KeyguardUpdateMonitor.getCurrentUser());
} else {
resetState();
}
}
};
private ImageView mSimImageView;
private AlertDialog mRemainingAttemptsDialog;
protected KeyguardSimPukViewController(KeyguardSimPukView view,
KeyguardUpdateMonitor keyguardUpdateMonitor,
SecurityMode securityMode, LockPatternUtils lockPatternUtils,
KeyguardSecurityCallback keyguardSecurityCallback,
KeyguardMessageAreaController.Factory messageAreaControllerFactory,
LatencyTracker latencyTracker,
LiftToActivateListener liftToActivateListener,
TelephonyManager telephonyManager) {
super(view, keyguardUpdateMonitor, securityMode, lockPatternUtils, keyguardSecurityCallback,
messageAreaControllerFactory, latencyTracker, liftToActivateListener);
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mTelephonyManager = telephonyManager;
mSimImageView = mView.findViewById(R.id.keyguard_sim);
}
@Override
protected void onViewAttached() {
super.onViewAttached();
mKeyguardUpdateMonitor.registerCallback(mUpdateMonitorCallback);
}
@Override
protected void onViewDetached() {
super.onViewDetached();
mKeyguardUpdateMonitor.removeCallback(mUpdateMonitorCallback);
}
@Override
void resetState() {
super.resetState();
mStateMachine.reset();
}
@Override
protected void verifyPasswordAndUnlock() {
mStateMachine.next();
}
private class StateMachine {
static final int ENTER_PUK = 0;
static final int ENTER_PIN = 1;
static final int CONFIRM_PIN = 2;
static final int DONE = 3;
private int mState = ENTER_PUK;
public void next() {
int msg = 0;
if (mState == ENTER_PUK) {
if (checkPuk()) {
mState = ENTER_PIN;
msg = com.android.systemui.R.string.kg_puk_enter_pin_hint;
} else {
msg = com.android.systemui.R.string.kg_invalid_sim_puk_hint;
}
} else if (mState == ENTER_PIN) {
if (checkPin()) {
mState = CONFIRM_PIN;
msg = com.android.systemui.R.string.kg_enter_confirm_pin_hint;
} else {
msg = com.android.systemui.R.string.kg_invalid_sim_pin_hint;
}
} else if (mState == CONFIRM_PIN) {
if (confirmPin()) {
mState = DONE;
msg = com.android.systemui.R.string.keyguard_sim_unlock_progress_dialog_message;
updateSim();
} else {
mState = ENTER_PIN; // try again?
msg = com.android.systemui.R.string.kg_invalid_confirm_pin_hint;
}
}
mView.resetPasswordText(true /* animate */, true /* announce */);
if (msg != 0) {
mMessageAreaController.setMessage(msg);
}
}
void reset() {
mPinText = "";
mPukText = "";
mState = ENTER_PUK;
handleSubInfoChangeIfNeeded();
if (mShowDefaultMessage) {
showDefaultMessage();
}
boolean isEsimLocked = KeyguardEsimArea.isEsimLocked(mView.getContext(), mSubId);
KeyguardEsimArea esimButton = mView.findViewById(R.id.keyguard_esim_area);
esimButton.setVisibility(isEsimLocked ? View.VISIBLE : View.GONE);
mPasswordEntry.requestFocus();
}
}
private void showDefaultMessage() {
if (mRemainingAttempts >= 0) {
mMessageAreaController.setMessage(mView.getPukPasswordErrorMessage(
mRemainingAttempts, true,
KeyguardEsimArea.isEsimLocked(mView.getContext(), mSubId)));
return;
}
boolean isEsimLocked = KeyguardEsimArea.isEsimLocked(mView.getContext(), mSubId);
int count = 1;
if (mTelephonyManager != null) {
count = mTelephonyManager.getActiveModemCount();
}
Resources rez = mView.getResources();
String msg;
TypedArray array = mView.getContext().obtainStyledAttributes(
new int[] { R.attr.wallpaperTextColor });
int color = array.getColor(0, Color.WHITE);
array.recycle();
if (count < 2) {
msg = rez.getString(R.string.kg_puk_enter_puk_hint);
} else {
SubscriptionInfo info = Dependency.get(KeyguardUpdateMonitor.class)
.getSubscriptionInfoForSubId(mSubId);
CharSequence displayName = info != null ? info.getDisplayName() : "";
msg = rez.getString(R.string.kg_puk_enter_puk_hint_multi, displayName);
if (info != null) {
color = info.getIconTint();
}
}
if (isEsimLocked) {
msg = rez.getString(R.string.kg_sim_lock_esim_instructions, msg);
}
mMessageAreaController.setMessage(msg);
mSimImageView.setImageTintList(ColorStateList.valueOf(color));
// Sending empty PUK here to query the number of remaining PIN attempts
new CheckSimPuk("", "", mSubId) {
void onSimLockChangedResponse(final PinResult result) {
if (result == null) Log.e(TAG, "onSimCheckResponse, pin result is NULL");
else {
Log.d(TAG, "onSimCheckResponse " + " empty One result "
+ result.toString());
if (result.getAttemptsRemaining() >= 0) {
mRemainingAttempts = result.getAttemptsRemaining();
mMessageAreaController.setMessage(
mView.getPukPasswordErrorMessage(
result.getAttemptsRemaining(), true,
KeyguardEsimArea.isEsimLocked(mView.getContext(), mSubId)));
}
}
}
}.start();
}
private boolean checkPuk() {
// make sure the puk is at least 8 digits long.
if (mPasswordEntry.getText().length() == 8) {
mPukText = mPasswordEntry.getText();
return true;
}
return false;
}
private boolean checkPin() {
// make sure the PIN is between 4 and 8 digits
int length = mPasswordEntry.getText().length();
if (length >= 4 && length <= 8) {
mPinText = mPasswordEntry.getText();
return true;
}
return false;
}
public boolean confirmPin() {
return mPinText.equals(mPasswordEntry.getText());
}
private void updateSim() {
getSimUnlockProgressDialog().show();
if (mCheckSimPukThread == null) {
mCheckSimPukThread = new CheckSimPuk(mPukText, mPinText, mSubId) {
@Override
void onSimLockChangedResponse(final PinResult result) {
mView.post(() -> {
if (mSimUnlockProgressDialog != null) {
mSimUnlockProgressDialog.hide();
}
mView.resetPasswordText(true /* animate */,
/* announce */
result.getType() != PinResult.PIN_RESULT_TYPE_SUCCESS);
if (result.getType() == PinResult.PIN_RESULT_TYPE_SUCCESS) {
mKeyguardUpdateMonitor.reportSimUnlocked(mSubId);
mRemainingAttempts = -1;
mShowDefaultMessage = true;
mKeyguardSecurityCallback.dismiss(
true, KeyguardUpdateMonitor.getCurrentUser());
} else {
mShowDefaultMessage = false;
if (result.getType() == PinResult.PIN_RESULT_TYPE_INCORRECT) {
// show message
mMessageAreaController.setMessage(mView.getPukPasswordErrorMessage(
result.getAttemptsRemaining(), false,
KeyguardEsimArea.isEsimLocked(mView.getContext(), mSubId)));
if (result.getAttemptsRemaining() <= 2) {
// this is getting critical - show dialog
getPukRemainingAttemptsDialog(
result.getAttemptsRemaining()).show();
} else {
// show message
mMessageAreaController.setMessage(
mView.getPukPasswordErrorMessage(
result.getAttemptsRemaining(), false,
KeyguardEsimArea.isEsimLocked(
mView.getContext(), mSubId)));
}
} else {
mMessageAreaController.setMessage(mView.getResources().getString(
R.string.kg_password_puk_failed));
}
if (DEBUG) {
Log.d(TAG, "verifyPasswordAndUnlock "
+ " UpdateSim.onSimCheckResponse: "
+ " attemptsRemaining=" + result.getAttemptsRemaining());
}
}
mStateMachine.reset();
mCheckSimPukThread = null;
});
}
};
mCheckSimPukThread.start();
}
}
@Override
protected boolean shouldLockout(long deadline) {
// SIM PUK doesn't have a timed lockout
return false;
}
private Dialog getSimUnlockProgressDialog() {
if (mSimUnlockProgressDialog == null) {
mSimUnlockProgressDialog = new ProgressDialog(mView.getContext());
mSimUnlockProgressDialog.setMessage(
mView.getResources().getString(R.string.kg_sim_unlock_progress_dialog_message));
mSimUnlockProgressDialog.setIndeterminate(true);
mSimUnlockProgressDialog.setCancelable(false);
if (!(mView.getContext() instanceof Activity)) {
mSimUnlockProgressDialog.getWindow().setType(
WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
}
}
return mSimUnlockProgressDialog;
}
private void handleSubInfoChangeIfNeeded() {
int subId = mKeyguardUpdateMonitor.getNextSubIdForState(
TelephonyManager.SIM_STATE_PUK_REQUIRED);
if (subId != mSubId && SubscriptionManager.isValidSubscriptionId(subId)) {
mSubId = subId;
mShowDefaultMessage = true;
mRemainingAttempts = -1;
}
}
private Dialog getPukRemainingAttemptsDialog(int remaining) {
String msg = mView.getPukPasswordErrorMessage(remaining, false,
KeyguardEsimArea.isEsimLocked(mView.getContext(), mSubId));
if (mRemainingAttemptsDialog == null) {
AlertDialog.Builder builder = new AlertDialog.Builder(mView.getContext());
builder.setMessage(msg);
builder.setCancelable(false);
builder.setNeutralButton(R.string.ok, null);
mRemainingAttemptsDialog = builder.create();
mRemainingAttemptsDialog.getWindow().setType(
WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
} else {
mRemainingAttemptsDialog.setMessage(msg);
}
return mRemainingAttemptsDialog;
}
/**
* Since the IPC can block, we want to run the request in a separate thread
* with a callback.
*/
private abstract class CheckSimPuk extends Thread {
private final String mPin, mPuk;
private final int mSubId;
protected CheckSimPuk(String puk, String pin, int subId) {
mPuk = puk;
mPin = pin;
mSubId = subId;
}
abstract void onSimLockChangedResponse(@NonNull PinResult result);
@Override
public void run() {
if (DEBUG) Log.v(TAG, "call supplyPukReportResult()");
TelephonyManager telephonyManager = mTelephonyManager.createForSubscriptionId(mSubId);
final PinResult result = telephonyManager.supplyPukReportPinResult(mPuk, mPin);
if (result == null) {
Log.e(TAG, "Error result for supplyPukReportResult.");
mView.post(() -> onSimLockChangedResponse(PinResult.getDefaultFailedResult()));
} else {
if (DEBUG) {
Log.v(TAG, "supplyPukReportResult returned: " + result.toString());
}
mView.post(new Runnable() {
@Override
public void run() {
onSimLockChangedResponse(result);
}
});
}
}
}
}

View File

@@ -16,11 +16,12 @@
package com.android.keyguard;
import android.content.Context;
import android.view.MotionEvent;
import android.view.View;
import android.view.accessibility.AccessibilityManager;
import javax.inject.Inject;
/**
* Hover listener that implements lift-to-activate interaction for
* accessibility. May be added to multiple views.
@@ -31,9 +32,9 @@ class LiftToActivateListener implements View.OnHoverListener {
private boolean mCachedClickableState;
public LiftToActivateListener(Context context) {
mAccessibilityManager = (AccessibilityManager) context.getSystemService(
Context.ACCESSIBILITY_SERVICE);
@Inject
LiftToActivateListener(AccessibilityManager accessibilityManager) {
mAccessibilityManager = accessibilityManager;
}
@Override

View File

@@ -26,6 +26,7 @@ import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityManager;
import android.widget.TextView;
import com.android.internal.widget.LockPatternUtils;
@@ -90,7 +91,8 @@ public class NumPadKey extends ViewGroup {
}
setOnClickListener(mListener);
setOnHoverListener(new LiftToActivateListener(context));
setOnHoverListener(new LiftToActivateListener(
(AccessibilityManager) context.getSystemService(Context.ACCESSIBILITY_SERVICE)));
mLockPatternUtils = new LockPatternUtils(context);
mPM = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);

View File

@@ -62,6 +62,7 @@ import android.view.ViewConfiguration;
import android.view.WindowManager;
import android.view.WindowManagerGlobal;
import android.view.accessibility.AccessibilityManager;
import android.view.inputmethod.InputMethodManager;
import com.android.internal.app.IBatteryStats;
import com.android.internal.statusbar.IStatusBarService;
@@ -181,6 +182,12 @@ public class FrameworkServicesModule {
return context.getSystemService(FingerprintManager.class);
}
@Provides
@Singleton
static InputMethodManager provideInputMethodManager(Context context) {
return context.getSystemService(InputMethodManager.class);
}
@Provides
@Singleton
static IPackageManager provideIPackageManager() {

View File

@@ -23,7 +23,20 @@ import android.view.View.OnAttachStateChangeListener;
* Utility class that handles view lifecycle events for View Controllers.
*
* Implementations should handle setup and teardown related activities inside of
* {@link #onViewAttached()} and {@link #onViewDetached()}.
* {@link #onViewAttached()} and {@link #onViewDetached()}. Be sure to call {@link #init()} on
* any child controllers that this uses. This can be done in {@link init()} if the controllers
* are injected, or right after creation time of the child controller.
*
* Tip: View "attachment" happens top down - parents are notified that they are attached before
* any children. That means that if you call a method on a child controller in
* {@link #onViewAttached()}, the child controller may not have had its onViewAttach method
* called, so it may not be fully set up.
*
* As such, make sure that methods on your controller are safe to call _before_ its {@link #init()}
* and {@link #onViewAttached()} methods are called. Specifically, if your controller must call
* {@link View#findViewById(int)} on its root view to setup member variables, do so in its
* constructor. Save {@link #onViewAttached()} for things that can happen post-construction - adding
* listeners, dynamically changing content, or other runtime decisions.
*
* @param <T> View class that this ViewController is for.
*/

View File

@@ -0,0 +1,120 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.keyguard;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper.RunWithLooper;
import android.view.KeyEvent;
import androidx.test.filters.SmallTest;
import com.android.internal.util.LatencyTracker;
import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.KeyguardAbsKeyInputView.KeyDownListener;
import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@SmallTest
@RunWith(AndroidTestingRunner.class)
@RunWithLooper
public class KeyguardAbsKeyInputViewControllerTest extends SysuiTestCase {
@Mock
private KeyguardAbsKeyInputView mAbsKeyInputView;
@Mock
private PasswordTextView mPasswordEntry;
@Mock
private KeyguardMessageArea mKeyguardMessageArea;
@Mock
private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
@Mock
private SecurityMode mSecurityMode;
@Mock
private LockPatternUtils mLockPatternUtils;
@Mock
private KeyguardSecurityCallback mKeyguardSecurityCallback;
@Mock
private KeyguardMessageAreaController.Factory mKeyguardMessageAreaControllerFactory;
@Mock
private KeyguardMessageAreaController mKeyguardMessageAreaController;
@Mock
private LatencyTracker mLatencyTracker;
private KeyguardAbsKeyInputViewController mKeyguardAbsKeyInputViewController;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
when(mKeyguardMessageAreaControllerFactory.create(any(KeyguardMessageArea.class)))
.thenReturn(mKeyguardMessageAreaController);
when(mAbsKeyInputView.getPasswordTextViewId()).thenReturn(1);
when(mAbsKeyInputView.findViewById(1)).thenReturn(mPasswordEntry);
when(mAbsKeyInputView.isAttachedToWindow()).thenReturn(true);
when(mAbsKeyInputView.findViewById(R.id.keyguard_message_area))
.thenReturn(mKeyguardMessageArea);
mKeyguardAbsKeyInputViewController = new KeyguardAbsKeyInputViewController(mAbsKeyInputView,
mKeyguardUpdateMonitor, mSecurityMode, mLockPatternUtils, mKeyguardSecurityCallback,
mKeyguardMessageAreaControllerFactory, mLatencyTracker) {
@Override
void resetState() {
}
@Override
public void onResume(int reason) {
super.onResume(reason);
}
};
mKeyguardAbsKeyInputViewController.init();
}
@Test
public void onKeyDown_clearsSecurityMessage() {
ArgumentCaptor<KeyDownListener> onKeyDownListenerArgumentCaptor =
ArgumentCaptor.forClass(KeyDownListener.class);
verify(mAbsKeyInputView).setKeyDownListener(onKeyDownListenerArgumentCaptor.capture());
onKeyDownListenerArgumentCaptor.getValue().onKeyDown(
KeyEvent.KEYCODE_0, mock(KeyEvent.class));
verify(mKeyguardSecurityCallback).userActivity();
verify(mKeyguardMessageAreaController).setMessage(eq(""));
}
@Test
public void onKeyDown_noSecurityMessageInteraction() {
ArgumentCaptor<KeyDownListener> onKeyDownListenerArgumentCaptor =
ArgumentCaptor.forClass(KeyDownListener.class);
verify(mAbsKeyInputView).setKeyDownListener(onKeyDownListenerArgumentCaptor.capture());
onKeyDownListenerArgumentCaptor.getValue().onKeyDown(
KeyEvent.KEYCODE_UNKNOWN, mock(KeyEvent.class));
verifyZeroInteractions(mKeyguardSecurityCallback);
verifyZeroInteractions(mKeyguardMessageAreaController);
}
}

View File

@@ -0,0 +1,106 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.keyguard;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper.RunWithLooper;
import android.view.View;
import androidx.test.filters.SmallTest;
import com.android.internal.util.LatencyTracker;
import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@SmallTest
@RunWith(AndroidTestingRunner.class)
@RunWithLooper
public class KeyguardPinBasedInputViewControllerTest extends SysuiTestCase {
@Mock
private KeyguardPinBasedInputView mPinBasedInputView;
@Mock
private PasswordTextView mPasswordEntry;
@Mock
private KeyguardMessageArea mKeyguardMessageArea;
@Mock
private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
@Mock
private SecurityMode mSecurityMode;
@Mock
private LockPatternUtils mLockPatternUtils;
@Mock
private KeyguardSecurityCallback mKeyguardSecurityCallback;
@Mock
private KeyguardMessageAreaController.Factory mKeyguardMessageAreaControllerFactory;
@Mock
private KeyguardMessageAreaController mKeyguardMessageAreaController;
@Mock
private LatencyTracker mLatencyTracker;
@Mock
private LiftToActivateListener mLiftToactivateListener;
@Mock
private View mDeleteButton;
@Mock
private View mOkButton;
private KeyguardPinBasedInputViewController mKeyguardPinViewController;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
when(mKeyguardMessageAreaControllerFactory.create(any(KeyguardMessageArea.class)))
.thenReturn(mKeyguardMessageAreaController);
when(mPinBasedInputView.getPasswordTextViewId()).thenReturn(1);
when(mPinBasedInputView.findViewById(1)).thenReturn(mPasswordEntry);
when(mPinBasedInputView.isAttachedToWindow()).thenReturn(true);
when(mPinBasedInputView.findViewById(R.id.keyguard_message_area))
.thenReturn(mKeyguardMessageArea);
when(mPinBasedInputView.findViewById(R.id.delete_button))
.thenReturn(mDeleteButton);
when(mPinBasedInputView.findViewById(R.id.key_enter))
.thenReturn(mOkButton);
mKeyguardPinViewController = new KeyguardPinBasedInputViewController(mPinBasedInputView,
mKeyguardUpdateMonitor, mSecurityMode, mLockPatternUtils, mKeyguardSecurityCallback,
mKeyguardMessageAreaControllerFactory, mLatencyTracker, mLiftToactivateListener) {
@Override
public void onResume(int reason) {
super.onResume(reason);
}
};
mKeyguardPinViewController.init();
}
@Test
public void onResume_requestsFocus() {
mKeyguardPinViewController.onResume(KeyguardSecurityView.SCREEN_ON);
verify(mPasswordEntry).requestFocus();
}
}

View File

@@ -1,79 +0,0 @@
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License
*/
package com.android.keyguard;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper.RunWithLooper;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import androidx.test.filters.SmallTest;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@SmallTest
@RunWith(AndroidTestingRunner.class)
@RunWithLooper
public class KeyguardPinBasedInputViewTest extends SysuiTestCase {
@Mock
private PasswordTextView mPasswordEntry;
@Mock
private SecurityMessageDisplay mSecurityMessageDisplay;
@InjectMocks
private KeyguardPinBasedInputView mKeyguardPinView;
@Before
public void setup() {
LayoutInflater inflater = LayoutInflater.from(getContext());
mDependency.injectMockDependency(KeyguardUpdateMonitor.class);
mKeyguardPinView =
(KeyguardPinBasedInputView) inflater.inflate(R.layout.keyguard_pin_view, null);
MockitoAnnotations.initMocks(this);
}
@Test
public void onResume_requestsFocus() {
mKeyguardPinView.onResume(KeyguardSecurityView.SCREEN_ON);
verify(mPasswordEntry).requestFocus();
}
@Test
public void onKeyDown_clearsSecurityMessage() {
mKeyguardPinView.onKeyDown(KeyEvent.KEYCODE_0, mock(KeyEvent.class));
verify(mSecurityMessageDisplay).setMessage(eq(""));
}
@Test
public void onKeyDown_noSecurityMessageInteraction() {
mKeyguardPinView.onKeyDown(KeyEvent.KEYCODE_UNKNOWN, mock(KeyEvent.class));
verifyZeroInteractions(mSecurityMessageDisplay);
}
}