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.putExtra(Intent.EXTRA_KEY_EVENT, event); 154 mContext.sendOrderedBroadcastAsUser(intent, UserHandle.CURRENT_OR_SELF, 155 null, null, null, 0, null, null); 156 } else { 157 Log.i(TAG, "Not dispatching CAMERA long press because user " 158 + "setup is in progress."); 159 } 160 } 161 return true; 162 } 163 164 case KeyEvent.KEYCODE_SEARCH: { 165 if (getKeyguardManager().inKeyguardRestrictedInputMode() || dispatcher == null) { 166 break; 167 } 168 if (event.getRepeatCount() == 0) { 169 dispatcher.startTracking(event, this); 170 } else if (event.isLongPress() && dispatcher.isTracking(event)) { 171 Configuration config = mContext.getResources().getConfiguration(); 172 if (config.keyboard == Configuration.KEYBOARD_NOKEYS 173 || config.hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_YES) { 174 if (isUserSetupComplete()) { 175 // launch the search activity 176 Intent intent = new Intent(Intent.ACTION_SEARCH_LONG_PRESS); 177 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 178 try { 179 mView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS); 180 sendCloseSystemWindows(); 181 getSearchManager().stopSearch(); 182 mContext.startActivity(intent); 183 // Only clear this if we successfully start the 184 // activity; otherwise we will allow the normal short 185 // press action to be performed. 186 dispatcher.performedLongPress(event); 187 return true; 188 } catch (ActivityNotFoundException e) { 189 // Ignore 190 } 191 } else { 192 Log.i(TAG, "Not dispatching SEARCH long press because user " 193 + "setup is in progress."); 194 } 195 } 196 } 197 break; 198 } 199 } 200 return false; 201 } 202 203 boolean onKeyUp(int keyCode, KeyEvent event) { 204 if (DEBUG) { 205 Log.d(TAG, "up " + keyCode); 206 } 207 final KeyEvent.DispatcherState dispatcher = mView.getKeyDispatcherState(); 208 if (dispatcher != null) { 209 dispatcher.handleUpEvent(event); 210 } 211 212 switch (keyCode) { 213 case KeyEvent.KEYCODE_VOLUME_UP: 214 case KeyEvent.KEYCODE_VOLUME_DOWN: 215 case KeyEvent.KEYCODE_VOLUME_MUTE: { 216 if (!event.isCanceled()) { 217 MediaSessionLegacyHelper.getHelper(mContext).sendVolumeKeyEvent(event, false); 218 } 219 return true; 220 } 221 222 case KeyEvent.KEYCODE_HEADSETHOOK: 223 case KeyEvent.KEYCODE_MUTE: 224 case KeyEvent.KEYCODE_MEDIA_PLAY: 225 case KeyEvent.KEYCODE_MEDIA_PAUSE: 226 case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE: 227 case KeyEvent.KEYCODE_MEDIA_STOP: 228 case KeyEvent.KEYCODE_MEDIA_NEXT: 229 case KeyEvent.KEYCODE_MEDIA_PREVIOUS: 230 case KeyEvent.KEYCODE_MEDIA_REWIND: 231 case KeyEvent.KEYCODE_MEDIA_RECORD: 232 case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD: 233 case KeyEvent.KEYCODE_MEDIA_AUDIO_TRACK: { 234 handleMediaKeyEvent(event); 235 return true; 236 } 237 238 case KeyEvent.KEYCODE_CAMERA: { 239 if (getKeyguardManager().inKeyguardRestrictedInputMode()) { 240 break; 241 } 242 if (event.isTracking() && !event.isCanceled()) { 243 // Add short press behavior here if desired 244 } 245 return true; 246 } 247 248 case KeyEvent.KEYCODE_CALL: { 249 if (getKeyguardManager().inKeyguardRestrictedInputMode()) { 250 break; 251 } 252 if (event.isTracking() && !event.isCanceled()) { 253 if (isUserSetupComplete()) { 254 startCallActivity(); 255 } else { 256 Log.i(TAG, "Not starting call activity because user " 257 + "setup is in progress."); 258 } 259 } 260 return true; 261 } 262 } 263 return false; 264 } 265 266 void startCallActivity() { 267 sendCloseSystemWindows(); 268 Intent intent = new Intent(Intent.ACTION_CALL_BUTTON); 269 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 270 try { 271 mContext.startActivity(intent); 272 } catch (ActivityNotFoundException e) { 273 Log.w(TAG, "No activity found for android.intent.action.CALL_BUTTON."); 274 } 275 } 276 277 SearchManager getSearchManager() { 278 if (mSearchManager == null) { 279 mSearchManager = (SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE); 280 } 281 return mSearchManager; 282 } 283 284 TelephonyManager getTelephonyManager() { 285 if (mTelephonyManager == null) { 286 mTelephonyManager = (TelephonyManager)mContext.getSystemService( 287 Context.TELEPHONY_SERVICE); 288 } 289 return mTelephonyManager; 290 } 291 292 KeyguardManager getKeyguardManager() { 293 if (mKeyguardManager == null) { 294 mKeyguardManager = (KeyguardManager)mContext.getSystemService(Context.KEYGUARD_SERVICE); 295 } 296 return mKeyguardManager; 297 } 298 299 AudioManager getAudioManager() { 300 if (mAudioManager == null) { 301 mAudioManager = (AudioManager)mContext.getSystemService(Context.AUDIO_SERVICE); 302 } 303 return mAudioManager; 304 } 305 306 void sendCloseSystemWindows() { 307 PhoneWindow.sendCloseSystemWindows(mContext, null); 308 } 309 310 private void handleMediaKeyEvent(KeyEvent keyEvent) { 311 MediaSessionLegacyHelper.getHelper(mContext).sendMediaButtonEvent(keyEvent, false); 312 } 313 314 private boolean isUserSetupComplete() { 315 return Settings.Secure.getInt(mContext.getContentResolver(), 316 Settings.Secure.USER_SETUP_COMPLETE, 0) != 0; 317 } 318} 319 320