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
17package com.android.internal.policy.impl;
18
19import android.app.KeyguardManager;
20import android.app.SearchManager;
21import android.content.ActivityNotFoundException;
22import android.content.Context;
23import android.content.Intent;
24import android.content.res.Configuration;
25import android.media.AudioManager;
26import android.media.IAudioService;
27import android.media.session.MediaSessionLegacyHelper;
28import android.os.RemoteException;
29import android.os.ServiceManager;
30import android.os.UserHandle;
31import android.provider.Settings;
32import android.telephony.TelephonyManager;
33import android.util.Log;
34import android.view.View;
35import android.view.HapticFeedbackConstants;
36import android.view.FallbackEventHandler;
37import android.view.KeyEvent;
38
39public class PhoneFallbackEventHandler implements FallbackEventHandler {
40    private static String TAG = "PhoneFallbackEventHandler";
41    private static final boolean DEBUG = false;
42
43    Context mContext;
44    View mView;
45
46    AudioManager mAudioManager;
47    KeyguardManager mKeyguardManager;
48    SearchManager mSearchManager;
49    TelephonyManager mTelephonyManager;
50
51    public PhoneFallbackEventHandler(Context context) {
52        mContext = context;
53    }
54
55    public void setView(View v) {
56        mView = v;
57    }
58
59    public void preDispatchKeyEvent(KeyEvent event) {
60        getAudioManager().preDispatchKeyEvent(event, AudioManager.USE_DEFAULT_STREAM_TYPE);
61    }
62
63    public boolean dispatchKeyEvent(KeyEvent event) {
64
65        final int action = event.getAction();
66        final int keyCode = event.getKeyCode();
67
68        if (action == KeyEvent.ACTION_DOWN) {
69            return onKeyDown(keyCode, event);
70        } else {
71            return onKeyUp(keyCode, event);
72        }
73    }
74
75    boolean onKeyDown(int keyCode, KeyEvent event) {
76        /* ****************************************************************************
77         * HOW TO DECIDE WHERE YOUR KEY HANDLING GOES.
78         * See the comment in PhoneWindow.onKeyDown
79         * ****************************************************************************/
80        final KeyEvent.DispatcherState dispatcher = mView.getKeyDispatcherState();
81
82        switch (keyCode) {
83            case KeyEvent.KEYCODE_VOLUME_UP:
84            case KeyEvent.KEYCODE_VOLUME_DOWN:
85            case KeyEvent.KEYCODE_VOLUME_MUTE: {
86                MediaSessionLegacyHelper.getHelper(mContext).sendVolumeKeyEvent(event, false);
87                return true;
88            }
89
90
91            case KeyEvent.KEYCODE_MEDIA_PLAY:
92            case KeyEvent.KEYCODE_MEDIA_PAUSE:
93            case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
94                /* Suppress PLAY/PAUSE toggle when phone is ringing or in-call
95                 * to avoid music playback */
96                if (getTelephonyManager().getCallState() != TelephonyManager.CALL_STATE_IDLE) {
97                    return true;  // suppress key event
98                }
99            case KeyEvent.KEYCODE_MUTE:
100            case KeyEvent.KEYCODE_HEADSETHOOK:
101            case KeyEvent.KEYCODE_MEDIA_STOP:
102            case KeyEvent.KEYCODE_MEDIA_NEXT:
103            case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
104            case KeyEvent.KEYCODE_MEDIA_REWIND:
105            case KeyEvent.KEYCODE_MEDIA_RECORD:
106            case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD:
107            case KeyEvent.KEYCODE_MEDIA_AUDIO_TRACK: {
108                handleMediaKeyEvent(event);
109                return true;
110            }
111
112            case KeyEvent.KEYCODE_CALL: {
113                if (getKeyguardManager().inKeyguardRestrictedInputMode() || dispatcher == null) {
114                    break;
115                }
116                if (event.getRepeatCount() == 0) {
117                    dispatcher.startTracking(event, this);
118                } else if (event.isLongPress() && dispatcher.isTracking(event)) {
119                    dispatcher.performedLongPress(event);
120                    if (isUserSetupComplete()) {
121                        mView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
122                        // launch the VoiceDialer
123                        Intent intent = new Intent(Intent.ACTION_VOICE_COMMAND);
124                        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
125                        try {
126                            sendCloseSystemWindows();
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 (getKeyguardManager().inKeyguardRestrictedInputMode() || dispatcher == null) {
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                        sendCloseSystemWindows();
150                        // Broadcast an intent that the Camera button was longpressed
151                        Intent intent = new Intent(Intent.ACTION_CAMERA_BUTTON, null);
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 (getKeyguardManager().inKeyguardRestrictedInputMode() || dispatcher == null) {
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                                sendCloseSystemWindows();
180                                getSearchManager().stopSearch();
181                                mContext.startActivity(intent);
182                                // Only clear this if we successfully start the
183                                // activity; otherwise we will allow the normal short
184                                // press action to be performed.
185                                dispatcher.performedLongPress(event);
186                                return true;
187                            } catch (ActivityNotFoundException e) {
188                                // Ignore
189                            }
190                        } else {
191                            Log.i(TAG, "Not dispatching SEARCH long press because user "
192                                    + "setup is in progress.");
193                        }
194                    }
195                }
196                break;
197            }
198        }
199        return false;
200    }
201
202    boolean onKeyUp(int keyCode, KeyEvent event) {
203        if (DEBUG) {
204            Log.d(TAG, "up " + keyCode);
205        }
206        final KeyEvent.DispatcherState dispatcher = mView.getKeyDispatcherState();
207        if (dispatcher != null) {
208            dispatcher.handleUpEvent(event);
209        }
210
211        switch (keyCode) {
212            case KeyEvent.KEYCODE_VOLUME_UP:
213            case KeyEvent.KEYCODE_VOLUME_DOWN:
214            case KeyEvent.KEYCODE_VOLUME_MUTE: {
215                if (!event.isCanceled()) {
216                    MediaSessionLegacyHelper.getHelper(mContext).sendVolumeKeyEvent(event, false);
217                }
218                return true;
219            }
220
221            case KeyEvent.KEYCODE_HEADSETHOOK:
222            case KeyEvent.KEYCODE_MUTE:
223            case KeyEvent.KEYCODE_MEDIA_PLAY:
224            case KeyEvent.KEYCODE_MEDIA_PAUSE:
225            case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
226            case KeyEvent.KEYCODE_MEDIA_STOP:
227            case KeyEvent.KEYCODE_MEDIA_NEXT:
228            case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
229            case KeyEvent.KEYCODE_MEDIA_REWIND:
230            case KeyEvent.KEYCODE_MEDIA_RECORD:
231            case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD:
232            case KeyEvent.KEYCODE_MEDIA_AUDIO_TRACK: {
233                handleMediaKeyEvent(event);
234                return true;
235            }
236
237            case KeyEvent.KEYCODE_CAMERA: {
238                if (getKeyguardManager().inKeyguardRestrictedInputMode()) {
239                    break;
240                }
241                if (event.isTracking() && !event.isCanceled()) {
242                    // Add short press behavior here if desired
243                }
244                return true;
245            }
246
247            case KeyEvent.KEYCODE_CALL: {
248                if (getKeyguardManager().inKeyguardRestrictedInputMode()) {
249                    break;
250                }
251                if (event.isTracking() && !event.isCanceled()) {
252                    if (isUserSetupComplete()) {
253                        startCallActivity();
254                    } else {
255                        Log.i(TAG, "Not starting call activity because user "
256                                + "setup is in progress.");
257                    }
258                }
259                return true;
260            }
261        }
262        return false;
263    }
264
265    void startCallActivity() {
266        sendCloseSystemWindows();
267        Intent intent = new Intent(Intent.ACTION_CALL_BUTTON);
268        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
269        try {
270            mContext.startActivity(intent);
271        } catch (ActivityNotFoundException e) {
272            Log.w(TAG, "No activity found for android.intent.action.CALL_BUTTON.");
273        }
274    }
275
276    SearchManager getSearchManager() {
277        if (mSearchManager == null) {
278            mSearchManager = (SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE);
279        }
280        return mSearchManager;
281    }
282
283    TelephonyManager getTelephonyManager() {
284        if (mTelephonyManager == null) {
285            mTelephonyManager = (TelephonyManager)mContext.getSystemService(
286                    Context.TELEPHONY_SERVICE);
287        }
288        return mTelephonyManager;
289    }
290
291    KeyguardManager getKeyguardManager() {
292        if (mKeyguardManager == null) {
293            mKeyguardManager = (KeyguardManager)mContext.getSystemService(Context.KEYGUARD_SERVICE);
294        }
295        return mKeyguardManager;
296    }
297
298    AudioManager getAudioManager() {
299        if (mAudioManager == null) {
300            mAudioManager = (AudioManager)mContext.getSystemService(Context.AUDIO_SERVICE);
301        }
302        return mAudioManager;
303    }
304
305    void sendCloseSystemWindows() {
306        PhoneWindowManager.sendCloseSystemWindows(mContext, null);
307    }
308
309    private void handleMediaKeyEvent(KeyEvent keyEvent) {
310        MediaSessionLegacyHelper.getHelper(mContext).sendMediaButtonEvent(keyEvent, false);
311    }
312
313    private boolean isUserSetupComplete() {
314        return Settings.Secure.getInt(mContext.getContentResolver(),
315                Settings.Secure.USER_SETUP_COMPLETE, 0) != 0;
316    }
317}
318
319