Revert "Remove KeyguardHostView"

Revert submission 21278515-remove_host_view

Reason for revert: broke many tests

Reverted changes: /q/submissionid:21278515-remove_host_view

Change-Id: I47acf6bc271fd7f05bbb21670d67f0a120a949db
This commit is contained in:
Aaron Liu
2023-02-09 00:38:49 +00:00
committed by Android (Google) Code Review
parent 2a0091d289
commit 6737251e36
15 changed files with 1059 additions and 641 deletions

View File

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

View File

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

View File

@@ -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);
}
}

View File

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

View File

@@ -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();
}

View File

@@ -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();

View File

@@ -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);
mSecurityCallback.dismiss(authenticated, targetId, bypassSecondaryLockScreen,
expectedSecurityMode);
}
@Override
public void userActivity() {
mViewMediatorCallback.userActivity();
}
@Override
public boolean isVerifyUnlockOnly() {
return false;
}
@Override
public void reportUnlockAttempt(int userId, boolean success, int timeoutMs) {
int bouncerSide = SysUiStatsLog.KEYGUARD_BOUNCER_PASSWORD_ENTERED__SIDE__DEFAULT;
if (mView.isSidedSecurityMode()) {
@@ -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();
}
};
@@ -322,34 +262,6 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard
private boolean mBouncerVisible = false;
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);
}
/**
* @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();
}
public CharSequence getTitle() {
return mView.getTitle();
}
@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,113 +596,10 @@ 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);
}
}
}

View File

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

View File

@@ -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();
}

View File

@@ -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);
}
/** */

View File

@@ -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()
}
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}

View File

@@ -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(),

View File

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