Files
frameworks_base/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java
Dave Mankoff dc01930044 Revert^2 "7/N controllers for remaining Keyguard Password ..."
Revert submission 12656261-revert-12585643-b166448040-keyguard-message-area-NUUJOVRYJS

This also fixes the original issue introduced in this CL.
KeyguardAbsKeyInputViewController#reset would call mView#resetState
instead of its own internal resetState method. However, all the logic
from the various resetState methods had been moved from the views to
their controllers. Simply calling resetState direclty resolves the
issue (line 105 of KeyguardAbsKeyInputViewController).

Reason for revert: Fixing http://b/169231892 and http://b/169145796
Reverted Changes:
I7683b2234:Revert "4/N Setup Controller fo KeyguardSecurityCo...
I5cbe04c0c:Revert "5/N Add KeyguardSecurityViewFlipperControl...
I11dff38ad:Revert "6/N Add Controller for KeyguardPatternView...
I55c250121:Revert "7/N controllers for remaining Keyguard Pas...
Ie84234cb8:Revert "8/N Remove View Injection from KeyguardMes...
Ic62f199a5:Revert "9/N Clean Up Keyguard Class Structure"

Change-Id: Ie3669e0c9a23ffd4443ced0fb08ec754f1df55db
Fixes: 169145796
2020-09-24 11:04:51 -04:00

461 lines
18 KiB
Java

/*
* 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.service.trust.TrustAgentService;
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 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.statusbar.phone.KeyguardBouncer;
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 final KeyguardUpdateMonitorCallback mUpdateCallback =
new KeyguardUpdateMonitorCallback() {
@Override
public void onUserSwitchComplete(int userId) {
mKeyguardSecurityContainerController.showPrimarySecurityScreen(
false /* turning off */);
}
@Override
public void onTrustGrantedWithFlags(int flags, int userId) {
if (userId != KeyguardUpdateMonitor.getCurrentUser()) return;
boolean bouncerVisible = mView.isVisibleToUser();
boolean initiatedByUser =
(flags & TrustAgentService.FLAG_GRANT_TRUST_INITIATED_BY_USER) != 0;
boolean dismissKeyguard =
(flags & TrustAgentService.FLAG_GRANT_TRUST_DISMISS_KEYGUARD) != 0;
if (initiatedByUser || dismissKeyguard) {
if (mViewMediatorCallback.isScreenOn()
&& (bouncerVisible || dismissKeyguard)) {
if (!bouncerVisible) {
// 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 */, userId,
/* bypassSecondaryLockScreen */ false);
} else {
mViewMediatorCallback.playTrustedSound();
}
}
}
};
private final SecurityCallback mSecurityCallback = new SecurityCallback() {
@Override
public boolean dismiss(boolean authenticated, int targetUserId,
boolean bypassSecondaryLockScreen) {
return mKeyguardSecurityContainerController.showNextSecurityScreenOrFinish(
authenticated, targetUserId, bypassSecondaryLockScreen);
}
@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,
KeyguardSecurityContainerController keyguardSecurityContainerController,
AudioManager audioManager,
TelephonyManager telephonyManager,
ViewMediatorCallback viewMediatorCallback) {
super(view);
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mKeyguardSecurityContainerController = keyguardSecurityContainerController;
mAudioManager = audioManager;
mTelephonyManager = telephonyManager;
mViewMediatorCallback = viewMediatorCallback;
}
/** Initialize the Controller. */
public void init() {
super.init();
mKeyguardSecurityContainerController.init();
}
@Override
protected void onViewAttached() {
mView.setViewMediatorCallback(mViewMediatorCallback);
// Update ViewMediator with the current input method requirements
mViewMediatorCallback.setNeedsInput(mKeyguardSecurityContainerController.needsInput());
mKeyguardUpdateMonitor.registerCallback(mUpdateCallback);
mView.setOnKeyListener(mOnKeyListener);
mKeyguardSecurityContainerController.setSecurityCallback(mSecurityCallback);
mKeyguardSecurityContainerController.showPrimarySecurityScreen(false);
}
@Override
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);
}
/**
* 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);
}
public void setExpansion(float fraction) {
float alpha = MathUtils.map(KeyguardBouncer.ALPHA_EXPANSION_THRESHOLD, 1, 1, 0, fraction);
mView.setAlpha(MathUtils.constrain(alpha, 0f, 1f));
mView.setTranslationY(fraction * mView.getHeight());
}
/**
* When bouncer was visible and is starting to become hidden.
*/
public void onStartingToHide() {
mKeyguardSecurityContainerController.onStartingToHide();
}
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() {
if (mKeyguardSecurityContainerController.getCurrentSecurityMode()
!= SecurityMode.None) {
mKeyguardSecurityContainerController.dismiss(
false, KeyguardUpdateMonitor.getCurrentUser());
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;
}
/**
* 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);
}
}