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