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