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