1/* 2 * Copyright (C) 2008 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; 18 19import android.app.KeyguardManager; 20import android.app.SearchManager; 21import android.content.ActivityNotFoundException; 22import android.content.Context; 23import android.content.Intent; 24import android.content.res.Configuration; 25import android.media.AudioManager; 26import android.media.session.MediaSessionLegacyHelper; 27import android.os.UserHandle; 28import android.provider.Settings; 29import android.telephony.TelephonyManager; 30import android.util.Log; 31import android.view.FallbackEventHandler; 32import android.view.HapticFeedbackConstants; 33import android.view.KeyEvent; 34import android.view.View; 35import com.android.internal.policy.PhoneWindow; 36 37/** 38 * @hide 39 */ 40public class PhoneFallbackEventHandler implements FallbackEventHandler { 41 private static String TAG = "PhoneFallbackEventHandler"; 42 private static final boolean DEBUG = false; 43 44 Context mContext; 45 View mView; 46 47 AudioManager mAudioManager; 48 KeyguardManager mKeyguardManager; 49 SearchManager mSearchManager; 50 TelephonyManager mTelephonyManager; 51 52 public PhoneFallbackEventHandler(Context context) { 53 mContext = context; 54 } 55 56 public void setView(View v) { 57 mView = v; 58 } 59 60 public void preDispatchKeyEvent(KeyEvent event) { 61 getAudioManager().preDispatchKeyEvent(event, AudioManager.USE_DEFAULT_STREAM_TYPE); 62 } 63 64 public boolean dispatchKeyEvent(KeyEvent event) { 65 66 final int action = event.getAction(); 67 final int keyCode = event.getKeyCode(); 68 69 if (action == KeyEvent.ACTION_DOWN) { 70 return onKeyDown(keyCode, event); 71 } else { 72 return onKeyUp(keyCode, event); 73 } 74 } 75 76 boolean onKeyDown(int keyCode, KeyEvent event) { 77 /* **************************************************************************** 78 * HOW TO DECIDE WHERE YOUR KEY HANDLING GOES. 79 * See the comment in PhoneWindow.onKeyDown 80 * ****************************************************************************/ 81 final KeyEvent.DispatcherState dispatcher = mView.getKeyDispatcherState(); 82 83 switch (keyCode) { 84 case KeyEvent.KEYCODE_VOLUME_UP: 85 case KeyEvent.KEYCODE_VOLUME_DOWN: 86 case KeyEvent.KEYCODE_VOLUME_MUTE: { 87 MediaSessionLegacyHelper.getHelper(mContext).sendVolumeKeyEvent(event, false); 88 return true; 89 } 90 91 92 case KeyEvent.KEYCODE_MEDIA_PLAY: 93 case KeyEvent.KEYCODE_MEDIA_PAUSE: 94 case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE: 95 /* Suppress PLAY/PAUSE toggle when phone is ringing or in-call 96 * to avoid music playback */ 97 if (getTelephonyManager().getCallState() != TelephonyManager.CALL_STATE_IDLE) { 98 return true; // suppress key event 99 } 100 case KeyEvent.KEYCODE_MUTE: 101 case KeyEvent.KEYCODE_HEADSETHOOK: 102 case KeyEvent.KEYCODE_MEDIA_STOP: 103 case KeyEvent.KEYCODE_MEDIA_NEXT: 104 case KeyEvent.KEYCODE_MEDIA_PREVIOUS: 105 case KeyEvent.KEYCODE_MEDIA_REWIND: 106 case KeyEvent.KEYCODE_MEDIA_RECORD: 107 case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD: 108 case KeyEvent.KEYCODE_MEDIA_AUDIO_TRACK: { 109 handleMediaKeyEvent(event); 110 return true; 111 } 112 113 case KeyEvent.KEYCODE_CALL: { 114 if (getKeyguardManager().inKeyguardRestrictedInputMode() || dispatcher == null) { 115 break; 116 } 117 if (event.getRepeatCount() == 0) { 118 dispatcher.startTracking(event, this); 119 } else if (event.isLongPress() && dispatcher.isTracking(event)) { 120 dispatcher.performedLongPress(event); 121 if (isUserSetupComplete()) { 122 mView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS); 123 // launch the VoiceDialer 124 Intent intent = new Intent(Intent.ACTION_VOICE_COMMAND); 125 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 126 try { 127 sendCloseSystemWindows(); 128 mContext.startActivity(intent); 129 } catch (ActivityNotFoundException e) { 130 startCallActivity(); 131 } 132 } else { 133 Log.i(TAG, "Not starting call activity because user " 134 + "setup is in progress."); 135 } 136 } 137 return true; 138 } 139 140 case KeyEvent.KEYCODE_CAMERA: { 141 if (getKeyguardManager().inKeyguardRestrictedInputMode() || dispatcher == null) { 142 break; 143 } 144 if (event.getRepeatCount() == 0) { 145 dispatcher.startTracking(event, this); 146 } else if (event.isLongPress() && dispatcher.isTracking(event)) { 147 dispatcher.performedLongPress(event); 148 if (isUserSetupComplete()) { 149 mView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS); 150 sendCloseSystemWindows(); 151 // Broadcast an intent that the Camera button was longpressed 152 Intent intent = new Intent(Intent.ACTION_CAMERA_BUTTON, null); 153 intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); 154 intent.putExtra(Intent.EXTRA_KEY_EVENT, event); 155 mContext.sendOrderedBroadcastAsUser(intent, UserHandle.CURRENT_OR_SELF, 156 null, null, null, 0, null, null); 157 } else { 158 Log.i(TAG, "Not dispatching CAMERA long press because user " 159 + "setup is in progress."); 160 } 161 } 162 return true; 163 } 164 165 case KeyEvent.KEYCODE_SEARCH: { 166 if (getKeyguardManager().inKeyguardRestrictedInputMode() || dispatcher == null) { 167 break; 168 } 169 if (event.getRepeatCount() == 0) { 170 dispatcher.startTracking(event, this); 171 } else if (event.isLongPress() && dispatcher.isTracking(event)) { 172 Configuration config = mContext.getResources().getConfiguration(); 173 if (config.keyboard == Configuration.KEYBOARD_NOKEYS 174 || config.hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_YES) { 175 if (isUserSetupComplete()) { 176 // launch the search activity 177 Intent intent = new Intent(Intent.ACTION_SEARCH_LONG_PRESS); 178 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 179 try { 180 mView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS); 181 sendCloseSystemWindows(); 182 getSearchManager().stopSearch(); 183 mContext.startActivity(intent); 184 // Only clear this if we successfully start the 185 // activity; otherwise we will allow the normal short 186 // press action to be performed. 187 dispatcher.performedLongPress(event); 188 return true; 189 } catch (ActivityNotFoundException e) { 190 // Ignore 191 } 192 } else { 193 Log.i(TAG, "Not dispatching SEARCH long press because user " 194 + "setup is in progress."); 195 } 196 } 197 } 198 break; 199 } 200 } 201 return false; 202 } 203 204 boolean onKeyUp(int keyCode, KeyEvent event) { 205 if (DEBUG) { 206 Log.d(TAG, "up " + keyCode); 207 } 208 final KeyEvent.DispatcherState dispatcher = mView.getKeyDispatcherState(); 209 if (dispatcher != null) { 210 dispatcher.handleUpEvent(event); 211 } 212 213 switch (keyCode) { 214 case KeyEvent.KEYCODE_VOLUME_UP: 215 case KeyEvent.KEYCODE_VOLUME_DOWN: 216 case KeyEvent.KEYCODE_VOLUME_MUTE: { 217 if (!event.isCanceled()) { 218 MediaSessionLegacyHelper.getHelper(mContext).sendVolumeKeyEvent(event, false); 219 } 220 return true; 221 } 222 223 case KeyEvent.KEYCODE_HEADSETHOOK: 224 case KeyEvent.KEYCODE_MUTE: 225 case KeyEvent.KEYCODE_MEDIA_PLAY: 226 case KeyEvent.KEYCODE_MEDIA_PAUSE: 227 case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE: 228 case KeyEvent.KEYCODE_MEDIA_STOP: 229 case KeyEvent.KEYCODE_MEDIA_NEXT: 230 case KeyEvent.KEYCODE_MEDIA_PREVIOUS: 231 case KeyEvent.KEYCODE_MEDIA_REWIND: 232 case KeyEvent.KEYCODE_MEDIA_RECORD: 233 case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD: 234 case KeyEvent.KEYCODE_MEDIA_AUDIO_TRACK: { 235 handleMediaKeyEvent(event); 236 return true; 237 } 238 239 case KeyEvent.KEYCODE_CAMERA: { 240 if (getKeyguardManager().inKeyguardRestrictedInputMode()) { 241 break; 242 } 243 if (event.isTracking() && !event.isCanceled()) { 244 // Add short press behavior here if desired 245 } 246 return true; 247 } 248 249 case KeyEvent.KEYCODE_CALL: { 250 if (getKeyguardManager().inKeyguardRestrictedInputMode()) { 251 break; 252 } 253 if (event.isTracking() && !event.isCanceled()) { 254 if (isUserSetupComplete()) { 255 startCallActivity(); 256 } else { 257 Log.i(TAG, "Not starting call activity because user " 258 + "setup is in progress."); 259 } 260 } 261 return true; 262 } 263 } 264 return false; 265 } 266 267 void startCallActivity() { 268 sendCloseSystemWindows(); 269 Intent intent = new Intent(Intent.ACTION_CALL_BUTTON); 270 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 271 try { 272 mContext.startActivity(intent); 273 } catch (ActivityNotFoundException e) { 274 Log.w(TAG, "No activity found for android.intent.action.CALL_BUTTON."); 275 } 276 } 277 278 SearchManager getSearchManager() { 279 if (mSearchManager == null) { 280 mSearchManager = (SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE); 281 } 282 return mSearchManager; 283 } 284 285 TelephonyManager getTelephonyManager() { 286 if (mTelephonyManager == null) { 287 mTelephonyManager = (TelephonyManager)mContext.getSystemService( 288 Context.TELEPHONY_SERVICE); 289 } 290 return mTelephonyManager; 291 } 292 293 KeyguardManager getKeyguardManager() { 294 if (mKeyguardManager == null) { 295 mKeyguardManager = (KeyguardManager)mContext.getSystemService(Context.KEYGUARD_SERVICE); 296 } 297 return mKeyguardManager; 298 } 299 300 AudioManager getAudioManager() { 301 if (mAudioManager == null) { 302 mAudioManager = (AudioManager)mContext.getSystemService(Context.AUDIO_SERVICE); 303 } 304 return mAudioManager; 305 } 306 307 void sendCloseSystemWindows() { 308 PhoneWindow.sendCloseSystemWindows(mContext, null); 309 } 310 311 private void handleMediaKeyEvent(KeyEvent keyEvent) { 312 MediaSessionLegacyHelper.getHelper(mContext).sendMediaButtonEvent(keyEvent, false); 313 } 314 315 private boolean isUserSetupComplete() { 316 return Settings.Secure.getInt(mContext.getContentResolver(), 317 Settings.Secure.USER_SETUP_COMPLETE, 0) != 0; 318 } 319} 320 321