diff options
| author | 2020-08-31 16:59:58 -0400 | |
|---|---|---|
| committer | 2020-09-08 14:47:43 -0400 | |
| commit | 860a2c0343354be81c8b845b1ee3075c0c5ca10e (patch) | |
| tree | f1d872e09b1b3d6037e5d6822505aeb1143f9f19 | |
| parent | f6032e30d71f51d8f5b5c037ec500b4aa589b731 (diff) | |
3/N Use KeyguardSecurityContainerController in KHVC.
Use the KeyguardSecurityContainerController in the
KeyguardHostViewController instead of the view directly.
This actually cleans up KeyguardHostView quite a bit,
with all lot of its business logic moved over to its
view.
The KeyguardSecurityContainerController doesn't do much
except to proxy through calls to its view for now.
Bug: 166448040
Test: atest SystemUITests && manual
Change-Id: I96a27b673c4579983bb07b7fb7ef321a022f0f65
| -rw-r--r-- | packages/SystemUI/src/com/android/keyguard/KeyguardHostView.java | 359 | ||||
| -rw-r--r-- | packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java | 298 | ||||
| -rw-r--r-- | packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java | 73 | ||||
| -rw-r--r-- | packages/SystemUI/tests/src/com/android/keyguard/KeyguardHostViewControllerTest.java (renamed from packages/SystemUI/tests/src/com/android/keyguard/KeyguardHostViewTest.java) | 44 |
4 files changed, 360 insertions, 414 deletions
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardHostView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardHostView.java index 8fa4bf6cd237..08e9cf60a65a 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardHostView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardHostView.java @@ -16,30 +16,11 @@ package com.android.keyguard; -import android.app.ActivityManager; import android.content.Context; -import android.content.res.ColorStateList; -import android.content.res.Resources; import android.graphics.Canvas; -import android.media.AudioManager; -import android.os.SystemClock; -import android.telephony.TelephonyManager; import android.util.AttributeSet; -import android.util.Log; -import android.view.KeyEvent; import android.widget.FrameLayout; -import androidx.annotation.VisibleForTesting; - -import com.android.internal.widget.LockPatternUtils; -import com.android.keyguard.KeyguardSecurityContainer.SecurityCallback; -import com.android.keyguard.KeyguardSecurityModel.SecurityMode; -import com.android.settingslib.Utils; -import com.android.systemui.R; -import com.android.systemui.plugins.ActivityStarter.OnDismissAction; - -import java.io.File; - /** * Base class for keyguard view. {@link #reset} is where you should * reset the state of your view. Use the {@link KeyguardViewCallback} via @@ -49,26 +30,11 @@ import java.io.File; * Handles intercepting of media keys that still work when the keyguard is * showing. */ -public class KeyguardHostView extends FrameLayout implements SecurityCallback { +public class KeyguardHostView extends FrameLayout { - private AudioManager mAudioManager; - private TelephonyManager mTelephonyManager = null; protected ViewMediatorCallback mViewMediatorCallback; - protected LockPatternUtils mLockPatternUtils; - private OnDismissAction mDismissAction; - private Runnable mCancelAction; - // 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; - public static final boolean DEBUG = KeyguardConstants.DEBUG; - private static final String TAG = "KeyguardViewBase"; - - @VisibleForTesting - protected KeyguardSecurityContainer mSecurityContainer; - public KeyguardHostView(Context context) { this(context, null); } @@ -85,330 +51,7 @@ public class KeyguardHostView extends FrameLayout implements SecurityCallback { } } - /** - * Sets an action to run when keyguard finishes. - * - * @param action - */ - public void setOnDismissAction(OnDismissAction action, Runnable cancelAction) { - if (mCancelAction != null) { - mCancelAction.run(); - mCancelAction = null; - } - mDismissAction = action; - mCancelAction = cancelAction; - } - - public boolean hasDismissActions() { - return mDismissAction != null || mCancelAction != null; - } - - public void cancelDismissAction() { - setOnDismissAction(null, null); - } - - @Override - protected void onFinishInflate() { - mSecurityContainer = - findViewById(R.id.keyguard_security_container); - mLockPatternUtils = new LockPatternUtils(mContext); - mSecurityContainer.setLockPatternUtils(mLockPatternUtils); - mSecurityContainer.setSecurityCallback(this); - mSecurityContainer.showPrimarySecurityScreen(false); - } - - /** - * Called when the view needs to be shown. - */ - public void showPrimarySecurityScreen() { - if (DEBUG) Log.d(TAG, "show()"); - mSecurityContainer.showPrimarySecurityScreen(false); - } - - public KeyguardSecurityView getCurrentSecurityView() { - return mSecurityContainer != null ? mSecurityContainer.getCurrentSecurityView() : null; - } - - /** - * 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) { - mSecurityContainer.showPromptReason(reason); - } - - public void showMessage(CharSequence message, ColorStateList colorState) { - mSecurityContainer.showMessage(message, colorState); - } - - public void showErrorMessage(CharSequence message) { - showMessage(message, Utils.getColorError(mContext)); - } - - /** - * 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 dismiss(false, targetUserId, false); - } - - public boolean handleBackKey() { - if (mSecurityContainer.getCurrentSecuritySelection() != SecurityMode.None) { - mSecurityContainer.dismiss(false, KeyguardUpdateMonitor.getCurrentUser()); - return true; - } - return false; - } - - protected KeyguardSecurityContainer getSecurityContainer() { - return mSecurityContainer; - } - - @Override - public boolean dismiss(boolean authenticated, int targetUserId, - boolean bypassSecondaryLockScreen) { - return mSecurityContainer.showNextSecurityScreenOrFinish(authenticated, targetUserId, - bypassSecondaryLockScreen); - } - - /** - * 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(); - } - - public void resetSecurityContainer() { - mSecurityContainer.reset(); - } - - @Override - public void onSecurityModeChanged(SecurityMode securityMode, boolean needsInput) { - if (mViewMediatorCallback != null) { - mViewMediatorCallback.setNeedsInput(needsInput); - } - } - - public CharSequence getAccessibilityTitleForCurrentMode() { - return mSecurityContainer.getTitle(); - } - - public void userActivity() { - if (mViewMediatorCallback != null) { - mViewMediatorCallback.userActivity(); - } - } - - /** - * 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())); - mSecurityContainer.showPrimarySecurityScreen(true); - mSecurityContainer.onPause(); - clearFocus(); - } - - /** - * Called when the Keyguard is actively shown on the screen. - */ - public void onResume() { - if (DEBUG) Log.d(TAG, "screen on, instance " + Integer.toHexString(hashCode())); - mSecurityContainer.onResume(KeyguardSecurityView.SCREEN_ON); - requestFocus(); - } - - /** - * Starts the animation when the Keyguard gets shown. - */ - public void startAppearAnimation() { - mSecurityContainer.startAppearAnimation(); - } - - public void startDisappearAnimation(Runnable finishRunnable) { - if (!mSecurityContainer.startDisappearAnimation(finishRunnable) && finishRunnable != null) { - finishRunnable.run(); - } - } - - @Override - public boolean dispatchKeyEvent(KeyEvent event) { - if (interceptMediaKey(event)) { - return true; - } - return super.dispatchKeyEvent(event); - } - - /** - * 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) { - final 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 = (TelephonyManager) getContext().getSystemService( - Context.TELEPHONY_SERVICE); - } - 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) { - synchronized (this) { - if (mAudioManager == null) { - mAudioManager = (AudioManager) getContext().getSystemService( - Context.AUDIO_SERVICE); - } - } - // 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) { - synchronized (this) { - if (mAudioManager == null) { - mAudioManager = (AudioManager) getContext().getSystemService( - Context.AUDIO_SERVICE); - } - } - 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 - */ - private static final String ENABLE_MENU_KEY_FILE = "/data/local/enable_menu_key"; - public boolean shouldEnableMenuKey() { - final Resources res = 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; - } - public void setViewMediatorCallback(ViewMediatorCallback viewMediatorCallback) { mViewMediatorCallback = viewMediatorCallback; - // Update ViewMediator with the current input method requirements - mViewMediatorCallback.setNeedsInput(mSecurityContainer.needsInput()); - } - - public void setLockPatternUtils(LockPatternUtils utils) { - mLockPatternUtils = utils; - mSecurityContainer.setLockPatternUtils(utils); - } - - public SecurityMode getSecurityMode() { - return mSecurityContainer.getSecurityMode(); - } - - public SecurityMode getCurrentSecurityMode() { - return mSecurityContainer.getCurrentSecurityMode(); - } - - /** - * When bouncer was visible and is starting to become hidden. - */ - public void onStartingToHide() { - mSecurityContainer.onStartingToHide(); } } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java index 381071994e7b..7aabb17de90c 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java @@ -16,39 +16,60 @@ 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.internal.widget.LockPatternUtils; +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 LockPatternUtils mLockPatternUtils; + 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) { - mView.getSecurityContainer().showPrimarySecurityScreen(false /* turning off */); + mKeyguardSecurityContainerController.showPrimarySecurityScreen( + false /* turning off */); } @Override @@ -70,7 +91,7 @@ public class KeyguardHostViewController extends ViewController<KeyguardHostView> //agent. Log.i(TAG, "TrustAgent dismissed Keyguard."); } - mView.dismiss(false /* authenticated */, userId, + mSecurityCallback.dismiss(false /* authenticated */, userId, /* bypassSecondaryLockScreen */ false); } else { mViewMediatorCallback.playTrustedSound(); @@ -79,35 +100,102 @@ public class KeyguardHostViewController extends ViewController<KeyguardHostView> } }; + 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, - LockPatternUtils lockPatternUtils, + AudioManager audioManager, + TelephonyManager telephonyManager, ViewMediatorCallback viewMediatorCallback) { super(view); mKeyguardUpdateMonitor = keyguardUpdateMonitor; mKeyguardSecurityContainerController = keyguardSecurityContainerController; - mLockPatternUtils = lockPatternUtils; + mAudioManager = audioManager; + mTelephonyManager = telephonyManager; mViewMediatorCallback = viewMediatorCallback; } /** Initialize the Controller. */ public void init() { super.init(); - mView.setLockPatternUtils(mLockPatternUtils); mView.setViewMediatorCallback(mViewMediatorCallback); + // Update ViewMediator with the current input method requirements + mViewMediatorCallback.setNeedsInput(mKeyguardSecurityContainerController.needsInput()); mKeyguardSecurityContainerController.init(); + mKeyguardSecurityContainerController.setSecurityCallback(mSecurityCallback); + mKeyguardSecurityContainerController.showPrimarySecurityScreen(false); } @Override protected void onViewAttached() { mKeyguardUpdateMonitor.registerCallback(mUpdateCallback); + mView.setOnKeyListener(mOnKeyListener); } @Override protected void onViewDetached() { mKeyguardUpdateMonitor.removeCallback(mUpdateCallback); + mView.setOnKeyListener(null); } /** Called before this view is being removed. */ @@ -116,37 +204,46 @@ public class KeyguardHostViewController extends ViewController<KeyguardHostView> } public void resetSecurityContainer() { - mView.resetSecurityContainer(); + mKeyguardSecurityContainerController.reset(); } - public boolean dismiss(int activeUserId) { - return mView.dismiss(activeUserId); + /** + * 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() { - mView.onResume(); + if (DEBUG) Log.d(TAG, "screen on, instance " + Integer.toHexString(hashCode())); + mKeyguardSecurityContainerController.onResume(KeyguardSecurityView.SCREEN_ON); + mView.requestFocus(); } public CharSequence getAccessibilityTitleForCurrentMode() { - return mView.getAccessibilityTitleForCurrentMode(); - } - - public void showErrorMessage(CharSequence customMessage) { - mView.showErrorMessage(customMessage); + 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) { - mView.startAppearAnimation(); + mKeyguardSecurityContainerController.startAppearAnimation(); } else { mView.getViewTreeObserver().addOnPreDrawListener( new ViewTreeObserver.OnPreDrawListener() { @Override public boolean onPreDraw() { mView.getViewTreeObserver().removeOnPreDrawListener(this); - mView.startAppearAnimation(); + mKeyguardSecurityContainerController.startAppearAnimation(); return true; } }); @@ -154,32 +251,71 @@ public class KeyguardHostViewController extends ViewController<KeyguardHostView> } } + /** + * 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) { - mView.showPromptReason(reason); + mKeyguardSecurityContainerController.showPromptReason(reason); } - public void showMessage(String message, ColorStateList colorState) { - mView.showMessage(message, colorState); + 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) { - mView.setOnDismissAction(action, cancelAction); + if (mCancelAction != null) { + mCancelAction.run(); + mCancelAction = null; + } + mDismissAction = action; + mCancelAction = cancelAction; } public void cancelDismissAction() { - mView.cancelDismissAction(); + setOnDismissAction(null, null); } - public void startDisappearAnimation(Runnable runnable) { - mView.startDisappearAnimation(runnable); + 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() { - mView.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() { - mView.showPrimarySecurityScreen(); + if (DEBUG) Log.d(TAG, "show()"); + mKeyguardSecurityContainerController.showPrimarySecurityScreen(false); } public void setExpansion(float fraction) { @@ -188,16 +324,19 @@ public class KeyguardHostViewController extends ViewController<KeyguardHostView> mView.setTranslationY(fraction * mView.getHeight()); } + /** + * When bouncer was visible and is starting to become hidden. + */ public void onStartingToHide() { - mView.onStartingToHide(); + mKeyguardSecurityContainerController.onStartingToHide(); } public boolean hasDismissActions() { - return mView.hasDismissActions(); + return mDismissAction != null || mCancelAction != null; } public SecurityMode getCurrentSecurityMode() { - return mView.getCurrentSecurityMode(); + return mKeyguardSecurityContainerController.getCurrentSecurityMode(); } public int getTop() { @@ -211,19 +350,110 @@ public class KeyguardHostViewController extends ViewController<KeyguardHostView> } public boolean handleBackKey() { - return mView.handleBackKey(); + if (mKeyguardSecurityContainerController.getCurrentSecuritySelection() + != 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() { - return mView.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) { - return mView.interceptMediaKey(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) { - mView.finish(strongAuth, currentUser); + mSecurityCallback.finish(strongAuth, currentUser); } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java index 30480a15b437..17f25bd08ef4 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java @@ -16,6 +16,11 @@ package com.android.keyguard; +import android.content.res.ColorStateList; + +import com.android.internal.widget.LockPatternUtils; +import com.android.keyguard.KeyguardSecurityContainer.SecurityCallback; +import com.android.keyguard.KeyguardSecurityModel.SecurityMode; import com.android.systemui.util.ViewController; import javax.inject.Inject; @@ -23,27 +28,91 @@ import javax.inject.Inject; /** Controller for {@link KeyguardSecurityContainer} */ public class KeyguardSecurityContainerController extends ViewController<KeyguardSecurityContainer> { + private final LockPatternUtils mLockPatternUtils; private final KeyguardSecurityViewController.Factory mKeyguardSecurityViewControllerFactory; @Inject KeyguardSecurityContainerController(KeyguardSecurityContainer view, + LockPatternUtils lockPatternUtils, KeyguardSecurityViewController.Factory keyguardSecurityViewControllerFactory) { super(view); + mLockPatternUtils = lockPatternUtils; + view.setLockPatternUtils(mLockPatternUtils); mKeyguardSecurityViewControllerFactory = keyguardSecurityViewControllerFactory; } @Override protected void onViewAttached() { - } @Override protected void onViewDetached() { - } /** */ public void onPause() { mView.onPause(); } + + public void showPrimarySecurityScreen(boolean turningOff) { + mView.showPrimarySecurityScreen(turningOff); + } + + public void showPromptReason(int reason) { + mView.showPromptReason(reason); + } + + public void showMessage(CharSequence message, ColorStateList colorState) { + mView.showMessage(message, colorState); + } + + public SecurityMode getCurrentSecuritySelection() { + return mView.getCurrentSecuritySelection(); + } + + public void dismiss(boolean authenticated, int targetUserId) { + mView.dismiss(authenticated, targetUserId); + } + + public void reset() { + mView.reset(); + } + + public CharSequence getTitle() { + return mView.getTitle(); + } + + public void onResume(int screenOn) { + mView.onResume(screenOn); + } + + public void startAppearAnimation() { + mView.startAppearAnimation(); + } + + public boolean startDisappearAnimation(Runnable onFinishRunnable) { + return mView.startDisappearAnimation(onFinishRunnable); + } + + public void onStartingToHide() { + mView.onStartingToHide(); + } + + public void setSecurityCallback(SecurityCallback securityCallback) { + mView.setSecurityCallback(securityCallback); + } + + public boolean showNextSecurityScreenOrFinish(boolean authenticated, int targetUserId, + boolean bypassSecondaryLockScreen) { + return mView.showNextSecurityScreenOrFinish( + authenticated, targetUserId, bypassSecondaryLockScreen); + } + + public boolean needsInput() { + return mView.needsInput(); + } + + public SecurityMode getCurrentSecurityMode() { + return mView.getCurrentSecurityMode(); + } } diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardHostViewTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardHostViewControllerTest.java index dd5c8335eefa..54e879e2ff38 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardHostViewTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardHostViewControllerTest.java @@ -11,7 +11,7 @@ * 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 + * limitations under the License. */ package com.android.keyguard; @@ -19,11 +19,12 @@ package com.android.keyguard; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; +import android.media.AudioManager; +import android.telephony.TelephonyManager; import android.test.suitebuilder.annotation.SmallTest; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; -import com.android.internal.widget.LockPatternUtils; import com.android.systemui.SysuiTestCase; import com.android.systemui.plugins.ActivityStarter.OnDismissAction; @@ -39,41 +40,44 @@ import org.mockito.junit.MockitoRule; @SmallTest @RunWith(AndroidTestingRunner.class) @TestableLooper.RunWithLooper -public class KeyguardHostViewTest extends SysuiTestCase { +public class KeyguardHostViewControllerTest extends SysuiTestCase { @Mock - private KeyguardSecurityContainer mSecurityContainer; + private KeyguardUpdateMonitor mKeyguardUpdateMonitor; @Mock - private LockPatternUtils mLockPatternUtils; + private KeyguardHostView mKeyguardHostView; + @Mock + private KeyguardSecurityContainerController mKeyguardSecurityContainerController; + @Mock + private AudioManager mAudioManager; + @Mock + private TelephonyManager mTelephonyManager; + @Mock + private ViewMediatorCallback mViewMediatorCallback; + @Rule public MockitoRule mMockitoRule = MockitoJUnit.rule(); - private KeyguardHostView mKeyguardHostView; + private KeyguardHostViewController mKeyguardHostViewController; @Before public void setup() { - mDependency.injectMockDependency(KeyguardUpdateMonitor.class); - mKeyguardHostView = new KeyguardHostView(getContext()) { - @Override - protected void onFinishInflate() { - mSecurityContainer = KeyguardHostViewTest.this.mSecurityContainer; - mLockPatternUtils = KeyguardHostViewTest.this.mLockPatternUtils; - } - }; - mKeyguardHostView.onFinishInflate(); + mKeyguardHostViewController = new KeyguardHostViewController( + mKeyguardHostView, mKeyguardUpdateMonitor, mKeyguardSecurityContainerController, + mAudioManager, mTelephonyManager, mViewMediatorCallback); } @Test public void testHasDismissActions() { - Assert.assertFalse("Action not set yet", mKeyguardHostView.hasDismissActions()); - mKeyguardHostView.setOnDismissAction(mock(OnDismissAction.class), + Assert.assertFalse("Action not set yet", mKeyguardHostViewController.hasDismissActions()); + mKeyguardHostViewController.setOnDismissAction(mock(OnDismissAction.class), null /* cancelAction */); - Assert.assertTrue("Action should exist", mKeyguardHostView.hasDismissActions()); + Assert.assertTrue("Action should exist", mKeyguardHostViewController.hasDismissActions()); } @Test public void testOnStartingToHide() { - mKeyguardHostView.onStartingToHide(); - verify(mSecurityContainer).onStartingToHide(); + mKeyguardHostViewController.onStartingToHide(); + verify(mKeyguardSecurityContainerController).onStartingToHide(); } } |