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