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