/* * 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.internal.policy.impl.keyguard; import android.app.Activity; import android.content.Context; import android.graphics.Canvas; import android.graphics.ColorFilter; import android.graphics.PixelFormat; import android.graphics.PorterDuff; import android.graphics.drawable.Drawable; import android.media.AudioManager; import android.media.IAudioService; import android.os.RemoteException; import android.os.ServiceManager; import android.telephony.TelephonyManager; import android.util.AttributeSet; import android.util.Log; import android.util.Slog; import android.view.KeyEvent; 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 abstract class KeyguardViewBase extends FrameLayout { private static final int BACKGROUND_COLOR = 0x70000000; private AudioManager mAudioManager; private TelephonyManager mTelephonyManager = null; protected KeyguardViewMediator.ViewMediatorCallback mViewMediatorCallback; // 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 = true; // This is a faster way to draw the background on devices without hardware acceleration private static final Drawable mBackgroundDrawable = new Drawable() { @Override public void draw(Canvas canvas) { canvas.drawColor(BACKGROUND_COLOR, PorterDuff.Mode.SRC); } @Override public void setAlpha(int alpha) { } @Override public void setColorFilter(ColorFilter cf) { } @Override public int getOpacity() { return PixelFormat.TRANSLUCENT; } }; public KeyguardViewBase(Context context) { this(context, null); } public KeyguardViewBase(Context context, AttributeSet attrs) { super(context, attrs); resetBackground(); } public void resetBackground() { setBackground(mBackgroundDrawable); } /** * Called when you need to reset the state of your view. */ abstract public void reset(); /** * Called when the screen turned off. */ abstract public void onScreenTurnedOff(); /** * Called when the screen turned on. */ abstract public void onScreenTurnedOn(); /** * Called when the view needs to be shown. */ abstract public void show(); /** * Called when a key has woken the device to give us a chance to adjust our * state according the the key. We are responsible for waking the device * (by poking the wake lock) once we are ready. * * The 'Tq' suffix is per the documentation in {@link android.view.WindowManagerPolicy}. * Be sure not to take any action that takes a long time; any significant * action should be posted to a handler. * * @param keyCode The wake key, which may be relevant for configuring the * keyguard. May be {@link KeyEvent#KEYCODE_UNKNOWN} if waking for a reason * other than a key press. */ abstract public void wakeWhenReadyTq(int keyCode); /** * 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)} */ abstract public void verifyUnlock(); /** * Called before this view is being removed. */ abstract public void cleanUp(); /** * Gets the desired user activity timeout in milliseconds, or -1 if the * default should be used. */ abstract public long getUserActivityTimeout(); @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. */ private 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: { 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.adjustLocalOrRemoteStreamVolume( AudioManager.STREAM_MUSIC, keyCode == KeyEvent.KEYCODE_VOLUME_UP ? AudioManager.ADJUST_RAISE : AudioManager.ADJUST_LOWER); // 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: { handleMediaKeyEvent(event); return true; } } } return false; } void handleMediaKeyEvent(KeyEvent keyEvent) { IAudioService audioService = IAudioService.Stub.asInterface( ServiceManager.checkService(Context.AUDIO_SERVICE)); if (audioService != null) { try { audioService.dispatchMediaKeyEvent(keyEvent); } catch (RemoteException e) { Log.e("KeyguardViewBase", "dispatchMediaKeyEvent threw exception " + e); } } else { Slog.w("KeyguardViewBase", "Unable to find IAudioService for media key event"); } } @Override public void dispatchSystemUiVisibilityChanged(int visibility) { super.dispatchSystemUiVisibilityChanged(visibility); if (!(mContext instanceof Activity)) { setSystemUiVisibility(STATUS_BAR_DISABLE_BACK); } } public void setViewMediatorCallback( KeyguardViewMediator.ViewMediatorCallback viewMediatorCallback) { mViewMediatorCallback = viewMediatorCallback; } }