diff options
15 files changed, 640 insertions, 1058 deletions
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_host_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_security_container_view.xml index 8497ff094c03..426cfafb190e 100644 --- a/packages/SystemUI/res-keyguard/layout/keyguard_host_view.xml +++ b/packages/SystemUI/res-keyguard/layout/keyguard_security_container_view.xml @@ -1,7 +1,7 @@ <?xml version="1.0" encoding="utf-8"?> <!-- ** -** Copyright 2012, The Android Open Source Project +** Copyright 2023, 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. @@ -17,12 +17,10 @@ */ --> -<!-- This is the host view that generally contains two sub views: the widget view - and the security view. --> -<com.android.keyguard.KeyguardHostView +<com.android.keyguard.KeyguardSecurityContainer xmlns:android="http://schemas.android.com/apk/res/android" xmlns:androidprv="http://schemas.android.com/apk/res-auto" - android:id="@+id/keyguard_host_view" + android:id="@+id/keyguard_security_container" android:layout_width="match_parent" android:layout_height="match_parent" android:clipChildren="false" @@ -30,27 +28,15 @@ android:paddingTop="@dimen/keyguard_lock_padding" android:importantForAccessibility="yes"> <!-- Needed because TYPE_WINDOW_STATE_CHANGED is sent from this view when bouncer is shown --> - - <com.android.keyguard.KeyguardSecurityContainer - android:id="@+id/keyguard_security_container" - android:layout_width="match_parent" + <com.android.keyguard.KeyguardSecurityViewFlipper + android:id="@+id/view_flipper" + android:layout_width="wrap_content" android:layout_height="match_parent" android:clipChildren="false" android:clipToPadding="false" - android:padding="0dp" - android:fitsSystemWindows="true" - android:layout_gravity="center"> - <com.android.keyguard.KeyguardSecurityViewFlipper - android:id="@+id/view_flipper" - android:layout_width="wrap_content" - android:layout_height="match_parent" - android:clipChildren="false" - android:clipToPadding="false" - android:paddingTop="@dimen/keyguard_security_view_top_margin" - android:layout_gravity="center" - android:gravity="center"> - </com.android.keyguard.KeyguardSecurityViewFlipper> - </com.android.keyguard.KeyguardSecurityContainer> - -</com.android.keyguard.KeyguardHostView> + android:paddingTop="@dimen/keyguard_security_view_top_margin" + android:layout_gravity="center" + android:gravity="center"> + </com.android.keyguard.KeyguardSecurityViewFlipper> +</com.android.keyguard.KeyguardSecurityContainer> diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardHostView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardHostView.java deleted file mode 100644 index 2a389b6132e9..000000000000 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardHostView.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright (C) 2007 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.Context; -import android.graphics.Canvas; -import android.util.AttributeSet; -import android.view.MotionEvent; -import android.widget.FrameLayout; - -/** - * Base class for keyguard view. {@link #reset} is where you should - * reset the state of your view. Use the {@link KeyguardViewCallback} via - * {@link #getCallback()} to send information back (such as poking the wake lock, - * or finishing the keyguard). - * - * Handles intercepting of media keys that still work when the keyguard is - * showing. - */ -public class KeyguardHostView extends FrameLayout { - - protected ViewMediatorCallback mViewMediatorCallback; - private boolean mIsInteractable; - - public KeyguardHostView(Context context) { - this(context, null); - } - - public KeyguardHostView(Context context, AttributeSet attrs) { - super(context, attrs); - } - - @Override - protected void dispatchDraw(Canvas canvas) { - super.dispatchDraw(canvas); - if (mViewMediatorCallback != null) { - mViewMediatorCallback.keyguardDoneDrawing(); - } - } - - public void setViewMediatorCallback(ViewMediatorCallback viewMediatorCallback) { - mViewMediatorCallback = viewMediatorCallback; - } - - /** Set true if the view can be interacted with */ - public void setInteractable(boolean isInteractable) { - mIsInteractable = isInteractable; - } - - /** - * Make sure to disallow touches while transitioning the bouncer, otherwise - * it can remain interactable even when barely visible. - */ - @Override - public boolean onInterceptTouchEvent(MotionEvent ev) { - return !mIsInteractable; - } - - /** True to consume any events that are sent to it */ - @Override - public boolean onTouchEvent(MotionEvent ev) { - return true; - } -} diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java deleted file mode 100644 index 61394035d731..000000000000 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java +++ /dev/null @@ -1,535 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.keyguard; - -import android.app.ActivityManager; -import android.content.res.ColorStateList; -import android.content.res.Resources; -import android.media.AudioManager; -import android.os.SystemClock; -import android.telephony.TelephonyManager; -import android.util.Log; -import android.util.MathUtils; -import android.view.KeyEvent; -import android.view.View; -import android.view.View.OnKeyListener; -import android.view.ViewTreeObserver; -import android.widget.FrameLayout; -import android.window.OnBackAnimationCallback; - -import androidx.annotation.NonNull; - -import com.android.keyguard.KeyguardSecurityContainer.SecurityCallback; -import com.android.keyguard.KeyguardSecurityModel.SecurityMode; -import com.android.keyguard.dagger.KeyguardBouncerScope; -import com.android.settingslib.Utils; -import com.android.systemui.R; -import com.android.systemui.plugins.ActivityStarter; -import com.android.systemui.util.ViewController; - -import java.io.File; - -import javax.inject.Inject; - -/** Controller for a {@link KeyguardHostView}. */ -@KeyguardBouncerScope -public class KeyguardHostViewController extends ViewController<KeyguardHostView> { - private static final String TAG = "KeyguardViewBase"; - public static final boolean DEBUG = KeyguardConstants.DEBUG; - // Whether the volume keys should be handled by keyguard. If true, then - // they will be handled here for specific media types such as music, otherwise - // the audio service will bring up the volume dialog. - private static final boolean KEYGUARD_MANAGES_VOLUME = false; - - private static final String ENABLE_MENU_KEY_FILE = "/data/local/enable_menu_key"; - - private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; - private final KeyguardSecurityContainerController mKeyguardSecurityContainerController; - private final TelephonyManager mTelephonyManager; - private final ViewMediatorCallback mViewMediatorCallback; - private final AudioManager mAudioManager; - - private ActivityStarter.OnDismissAction mDismissAction; - private Runnable mCancelAction; - private int mTranslationY; - - private final KeyguardUpdateMonitorCallback mUpdateCallback = - new KeyguardUpdateMonitorCallback() { - @Override - public void onTrustGrantedForCurrentUser( - boolean dismissKeyguard, - boolean newlyUnlocked, - TrustGrantFlags flags, - String message - ) { - if (dismissKeyguard) { - if (!mView.isVisibleToUser()) { - // The trust agent dismissed the keyguard without the user proving - // that they are present (by swiping up to show the bouncer). That's - // fine if the user proved presence via some other way to the trust - // agent. - Log.i(TAG, "TrustAgent dismissed Keyguard."); - } - mSecurityCallback.dismiss( - false /* authenticated */, - KeyguardUpdateMonitor.getCurrentUser(), - /* bypassSecondaryLockScreen */ false, - SecurityMode.Invalid - ); - } else { - if (flags.isInitiatedByUser() || flags.dismissKeyguardRequested()) { - mViewMediatorCallback.playTrustedSound(); - } - } - } - }; - - private final SecurityCallback mSecurityCallback = new SecurityCallback() { - - @Override - public boolean dismiss(boolean authenticated, int targetUserId, - boolean bypassSecondaryLockScreen, SecurityMode expectedSecurityMode) { - return mKeyguardSecurityContainerController.showNextSecurityScreenOrFinish( - authenticated, targetUserId, bypassSecondaryLockScreen, expectedSecurityMode); - } - - @Override - public void userActivity() { - mViewMediatorCallback.userActivity(); - } - - @Override - public void onSecurityModeChanged(SecurityMode securityMode, boolean needsInput) { - mViewMediatorCallback.setNeedsInput(needsInput); - } - - /** - * Authentication has happened and it's time to dismiss keyguard. This function - * should clean up and inform KeyguardViewMediator. - * - * @param strongAuth whether the user has authenticated with strong authentication like - * pattern, password or PIN but not by trust agents or fingerprint - * @param targetUserId a user that needs to be the foreground user at the dismissal - * completion. - */ - @Override - public void finish(boolean strongAuth, int targetUserId) { - // If there's a pending runnable because the user interacted with a widget - // and we're leaving keyguard, then run it. - boolean deferKeyguardDone = false; - if (mDismissAction != null) { - deferKeyguardDone = mDismissAction.onDismiss(); - mDismissAction = null; - mCancelAction = null; - } - if (mViewMediatorCallback != null) { - if (deferKeyguardDone) { - mViewMediatorCallback.keyguardDonePending(strongAuth, targetUserId); - } else { - mViewMediatorCallback.keyguardDone(strongAuth, targetUserId); - } - } - } - - @Override - public void reset() { - mViewMediatorCallback.resetKeyguard(); - } - - @Override - public void onCancelClicked() { - mViewMediatorCallback.onCancelClicked(); - } - }; - - private OnKeyListener mOnKeyListener = (v, keyCode, event) -> interceptMediaKey(event); - - @Inject - public KeyguardHostViewController(KeyguardHostView view, - KeyguardUpdateMonitor keyguardUpdateMonitor, - AudioManager audioManager, - TelephonyManager telephonyManager, - ViewMediatorCallback viewMediatorCallback, - KeyguardSecurityContainerController.Factory - keyguardSecurityContainerControllerFactory) { - super(view); - mKeyguardUpdateMonitor = keyguardUpdateMonitor; - mAudioManager = audioManager; - mTelephonyManager = telephonyManager; - mViewMediatorCallback = viewMediatorCallback; - mKeyguardSecurityContainerController = keyguardSecurityContainerControllerFactory.create( - mSecurityCallback); - } - - /** Initialize the Controller. */ - public void onInit() { - mKeyguardSecurityContainerController.init(); - updateResources(); - } - - @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.showPrimarySecurityScreen(false); - } - - @Override - protected void onViewDetached() { - mKeyguardUpdateMonitor.removeCallback(mUpdateCallback); - mView.setOnKeyListener(null); - } - - /** Called before this view is being removed. */ - public void cleanUp() { - mKeyguardSecurityContainerController.onPause(); - } - - public void resetSecurityContainer() { - mKeyguardSecurityContainerController.reset(); - } - - /** - * Dismisses the keyguard by going to the next screen or making it gone. - * @param targetUserId a user that needs to be the foreground user at the dismissal completion. - * @return True if the keyguard is done. - */ - public boolean dismiss(int targetUserId) { - return mSecurityCallback.dismiss(false, targetUserId, false, - getCurrentSecurityMode()); - } - - /** - * Called when the Keyguard is actively shown on the screen. - */ - public void onResume() { - if (DEBUG) Log.d(TAG, "screen on, instance " + Integer.toHexString(hashCode())); - mKeyguardSecurityContainerController.onResume(KeyguardSecurityView.SCREEN_ON); - mView.requestFocus(); - } - - public CharSequence getAccessibilityTitleForCurrentMode() { - return mKeyguardSecurityContainerController.getTitle(); - } - - /** - * Starts the animation when the Keyguard gets shown. - */ - public void appear(int statusBarHeight) { - // We might still be collapsed and the view didn't have time to layout yet or still - // be small, let's wait on the predraw to do the animation in that case. - if (mView.getHeight() != 0 && mView.getHeight() != statusBarHeight) { - mKeyguardSecurityContainerController.startAppearAnimation(); - } else { - mView.getViewTreeObserver().addOnPreDrawListener( - new ViewTreeObserver.OnPreDrawListener() { - @Override - public boolean onPreDraw() { - mView.getViewTreeObserver().removeOnPreDrawListener(this); - mKeyguardSecurityContainerController.startAppearAnimation(); - return true; - } - }); - mView.requestLayout(); - } - } - - /** - * Show a string explaining why the security view needs to be solved. - * - * @param reason a flag indicating which string should be shown, see - * {@link KeyguardSecurityView#PROMPT_REASON_NONE}, - * {@link KeyguardSecurityView#PROMPT_REASON_RESTART}, - * {@link KeyguardSecurityView#PROMPT_REASON_TIMEOUT}, and - * {@link KeyguardSecurityView#PROMPT_REASON_PREPARE_FOR_UPDATE}. - */ - public void showPromptReason(int reason) { - mKeyguardSecurityContainerController.showPromptReason(reason); - } - - public void showMessage(CharSequence message, ColorStateList colorState) { - mKeyguardSecurityContainerController.showMessage(message, colorState); - } - - public void showErrorMessage(CharSequence customMessage) { - showMessage(customMessage, Utils.getColorError(mView.getContext())); - } - - /** - * Sets an action to run when keyguard finishes. - * - * @param action - */ - public void setOnDismissAction(ActivityStarter.OnDismissAction action, Runnable cancelAction) { - if (mCancelAction != null) { - mCancelAction.run(); - mCancelAction = null; - } - mDismissAction = action; - mCancelAction = cancelAction; - } - - public void cancelDismissAction() { - setOnDismissAction(null, null); - } - - public void startDisappearAnimation(Runnable finishRunnable) { - if (!mKeyguardSecurityContainerController.startDisappearAnimation(finishRunnable) - && finishRunnable != null) { - finishRunnable.run(); - } - } - - /** - * Called when the Keyguard is not actively shown anymore on the screen. - */ - public void onPause() { - if (DEBUG) { - Log.d(TAG, String.format("screen off, instance %s at %s", - Integer.toHexString(hashCode()), SystemClock.uptimeMillis())); - } - mKeyguardSecurityContainerController.showPrimarySecurityScreen(true); - mKeyguardSecurityContainerController.onPause(); - mView.clearFocus(); - } - - /** - * Called when the view needs to be shown. - */ - public void showPrimarySecurityScreen() { - if (DEBUG) Log.d(TAG, "show()"); - mKeyguardSecurityContainerController.showPrimarySecurityScreen(false); - } - - /** - * Fades and translates in/out the security screen. - * Fades in as expansion approaches 0. - * Animation duration is between 0.33f and 0.67f of panel expansion fraction. - * @param fraction amount of the screen that should show. - */ - public void setExpansion(float fraction) { - float scaledFraction = BouncerPanelExpansionCalculator.showBouncerProgress(fraction); - mView.setAlpha(MathUtils.constrain(1 - scaledFraction, 0f, 1f)); - mView.setTranslationY(scaledFraction * mTranslationY); - } - - /** - * When bouncer was visible and is starting to become hidden. - */ - public void onStartingToHide() { - mKeyguardSecurityContainerController.onStartingToHide(); - } - - /** Called when bouncer visibility changes. */ - public void onBouncerVisibilityChanged(@View.Visibility int visibility) { - mKeyguardSecurityContainerController.onBouncerVisibilityChanged(visibility); - } - - public boolean hasDismissActions() { - return mDismissAction != null || mCancelAction != null; - } - - public SecurityMode getCurrentSecurityMode() { - return mKeyguardSecurityContainerController.getCurrentSecurityMode(); - } - - public int getTop() { - int top = mView.getTop(); - // The password view has an extra top padding that should be ignored. - if (getCurrentSecurityMode() == SecurityMode.Password) { - View messageArea = mView.findViewById(R.id.keyguard_message_area); - top += messageArea.getTop(); - } - return top; - } - - public boolean handleBackKey() { - SecurityMode securityMode = mKeyguardSecurityContainerController.getCurrentSecurityMode(); - if (securityMode != SecurityMode.None) { - mKeyguardSecurityContainerController.dismiss( - false, KeyguardUpdateMonitor.getCurrentUser(), securityMode); - return true; - } - return false; - } - - /** - * In general, we enable unlocking the insecure keyguard with the menu key. However, there are - * some cases where we wish to disable it, notably when the menu button placement or technology - * is prone to false positives. - * - * @return true if the menu key should be enabled - */ - public boolean shouldEnableMenuKey() { - final Resources res = mView.getResources(); - final boolean configDisabled = res.getBoolean(R.bool.config_disableMenuKeyInLockScreen); - final boolean isTestHarness = ActivityManager.isRunningInTestHarness(); - final boolean fileOverride = (new File(ENABLE_MENU_KEY_FILE)).exists(); - return !configDisabled || isTestHarness || fileOverride; - } - - /** - * @return true if the current bouncer is password - */ - public boolean dispatchBackKeyEventPreIme() { - if (mKeyguardSecurityContainerController.getCurrentSecurityMode() - == SecurityMode.Password) { - return true; - } - return false; - } - - /** - * @return the {@link OnBackAnimationCallback} to animate this view during a back gesture. - */ - @NonNull - public OnBackAnimationCallback getBackCallback() { - return mKeyguardSecurityContainerController.getBackCallback(); - } - - /** - * Allows the media keys to work when the keyguard is showing. - * The media keys should be of no interest to the actual keyguard view(s), - * so intercepting them here should not be of any harm. - * @param event The key event - * @return whether the event was consumed as a media key. - */ - public boolean interceptMediaKey(KeyEvent event) { - int keyCode = event.getKeyCode(); - if (event.getAction() == KeyEvent.ACTION_DOWN) { - switch (keyCode) { - case KeyEvent.KEYCODE_MEDIA_PLAY: - case KeyEvent.KEYCODE_MEDIA_PAUSE: - case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE: - /* Suppress PLAY/PAUSE toggle when phone is ringing or - * in-call to avoid music playback */ - if (mTelephonyManager != null && - mTelephonyManager.getCallState() != TelephonyManager.CALL_STATE_IDLE) { - return true; // suppress key event - } - case KeyEvent.KEYCODE_MUTE: - case KeyEvent.KEYCODE_HEADSETHOOK: - case KeyEvent.KEYCODE_MEDIA_STOP: - case KeyEvent.KEYCODE_MEDIA_NEXT: - case KeyEvent.KEYCODE_MEDIA_PREVIOUS: - case KeyEvent.KEYCODE_MEDIA_REWIND: - case KeyEvent.KEYCODE_MEDIA_RECORD: - case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD: - case KeyEvent.KEYCODE_MEDIA_AUDIO_TRACK: { - handleMediaKeyEvent(event); - return true; - } - - case KeyEvent.KEYCODE_VOLUME_UP: - case KeyEvent.KEYCODE_VOLUME_DOWN: - case KeyEvent.KEYCODE_VOLUME_MUTE: { - if (KEYGUARD_MANAGES_VOLUME) { - // Volume buttons should only function for music (local or remote). - // TODO: Actually handle MUTE. - mAudioManager.adjustSuggestedStreamVolume( - keyCode == KeyEvent.KEYCODE_VOLUME_UP - ? AudioManager.ADJUST_RAISE - : AudioManager.ADJUST_LOWER /* direction */, - AudioManager.STREAM_MUSIC /* stream */, 0 /* flags */); - // Don't execute default volume behavior - return true; - } else { - return false; - } - } - } - } else if (event.getAction() == KeyEvent.ACTION_UP) { - switch (keyCode) { - case KeyEvent.KEYCODE_MUTE: - case KeyEvent.KEYCODE_HEADSETHOOK: - case KeyEvent.KEYCODE_MEDIA_PLAY: - case KeyEvent.KEYCODE_MEDIA_PAUSE: - case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE: - case KeyEvent.KEYCODE_MEDIA_STOP: - case KeyEvent.KEYCODE_MEDIA_NEXT: - case KeyEvent.KEYCODE_MEDIA_PREVIOUS: - case KeyEvent.KEYCODE_MEDIA_REWIND: - case KeyEvent.KEYCODE_MEDIA_RECORD: - case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD: - case KeyEvent.KEYCODE_MEDIA_AUDIO_TRACK: { - handleMediaKeyEvent(event); - return true; - } - } - } - return false; - } - - - private void handleMediaKeyEvent(KeyEvent keyEvent) { - mAudioManager.dispatchMediaKeyEvent(keyEvent); - } - - public void finish(boolean strongAuth, int currentUser) { - mSecurityCallback.finish(strongAuth, currentUser); - } - - /** - * Apply keyguard configuration from the currently active resources. This can be called when the - * device configuration changes, to re-apply some resources that are qualified on the device - * configuration. - */ - public void updateResources() { - int gravity; - - Resources resources = mView.getResources(); - - if (resources.getBoolean(R.bool.can_use_one_handed_bouncer)) { - gravity = resources.getInteger( - R.integer.keyguard_host_view_one_handed_gravity); - } else { - gravity = resources.getInteger(R.integer.keyguard_host_view_gravity); - } - - mTranslationY = resources - .getDimensionPixelSize(R.dimen.keyguard_host_view_translation_y); - // Android SysUI uses a FrameLayout as the top-level, but Auto uses RelativeLayout. - // We're just changing the gravity here though (which can't be applied to RelativeLayout), - // so only attempt the update if mView is inside a FrameLayout. - if (mView.getLayoutParams() instanceof FrameLayout.LayoutParams) { - FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mView.getLayoutParams(); - if (lp.gravity != gravity) { - lp.gravity = gravity; - mView.setLayoutParams(lp); - } - } - - if (mKeyguardSecurityContainerController != null) { - mKeyguardSecurityContainerController.updateResources(); - } - } - - /** Update keyguard position based on a tapped X coordinate. */ - public void updateKeyguardPosition(float x) { - if (mKeyguardSecurityContainerController != null) { - mKeyguardSecurityContainerController.updateKeyguardPosition(x); - } - } - - /** Set true if the view can be interacted with */ - public void setInteractable(boolean isInteractable) { - mView.setInteractable(isInteractable); - } -} diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java index b143c5b90373..7054393f5fed 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java @@ -51,26 +51,7 @@ public abstract class KeyguardInputViewController<T extends KeyguardInputView> // The following is used to ignore callbacks from SecurityViews that are no longer current // (e.g. face unlock). This avoids unwanted asynchronous events from messing with the // state for the current security method. - private KeyguardSecurityCallback mNullCallback = new KeyguardSecurityCallback() { - @Override - public void userActivity() { } - @Override - public void reportUnlockAttempt(int userId, boolean success, int timeoutMs) { } - @Override - public boolean isVerifyUnlockOnly() { - return false; - } - @Override - public void dismiss(boolean securityVerified, int targetUserId, - SecurityMode expectedSecurityMode) { } - @Override - public void dismiss(boolean authenticated, int targetId, - boolean bypassSecondaryLockScreen, SecurityMode expectedSecurityMode) { } - @Override - public void onUserInput() { } - @Override - public void reset() {} - }; + private KeyguardSecurityCallback mNullCallback = new KeyguardSecurityCallback() {}; protected KeyguardInputViewController(T view, SecurityMode securityMode, KeyguardSecurityCallback keyguardSecurityCallback, diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityCallback.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityCallback.java index bc72f7979a74..bf9c3bbddc30 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityCallback.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityCallback.java @@ -25,7 +25,9 @@ public interface KeyguardSecurityCallback { * @param targetUserId a user that needs to be the foreground user at the dismissal completion. * @param expectedSecurityMode The security mode that is invoking this dismiss. */ - void dismiss(boolean securityVerified, int targetUserId, SecurityMode expectedSecurityMode); + default void dismiss(boolean securityVerified, int targetUserId, + SecurityMode expectedSecurityMode) { + } /** * Dismiss the given security screen. @@ -35,19 +37,26 @@ public interface KeyguardSecurityCallback { * if any, during this dismissal. * @param expectedSecurityMode The security mode that is invoking this dismiss. */ - void dismiss(boolean securityVerified, int targetUserId, boolean bypassSecondaryLockScreen, - SecurityMode expectedSecurityMode); + default boolean dismiss(boolean securityVerified, int targetUserId, + boolean bypassSecondaryLockScreen, + SecurityMode expectedSecurityMode) { + return false; + } /** * Manually report user activity to keep the device awake. */ - void userActivity(); + default void userActivity() { + } /** * Checks if keyguard is in "verify credentials" mode. + * * @return true if user has been asked to verify security. */ - boolean isVerifyUnlockOnly(); + default boolean isVerifyUnlockOnly() { + return false; + } /** * Call to report an unlock attempt. @@ -56,12 +65,14 @@ public interface KeyguardSecurityCallback { * @param timeoutMs timeout in milliseconds to wait before reattempting an unlock. * Only nonzero if 'success' is false */ - void reportUnlockAttempt(int userId, boolean success, int timeoutMs); + default void reportUnlockAttempt(int userId, boolean success, int timeoutMs) { + } /** * Resets the keyguard view. */ - void reset(); + default void reset() { + } /** * Call when cancel button is pressed in bouncer. @@ -73,5 +84,19 @@ public interface KeyguardSecurityCallback { /** * Invoked whenever users are typing their password or drawing a pattern. */ - void onUserInput(); + default void onUserInput() { + } + + + /** + * Dismisses keyguard and go to unlocked state. + */ + default void finish(boolean strongAuth, int targetUserId) { + } + + /** + * Specifies that security mode has changed. + */ + default void onSecurityModeChanged(SecurityMode securityMode, boolean needsInput) { + } } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java index 9f07a20ce812..eec788b7add8 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java @@ -50,6 +50,7 @@ import android.content.res.Configuration; import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.BlendMode; +import android.graphics.Canvas; import android.graphics.Rect; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; @@ -164,6 +165,8 @@ public class KeyguardSecurityContainer extends ConstraintLayout { private boolean mDisappearAnimRunning; private SwipeListener mSwipeListener; private ViewMode mViewMode = new DefaultViewMode(); + private boolean mIsInteractable; + protected ViewMediatorCallback mViewMediatorCallback; /* * Using MODE_UNINITIALIZED to mean the view mode is set to DefaultViewMode, but init() has not * yet been called on it. This will happen when the ViewController is initialized. @@ -265,31 +268,6 @@ public class KeyguardSecurityContainer extends ConstraintLayout { return mBackCallback; } - // Used to notify the container when something interesting happens. - public interface SecurityCallback { - /** - * Potentially dismiss the current security screen, after validating that all device - * security has been unlocked. Otherwise show the next screen. - */ - boolean dismiss(boolean authenticated, int targetUserId, boolean bypassSecondaryLockScreen, - SecurityMode expectedSecurityMode); - - void userActivity(); - - void onSecurityModeChanged(SecurityMode securityMode, boolean needsInput); - - /** - * @param strongAuth wheher the user has authenticated with strong authentication like - * pattern, password or PIN but not by trust agents or fingerprint - * @param targetUserId a user that needs to be the foreground user at the finish completion. - */ - void finish(boolean strongAuth, int targetUserId); - - void reset(); - - void onCancelClicked(); - } - public interface SwipeListener { void onSwipeUp(); } @@ -342,7 +320,7 @@ public class KeyguardSecurityContainer extends ConstraintLayout { public KeyguardSecurityContainer(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); - mSpringAnimation = new SpringAnimation(this, DynamicAnimation.Y); + mSpringAnimation = new SpringAnimation(this, DynamicAnimation.TRANSLATION_Y); mViewConfiguration = ViewConfiguration.get(context); mDoubleTapDetector = new GestureDetector(context, new DoubleTapListener()); } @@ -445,6 +423,11 @@ public class KeyguardSecurityContainer extends ConstraintLayout { mViewMode.reset(); } + /** Set true if the view can be interacted with */ + public void setInteractable(boolean isInteractable) { + mIsInteractable = isInteractable; + } + @Override public boolean shouldDelayChildPressedState() { return true; @@ -452,6 +435,10 @@ public class KeyguardSecurityContainer extends ConstraintLayout { @Override public boolean onInterceptTouchEvent(MotionEvent event) { + if (!mIsInteractable) { + return true; + } + boolean result = mMotionEventListeners.stream().anyMatch( listener -> listener.onInterceptTouchEvent(event)) || super.onInterceptTouchEvent(event); @@ -639,6 +626,18 @@ public class KeyguardSecurityContainer extends ConstraintLayout { return insets.inset(0, 0, 0, inset); } + @Override + protected void dispatchDraw(Canvas canvas) { + super.dispatchDraw(canvas); + if (mViewMediatorCallback != null) { + mViewMediatorCallback.keyguardDoneDrawing(); + } + } + + public void setViewMediatorCallback(ViewMediatorCallback viewMediatorCallback) { + mViewMediatorCallback = viewMediatorCallback; + } + private void showDialog(String title, String message) { if (mAlertDialog != null) { mAlertDialog.dismiss(); diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java index 9fcacce311d1..de05876f7f60 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java @@ -29,17 +29,26 @@ import static com.android.keyguard.KeyguardSecurityContainer.USER_TYPE_SECONDARY import static com.android.keyguard.KeyguardSecurityContainer.USER_TYPE_WORK_PROFILE; import static com.android.systemui.DejankUtils.whitelistIpcs; +import android.app.ActivityManager; import android.app.admin.DevicePolicyManager; import android.content.Intent; import android.content.res.ColorStateList; import android.content.res.Configuration; +import android.content.res.Resources; import android.hardware.biometrics.BiometricSourceType; +import android.media.AudioManager; import android.metrics.LogMaker; +import android.os.SystemClock; import android.os.UserHandle; +import android.telephony.TelephonyManager; import android.util.Log; +import android.util.MathUtils; import android.util.Slog; +import android.view.KeyEvent; import android.view.MotionEvent; import android.view.View; +import android.view.ViewTreeObserver; +import android.widget.FrameLayout; import android.window.OnBackAnimationCallback; import androidx.annotation.NonNull; @@ -53,7 +62,6 @@ import com.android.internal.logging.nano.MetricsProto; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.widget.LockPatternUtils; import com.android.keyguard.KeyguardSecurityContainer.BouncerUiEvent; -import com.android.keyguard.KeyguardSecurityContainer.SecurityCallback; import com.android.keyguard.KeyguardSecurityContainer.SwipeListener; import com.android.keyguard.KeyguardSecurityModel.SecurityMode; import com.android.keyguard.dagger.KeyguardBouncerScope; @@ -67,6 +75,7 @@ import com.android.systemui.classifier.FalsingCollector; import com.android.systemui.flags.FeatureFlags; import com.android.systemui.flags.Flags; import com.android.systemui.log.SessionTracker; +import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.FalsingManager; import com.android.systemui.shared.system.SysUiStatsLog; import com.android.systemui.statusbar.policy.ConfigurationController; @@ -75,6 +84,7 @@ import com.android.systemui.statusbar.policy.UserSwitcherController; import com.android.systemui.util.ViewController; import com.android.systemui.util.settings.GlobalSettings; +import java.io.File; import java.util.Optional; import javax.inject.Inject; @@ -95,7 +105,6 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard private final UiEventLogger mUiEventLogger; private final KeyguardStateController mKeyguardStateController; private final KeyguardSecurityViewFlipperController mSecurityViewFlipperController; - private final SecurityCallback mSecurityCallback; private final ConfigurationController mConfigurationController; private final FalsingCollector mFalsingCollector; private final FalsingManager mFalsingManager; @@ -105,6 +114,20 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard private final SessionTracker mSessionTracker; private final Optional<SideFpsController> mSideFpsController; private final FalsingA11yDelegate mFalsingA11yDelegate; + private int mTranslationY; + // Whether the volume keys should be handled by keyguard. If true, then + // they will be handled here for specific media types such as music, otherwise + // the audio service will bring up the volume dialog. + private static final boolean KEYGUARD_MANAGES_VOLUME = false; + + private static final String ENABLE_MENU_KEY_FILE = "/data/local/enable_menu_key"; + + private final TelephonyManager mTelephonyManager; + private final ViewMediatorCallback mViewMediatorCallback; + private final AudioManager mAudioManager; + private View.OnKeyListener mOnKeyListener = (v, keyCode, event) -> interceptMediaKey(event); + private ActivityStarter.OnDismissAction mDismissAction; + private Runnable mCancelAction; private int mLastOrientation = Configuration.ORIENTATION_UNDEFINED; @@ -149,11 +172,6 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard }; private KeyguardSecurityCallback mKeyguardSecurityCallback = new KeyguardSecurityCallback() { - public void userActivity() { - if (mSecurityCallback != null) { - mSecurityCallback.userActivity(); - } - } @Override public void onUserInput() { @@ -168,16 +186,23 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard } @Override - public void dismiss(boolean authenticated, int targetId, + public boolean dismiss(boolean authenticated, int targetId, boolean bypassSecondaryLockScreen, SecurityMode expectedSecurityMode) { - mSecurityCallback.dismiss(authenticated, targetId, bypassSecondaryLockScreen, - expectedSecurityMode); + return showNextSecurityScreenOrFinish( + authenticated, targetId, bypassSecondaryLockScreen, expectedSecurityMode); + } + + @Override + public void userActivity() { + mViewMediatorCallback.userActivity(); } + @Override public boolean isVerifyUnlockOnly() { return false; } + @Override public void reportUnlockAttempt(int userId, boolean success, int timeoutMs) { int bouncerSide = SysUiStatsLog.KEYGUARD_BOUNCER_PASSWORD_ENTERED__SIDE__DEFAULT; if (mView.isSidedSecurityMode()) { @@ -214,12 +239,47 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard : BouncerUiEvent.BOUNCER_PASSWORD_FAILURE, getSessionId()); } + @Override public void reset() { - mSecurityCallback.reset(); + mViewMediatorCallback.resetKeyguard(); } + @Override public void onCancelClicked() { - mSecurityCallback.onCancelClicked(); + mViewMediatorCallback.onCancelClicked(); + } + + /** + * Authentication has happened and it's time to dismiss keyguard. This function + * should clean up and inform KeyguardViewMediator. + * + * @param strongAuth whether the user has authenticated with strong authentication like + * pattern, password or PIN but not by trust agents or fingerprint + * @param targetUserId a user that needs to be the foreground user at the dismissal + * completion. + */ + @Override + public void finish(boolean strongAuth, int targetUserId) { + // If there's a pending runnable because the user interacted with a widget + // and we're leaving keyguard, then run it. + boolean deferKeyguardDone = false; + if (mDismissAction != null) { + deferKeyguardDone = mDismissAction.onDismiss(); + mDismissAction = null; + mCancelAction = null; + } + if (mViewMediatorCallback != null) { + if (deferKeyguardDone) { + mViewMediatorCallback.keyguardDonePending(strongAuth, targetUserId); + } else { + mViewMediatorCallback.keyguardDone(strongAuth, targetUserId); + } + } + } + + @Override + public void onSecurityModeChanged(SecurityMode securityMode, boolean needsInput) { + mViewMediatorCallback.setNeedsInput(needsInput); } }; @@ -263,6 +323,34 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard private final KeyguardUpdateMonitorCallback mKeyguardUpdateMonitorCallback = new KeyguardUpdateMonitorCallback() { @Override + public void onTrustGrantedForCurrentUser( + boolean dismissKeyguard, + boolean newlyUnlocked, + TrustGrantFlags flags, + String message + ) { + if (dismissKeyguard) { + if (!mView.isVisibleToUser()) { + // The trust agent dismissed the keyguard without the user proving + // that they are present (by swiping up to show the bouncer). That's + // fine if the user proved presence via some other way to the trust + // agent. + Log.i(TAG, "TrustAgent dismissed Keyguard."); + } + mKeyguardSecurityCallback.dismiss( + false /* authenticated */, + KeyguardUpdateMonitor.getCurrentUser(), + /* bypassSecondaryLockScreen */ false, + SecurityMode.Invalid + ); + } else { + if (flags.isInitiatedByUser() || flags.dismissKeyguardRequested()) { + mViewMediatorCallback.playTrustedSound(); + } + } + } + + @Override public void onDevicePolicyManagerStateChanged() { showPrimarySecurityScreen(false); } @@ -281,7 +369,8 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard } }; - private KeyguardSecurityContainerController(KeyguardSecurityContainer view, + @Inject + public KeyguardSecurityContainerController(KeyguardSecurityContainer view, AdminSecondaryLockScreenController.Factory adminSecondaryLockScreenControllerFactory, LockPatternUtils lockPatternUtils, KeyguardUpdateMonitor keyguardUpdateMonitor, @@ -289,7 +378,6 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard MetricsLogger metricsLogger, UiEventLogger uiEventLogger, KeyguardStateController keyguardStateController, - SecurityCallback securityCallback, KeyguardSecurityViewFlipperController securityViewFlipperController, ConfigurationController configurationController, FalsingCollector falsingCollector, @@ -299,7 +387,11 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard GlobalSettings globalSettings, SessionTracker sessionTracker, Optional<SideFpsController> sideFpsController, - FalsingA11yDelegate falsingA11yDelegate) { + FalsingA11yDelegate falsingA11yDelegate, + TelephonyManager telephonyManager, + ViewMediatorCallback viewMediatorCallback, + AudioManager audioManager + ) { super(view); mLockPatternUtils = lockPatternUtils; mUpdateMonitor = keyguardUpdateMonitor; @@ -307,7 +399,6 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard mMetricsLogger = metricsLogger; mUiEventLogger = uiEventLogger; mKeyguardStateController = keyguardStateController; - mSecurityCallback = securityCallback; mSecurityViewFlipperController = securityViewFlipperController; mAdminSecondaryLockScreenController = adminSecondaryLockScreenControllerFactory.create( mKeyguardSecurityCallback); @@ -321,11 +412,15 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard mSessionTracker = sessionTracker; mSideFpsController = sideFpsController; mFalsingA11yDelegate = falsingA11yDelegate; + mTelephonyManager = telephonyManager; + mViewMediatorCallback = viewMediatorCallback; + mAudioManager = audioManager; } @Override public void onInit() { mSecurityViewFlipperController.init(); + updateResources(); configureMode(); } @@ -336,6 +431,11 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard mView.addMotionEventListener(mGlobalTouchListener); mConfigurationController.addCallback(mConfigurationListener); mUserSwitcherController.addUserSwitchCallback(mUserSwitchCallback); + mView.setViewMediatorCallback(mViewMediatorCallback); + // Update ViewMediator with the current input method requirements + mViewMediatorCallback.setNeedsInput(needsInput()); + mView.setOnKeyListener(mOnKeyListener); + showPrimarySecurityScreen(false); } @Override @@ -348,6 +448,11 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard /** */ public void onPause() { + if (DEBUG) { + Log.d(TAG, String.format("screen off, instance %s at %s", + Integer.toHexString(hashCode()), SystemClock.uptimeMillis())); + } + showPrimarySecurityScreen(true); mAdminSecondaryLockScreenController.hide(); if (mCurrentSecurityMode != SecurityMode.None) { getCurrentSecurityController().onPause(); @@ -356,6 +461,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard // It might happen that onStartingToHide is not called when the device is locked while on // bouncer. setBouncerVisible(false); + mView.clearFocus(); } private void updateSideFpsVisibility() { @@ -391,12 +497,22 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard * @param turningOff true if the device is being turned off */ public void showPrimarySecurityScreen(boolean turningOff) { + if (DEBUG) Log.d(TAG, "show()"); SecurityMode securityMode = whitelistIpcs(() -> mSecurityModel.getSecurityMode( KeyguardUpdateMonitor.getCurrentUser())); if (DEBUG) Log.v(TAG, "showPrimarySecurityScreen(turningOff=" + turningOff + ")"); showSecurityScreen(securityMode); } + /** + * Show a string explaining why the security view needs to be solved. + * + * @param reason a flag indicating which string should be shown, see + * {@link KeyguardSecurityView#PROMPT_REASON_NONE}, + * {@link KeyguardSecurityView#PROMPT_REASON_RESTART}, + * {@link KeyguardSecurityView#PROMPT_REASON_TIMEOUT}, and + * {@link KeyguardSecurityView#PROMPT_REASON_PREPARE_FOR_UPDATE}. + */ @Override public void showPromptReason(int reason) { if (mCurrentSecurityMode != SecurityMode.None) { @@ -413,8 +529,32 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard } } - public SecurityMode getCurrentSecurityMode() { - return mCurrentSecurityMode; + /** + * Sets an action to run when keyguard finishes. + * + * @param action callback to be invoked when keyguard disappear animation completes. + */ + public void setOnDismissAction(ActivityStarter.OnDismissAction action, Runnable cancelAction) { + if (mCancelAction != null) { + mCancelAction.run(); + mCancelAction = null; + } + mDismissAction = action; + mCancelAction = cancelAction; + } + + /** + * @return whether dismiss action or cancel action has been set. + */ + public boolean hasDismissActions() { + return mDismissAction != null || mCancelAction != null; + } + + /** + * Remove any dismiss action or cancel action that was set. + */ + public void cancelDismissAction() { + setOnDismissAction(null, null); } /** @@ -426,17 +566,64 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard mKeyguardSecurityCallback.dismiss(authenticated, targetUserId, expectedSecurityMode); } - public void reset() { - mView.reset(); - mSecurityViewFlipperController.reset(); + /** + * Dismisses the keyguard by going to the next screen or making it gone. + * @param targetUserId a user that needs to be the foreground user at the dismissal completion. + * @return True if the keyguard is done. + */ + public boolean dismiss(int targetUserId) { + return mKeyguardSecurityCallback.dismiss(false, targetUserId, false, + getCurrentSecurityMode()); } + public SecurityMode getCurrentSecurityMode() { + return mCurrentSecurityMode; + } + + /** + * @return the top of the corresponding view. + */ + public int getTop() { + int top = mView.getTop(); + // The password view has an extra top padding that should be ignored. + if (getCurrentSecurityMode() == SecurityMode.Password) { + View messageArea = mView.findViewById(R.id.keyguard_message_area); + top += messageArea.getTop(); + } + return top; + } + + /** Set true if the view can be interacted with */ + public void setInteractable(boolean isInteractable) { + mView.setInteractable(isInteractable); + } + + /** + * Dismiss keyguard due to a user unlock event. + */ + public void finish(boolean strongAuth, int currentUser) { + mKeyguardSecurityCallback.finish(strongAuth, currentUser); + } + + /** + * @return the text of the KeyguardMessageArea. + */ public CharSequence getTitle() { return mView.getTitle(); } + /** + * Resets the state of the views. + */ + public void reset() { + mView.reset(); + mSecurityViewFlipperController.reset(); + } + @Override public void onResume(int reason) { + if (DEBUG) Log.d(TAG, "screen on, instance " + Integer.toHexString(hashCode())); + mView.requestFocus(); if (mCurrentSecurityMode != SecurityMode.None) { int state = SysUiStatsLog.KEYGUARD_BOUNCER_STATE_CHANGED__STATE__SHOWN; if (mView.isSidedSecurityMode()) { @@ -454,6 +641,30 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard mKeyguardStateController.isFaceAuthEnabled()); } + /** + * Show the bouncer and start appear animations. + * + * @param statusBarHeight + */ + public void appear(int statusBarHeight) { + // We might still be collapsed and the view didn't have time to layout yet or still + // be small, let's wait on the predraw to do the animation in that case. + if (mView.getHeight() != 0 && mView.getHeight() != statusBarHeight) { + startAppearAnimation(); + } else { + mView.getViewTreeObserver().addOnPreDrawListener( + new ViewTreeObserver.OnPreDrawListener() { + @Override + public boolean onPreDraw() { + mView.getViewTreeObserver().removeOnPreDrawListener(this); + startAppearAnimation(); + return true; + } + }); + mView.requestLayout(); + } + } + public void startAppearAnimation() { if (mCurrentSecurityMode != SecurityMode.None) { mView.setAlpha(1f); @@ -465,9 +676,13 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard public boolean startDisappearAnimation(Runnable onFinishRunnable) { if (mCurrentSecurityMode != SecurityMode.None) { mView.startDisappearAnimation(mCurrentSecurityMode); - return getCurrentSecurityController().startDisappearAnimation(onFinishRunnable); + boolean animating = getCurrentSecurityController().startDisappearAnimation( + onFinishRunnable); + if (!animating && onFinishRunnable != null) { + onFinishRunnable.run(); + } + return animating; } - return false; } @@ -583,7 +798,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard mUiEventLogger.log(uiEvent, getSessionId()); } if (finish) { - mSecurityCallback.finish(strongAuth, targetUserId); + mKeyguardSecurityCallback.finish(strongAuth, targetUserId); } return finish; } @@ -596,11 +811,114 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard * @return the {@link OnBackAnimationCallback} to animate this view during a back gesture. */ @NonNull - OnBackAnimationCallback getBackCallback() { + public OnBackAnimationCallback getBackCallback() { return mView.getBackCallback(); } /** + * @return whether we should dispatch the back key event before Ime. + */ + public boolean dispatchBackKeyEventPreIme() { + return getCurrentSecurityMode() == SecurityMode.Password; + } + + /** + * Allows the media keys to work when the keyguard is showing. + * The media keys should be of no interest to the actual keyguard view(s), + * so intercepting them here should not be of any harm. + * @param event The key event + * @return whether the event was consumed as a media key. + */ + public boolean interceptMediaKey(KeyEvent event) { + int keyCode = event.getKeyCode(); + if (event.getAction() == KeyEvent.ACTION_DOWN) { + switch (keyCode) { + case KeyEvent.KEYCODE_MEDIA_PLAY: + case KeyEvent.KEYCODE_MEDIA_PAUSE: + case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE: + /* Suppress PLAY/PAUSE toggle when phone is ringing or + * in-call to avoid music playback */ + if (mTelephonyManager != null + && mTelephonyManager.getCallState() + != TelephonyManager.CALL_STATE_IDLE) { + return true; // suppress key event + } + return false; + case KeyEvent.KEYCODE_MUTE: + case KeyEvent.KEYCODE_HEADSETHOOK: + case KeyEvent.KEYCODE_MEDIA_STOP: + case KeyEvent.KEYCODE_MEDIA_NEXT: + case KeyEvent.KEYCODE_MEDIA_PREVIOUS: + case KeyEvent.KEYCODE_MEDIA_REWIND: + case KeyEvent.KEYCODE_MEDIA_RECORD: + case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD: + case KeyEvent.KEYCODE_MEDIA_AUDIO_TRACK: { + handleMediaKeyEvent(event); + return true; + } + + case KeyEvent.KEYCODE_VOLUME_UP: + case KeyEvent.KEYCODE_VOLUME_DOWN: + case KeyEvent.KEYCODE_VOLUME_MUTE: { + if (KEYGUARD_MANAGES_VOLUME) { + // Volume buttons should only function for music (local or remote). + // TODO: Actually handle MUTE. + mAudioManager.adjustSuggestedStreamVolume( + keyCode == KeyEvent.KEYCODE_VOLUME_UP + ? AudioManager.ADJUST_RAISE + : AudioManager.ADJUST_LOWER /* direction */, + AudioManager.STREAM_MUSIC /* stream */, 0 /* flags */); + // Don't execute default volume behavior + return true; + } else { + return false; + } + } + } + } else if (event.getAction() == KeyEvent.ACTION_UP) { + switch (keyCode) { + case KeyEvent.KEYCODE_MUTE: + case KeyEvent.KEYCODE_HEADSETHOOK: + case KeyEvent.KEYCODE_MEDIA_PLAY: + case KeyEvent.KEYCODE_MEDIA_PAUSE: + case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE: + case KeyEvent.KEYCODE_MEDIA_STOP: + case KeyEvent.KEYCODE_MEDIA_NEXT: + case KeyEvent.KEYCODE_MEDIA_PREVIOUS: + case KeyEvent.KEYCODE_MEDIA_REWIND: + case KeyEvent.KEYCODE_MEDIA_RECORD: + case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD: + case KeyEvent.KEYCODE_MEDIA_AUDIO_TRACK: { + handleMediaKeyEvent(event); + return true; + } + } + } + return false; + } + + + private void handleMediaKeyEvent(KeyEvent keyEvent) { + mAudioManager.dispatchMediaKeyEvent(keyEvent); + } + + /** + * In general, we enable unlocking the insecure keyguard with the menu key. However, there are + * some cases where we wish to disable it, notably when the menu button placement or technology + * is prone to false positives. + * + * @return true if the menu key should be enabled + */ + public boolean shouldEnableMenuKey() { + final Resources res = mView.getResources(); + final boolean configDisabled = res.getBoolean(R.bool.config_disableMenuKeyInLockScreen); + final boolean isTestHarness = ActivityManager.isRunningInTestHarness(); + final boolean fileOverride = (new File(ENABLE_MENU_KEY_FILE)).exists(); + return !configDisabled || isTestHarness || fileOverride; + } + + + /** * Switches to the given security view unless it's already being shown, in which case * this is a no-op. * @@ -628,7 +946,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard configureMode(); } - mSecurityCallback.onSecurityModeChanged( + mKeyguardSecurityCallback.onSecurityModeChanged( securityMode, newView != null && newView.needsInput()); } @@ -726,6 +1044,30 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard * configuration. */ public void updateResources() { + int gravity; + + Resources resources = mView.getResources(); + + if (resources.getBoolean(R.bool.can_use_one_handed_bouncer)) { + gravity = resources.getInteger( + R.integer.keyguard_host_view_one_handed_gravity); + } else { + gravity = resources.getInteger(R.integer.keyguard_host_view_gravity); + } + + mTranslationY = resources + .getDimensionPixelSize(R.dimen.keyguard_host_view_translation_y); + // Android SysUI uses a FrameLayout as the top-level, but Auto uses RelativeLayout. + // We're just changing the gravity here though (which can't be applied to RelativeLayout), + // so only attempt the update if mView is inside a FrameLayout. + if (mView.getLayoutParams() instanceof FrameLayout.LayoutParams) { + FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mView.getLayoutParams(); + if (lp.gravity != gravity) { + lp.gravity = gravity; + mView.setLayoutParams(lp); + } + } + int newOrientation = getResources().getConfiguration().orientation; if (newOrientation != mLastOrientation) { mLastOrientation = newOrientation; @@ -759,77 +1101,15 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard mKeyguardSecurityCallback); } - static class Factory { - - private final KeyguardSecurityContainer mView; - private final AdminSecondaryLockScreenController.Factory - mAdminSecondaryLockScreenControllerFactory; - private final LockPatternUtils mLockPatternUtils; - private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; - private final KeyguardSecurityModel mKeyguardSecurityModel; - private final MetricsLogger mMetricsLogger; - private final UiEventLogger mUiEventLogger; - private final KeyguardStateController mKeyguardStateController; - private final KeyguardSecurityViewFlipperController mSecurityViewFlipperController; - private final ConfigurationController mConfigurationController; - private final FalsingCollector mFalsingCollector; - private final FalsingManager mFalsingManager; - private final GlobalSettings mGlobalSettings; - private final FeatureFlags mFeatureFlags; - private final UserSwitcherController mUserSwitcherController; - private final SessionTracker mSessionTracker; - private final Optional<SideFpsController> mSidefpsController; - private final FalsingA11yDelegate mFalsingA11yDelegate; - - @Inject - Factory(KeyguardSecurityContainer view, - AdminSecondaryLockScreenController.Factory - adminSecondaryLockScreenControllerFactory, - LockPatternUtils lockPatternUtils, - KeyguardUpdateMonitor keyguardUpdateMonitor, - KeyguardSecurityModel keyguardSecurityModel, - MetricsLogger metricsLogger, - UiEventLogger uiEventLogger, - KeyguardStateController keyguardStateController, - KeyguardSecurityViewFlipperController securityViewFlipperController, - ConfigurationController configurationController, - FalsingCollector falsingCollector, - FalsingManager falsingManager, - UserSwitcherController userSwitcherController, - FeatureFlags featureFlags, - GlobalSettings globalSettings, - SessionTracker sessionTracker, - Optional<SideFpsController> sidefpsController, - FalsingA11yDelegate falsingA11yDelegate) { - mView = view; - mAdminSecondaryLockScreenControllerFactory = adminSecondaryLockScreenControllerFactory; - mLockPatternUtils = lockPatternUtils; - mKeyguardUpdateMonitor = keyguardUpdateMonitor; - mKeyguardSecurityModel = keyguardSecurityModel; - mMetricsLogger = metricsLogger; - mUiEventLogger = uiEventLogger; - mKeyguardStateController = keyguardStateController; - mSecurityViewFlipperController = securityViewFlipperController; - mConfigurationController = configurationController; - mFalsingCollector = falsingCollector; - mFalsingManager = falsingManager; - mFeatureFlags = featureFlags; - mGlobalSettings = globalSettings; - mUserSwitcherController = userSwitcherController; - mSessionTracker = sessionTracker; - mSidefpsController = sidefpsController; - mFalsingA11yDelegate = falsingA11yDelegate; - } - - public KeyguardSecurityContainerController create( - SecurityCallback securityCallback) { - return new KeyguardSecurityContainerController(mView, - mAdminSecondaryLockScreenControllerFactory, mLockPatternUtils, - mKeyguardUpdateMonitor, mKeyguardSecurityModel, mMetricsLogger, mUiEventLogger, - mKeyguardStateController, securityCallback, mSecurityViewFlipperController, - mConfigurationController, mFalsingCollector, mFalsingManager, - mUserSwitcherController, mFeatureFlags, mGlobalSettings, mSessionTracker, - mSidefpsController, mFalsingA11yDelegate); - } + /** + * Fades and translates in/out the security screen. + * Fades in as expansion approaches 0. + * Animation duration is between 0.33f and 0.67f of panel expansion fraction. + * @param fraction amount of the screen that should show. + */ + public void setExpansion(float fraction) { + float scaledFraction = BouncerPanelExpansionCalculator.showBouncerProgress(fraction); + mView.setAlpha(MathUtils.constrain(1 - scaledFraction, 0f, 1f)); + mView.setTranslationY(scaledFraction * mTranslationY); } } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java index 4a315d46cbd2..d491a720b7e7 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java @@ -3747,7 +3747,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab } // TODO: use these callbacks elsewhere in place of the existing notifyScreen*() - // (KeyguardViewMediator, KeyguardHostView) + // (KeyguardViewMediator, KeyguardSecurityContainer) /** * Dispatch wakeup events to: * - update biometric listening states diff --git a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardBouncerComponent.java b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardBouncerComponent.java index 5ad21df1e652..154b0ed2c4d1 100644 --- a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardBouncerComponent.java +++ b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardBouncerComponent.java @@ -18,7 +18,7 @@ package com.android.keyguard.dagger; import android.view.ViewGroup; -import com.android.keyguard.KeyguardHostViewController; +import com.android.keyguard.KeyguardSecurityContainerController; import com.android.systemui.dagger.qualifiers.RootView; import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerInteractor; @@ -37,6 +37,6 @@ public interface KeyguardBouncerComponent { KeyguardBouncerComponent create(@BindsInstance @RootView ViewGroup bouncerContainer); } - /** Returns a {@link KeyguardHostViewController}. */ - KeyguardHostViewController getKeyguardHostViewController(); + /** Returns a {@link KeyguardSecurityContainerController}. */ + KeyguardSecurityContainerController getSecurityContainerController(); } diff --git a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardBouncerModule.java b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardBouncerModule.java index cb7a0a9b1653..38f252a221eb 100644 --- a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardBouncerModule.java +++ b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardBouncerModule.java @@ -23,7 +23,6 @@ import android.hardware.fingerprint.FingerprintManager; import android.view.LayoutInflater; import android.view.ViewGroup; -import com.android.keyguard.KeyguardHostView; import com.android.keyguard.KeyguardSecurityContainer; import com.android.keyguard.KeyguardSecurityViewFlipper; import com.android.systemui.R; @@ -47,19 +46,13 @@ public interface KeyguardBouncerModule { /** */ @Provides @KeyguardBouncerScope - static KeyguardHostView providesKeyguardHostView(@RootView ViewGroup rootView, + static KeyguardSecurityContainer providesKeyguardSecurityContainer(@RootView ViewGroup rootView, LayoutInflater layoutInflater) { - KeyguardHostView hostView = (KeyguardHostView) layoutInflater.inflate( - R.layout.keyguard_host_view, rootView, false); - rootView.addView(hostView); - return hostView; - } - - /** */ - @Provides - @KeyguardBouncerScope - static KeyguardSecurityContainer providesKeyguardSecurityContainer(KeyguardHostView hostView) { - return hostView.findViewById(R.id.keyguard_security_container); + KeyguardSecurityContainer securityContainer = + (KeyguardSecurityContainer) layoutInflater.inflate( + R.layout.keyguard_security_container_view, rootView, false); + rootView.addView(securityContainer); + return securityContainer; } /** */ diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBouncerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBouncerViewBinder.kt index 9f09d53c99f3..8a533151617c 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBouncerViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBouncerViewBinder.kt @@ -23,10 +23,12 @@ import android.window.OnBackAnimationCallback import androidx.lifecycle.Lifecycle import androidx.lifecycle.repeatOnLifecycle import com.android.internal.policy.SystemBarUtils -import com.android.keyguard.KeyguardHostViewController +import com.android.keyguard.KeyguardSecurityContainerController import com.android.keyguard.KeyguardSecurityModel +import com.android.keyguard.KeyguardSecurityView import com.android.keyguard.KeyguardUpdateMonitor import com.android.keyguard.dagger.KeyguardBouncerComponent +import com.android.settingslib.Utils import com.android.systemui.keyguard.data.BouncerViewDelegate import com.android.systemui.keyguard.shared.constants.KeyguardBouncerConstants.EXPANSION_VISIBLE import com.android.systemui.keyguard.ui.viewmodel.KeyguardBouncerViewModel @@ -44,52 +46,54 @@ object KeyguardBouncerViewBinder { viewModel: KeyguardBouncerViewModel, componentFactory: KeyguardBouncerComponent.Factory ) { - // Builds the KeyguardHostViewController from bouncer view group. - val hostViewController: KeyguardHostViewController = - componentFactory.create(view).keyguardHostViewController - hostViewController.init() + // Builds the KeyguardSecurityContainerController from bouncer view group. + val securityContainerController: KeyguardSecurityContainerController = + componentFactory.create(view).securityContainerController + securityContainerController.init() val delegate = object : BouncerViewDelegate { override fun isFullScreenBouncer(): Boolean { - val mode = hostViewController.currentSecurityMode + val mode = securityContainerController.currentSecurityMode return mode == KeyguardSecurityModel.SecurityMode.SimPin || mode == KeyguardSecurityModel.SecurityMode.SimPuk } override fun getBackCallback(): OnBackAnimationCallback { - return hostViewController.backCallback + return securityContainerController.backCallback } override fun shouldDismissOnMenuPressed(): Boolean { - return hostViewController.shouldEnableMenuKey() + return securityContainerController.shouldEnableMenuKey() } override fun interceptMediaKey(event: KeyEvent?): Boolean { - return hostViewController.interceptMediaKey(event) + return securityContainerController.interceptMediaKey(event) } override fun dispatchBackKeyEventPreIme(): Boolean { - return hostViewController.dispatchBackKeyEventPreIme() + return securityContainerController.dispatchBackKeyEventPreIme() } override fun showNextSecurityScreenOrFinish(): Boolean { - return hostViewController.dismiss(KeyguardUpdateMonitor.getCurrentUser()) + return securityContainerController.dismiss( + KeyguardUpdateMonitor.getCurrentUser() + ) } override fun resume() { - hostViewController.showPrimarySecurityScreen() - hostViewController.onResume() + securityContainerController.showPrimarySecurityScreen(/* isTurningOff= */ false) + securityContainerController.onResume(KeyguardSecurityView.SCREEN_ON) } override fun setDismissAction( onDismissAction: ActivityStarter.OnDismissAction?, cancelAction: Runnable? ) { - hostViewController.setOnDismissAction(onDismissAction, cancelAction) + securityContainerController.setOnDismissAction(onDismissAction, cancelAction) } override fun willDismissWithActions(): Boolean { - return hostViewController.hasDismissActions() + return securityContainerController.hasDismissActions() } } view.repeatWhenAttached { @@ -98,39 +102,46 @@ object KeyguardBouncerViewBinder { viewModel.setBouncerViewDelegate(delegate) launch { viewModel.show.collect { - hostViewController.showPromptReason(it.promptReason) + securityContainerController.showPromptReason(it.promptReason) it.errorMessage?.let { errorMessage -> - hostViewController.showErrorMessage(errorMessage) + securityContainerController.showMessage( + errorMessage, + Utils.getColorError(view.context) + ) } - hostViewController.showPrimarySecurityScreen() - hostViewController.appear( + securityContainerController.showPrimarySecurityScreen( + /* turningOff= */ false + ) + securityContainerController.appear( SystemBarUtils.getStatusBarHeight(view.context) ) - hostViewController.onResume() + securityContainerController.onResume(KeyguardSecurityView.SCREEN_ON) } } launch { viewModel.hide.collect { - hostViewController.cancelDismissAction() - hostViewController.cleanUp() - hostViewController.resetSecurityContainer() + securityContainerController.cancelDismissAction() + securityContainerController.onPause() + securityContainerController.reset() } } launch { - viewModel.startingToHide.collect { hostViewController.onStartingToHide() } + viewModel.startingToHide.collect { + securityContainerController.onStartingToHide() + } } launch { viewModel.startDisappearAnimation.collect { - hostViewController.startDisappearAnimation(it) + securityContainerController.startDisappearAnimation(it) } } launch { viewModel.bouncerExpansionAmount.collect { expansion -> - hostViewController.setExpansion(expansion) + securityContainerController.setExpansion(expansion) } } @@ -138,10 +149,8 @@ object KeyguardBouncerViewBinder { viewModel.bouncerExpansionAmount .filter { it == EXPANSION_VISIBLE } .collect { - hostViewController.onResume() - view.announceForAccessibility( - hostViewController.accessibilityTitleForCurrentMode - ) + securityContainerController.onResume(KeyguardSecurityView.SCREEN_ON) + view.announceForAccessibility(securityContainerController.title) } } @@ -149,13 +158,13 @@ object KeyguardBouncerViewBinder { viewModel.isBouncerVisible.collect { isVisible -> val visibility = if (isVisible) View.VISIBLE else View.INVISIBLE view.visibility = visibility - hostViewController.onBouncerVisibilityChanged(visibility) + securityContainerController.onBouncerVisibilityChanged(visibility) } } launch { viewModel.isInteractable.collect { isInteractable -> - hostViewController.setInteractable(isInteractable) + securityContainerController.setInteractable(isInteractable) } } @@ -164,33 +173,36 @@ object KeyguardBouncerViewBinder { .filter { !it } .collect { // Remove existing input for security reasons. - hostViewController.resetSecurityContainer() + securityContainerController.reset() } } launch { viewModel.keyguardPosition.collect { position -> - hostViewController.updateKeyguardPosition(position) + securityContainerController.updateKeyguardPosition(position) } } launch { viewModel.updateResources.collect { - hostViewController.updateResources() + securityContainerController.updateResources() viewModel.notifyUpdateResources() } } launch { viewModel.bouncerShowMessage.collect { - hostViewController.showMessage(it.message, it.colorStateList) + securityContainerController.showMessage(it.message, it.colorStateList) viewModel.onMessageShown() } } launch { viewModel.keyguardAuthenticated.collect { - hostViewController.finish(it, KeyguardUpdateMonitor.getCurrentUser()) + securityContainerController.finish( + it, + KeyguardUpdateMonitor.getCurrentUser() + ) viewModel.notifyKeyguardAuthenticated() } } @@ -204,7 +216,7 @@ object KeyguardBouncerViewBinder { launch { viewModel.screenTurnedOff.collect { if (view.visibility == View.VISIBLE) { - hostViewController.onPause() + securityContainerController.onPause() } } } diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardHostViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardHostViewControllerTest.java deleted file mode 100644 index 4021652295c1..000000000000 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardHostViewControllerTest.java +++ /dev/null @@ -1,186 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.keyguard; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.inOrder; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import android.media.AudioManager; -import android.telephony.TelephonyManager; -import android.test.suitebuilder.annotation.SmallTest; -import android.testing.AndroidTestingRunner; -import android.testing.TestableLooper; -import android.testing.TestableResources; -import android.view.Gravity; -import android.view.View; -import android.view.ViewGroup; -import android.widget.FrameLayout; - -import com.android.systemui.R; -import com.android.systemui.SysuiTestCase; -import com.android.systemui.plugins.ActivityStarter.OnDismissAction; - -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.InOrder; -import org.mockito.Mock; -import org.mockito.junit.MockitoJUnit; -import org.mockito.junit.MockitoRule; - -@SmallTest -@RunWith(AndroidTestingRunner.class) -@TestableLooper.RunWithLooper -public class KeyguardHostViewControllerTest extends SysuiTestCase { - @Mock - private KeyguardUpdateMonitor mKeyguardUpdateMonitor; - - private KeyguardHostView mKeyguardHostView; - @Mock - private AudioManager mAudioManager; - @Mock - private TelephonyManager mTelephonyManager; - @Mock - private ViewMediatorCallback mViewMediatorCallback; - @Mock - KeyguardSecurityContainerController.Factory mKeyguardSecurityContainerControllerFactory; - @Mock - private KeyguardSecurityContainerController mKeyguardSecurityContainerController; - - @Rule - public MockitoRule mMockitoRule = MockitoJUnit.rule(); - - private TestableResources mTestableResources; - private KeyguardHostViewController mKeyguardHostViewController; - - @Before - public void setup() { - mTestableResources = mContext.getOrCreateTestableResources(); - - mKeyguardHostView = new KeyguardHostView(mContext); - - // Explicitly disable one handed keyguard. - mTestableResources.addOverride( - R.bool.can_use_one_handed_bouncer, false); - - when(mKeyguardSecurityContainerControllerFactory.create(any( - KeyguardSecurityContainer.SecurityCallback.class))) - .thenReturn(mKeyguardSecurityContainerController); - mKeyguardHostViewController = new KeyguardHostViewController( - mKeyguardHostView, mKeyguardUpdateMonitor, mAudioManager, mTelephonyManager, - mViewMediatorCallback, mKeyguardSecurityContainerControllerFactory); - } - - @Test - public void testHasDismissActions() { - assertFalse("Action not set yet", mKeyguardHostViewController.hasDismissActions()); - mKeyguardHostViewController.setOnDismissAction(mock(OnDismissAction.class), - null /* cancelAction */); - assertTrue("Action should exist", mKeyguardHostViewController.hasDismissActions()); - } - - @Test - public void testOnStartingToHide() { - mKeyguardHostViewController.onStartingToHide(); - verify(mKeyguardSecurityContainerController).onStartingToHide(); - } - - @Test - public void onBouncerVisible_propagatesToKeyguardSecurityContainerController() { - mKeyguardHostViewController.onBouncerVisibilityChanged(ViewGroup.VISIBLE); - mKeyguardHostViewController.onBouncerVisibilityChanged(ViewGroup.INVISIBLE); - - InOrder order = inOrder(mKeyguardSecurityContainerController); - order.verify(mKeyguardSecurityContainerController).onBouncerVisibilityChanged(View.VISIBLE); - order.verify(mKeyguardSecurityContainerController).onBouncerVisibilityChanged( - View.INVISIBLE); - } - - @Test - public void testGravityReappliedOnConfigurationChange() { - FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams( - ViewGroup.LayoutParams.MATCH_PARENT, - ViewGroup.LayoutParams.MATCH_PARENT); - mKeyguardHostView.setLayoutParams(lp); - - // Set initial gravity - mTestableResources.addOverride(R.integer.keyguard_host_view_gravity, - Gravity.CENTER); - - // Kick off the initial pass... - mKeyguardHostViewController.init(); - assertEquals( - ((FrameLayout.LayoutParams) mKeyguardHostView.getLayoutParams()).gravity, - Gravity.CENTER); - - // Now simulate a config change - mTestableResources.addOverride(R.integer.keyguard_host_view_gravity, - Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM); - - mKeyguardHostViewController.updateResources(); - assertEquals( - ((FrameLayout.LayoutParams) mKeyguardHostView.getLayoutParams()).gravity, - Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM); - } - - @Test - public void testGravityUsesOneHandGravityWhenApplicable() { - FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams( - ViewGroup.LayoutParams.MATCH_PARENT, - ViewGroup.LayoutParams.MATCH_PARENT); - mKeyguardHostView.setLayoutParams(lp); - - mTestableResources.addOverride( - R.integer.keyguard_host_view_gravity, - Gravity.CENTER); - mTestableResources.addOverride( - R.integer.keyguard_host_view_one_handed_gravity, - Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM); - - // Start disabled. - mTestableResources.addOverride( - R.bool.can_use_one_handed_bouncer, false); - - mKeyguardHostViewController.init(); - assertEquals( - ((FrameLayout.LayoutParams) mKeyguardHostView.getLayoutParams()).gravity, - Gravity.CENTER); - - // And enable - mTestableResources.addOverride( - R.bool.can_use_one_handed_bouncer, true); - - mKeyguardHostViewController.updateResources(); - assertEquals( - ((FrameLayout.LayoutParams) mKeyguardHostView.getLayoutParams()).gravity, - Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM); - } - - @Test - public void testUpdateKeyguardPositionDelegatesToSecurityContainer() { - mKeyguardHostViewController.updateKeyguardPosition(1.0f); - - verify(mKeyguardSecurityContainerController).updateKeyguardPosition(1.0f); - } -} diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java index 075ef9df9664..e39cbe1cb78c 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java @@ -23,12 +23,17 @@ import static com.android.keyguard.KeyguardSecurityContainer.MODE_ONE_HANDED; import static com.google.common.truth.Truth.assertThat; +import static junit.framework.Assert.assertFalse; +import static junit.framework.Assert.assertTrue; + import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.atLeastOnce; +import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; @@ -39,12 +44,17 @@ import static org.mockito.Mockito.when; import android.content.res.Configuration; import android.content.res.Resources; import android.hardware.biometrics.BiometricSourceType; +import android.media.AudioManager; +import android.telephony.TelephonyManager; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; +import android.testing.TestableResources; +import android.view.Gravity; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; import android.view.WindowInsetsController; +import android.widget.FrameLayout; import androidx.test.filters.SmallTest; @@ -60,6 +70,7 @@ import com.android.systemui.classifier.FalsingA11yDelegate; import com.android.systemui.classifier.FalsingCollector; import com.android.systemui.flags.FeatureFlags; import com.android.systemui.log.SessionTracker; +import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.FalsingManager; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.KeyguardStateController; @@ -71,6 +82,7 @@ import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; +import org.mockito.ArgumentMatcher; import org.mockito.Captor; import org.mockito.Mock; import org.mockito.junit.MockitoJUnit; @@ -108,8 +120,6 @@ public class KeyguardSecurityContainerControllerTest extends SysuiTestCase { @Mock private KeyguardInputViewController mInputViewController; @Mock - private KeyguardSecurityContainer.SecurityCallback mSecurityCallback; - @Mock private WindowInsetsController mWindowInsetsController; @Mock private KeyguardSecurityViewFlipper mSecurityViewFlipper; @@ -126,8 +136,6 @@ public class KeyguardSecurityContainerControllerTest extends SysuiTestCase { @Mock private EmergencyButtonController mEmergencyButtonController; @Mock - private Resources mResources; - @Mock private FalsingCollector mFalsingCollector; @Mock private FalsingManager mFalsingManager; @@ -147,6 +155,12 @@ public class KeyguardSecurityContainerControllerTest extends SysuiTestCase { private KeyguardPasswordViewController mKeyguardPasswordViewControllerMock; @Mock private FalsingA11yDelegate mFalsingA11yDelegate; + @Mock + private TelephonyManager mTelephonyManager; + @Mock + private ViewMediatorCallback mViewMediatorCallback; + @Mock + private AudioManager mAudioManager; @Captor private ArgumentCaptor<KeyguardUpdateMonitorCallback> mKeyguardUpdateMonitorCallback; @@ -158,18 +172,25 @@ public class KeyguardSecurityContainerControllerTest extends SysuiTestCase { private KeyguardSecurityContainerController mKeyguardSecurityContainerController; private KeyguardPasswordViewController mKeyguardPasswordViewController; private KeyguardPasswordView mKeyguardPasswordView; + private TestableResources mTestableResources; @Before public void setup() { mConfiguration = new Configuration(); mConfiguration.setToDefaults(); // Defaults to ORIENTATION_UNDEFINED. + mTestableResources = mContext.getOrCreateTestableResources(); - when(mResources.getConfiguration()).thenReturn(mConfiguration); when(mView.getContext()).thenReturn(mContext); - when(mView.getResources()).thenReturn(mResources); + when(mView.getResources()).thenReturn(mContext.getResources()); + FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(/* width= */ 0, /* height= */ + 0); + lp.gravity = 0; + when(mView.getLayoutParams()).thenReturn(lp); when(mAdminSecondaryLockScreenControllerFactory.create(any(KeyguardSecurityCallback.class))) .thenReturn(mAdminSecondaryLockScreenController); when(mSecurityViewFlipper.getWindowInsetsController()).thenReturn(mWindowInsetsController); + when(mKeyguardSecurityViewFlipperController.getSecurityView(any(SecurityMode.class), + any(KeyguardSecurityCallback.class))).thenReturn(mInputViewController); mKeyguardPasswordView = spy((KeyguardPasswordView) LayoutInflater.from(mContext).inflate( R.layout.keyguard_password_view, null)); when(mKeyguardPasswordView.getRootView()).thenReturn(mSecurityViewFlipper); @@ -178,20 +199,21 @@ public class KeyguardSecurityContainerControllerTest extends SysuiTestCase { when(mKeyguardMessageAreaControllerFactory.create(any(KeyguardMessageArea.class))) .thenReturn(mKeyguardMessageAreaController); when(mKeyguardPasswordView.getWindowInsetsController()).thenReturn(mWindowInsetsController); + when(mKeyguardSecurityModel.getSecurityMode(anyInt())).thenReturn(SecurityMode.PIN); mKeyguardPasswordViewController = new KeyguardPasswordViewController( (KeyguardPasswordView) mKeyguardPasswordView, mKeyguardUpdateMonitor, SecurityMode.Password, mLockPatternUtils, null, mKeyguardMessageAreaControllerFactory, null, null, mEmergencyButtonController, null, mock(Resources.class), null, mKeyguardViewController); - mKeyguardSecurityContainerController = new KeyguardSecurityContainerController.Factory( + mKeyguardSecurityContainerController = new KeyguardSecurityContainerController( mView, mAdminSecondaryLockScreenControllerFactory, mLockPatternUtils, mKeyguardUpdateMonitor, mKeyguardSecurityModel, mMetricsLogger, mUiEventLogger, mKeyguardStateController, mKeyguardSecurityViewFlipperController, mConfigurationController, mFalsingCollector, mFalsingManager, mUserSwitcherController, mFeatureFlags, mGlobalSettings, - mSessionTracker, Optional.of(mSideFpsController), mFalsingA11yDelegate).create( - mSecurityCallback); + mSessionTracker, Optional.of(mSideFpsController), mFalsingA11yDelegate, + mTelephonyManager, mViewMediatorCallback, mAudioManager); } @Test @@ -243,7 +265,8 @@ public class KeyguardSecurityContainerControllerTest extends SysuiTestCase { eq(mFalsingA11yDelegate)); // Update rotation. Should trigger update - mConfiguration.orientation = Configuration.ORIENTATION_LANDSCAPE; + mTestableResources.getResources().getConfiguration().orientation = + Configuration.ORIENTATION_LANDSCAPE; mKeyguardSecurityContainerController.updateResources(); verify(mView).initMode(eq(MODE_DEFAULT), eq(mGlobalSettings), eq(mFalsingManager), @@ -277,7 +300,7 @@ public class KeyguardSecurityContainerControllerTest extends SysuiTestCase { @Test public void showSecurityScreen_oneHandedMode_flagDisabled_noOneHandedMode() { - when(mResources.getBoolean(R.bool.can_use_one_handed_bouncer)).thenReturn(false); + mTestableResources.addOverride(R.bool.can_use_one_handed_bouncer, false); when(mKeyguardSecurityViewFlipperController.getSecurityView( eq(SecurityMode.Pattern), any(KeyguardSecurityCallback.class))) .thenReturn((KeyguardInputViewController) mKeyguardPasswordViewController); @@ -291,7 +314,7 @@ public class KeyguardSecurityContainerControllerTest extends SysuiTestCase { @Test public void showSecurityScreen_oneHandedMode_flagEnabled_oneHandedMode() { - when(mResources.getBoolean(R.bool.can_use_one_handed_bouncer)).thenReturn(true); + mTestableResources.addOverride(R.bool.can_use_one_handed_bouncer, true); when(mKeyguardSecurityViewFlipperController.getSecurityView( eq(SecurityMode.Pattern), any(KeyguardSecurityCallback.class))) .thenReturn((KeyguardInputViewController) mKeyguardPasswordViewController); @@ -305,7 +328,7 @@ public class KeyguardSecurityContainerControllerTest extends SysuiTestCase { @Test public void showSecurityScreen_twoHandedMode_flagEnabled_noOneHandedMode() { - when(mResources.getBoolean(R.bool.can_use_one_handed_bouncer)).thenReturn(true); + mTestableResources.addOverride(R.bool.can_use_one_handed_bouncer, true); setupGetSecurityView(); mKeyguardSecurityContainerController.showSecurityScreen(SecurityMode.Password); @@ -482,7 +505,9 @@ public class KeyguardSecurityContainerControllerTest extends SysuiTestCase { SecurityMode.SimPin); // THEN the next security method of PIN is set, and the keyguard is not marked as done - verify(mSecurityCallback, never()).finish(anyBoolean(), anyInt()); + + verify(mViewMediatorCallback, never()).keyguardDonePending(anyBoolean(), anyInt()); + verify(mViewMediatorCallback, never()).keyguardDone(anyBoolean(), anyInt()); assertThat(mKeyguardSecurityContainerController.getCurrentSecurityMode()) .isEqualTo(SecurityMode.PIN); } @@ -556,17 +581,19 @@ public class KeyguardSecurityContainerControllerTest extends SysuiTestCase { } @Test - public void onDensityorFontScaleChanged() { + public void onDensityOrFontScaleChanged() { ArgumentCaptor<ConfigurationController.ConfigurationListener> configurationListenerArgumentCaptor = ArgumentCaptor.forClass( ConfigurationController.ConfigurationListener.class); mKeyguardSecurityContainerController.onViewAttached(); verify(mConfigurationController).addCallback(configurationListenerArgumentCaptor.capture()); + clearInvocations(mKeyguardSecurityViewFlipperController); + configurationListenerArgumentCaptor.getValue().onDensityOrFontScaleChanged(); verify(mView).onDensityOrFontScaleChanged(); verify(mKeyguardSecurityViewFlipperController).clearViews(); - verify(mKeyguardSecurityViewFlipperController).getSecurityView(any(SecurityMode.class), + verify(mKeyguardSecurityViewFlipperController).getSecurityView(eq(SecurityMode.PIN), any(KeyguardSecurityCallback.class)); } @@ -577,11 +604,13 @@ public class KeyguardSecurityContainerControllerTest extends SysuiTestCase { ConfigurationController.ConfigurationListener.class); mKeyguardSecurityContainerController.onViewAttached(); verify(mConfigurationController).addCallback(configurationListenerArgumentCaptor.capture()); + clearInvocations(mKeyguardSecurityViewFlipperController); + configurationListenerArgumentCaptor.getValue().onThemeChanged(); verify(mView).reloadColors(); verify(mKeyguardSecurityViewFlipperController).clearViews(); - verify(mKeyguardSecurityViewFlipperController).getSecurityView(any(SecurityMode.class), + verify(mKeyguardSecurityViewFlipperController).getSecurityView(eq(SecurityMode.PIN), any(KeyguardSecurityCallback.class)); } @@ -592,14 +621,90 @@ public class KeyguardSecurityContainerControllerTest extends SysuiTestCase { ConfigurationController.ConfigurationListener.class); mKeyguardSecurityContainerController.onViewAttached(); verify(mConfigurationController).addCallback(configurationListenerArgumentCaptor.capture()); + clearInvocations(mKeyguardSecurityViewFlipperController); + configurationListenerArgumentCaptor.getValue().onUiModeChanged(); verify(mView).reloadColors(); verify(mKeyguardSecurityViewFlipperController).clearViews(); - verify(mKeyguardSecurityViewFlipperController).getSecurityView(any(SecurityMode.class), + verify(mKeyguardSecurityViewFlipperController).getSecurityView(eq(SecurityMode.PIN), any(KeyguardSecurityCallback.class)); } + @Test + public void testHasDismissActions() { + assertFalse("Action not set yet", mKeyguardSecurityContainerController.hasDismissActions()); + mKeyguardSecurityContainerController.setOnDismissAction(mock( + ActivityStarter.OnDismissAction.class), + null /* cancelAction */); + assertTrue("Action should exist", mKeyguardSecurityContainerController.hasDismissActions()); + } + + @Test + public void testOnStartingToHide() { + mKeyguardSecurityContainerController.onStartingToHide(); + verify(mInputViewController).onStartingToHide(); + } + + @Test + public void testGravityReappliedOnConfigurationChange() { + // Set initial gravity + mTestableResources.addOverride(R.integer.keyguard_host_view_gravity, + Gravity.CENTER); + + // Kick off the initial pass... + mKeyguardSecurityContainerController.onInit(); + verify(mView).setLayoutParams(argThat( + (ArgumentMatcher<FrameLayout.LayoutParams>) argument -> + argument.gravity == Gravity.CENTER)); + clearInvocations(mView); + + // Now simulate a config change + mTestableResources.addOverride(R.integer.keyguard_host_view_gravity, + Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM); + + mKeyguardSecurityContainerController.updateResources(); + verify(mView).setLayoutParams(argThat( + (ArgumentMatcher<FrameLayout.LayoutParams>) argument -> + argument.gravity == (Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM))); + } + + @Test + public void testGravityUsesOneHandGravityWhenApplicable() { + mTestableResources.addOverride( + R.integer.keyguard_host_view_gravity, + Gravity.CENTER); + mTestableResources.addOverride( + R.integer.keyguard_host_view_one_handed_gravity, + Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM); + + // Start disabled. + mTestableResources.addOverride( + R.bool.can_use_one_handed_bouncer, false); + + mKeyguardSecurityContainerController.onInit(); + verify(mView).setLayoutParams(argThat( + (ArgumentMatcher<FrameLayout.LayoutParams>) argument -> + argument.gravity == Gravity.CENTER)); + clearInvocations(mView); + + // And enable + mTestableResources.addOverride( + R.bool.can_use_one_handed_bouncer, true); + + mKeyguardSecurityContainerController.updateResources(); + verify(mView).setLayoutParams(argThat( + (ArgumentMatcher<FrameLayout.LayoutParams>) argument -> + argument.gravity == (Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM))); + } + + @Test + public void testUpdateKeyguardPositionDelegatesToSecurityContainer() { + mKeyguardSecurityContainerController.updateKeyguardPosition(1.0f); + verify(mView).updatePositionByTouchX(1.0f); + } + + private KeyguardSecurityContainer.SwipeListener getRegisteredSwipeListener() { mKeyguardSecurityContainerController.onViewAttached(); verify(mView).setSwipeListener(mSwipeListenerArgumentCaptor.capture()); @@ -625,7 +730,7 @@ public class KeyguardSecurityContainerControllerTest extends SysuiTestCase { } private void setSideFpsHintEnabledFromResources(boolean enabled) { - when(mResources.getBoolean(R.bool.config_show_sidefps_hint_on_bouncer)).thenReturn( + mTestableResources.addOverride(R.bool.config_show_sidefps_hint_on_bouncer, enabled); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt index e5d5e3b8433a..d229a08ad7c4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt @@ -21,7 +21,7 @@ import android.testing.TestableLooper.RunWithLooper import android.view.MotionEvent import android.view.ViewGroup import androidx.test.filters.SmallTest -import com.android.keyguard.KeyguardHostViewController +import com.android.keyguard.KeyguardSecurityContainerController import com.android.keyguard.LockIconViewController import com.android.keyguard.dagger.KeyguardBouncerComponent import com.android.systemui.R @@ -106,7 +106,7 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() { private lateinit var alternateBouncerInteractor: AlternateBouncerInteractor @Mock lateinit var keyguardBouncerComponentFactory: KeyguardBouncerComponent.Factory @Mock lateinit var keyguardBouncerComponent: KeyguardBouncerComponent - @Mock lateinit var keyguardHostViewController: KeyguardHostViewController + @Mock lateinit var keyguardSecurityContainerController: KeyguardSecurityContainerController @Mock lateinit var keyguardTransitionInteractor: KeyguardTransitionInteractor private lateinit var interactionEventHandlerCaptor: ArgumentCaptor<InteractionEventHandler> @@ -122,8 +122,8 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() { .thenReturn(mock(ViewGroup::class.java)) whenever(keyguardBouncerComponentFactory.create(any(ViewGroup::class.java))) .thenReturn(keyguardBouncerComponent) - whenever(keyguardBouncerComponent.keyguardHostViewController) - .thenReturn(keyguardHostViewController) + whenever(keyguardBouncerComponent.securityContainerController) + .thenReturn(keyguardSecurityContainerController) underTest = NotificationShadeWindowViewController( lockscreenShadeTransitionController, FalsingCollectorFake(), diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.java index 5cc3ef1def9e..5e9c2199897d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.java @@ -33,7 +33,7 @@ import android.view.ViewGroup; import androidx.test.filters.SmallTest; -import com.android.keyguard.KeyguardHostViewController; +import com.android.keyguard.KeyguardSecurityContainerController; import com.android.keyguard.LockIconViewController; import com.android.keyguard.dagger.KeyguardBouncerComponent; import com.android.systemui.R; @@ -97,7 +97,7 @@ public class NotificationShadeWindowViewTest extends SysuiTestCase { @Mock private KeyguardBouncerViewModel mKeyguardBouncerViewModel; @Mock private KeyguardBouncerComponent.Factory mKeyguardBouncerComponentFactory; @Mock private KeyguardBouncerComponent mKeyguardBouncerComponent; - @Mock private KeyguardHostViewController mKeyguardHostViewController; + @Mock private KeyguardSecurityContainerController mKeyguardSecurityContainerController; @Mock private NotificationInsetsController mNotificationInsetsController; @Mock private AlternateBouncerInteractor mAlternateBouncerInteractor; @Mock private KeyguardTransitionInteractor mKeyguardTransitionInteractor; @@ -117,8 +117,8 @@ public class NotificationShadeWindowViewTest extends SysuiTestCase { when(mView.findViewById(R.id.keyguard_bouncer_container)).thenReturn(mock(ViewGroup.class)); when(mKeyguardBouncerComponentFactory.create(any(ViewGroup.class))).thenReturn( mKeyguardBouncerComponent); - when(mKeyguardBouncerComponent.getKeyguardHostViewController()).thenReturn( - mKeyguardHostViewController); + when(mKeyguardBouncerComponent.getSecurityContainerController()).thenReturn( + mKeyguardSecurityContainerController); when(mStatusBarStateController.isDozing()).thenReturn(false); mDependency.injectTestDependency(ShadeController.class, mShadeController); |