summaryrefslogtreecommitdiffstats
path: root/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java
diff options
context:
space:
mode:
Diffstat (limited to 'packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java')
-rw-r--r--packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java428
1 files changed, 428 insertions, 0 deletions
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java
new file mode 100644
index 0000000..7fa5055
--- /dev/null
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java
@@ -0,0 +1,428 @@
+/*
+ * 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.app.Activity;
+import android.app.ActivityManager;
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Canvas;
+import android.media.AudioManager;
+import android.os.Bundle;
+import android.os.SystemClock;
+import android.telephony.TelephonyManager;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.widget.FrameLayout;
+
+import com.android.internal.widget.LockPatternUtils;
+import com.android.keyguard.KeyguardSecurityContainer.SecurityCallback;
+import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
+
+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
+ * {@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 implements SecurityCallback {
+
+ public interface OnDismissAction {
+ /**
+ * @return true if the dismiss should be deferred
+ */
+ boolean onDismiss();
+ }
+
+ private AudioManager mAudioManager;
+ private TelephonyManager mTelephonyManager = null;
+ protected ViewMediatorCallback mViewMediatorCallback;
+ protected LockPatternUtils mLockPatternUtils;
+ private OnDismissAction mDismissAction;
+
+ private final KeyguardUpdateMonitorCallback mUpdateCallback =
+ new KeyguardUpdateMonitorCallback() {
+
+ @Override
+ public void onUserSwitchComplete(int userId) {
+ getSecurityContainer().showPrimarySecurityScreen(false /* turning off */);
+ }
+
+ @Override
+ public void onTrustInitiatedByUser(int userId) {
+ if (userId != mLockPatternUtils.getCurrentUser()) return;
+ if (!isAttachedToWindow()) return;
+
+ if (isVisibleToUser()) {
+ dismiss(false /* authenticated */);
+ } else {
+ mViewMediatorCallback.playTrustedSound();
+ }
+ }
+ };
+
+ // 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";
+
+ private KeyguardSecurityContainer mSecurityContainer;
+
+ public KeyguardHostView(Context context) {
+ this(context, null);
+ }
+
+ public KeyguardHostView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ KeyguardUpdateMonitor.getInstance(context).registerCallback(mUpdateCallback);
+ }
+
+ @Override
+ protected void dispatchDraw(Canvas canvas) {
+ super.dispatchDraw(canvas);
+ if (mViewMediatorCallback != null) {
+ mViewMediatorCallback.keyguardDoneDrawing();
+ }
+ }
+
+ /**
+ * Sets an action to run when keyguard finishes.
+ *
+ * @param action
+ */
+ public void setOnDismissAction(OnDismissAction action) {
+ mDismissAction = action;
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ mSecurityContainer =
+ (KeyguardSecurityContainer) findViewById(R.id.keyguard_security_container);
+ mLockPatternUtils = new LockPatternUtils(mContext);
+ mSecurityContainer.setLockPatternUtils(mLockPatternUtils);
+ mSecurityContainer.setSecurityCallback(this);
+ mSecurityContainer.showPrimarySecurityScreen(false);
+ // mSecurityContainer.updateSecurityViews(false /* not bouncing */);
+ }
+
+ /**
+ * Called when the view needs to be shown.
+ */
+ public void showPrimarySecurityScreen() {
+ if (DEBUG) Log.d(TAG, "show()");
+ mSecurityContainer.showPrimarySecurityScreen(false);
+ }
+
+ /**
+ * Dismisses the keyguard by going to the next screen or making it gone.
+ *
+ * @return True if the keyguard is done.
+ */
+ public boolean dismiss() {
+ return dismiss(false);
+ }
+
+ public boolean handleBackKey() {
+ if (mSecurityContainer.getCurrentSecuritySelection() != SecurityMode.None) {
+ mSecurityContainer.dismiss(false);
+ return true;
+ }
+ return false;
+ }
+
+ protected KeyguardSecurityContainer getSecurityContainer() {
+ return mSecurityContainer;
+ }
+
+ @Override
+ public boolean dismiss(boolean authenticated) {
+ return mSecurityContainer.showNextSecurityScreenOrFinish(authenticated);
+ }
+
+ /**
+ * Authentication has happened and it's time to dismiss keyguard. This function
+ * should clean up and inform KeyguardViewMediator.
+ */
+ @Override
+ public void finish() {
+ // If the alternate unlock was suppressed, it can now be safely
+ // enabled because the user has left keyguard.
+ KeyguardUpdateMonitor.getInstance(mContext).setAlternateUnlockEnabled(true);
+
+ // 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;
+ }
+ if (mViewMediatorCallback != null) {
+ if (deferKeyguardDone) {
+ mViewMediatorCallback.keyguardDonePending();
+ } else {
+ mViewMediatorCallback.keyguardDone(true);
+ }
+ }
+ }
+
+ @Override
+ public void onSecurityModeChanged(SecurityMode securityMode, boolean needsInput) {
+ if (mViewMediatorCallback != null) {
+ mViewMediatorCallback.setNeedsInput(needsInput);
+ }
+ }
+
+ 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()));
+ // Once the screen turns off, we no longer consider this to be first boot and we want the
+ // biometric unlock to start next time keyguard is shown.
+ KeyguardUpdateMonitor.getInstance(mContext).setAlternateUnlockEnabled(true);
+ 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();
+ }
+ }
+
+ /**
+ * Verify that the user can get past the keyguard securely. This is called,
+ * for example, when the phone disables the keyguard but then wants to launch
+ * something else that requires secure access.
+ *
+ * The result will be propogated back via {@link KeyguardViewCallback#keyguardDone(boolean)}
+ */
+ public void verifyUnlock() {
+ SecurityMode securityMode = mSecurityContainer.getSecurityMode();
+ if (securityMode == KeyguardSecurityModel.SecurityMode.None) {
+ if (mViewMediatorCallback != null) {
+ mViewMediatorCallback.keyguardDone(true);
+ }
+ } else if (securityMode != KeyguardSecurityModel.SecurityMode.Pattern
+ && securityMode != KeyguardSecurityModel.SecurityMode.PIN
+ && securityMode != KeyguardSecurityModel.SecurityMode.Password) {
+ // can only verify unlock when in pattern/password mode
+ if (mViewMediatorCallback != null) {
+ mViewMediatorCallback.keyguardDone(false);
+ }
+ } else {
+ // otherwise, go to the unlock screen, see if they can verify it
+ mSecurityContainer.verifyUnlock();
+ }
+ }
+
+ /**
+ * Called before this view is being removed.
+ */
+ public void cleanUp() {
+ getSecurityContainer().onPause();
+ }
+
+ @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);
+ }
+
+ @Override
+ public void dispatchSystemUiVisibilityChanged(int visibility) {
+ super.dispatchSystemUiVisibilityChanged(visibility);
+
+ if (!(mContext instanceof Activity)) {
+ setSystemUiVisibility(STATUS_BAR_DISABLE_BACK);
+ }
+ }
+
+ /**
+ * 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";
+ private 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 boolean handleMenuKey() {
+ // The following enables the MENU key to work for testing automation
+ if (shouldEnableMenuKey()) {
+ dismiss();
+ return true;
+ }
+ return false;
+ }
+
+ 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();
+ }
+
+
+}