1/* 2 * Copyright (C) 2007 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.internal.policy.impl; 18 19import android.content.Context; 20import android.content.Intent; 21import android.graphics.Canvas; 22import android.graphics.ColorFilter; 23import android.graphics.PixelFormat; 24import android.graphics.PorterDuff; 25import android.graphics.drawable.Drawable; 26import android.media.AudioManager; 27import android.media.IAudioService; 28import android.os.RemoteException; 29import android.os.ServiceManager; 30import android.telephony.TelephonyManager; 31import android.view.KeyEvent; 32import android.view.View; 33import android.view.Gravity; 34import android.widget.FrameLayout; 35import android.util.AttributeSet; 36import android.util.Log; 37import android.util.Slog; 38 39/** 40 * Base class for keyguard views. {@link #reset} is where you should 41 * reset the state of your view. Use the {@link KeyguardViewCallback} via 42 * {@link #getCallback()} to send information back (such as poking the wake lock, 43 * or finishing the keyguard). 44 * 45 * Handles intercepting of media keys that still work when the keyguard is 46 * showing. 47 */ 48public abstract class KeyguardViewBase extends FrameLayout { 49 50 private static final int BACKGROUND_COLOR = 0x70000000; 51 private KeyguardViewCallback mCallback; 52 private AudioManager mAudioManager; 53 private TelephonyManager mTelephonyManager = null; 54 // Whether the volume keys should be handled by keyguard. If true, then 55 // they will be handled here for specific media types such as music, otherwise 56 // the audio service will bring up the volume dialog. 57 private static final boolean KEYGUARD_MANAGES_VOLUME = true; 58 59 // This is a faster way to draw the background on devices without hardware acceleration 60 Drawable mBackgroundDrawable = new Drawable() { 61 @Override 62 public void draw(Canvas canvas) { 63 canvas.drawColor(BACKGROUND_COLOR, PorterDuff.Mode.SRC); 64 } 65 66 @Override 67 public void setAlpha(int alpha) { 68 } 69 70 @Override 71 public void setColorFilter(ColorFilter cf) { 72 } 73 74 @Override 75 public int getOpacity() { 76 return PixelFormat.TRANSLUCENT; 77 } 78 }; 79 80 public KeyguardViewBase(Context context, KeyguardViewCallback callback) { 81 super(context); 82 mCallback = callback; 83 resetBackground(); 84 } 85 86 public void resetBackground() { 87 setBackgroundDrawable(mBackgroundDrawable); 88 } 89 90 public KeyguardViewCallback getCallback() { 91 return mCallback; 92 } 93 94 /** 95 * Called when you need to reset the state of your view. 96 */ 97 abstract public void reset(); 98 99 /** 100 * Called when the screen turned off. 101 */ 102 abstract public void onScreenTurnedOff(); 103 104 /** 105 * Called when the screen turned on. 106 */ 107 abstract public void onScreenTurnedOn(); 108 109 /** 110 * Called when the view needs to be shown. 111 */ 112 abstract public void show(); 113 114 /** 115 * Called when a key has woken the device to give us a chance to adjust our 116 * state according the the key. We are responsible for waking the device 117 * (by poking the wake lock) once we are ready. 118 * 119 * The 'Tq' suffix is per the documentation in {@link android.view.WindowManagerPolicy}. 120 * Be sure not to take any action that takes a long time; any significant 121 * action should be posted to a handler. 122 * 123 * @param keyCode The wake key, which may be relevant for configuring the 124 * keyguard. May be {@link KeyEvent#KEYCODE_UNKNOWN} if waking for a reason 125 * other than a key press. 126 */ 127 abstract public void wakeWhenReadyTq(int keyCode); 128 129 /** 130 * Verify that the user can get past the keyguard securely. This is called, 131 * for example, when the phone disables the keyguard but then wants to launch 132 * something else that requires secure access. 133 * 134 * The result will be propogated back via {@link KeyguardViewCallback#keyguardDone(boolean)} 135 */ 136 abstract public void verifyUnlock(); 137 138 /** 139 * Called before this view is being removed. 140 */ 141 abstract public void cleanUp(); 142 143 @Override 144 public boolean dispatchKeyEvent(KeyEvent event) { 145 if (shouldEventKeepScreenOnWhileKeyguardShowing(event)) { 146 mCallback.pokeWakelock(); 147 } 148 149 if (interceptMediaKey(event)) { 150 return true; 151 } 152 return super.dispatchKeyEvent(event); 153 } 154 155 private boolean shouldEventKeepScreenOnWhileKeyguardShowing(KeyEvent event) { 156 if (event.getAction() != KeyEvent.ACTION_DOWN) { 157 return false; 158 } 159 switch (event.getKeyCode()) { 160 case KeyEvent.KEYCODE_DPAD_DOWN: 161 case KeyEvent.KEYCODE_DPAD_LEFT: 162 case KeyEvent.KEYCODE_DPAD_RIGHT: 163 case KeyEvent.KEYCODE_DPAD_UP: 164 return false; 165 default: 166 return true; 167 } 168 } 169 170 /** 171 * Allows the media keys to work when the keyguard is showing. 172 * The media keys should be of no interest to the actual keyguard view(s), 173 * so intercepting them here should not be of any harm. 174 * @param event The key event 175 * @return whether the event was consumed as a media key. 176 */ 177 private boolean interceptMediaKey(KeyEvent event) { 178 final int keyCode = event.getKeyCode(); 179 if (event.getAction() == KeyEvent.ACTION_DOWN) { 180 switch (keyCode) { 181 case KeyEvent.KEYCODE_MEDIA_PLAY: 182 case KeyEvent.KEYCODE_MEDIA_PAUSE: 183 case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE: 184 /* Suppress PLAY/PAUSE toggle when phone is ringing or 185 * in-call to avoid music playback */ 186 if (mTelephonyManager == null) { 187 mTelephonyManager = (TelephonyManager) getContext().getSystemService( 188 Context.TELEPHONY_SERVICE); 189 } 190 if (mTelephonyManager != null && 191 mTelephonyManager.getCallState() != TelephonyManager.CALL_STATE_IDLE) { 192 return true; // suppress key event 193 } 194 case KeyEvent.KEYCODE_MUTE: 195 case KeyEvent.KEYCODE_HEADSETHOOK: 196 case KeyEvent.KEYCODE_MEDIA_STOP: 197 case KeyEvent.KEYCODE_MEDIA_NEXT: 198 case KeyEvent.KEYCODE_MEDIA_PREVIOUS: 199 case KeyEvent.KEYCODE_MEDIA_REWIND: 200 case KeyEvent.KEYCODE_MEDIA_RECORD: 201 case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD: { 202 handleMediaKeyEvent(event); 203 return true; 204 } 205 206 case KeyEvent.KEYCODE_VOLUME_UP: 207 case KeyEvent.KEYCODE_VOLUME_DOWN: 208 case KeyEvent.KEYCODE_VOLUME_MUTE: { 209 if (KEYGUARD_MANAGES_VOLUME) { 210 synchronized (this) { 211 if (mAudioManager == null) { 212 mAudioManager = (AudioManager) getContext().getSystemService( 213 Context.AUDIO_SERVICE); 214 } 215 } 216 // Volume buttons should only function for music (local or remote). 217 // TODO: Actually handle MUTE. 218 mAudioManager.adjustLocalOrRemoteStreamVolume( 219 AudioManager.STREAM_MUSIC, 220 keyCode == KeyEvent.KEYCODE_VOLUME_UP 221 ? AudioManager.ADJUST_RAISE 222 : AudioManager.ADJUST_LOWER); 223 // Don't execute default volume behavior 224 return true; 225 } else { 226 return false; 227 } 228 } 229 } 230 } else if (event.getAction() == KeyEvent.ACTION_UP) { 231 switch (keyCode) { 232 case KeyEvent.KEYCODE_MUTE: 233 case KeyEvent.KEYCODE_HEADSETHOOK: 234 case KeyEvent.KEYCODE_MEDIA_PLAY: 235 case KeyEvent.KEYCODE_MEDIA_PAUSE: 236 case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE: 237 case KeyEvent.KEYCODE_MEDIA_STOP: 238 case KeyEvent.KEYCODE_MEDIA_NEXT: 239 case KeyEvent.KEYCODE_MEDIA_PREVIOUS: 240 case KeyEvent.KEYCODE_MEDIA_REWIND: 241 case KeyEvent.KEYCODE_MEDIA_RECORD: 242 case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD: { 243 handleMediaKeyEvent(event); 244 return true; 245 } 246 } 247 } 248 return false; 249 } 250 251 void handleMediaKeyEvent(KeyEvent keyEvent) { 252 IAudioService audioService = IAudioService.Stub.asInterface( 253 ServiceManager.checkService(Context.AUDIO_SERVICE)); 254 if (audioService != null) { 255 try { 256 audioService.dispatchMediaKeyEvent(keyEvent); 257 } catch (RemoteException e) { 258 Log.e("KeyguardViewBase", "dispatchMediaKeyEvent threw exception " + e); 259 } 260 } else { 261 Slog.w("KeyguardViewBase", "Unable to find IAudioService for media key event"); 262 } 263 } 264 265 @Override 266 public void dispatchSystemUiVisibilityChanged(int visibility) { 267 super.dispatchSystemUiVisibilityChanged(visibility); 268 setSystemUiVisibility(STATUS_BAR_DISABLE_BACK); 269 } 270} 271