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