diff options
15 files changed, 1058 insertions, 640 deletions
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_security_container_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_host_view.xml index 426cfafb190e..8497ff094c03 100644 --- a/packages/SystemUI/res-keyguard/layout/keyguard_security_container_view.xml +++ b/packages/SystemUI/res-keyguard/layout/keyguard_host_view.xml @@ -1,7 +1,7 @@ <?xml version="1.0" encoding="utf-8"?> <!-- ** -** Copyright 2023, The Android Open Source Project +** Copyright 2012, 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,10 +17,12 @@ */ --> -<com.android.keyguard.KeyguardSecurityContainer +<!-- This is the host view that generally contains two sub views: the widget view + and the security view. --> +<com.android.keyguard.KeyguardHostView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:androidprv="http://schemas.android.com/apk/res-auto" - android:id="@+id/keyguard_security_container" + android:id="@+id/keyguard_host_view" android:layout_width="match_parent" android:layout_height="match_parent" android:clipChildren="false" @@ -28,15 +30,27 @@ 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.KeyguardSecurityViewFlipper - android:id="@+id/view_flipper" - android:layout_width="wrap_content" + + <com.android.keyguard.KeyguardSecurityContainer + android:id="@+id/keyguard_security_container" + android:layout_width="match_parent" 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> + 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> diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardHostView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardHostView.java new file mode 100644 index 000000000000..2a389b6132e9 --- /dev/null +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardHostView.java @@ -0,0 +1,78 @@ +/* + * 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 new file mode 100644 index 000000000000..61394035d731 --- /dev/null +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java @@ -0,0 +1,535 @@ +/* + * 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 7054393f5fed..b143c5b90373 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java @@ -51,7 +51,26 @@ 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() {}; + 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() {} + }; 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 bf9c3bbddc30..bc72f7979a74 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityCallback.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityCallback.java @@ -25,9 +25,7 @@ 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. */ - default void dismiss(boolean securityVerified, int targetUserId, - SecurityMode expectedSecurityMode) { - } + void dismiss(boolean securityVerified, int targetUserId, SecurityMode expectedSecurityMode); /** * Dismiss the given security screen. @@ -37,26 +35,19 @@ public interface KeyguardSecurityCallback { * if any, during this dismissal. * @param expectedSecurityMode The security mode that is invoking this dismiss. */ - default boolean dismiss(boolean securityVerified, int targetUserId, - boolean bypassSecondaryLockScreen, - SecurityMode expectedSecurityMode) { - return false; - } + void dismiss(boolean securityVerified, int targetUserId, boolean bypassSecondaryLockScreen, + SecurityMode expectedSecurityMode); /** * Manually report user activity to keep the device awake. */ - default void userActivity() { - } + void userActivity(); /** * Checks if keyguard is in "verify credentials" mode. - * * @return true if user has been asked to verify security. */ - default boolean isVerifyUnlockOnly() { - return false; - } + boolean isVerifyUnlockOnly(); /** * Call to report an unlock attempt. @@ -65,14 +56,12 @@ public interface KeyguardSecurityCallback { * @param timeoutMs timeout in milliseconds to wait before reattempting an unlock. * Only nonzero if 'success' is false */ - default void reportUnlockAttempt(int userId, boolean success, int timeoutMs) { - } + void reportUnlockAttempt(int userId, boolean success, int timeoutMs); /** * Resets the keyguard view. */ - default void reset() { - } + void reset(); /** * Call when cancel button is pressed in bouncer. @@ -84,19 +73,5 @@ public interface KeyguardSecurityCallback { /** * Invoked whenever users are typing their password or drawing a pattern. */ - 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) { - } + void onUserInput(); } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java index eec788b7add8..9f07a20ce812 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java @@ -50,7 +50,6 @@ 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; @@ -165,8 +164,6 @@ 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. @@ -268,6 +265,31 @@ 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(); } @@ -320,7 +342,7 @@ public class KeyguardSecurityContainer extends ConstraintLayout { public KeyguardSecurityContainer(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); - mSpringAnimation = new SpringAnimation(this, DynamicAnimation.TRANSLATION_Y); + mSpringAnimation = new SpringAnimation(this, DynamicAnimation.Y); mViewConfiguration = ViewConfiguration.get(context); mDoubleTapDetector = new GestureDetector(context, new DoubleTapListener()); } @@ -423,11 +445,6 @@ 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; @@ -435,10 +452,6 @@ 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); @@ -626,18 +639,6 @@ 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 de05876f7f60..9fcacce311d1 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java @@ -29,26 +29,17 @@ 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; @@ -62,6 +53,7 @@ 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; @@ -75,7 +67,6 @@ 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; @@ -84,7 +75,6 @@ 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; @@ -105,6 +95,7 @@ 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; @@ -114,20 +105,6 @@ 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; @@ -172,6 +149,11 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard }; private KeyguardSecurityCallback mKeyguardSecurityCallback = new KeyguardSecurityCallback() { + public void userActivity() { + if (mSecurityCallback != null) { + mSecurityCallback.userActivity(); + } + } @Override public void onUserInput() { @@ -186,23 +168,16 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard } @Override - public boolean dismiss(boolean authenticated, int targetId, + public void dismiss(boolean authenticated, int targetId, boolean bypassSecondaryLockScreen, SecurityMode expectedSecurityMode) { - return showNextSecurityScreenOrFinish( - authenticated, targetId, bypassSecondaryLockScreen, expectedSecurityMode); - } - - @Override - public void userActivity() { - mViewMediatorCallback.userActivity(); + mSecurityCallback.dismiss(authenticated, targetId, bypassSecondaryLockScreen, + expectedSecurityMode); } - @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()) { @@ -239,47 +214,12 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard : BouncerUiEvent.BOUNCER_PASSWORD_FAILURE, getSessionId()); } - @Override public void reset() { - mViewMediatorCallback.resetKeyguard(); + mSecurityCallback.reset(); } - @Override public void 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); + mSecurityCallback.onCancelClicked(); } }; @@ -323,34 +263,6 @@ 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); } @@ -369,8 +281,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard } }; - @Inject - public KeyguardSecurityContainerController(KeyguardSecurityContainer view, + private KeyguardSecurityContainerController(KeyguardSecurityContainer view, AdminSecondaryLockScreenController.Factory adminSecondaryLockScreenControllerFactory, LockPatternUtils lockPatternUtils, KeyguardUpdateMonitor keyguardUpdateMonitor, @@ -378,6 +289,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard MetricsLogger metricsLogger, UiEventLogger uiEventLogger, KeyguardStateController keyguardStateController, + SecurityCallback securityCallback, KeyguardSecurityViewFlipperController securityViewFlipperController, ConfigurationController configurationController, FalsingCollector falsingCollector, @@ -387,11 +299,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard GlobalSettings globalSettings, SessionTracker sessionTracker, Optional<SideFpsController> sideFpsController, - FalsingA11yDelegate falsingA11yDelegate, - TelephonyManager telephonyManager, - ViewMediatorCallback viewMediatorCallback, - AudioManager audioManager - ) { + FalsingA11yDelegate falsingA11yDelegate) { super(view); mLockPatternUtils = lockPatternUtils; mUpdateMonitor = keyguardUpdateMonitor; @@ -399,6 +307,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard mMetricsLogger = metricsLogger; mUiEventLogger = uiEventLogger; mKeyguardStateController = keyguardStateController; + mSecurityCallback = securityCallback; mSecurityViewFlipperController = securityViewFlipperController; mAdminSecondaryLockScreenController = adminSecondaryLockScreenControllerFactory.create( mKeyguardSecurityCallback); @@ -412,15 +321,11 @@ 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(); } @@ -431,11 +336,6 @@ 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 @@ -448,11 +348,6 @@ 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(); @@ -461,7 +356,6 @@ 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() { @@ -497,22 +391,12 @@ 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) { @@ -529,32 +413,8 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard } } - /** - * 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); + public SecurityMode getCurrentSecurityMode() { + return mCurrentSecurityMode; } /** @@ -566,64 +426,17 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard mKeyguardSecurityCallback.dismiss(authenticated, targetUserId, expectedSecurityMode); } - /** - * 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); + public void reset() { + mView.reset(); + mSecurityViewFlipperController.reset(); } - /** - * @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()) { @@ -641,30 +454,6 @@ 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); @@ -676,13 +465,9 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard public boolean startDisappearAnimation(Runnable onFinishRunnable) { if (mCurrentSecurityMode != SecurityMode.None) { mView.startDisappearAnimation(mCurrentSecurityMode); - boolean animating = getCurrentSecurityController().startDisappearAnimation( - onFinishRunnable); - if (!animating && onFinishRunnable != null) { - onFinishRunnable.run(); - } - return animating; + return getCurrentSecurityController().startDisappearAnimation(onFinishRunnable); } + return false; } @@ -798,7 +583,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard mUiEventLogger.log(uiEvent, getSessionId()); } if (finish) { - mKeyguardSecurityCallback.finish(strongAuth, targetUserId); + mSecurityCallback.finish(strongAuth, targetUserId); } return finish; } @@ -811,114 +596,11 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard * @return the {@link OnBackAnimationCallback} to animate this view during a back gesture. */ @NonNull - public OnBackAnimationCallback getBackCallback() { + 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. * @@ -946,7 +628,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard configureMode(); } - mKeyguardSecurityCallback.onSecurityModeChanged( + mSecurityCallback.onSecurityModeChanged( securityMode, newView != null && newView.needsInput()); } @@ -1044,30 +726,6 @@ 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; @@ -1101,15 +759,77 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard mKeyguardSecurityCallback); } - /** - * 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); + 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); + } } } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java index d491a720b7e7..4a315d46cbd2 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, KeyguardSecurityContainer) + // (KeyguardViewMediator, KeyguardHostView) /** * 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 154b0ed2c4d1..5ad21df1e652 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.KeyguardSecurityContainerController; +import com.android.keyguard.KeyguardHostViewController; 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 KeyguardSecurityContainerController}. */ - KeyguardSecurityContainerController getSecurityContainerController(); + /** Returns a {@link KeyguardHostViewController}. */ + KeyguardHostViewController getKeyguardHostViewController(); } diff --git a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardBouncerModule.java b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardBouncerModule.java index 38f252a221eb..cb7a0a9b1653 100644 --- a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardBouncerModule.java +++ b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardBouncerModule.java @@ -23,6 +23,7 @@ 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; @@ -46,13 +47,19 @@ public interface KeyguardBouncerModule { /** */ @Provides @KeyguardBouncerScope - static KeyguardSecurityContainer providesKeyguardSecurityContainer(@RootView ViewGroup rootView, + static KeyguardHostView providesKeyguardHostView(@RootView ViewGroup rootView, LayoutInflater layoutInflater) { - KeyguardSecurityContainer securityContainer = - (KeyguardSecurityContainer) layoutInflater.inflate( - R.layout.keyguard_security_container_view, rootView, false); - rootView.addView(securityContainer); - return securityContainer; + 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); } /** */ 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 8a533151617c..9f09d53c99f3 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,12 +23,10 @@ import android.window.OnBackAnimationCallback import androidx.lifecycle.Lifecycle import androidx.lifecycle.repeatOnLifecycle import com.android.internal.policy.SystemBarUtils -import com.android.keyguard.KeyguardSecurityContainerController +import com.android.keyguard.KeyguardHostViewController 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 @@ -46,54 +44,52 @@ object KeyguardBouncerViewBinder { viewModel: KeyguardBouncerViewModel, componentFactory: KeyguardBouncerComponent.Factory ) { - // Builds the KeyguardSecurityContainerController from bouncer view group. - val securityContainerController: KeyguardSecurityContainerController = - componentFactory.create(view).securityContainerController - securityContainerController.init() + // Builds the KeyguardHostViewController from bouncer view group. + val hostViewController: KeyguardHostViewController = + componentFactory.create(view).keyguardHostViewController + hostViewController.init() val delegate = object : BouncerViewDelegate { override fun isFullScreenBouncer(): Boolean { - val mode = securityContainerController.currentSecurityMode + val mode = hostViewController.currentSecurityMode return mode == KeyguardSecurityModel.SecurityMode.SimPin || mode == KeyguardSecurityModel.SecurityMode.SimPuk } override fun getBackCallback(): OnBackAnimationCallback { - return securityContainerController.backCallback + return hostViewController.backCallback } override fun shouldDismissOnMenuPressed(): Boolean { - return securityContainerController.shouldEnableMenuKey() + return hostViewController.shouldEnableMenuKey() } override fun interceptMediaKey(event: KeyEvent?): Boolean { - return securityContainerController.interceptMediaKey(event) + return hostViewController.interceptMediaKey(event) } override fun dispatchBackKeyEventPreIme(): Boolean { - return securityContainerController.dispatchBackKeyEventPreIme() + return hostViewController.dispatchBackKeyEventPreIme() } override fun showNextSecurityScreenOrFinish(): Boolean { - return securityContainerController.dismiss( - KeyguardUpdateMonitor.getCurrentUser() - ) + return hostViewController.dismiss(KeyguardUpdateMonitor.getCurrentUser()) } override fun resume() { - securityContainerController.showPrimarySecurityScreen(/* isTurningOff= */ false) - securityContainerController.onResume(KeyguardSecurityView.SCREEN_ON) + hostViewController.showPrimarySecurityScreen() + hostViewController.onResume() } override fun setDismissAction( onDismissAction: ActivityStarter.OnDismissAction?, cancelAction: Runnable? ) { - securityContainerController.setOnDismissAction(onDismissAction, cancelAction) + hostViewController.setOnDismissAction(onDismissAction, cancelAction) } override fun willDismissWithActions(): Boolean { - return securityContainerController.hasDismissActions() + return hostViewController.hasDismissActions() } } view.repeatWhenAttached { @@ -102,46 +98,39 @@ object KeyguardBouncerViewBinder { viewModel.setBouncerViewDelegate(delegate) launch { viewModel.show.collect { - securityContainerController.showPromptReason(it.promptReason) + hostViewController.showPromptReason(it.promptReason) it.errorMessage?.let { errorMessage -> - securityContainerController.showMessage( - errorMessage, - Utils.getColorError(view.context) - ) + hostViewController.showErrorMessage(errorMessage) } - securityContainerController.showPrimarySecurityScreen( - /* turningOff= */ false - ) - securityContainerController.appear( + hostViewController.showPrimarySecurityScreen() + hostViewController.appear( SystemBarUtils.getStatusBarHeight(view.context) ) - securityContainerController.onResume(KeyguardSecurityView.SCREEN_ON) + hostViewController.onResume() } } launch { viewModel.hide.collect { - securityContainerController.cancelDismissAction() - securityContainerController.onPause() - securityContainerController.reset() + hostViewController.cancelDismissAction() + hostViewController.cleanUp() + hostViewController.resetSecurityContainer() } } launch { - viewModel.startingToHide.collect { - securityContainerController.onStartingToHide() - } + viewModel.startingToHide.collect { hostViewController.onStartingToHide() } } launch { viewModel.startDisappearAnimation.collect { - securityContainerController.startDisappearAnimation(it) + hostViewController.startDisappearAnimation(it) } } launch { viewModel.bouncerExpansionAmount.collect { expansion -> - securityContainerController.setExpansion(expansion) + hostViewController.setExpansion(expansion) } } @@ -149,8 +138,10 @@ object KeyguardBouncerViewBinder { viewModel.bouncerExpansionAmount .filter { it == EXPANSION_VISIBLE } .collect { - securityContainerController.onResume(KeyguardSecurityView.SCREEN_ON) - view.announceForAccessibility(securityContainerController.title) + hostViewController.onResume() + view.announceForAccessibility( + hostViewController.accessibilityTitleForCurrentMode + ) } } @@ -158,13 +149,13 @@ object KeyguardBouncerViewBinder { viewModel.isBouncerVisible.collect { isVisible -> val visibility = if (isVisible) View.VISIBLE else View.INVISIBLE view.visibility = visibility - securityContainerController.onBouncerVisibilityChanged(visibility) + hostViewController.onBouncerVisibilityChanged(visibility) } } launch { viewModel.isInteractable.collect { isInteractable -> - securityContainerController.setInteractable(isInteractable) + hostViewController.setInteractable(isInteractable) } } @@ -173,36 +164,33 @@ object KeyguardBouncerViewBinder { .filter { !it } .collect { // Remove existing input for security reasons. - securityContainerController.reset() + hostViewController.resetSecurityContainer() } } launch { viewModel.keyguardPosition.collect { position -> - securityContainerController.updateKeyguardPosition(position) + hostViewController.updateKeyguardPosition(position) } } launch { viewModel.updateResources.collect { - securityContainerController.updateResources() + hostViewController.updateResources() viewModel.notifyUpdateResources() } } launch { viewModel.bouncerShowMessage.collect { - securityContainerController.showMessage(it.message, it.colorStateList) + hostViewController.showMessage(it.message, it.colorStateList) viewModel.onMessageShown() } } launch { viewModel.keyguardAuthenticated.collect { - securityContainerController.finish( - it, - KeyguardUpdateMonitor.getCurrentUser() - ) + hostViewController.finish(it, KeyguardUpdateMonitor.getCurrentUser()) viewModel.notifyKeyguardAuthenticated() } } @@ -216,7 +204,7 @@ object KeyguardBouncerViewBinder { launch { viewModel.screenTurnedOff.collect { if (view.visibility == View.VISIBLE) { - securityContainerController.onPause() + hostViewController.onPause() } } } diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardHostViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardHostViewControllerTest.java new file mode 100644 index 000000000000..4021652295c1 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardHostViewControllerTest.java @@ -0,0 +1,186 @@ +/* + * 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 e39cbe1cb78c..075ef9df9664 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java @@ -23,17 +23,12 @@ 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; @@ -44,17 +39,12 @@ 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; @@ -70,7 +60,6 @@ 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; @@ -82,7 +71,6 @@ 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; @@ -120,6 +108,8 @@ public class KeyguardSecurityContainerControllerTest extends SysuiTestCase { @Mock private KeyguardInputViewController mInputViewController; @Mock + private KeyguardSecurityContainer.SecurityCallback mSecurityCallback; + @Mock private WindowInsetsController mWindowInsetsController; @Mock private KeyguardSecurityViewFlipper mSecurityViewFlipper; @@ -136,6 +126,8 @@ public class KeyguardSecurityContainerControllerTest extends SysuiTestCase { @Mock private EmergencyButtonController mEmergencyButtonController; @Mock + private Resources mResources; + @Mock private FalsingCollector mFalsingCollector; @Mock private FalsingManager mFalsingManager; @@ -155,12 +147,6 @@ 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; @@ -172,25 +158,18 @@ 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(mContext.getResources()); - FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(/* width= */ 0, /* height= */ - 0); - lp.gravity = 0; - when(mView.getLayoutParams()).thenReturn(lp); + when(mView.getResources()).thenReturn(mResources); 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); @@ -199,21 +178,20 @@ 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( + mKeyguardSecurityContainerController = new KeyguardSecurityContainerController.Factory( mView, mAdminSecondaryLockScreenControllerFactory, mLockPatternUtils, mKeyguardUpdateMonitor, mKeyguardSecurityModel, mMetricsLogger, mUiEventLogger, mKeyguardStateController, mKeyguardSecurityViewFlipperController, mConfigurationController, mFalsingCollector, mFalsingManager, mUserSwitcherController, mFeatureFlags, mGlobalSettings, - mSessionTracker, Optional.of(mSideFpsController), mFalsingA11yDelegate, - mTelephonyManager, mViewMediatorCallback, mAudioManager); + mSessionTracker, Optional.of(mSideFpsController), mFalsingA11yDelegate).create( + mSecurityCallback); } @Test @@ -265,8 +243,7 @@ public class KeyguardSecurityContainerControllerTest extends SysuiTestCase { eq(mFalsingA11yDelegate)); // Update rotation. Should trigger update - mTestableResources.getResources().getConfiguration().orientation = - Configuration.ORIENTATION_LANDSCAPE; + mConfiguration.orientation = Configuration.ORIENTATION_LANDSCAPE; mKeyguardSecurityContainerController.updateResources(); verify(mView).initMode(eq(MODE_DEFAULT), eq(mGlobalSettings), eq(mFalsingManager), @@ -300,7 +277,7 @@ public class KeyguardSecurityContainerControllerTest extends SysuiTestCase { @Test public void showSecurityScreen_oneHandedMode_flagDisabled_noOneHandedMode() { - mTestableResources.addOverride(R.bool.can_use_one_handed_bouncer, false); + when(mResources.getBoolean(R.bool.can_use_one_handed_bouncer)).thenReturn(false); when(mKeyguardSecurityViewFlipperController.getSecurityView( eq(SecurityMode.Pattern), any(KeyguardSecurityCallback.class))) .thenReturn((KeyguardInputViewController) mKeyguardPasswordViewController); @@ -314,7 +291,7 @@ public class KeyguardSecurityContainerControllerTest extends SysuiTestCase { @Test public void showSecurityScreen_oneHandedMode_flagEnabled_oneHandedMode() { - mTestableResources.addOverride(R.bool.can_use_one_handed_bouncer, true); + when(mResources.getBoolean(R.bool.can_use_one_handed_bouncer)).thenReturn(true); when(mKeyguardSecurityViewFlipperController.getSecurityView( eq(SecurityMode.Pattern), any(KeyguardSecurityCallback.class))) .thenReturn((KeyguardInputViewController) mKeyguardPasswordViewController); @@ -328,7 +305,7 @@ public class KeyguardSecurityContainerControllerTest extends SysuiTestCase { @Test public void showSecurityScreen_twoHandedMode_flagEnabled_noOneHandedMode() { - mTestableResources.addOverride(R.bool.can_use_one_handed_bouncer, true); + when(mResources.getBoolean(R.bool.can_use_one_handed_bouncer)).thenReturn(true); setupGetSecurityView(); mKeyguardSecurityContainerController.showSecurityScreen(SecurityMode.Password); @@ -505,9 +482,7 @@ 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(mViewMediatorCallback, never()).keyguardDonePending(anyBoolean(), anyInt()); - verify(mViewMediatorCallback, never()).keyguardDone(anyBoolean(), anyInt()); + verify(mSecurityCallback, never()).finish(anyBoolean(), anyInt()); assertThat(mKeyguardSecurityContainerController.getCurrentSecurityMode()) .isEqualTo(SecurityMode.PIN); } @@ -581,19 +556,17 @@ 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(eq(SecurityMode.PIN), + verify(mKeyguardSecurityViewFlipperController).getSecurityView(any(SecurityMode.class), any(KeyguardSecurityCallback.class)); } @@ -604,13 +577,11 @@ 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(eq(SecurityMode.PIN), + verify(mKeyguardSecurityViewFlipperController).getSecurityView(any(SecurityMode.class), any(KeyguardSecurityCallback.class)); } @@ -621,90 +592,14 @@ 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(eq(SecurityMode.PIN), + verify(mKeyguardSecurityViewFlipperController).getSecurityView(any(SecurityMode.class), 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()); @@ -730,7 +625,7 @@ public class KeyguardSecurityContainerControllerTest extends SysuiTestCase { } private void setSideFpsHintEnabledFromResources(boolean enabled) { - mTestableResources.addOverride(R.bool.config_show_sidefps_hint_on_bouncer, + when(mResources.getBoolean(R.bool.config_show_sidefps_hint_on_bouncer)).thenReturn( 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 d229a08ad7c4..e5d5e3b8433a 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.KeyguardSecurityContainerController +import com.android.keyguard.KeyguardHostViewController 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 keyguardSecurityContainerController: KeyguardSecurityContainerController + @Mock lateinit var keyguardHostViewController: KeyguardHostViewController @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.securityContainerController) - .thenReturn(keyguardSecurityContainerController) + whenever(keyguardBouncerComponent.keyguardHostViewController) + .thenReturn(keyguardHostViewController) 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 5e9c2199897d..5cc3ef1def9e 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.KeyguardSecurityContainerController; +import com.android.keyguard.KeyguardHostViewController; 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 KeyguardSecurityContainerController mKeyguardSecurityContainerController; + @Mock private KeyguardHostViewController mKeyguardHostViewController; @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.getSecurityContainerController()).thenReturn( - mKeyguardSecurityContainerController); + when(mKeyguardBouncerComponent.getKeyguardHostViewController()).thenReturn( + mKeyguardHostViewController); when(mStatusBarStateController.isDozing()).thenReturn(false); mDependency.injectTestDependency(ShadeController.class, mShadeController); |