Revert "7/N controllers for remaining Keyguard Password Views."

Revert submission 12585643-b166448040-keyguard-message-area

Reason for revert: http://b/169081305 & http://b/169020145
Reverted Changes:
I6fa05012c:4/N Setup Controller fo KeyguardSecurityContainer....
Iecf265744:5/N Add KeyguardSecurityViewFlipperController.
I90ab99b2f:6/N Add Controller for KeyguardPatternView
I4b74eddd1:7/N controllers for remaining Keyguard Password Vi...
I805286374:8/N Remove View Injection from KeyguardMessageArea...
I362755980:9/N Clean Up Keyguard Class Structure

Change-Id: I55c250121c2e234a41b30f2d647c44f6cb631028
Fixes: 169081305
Fixes: 169020145
This commit is contained in:
Dave Mankoff
2020-09-22 00:24:04 +00:00
parent dd7312c20a
commit bdc667325a
23 changed files with 1363 additions and 1837 deletions

View File

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

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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