|  | /* | 
|  | * Copyright (C) 2008 The Android Open Source Project | 
|  | * | 
|  | * Licensed under the Apache License, Version 2.0 (the "License"); | 
|  | * you may not use this file except in compliance with the License. | 
|  | * You may obtain a copy of the License at | 
|  | * | 
|  | *      http://www.apache.org/licenses/LICENSE-2.0 | 
|  | * | 
|  | * Unless required by applicable law or agreed to in writing, software | 
|  | * distributed under the License is distributed on an "AS IS" BASIS, | 
|  | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
|  | * See the License for the specific language governing permissions and | 
|  | * limitations under the License. | 
|  | */ | 
|  |  | 
|  | package com.android.internal.policy; | 
|  |  | 
|  | import android.app.KeyguardManager; | 
|  | import android.app.SearchManager; | 
|  | import android.content.ActivityNotFoundException; | 
|  | import android.content.Context; | 
|  | import android.content.Intent; | 
|  | import android.content.res.Configuration; | 
|  | import android.media.AudioManager; | 
|  | import android.media.session.MediaSessionLegacyHelper; | 
|  | import android.os.UserHandle; | 
|  | import android.provider.Settings; | 
|  | import android.telephony.TelephonyManager; | 
|  | import android.util.Log; | 
|  | import android.view.FallbackEventHandler; | 
|  | import android.view.HapticFeedbackConstants; | 
|  | import android.view.KeyEvent; | 
|  | import android.view.View; | 
|  | import com.android.internal.policy.PhoneWindow; | 
|  |  | 
|  | /** | 
|  | * @hide | 
|  | */ | 
|  | public class PhoneFallbackEventHandler implements FallbackEventHandler { | 
|  | private static String TAG = "PhoneFallbackEventHandler"; | 
|  | private static final boolean DEBUG = false; | 
|  |  | 
|  | Context mContext; | 
|  | View mView; | 
|  |  | 
|  | AudioManager mAudioManager; | 
|  | KeyguardManager mKeyguardManager; | 
|  | SearchManager mSearchManager; | 
|  | TelephonyManager mTelephonyManager; | 
|  |  | 
|  | public PhoneFallbackEventHandler(Context context) { | 
|  | mContext = context; | 
|  | } | 
|  |  | 
|  | public void setView(View v) { | 
|  | mView = v; | 
|  | } | 
|  |  | 
|  | public void preDispatchKeyEvent(KeyEvent event) { | 
|  | getAudioManager().preDispatchKeyEvent(event, AudioManager.USE_DEFAULT_STREAM_TYPE); | 
|  | } | 
|  |  | 
|  | public boolean dispatchKeyEvent(KeyEvent event) { | 
|  |  | 
|  | final int action = event.getAction(); | 
|  | final int keyCode = event.getKeyCode(); | 
|  |  | 
|  | if (action == KeyEvent.ACTION_DOWN) { | 
|  | return onKeyDown(keyCode, event); | 
|  | } else { | 
|  | return onKeyUp(keyCode, event); | 
|  | } | 
|  | } | 
|  |  | 
|  | boolean onKeyDown(int keyCode, KeyEvent event) { | 
|  | /* **************************************************************************** | 
|  | * HOW TO DECIDE WHERE YOUR KEY HANDLING GOES. | 
|  | * See the comment in PhoneWindow.onKeyDown | 
|  | * ****************************************************************************/ | 
|  | final KeyEvent.DispatcherState dispatcher = mView.getKeyDispatcherState(); | 
|  |  | 
|  | switch (keyCode) { | 
|  | case KeyEvent.KEYCODE_VOLUME_UP: | 
|  | case KeyEvent.KEYCODE_VOLUME_DOWN: | 
|  | case KeyEvent.KEYCODE_VOLUME_MUTE: { | 
|  | MediaSessionLegacyHelper.getHelper(mContext).sendVolumeKeyEvent( | 
|  | event, AudioManager.USE_DEFAULT_STREAM_TYPE, false); | 
|  | return true; | 
|  | } | 
|  |  | 
|  |  | 
|  | case KeyEvent.KEYCODE_MEDIA_PLAY: | 
|  | case KeyEvent.KEYCODE_MEDIA_PAUSE: | 
|  | case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE: | 
|  | /* Suppress PLAY/PAUSE toggle when phone is ringing or in-call | 
|  | * to avoid music playback */ | 
|  | if (getTelephonyManager().getCallState() != TelephonyManager.CALL_STATE_IDLE) { | 
|  | return true;  // suppress key event | 
|  | } | 
|  | case KeyEvent.KEYCODE_MUTE: | 
|  | case KeyEvent.KEYCODE_HEADSETHOOK: | 
|  | case KeyEvent.KEYCODE_MEDIA_STOP: | 
|  | case KeyEvent.KEYCODE_MEDIA_NEXT: | 
|  | case KeyEvent.KEYCODE_MEDIA_PREVIOUS: | 
|  | case KeyEvent.KEYCODE_MEDIA_REWIND: | 
|  | case KeyEvent.KEYCODE_MEDIA_RECORD: | 
|  | case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD: | 
|  | case KeyEvent.KEYCODE_MEDIA_AUDIO_TRACK: { | 
|  | handleMediaKeyEvent(event); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | case KeyEvent.KEYCODE_CALL: { | 
|  | if (getKeyguardManager().inKeyguardRestrictedInputMode() || dispatcher == null) { | 
|  | break; | 
|  | } | 
|  | if (event.getRepeatCount() == 0) { | 
|  | dispatcher.startTracking(event, this); | 
|  | } else if (event.isLongPress() && dispatcher.isTracking(event)) { | 
|  | dispatcher.performedLongPress(event); | 
|  | if (isUserSetupComplete()) { | 
|  | mView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS); | 
|  | // launch the VoiceDialer | 
|  | Intent intent = new Intent(Intent.ACTION_VOICE_COMMAND); | 
|  | intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); | 
|  | try { | 
|  | sendCloseSystemWindows(); | 
|  | mContext.startActivity(intent); | 
|  | } catch (ActivityNotFoundException e) { | 
|  | startCallActivity(); | 
|  | } | 
|  | } else { | 
|  | Log.i(TAG, "Not starting call activity because user " | 
|  | + "setup is in progress."); | 
|  | } | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | case KeyEvent.KEYCODE_CAMERA: { | 
|  | if (getKeyguardManager().inKeyguardRestrictedInputMode() || dispatcher == null) { | 
|  | break; | 
|  | } | 
|  | if (event.getRepeatCount() == 0) { | 
|  | dispatcher.startTracking(event, this); | 
|  | } else if (event.isLongPress() && dispatcher.isTracking(event)) { | 
|  | dispatcher.performedLongPress(event); | 
|  | if (isUserSetupComplete()) { | 
|  | mView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS); | 
|  | sendCloseSystemWindows(); | 
|  | // Broadcast an intent that the Camera button was longpressed | 
|  | Intent intent = new Intent(Intent.ACTION_CAMERA_BUTTON, null); | 
|  | intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); | 
|  | intent.putExtra(Intent.EXTRA_KEY_EVENT, event); | 
|  | mContext.sendOrderedBroadcastAsUser(intent, UserHandle.CURRENT_OR_SELF, | 
|  | null, null, null, 0, null, null); | 
|  | } else { | 
|  | Log.i(TAG, "Not dispatching CAMERA long press because user " | 
|  | + "setup is in progress."); | 
|  | } | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | case KeyEvent.KEYCODE_SEARCH: { | 
|  | if (getKeyguardManager().inKeyguardRestrictedInputMode() || dispatcher == null) { | 
|  | break; | 
|  | } | 
|  | if (event.getRepeatCount() == 0) { | 
|  | dispatcher.startTracking(event, this); | 
|  | } else if (event.isLongPress() && dispatcher.isTracking(event)) { | 
|  | Configuration config = mContext.getResources().getConfiguration(); | 
|  | if (config.keyboard == Configuration.KEYBOARD_NOKEYS | 
|  | || config.hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_YES) { | 
|  | if (isUserSetupComplete()) { | 
|  | // launch the search activity | 
|  | Intent intent = new Intent(Intent.ACTION_SEARCH_LONG_PRESS); | 
|  | intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); | 
|  | try { | 
|  | mView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS); | 
|  | sendCloseSystemWindows(); | 
|  | getSearchManager().stopSearch(); | 
|  | mContext.startActivity(intent); | 
|  | // Only clear this if we successfully start the | 
|  | // activity; otherwise we will allow the normal short | 
|  | // press action to be performed. | 
|  | dispatcher.performedLongPress(event); | 
|  | return true; | 
|  | } catch (ActivityNotFoundException e) { | 
|  | // Ignore | 
|  | } | 
|  | } else { | 
|  | Log.i(TAG, "Not dispatching SEARCH long press because user " | 
|  | + "setup is in progress."); | 
|  | } | 
|  | } | 
|  | } | 
|  | break; | 
|  | } | 
|  | } | 
|  | return false; | 
|  | } | 
|  |  | 
|  | boolean onKeyUp(int keyCode, KeyEvent event) { | 
|  | if (DEBUG) { | 
|  | Log.d(TAG, "up " + keyCode); | 
|  | } | 
|  | final KeyEvent.DispatcherState dispatcher = mView.getKeyDispatcherState(); | 
|  | if (dispatcher != null) { | 
|  | dispatcher.handleUpEvent(event); | 
|  | } | 
|  |  | 
|  | switch (keyCode) { | 
|  | case KeyEvent.KEYCODE_VOLUME_UP: | 
|  | case KeyEvent.KEYCODE_VOLUME_DOWN: | 
|  | case KeyEvent.KEYCODE_VOLUME_MUTE: { | 
|  | if (!event.isCanceled()) { | 
|  | MediaSessionLegacyHelper.getHelper(mContext).sendVolumeKeyEvent( | 
|  | event, AudioManager.USE_DEFAULT_STREAM_TYPE, false); | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | case KeyEvent.KEYCODE_HEADSETHOOK: | 
|  | case KeyEvent.KEYCODE_MUTE: | 
|  | case KeyEvent.KEYCODE_MEDIA_PLAY: | 
|  | case KeyEvent.KEYCODE_MEDIA_PAUSE: | 
|  | case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE: | 
|  | case KeyEvent.KEYCODE_MEDIA_STOP: | 
|  | case KeyEvent.KEYCODE_MEDIA_NEXT: | 
|  | case KeyEvent.KEYCODE_MEDIA_PREVIOUS: | 
|  | case KeyEvent.KEYCODE_MEDIA_REWIND: | 
|  | case KeyEvent.KEYCODE_MEDIA_RECORD: | 
|  | case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD: | 
|  | case KeyEvent.KEYCODE_MEDIA_AUDIO_TRACK: { | 
|  | handleMediaKeyEvent(event); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | case KeyEvent.KEYCODE_CAMERA: { | 
|  | if (getKeyguardManager().inKeyguardRestrictedInputMode()) { | 
|  | break; | 
|  | } | 
|  | if (event.isTracking() && !event.isCanceled()) { | 
|  | // Add short press behavior here if desired | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | case KeyEvent.KEYCODE_CALL: { | 
|  | if (getKeyguardManager().inKeyguardRestrictedInputMode()) { | 
|  | break; | 
|  | } | 
|  | if (event.isTracking() && !event.isCanceled()) { | 
|  | if (isUserSetupComplete()) { | 
|  | startCallActivity(); | 
|  | } else { | 
|  | Log.i(TAG, "Not starting call activity because user " | 
|  | + "setup is in progress."); | 
|  | } | 
|  | } | 
|  | return true; | 
|  | } | 
|  | } | 
|  | return false; | 
|  | } | 
|  |  | 
|  | void startCallActivity() { | 
|  | sendCloseSystemWindows(); | 
|  | Intent intent = new Intent(Intent.ACTION_CALL_BUTTON); | 
|  | intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); | 
|  | try { | 
|  | mContext.startActivity(intent); | 
|  | } catch (ActivityNotFoundException e) { | 
|  | Log.w(TAG, "No activity found for android.intent.action.CALL_BUTTON."); | 
|  | } | 
|  | } | 
|  |  | 
|  | SearchManager getSearchManager() { | 
|  | if (mSearchManager == null) { | 
|  | mSearchManager = (SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE); | 
|  | } | 
|  | return mSearchManager; | 
|  | } | 
|  |  | 
|  | TelephonyManager getTelephonyManager() { | 
|  | if (mTelephonyManager == null) { | 
|  | mTelephonyManager = (TelephonyManager)mContext.getSystemService( | 
|  | Context.TELEPHONY_SERVICE); | 
|  | } | 
|  | return mTelephonyManager; | 
|  | } | 
|  |  | 
|  | KeyguardManager getKeyguardManager() { | 
|  | if (mKeyguardManager == null) { | 
|  | mKeyguardManager = (KeyguardManager)mContext.getSystemService(Context.KEYGUARD_SERVICE); | 
|  | } | 
|  | return mKeyguardManager; | 
|  | } | 
|  |  | 
|  | AudioManager getAudioManager() { | 
|  | if (mAudioManager == null) { | 
|  | mAudioManager = (AudioManager)mContext.getSystemService(Context.AUDIO_SERVICE); | 
|  | } | 
|  | return mAudioManager; | 
|  | } | 
|  |  | 
|  | void sendCloseSystemWindows() { | 
|  | PhoneWindow.sendCloseSystemWindows(mContext, null); | 
|  | } | 
|  |  | 
|  | private void handleMediaKeyEvent(KeyEvent keyEvent) { | 
|  | MediaSessionLegacyHelper.getHelper(mContext).sendMediaButtonEvent(keyEvent, false); | 
|  | } | 
|  |  | 
|  | private boolean isUserSetupComplete() { | 
|  | return Settings.Secure.getInt(mContext.getContentResolver(), | 
|  | Settings.Secure.USER_SETUP_COMPLETE, 0) != 0; | 
|  | } | 
|  | } | 
|  |  |