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