blob: 700bba105232084d4fcda13ac1ac068403dab138 [file] [log] [blame]
Santos Cordondeb8c892014-05-30 01:38:03 -07001/*
2 * Copyright 2014, 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
Tyler Gunn7cc70b42014-09-12 22:17:27 -070017package com.android.server.telecom;
Santos Cordondeb8c892014-05-30 01:38:03 -070018
Santos Cordondeb8c892014-05-30 01:38:03 -070019import android.content.Context;
20import android.content.Intent;
RoboErik07920f62014-07-21 15:19:41 -070021import android.media.AudioAttributes;
Santos Cordondeb8c892014-05-30 01:38:03 -070022import android.media.session.MediaSession;
Ihab Awad731369c2015-05-19 09:23:21 -070023import android.os.Handler;
24import android.os.Looper;
25import android.os.Message;
Santos Cordondeb8c892014-05-30 01:38:03 -070026import android.view.KeyEvent;
27
28/**
29 * Static class to handle listening to the headset media buttons.
30 */
Ihab Awad8de76912015-02-17 12:25:52 -080031public class HeadsetMediaButton extends CallsManagerListenerBase {
Santos Cordondeb8c892014-05-30 01:38:03 -070032
Santos Cordondeb8c892014-05-30 01:38:03 -070033 // Types of media button presses
34 static final int SHORT_PRESS = 1;
35 static final int LONG_PRESS = 2;
36
RoboErik07920f62014-07-21 15:19:41 -070037 private static final AudioAttributes AUDIO_ATTRIBUTES = new AudioAttributes.Builder()
38 .setContentType(AudioAttributes.CONTENT_TYPE_SPEECH)
39 .setUsage(AudioAttributes.USAGE_VOICE_COMMUNICATION).build();
40
Ihab Awad731369c2015-05-19 09:23:21 -070041 private static final int MSG_MEDIA_SESSION_INITIALIZE = 0;
42 private static final int MSG_MEDIA_SESSION_SET_ACTIVE = 1;
43
Santos Cordondeb8c892014-05-30 01:38:03 -070044 private final MediaSession.Callback mSessionCallback = new MediaSession.Callback() {
45 @Override
RoboErika6e9d752014-08-07 16:10:08 -070046 public boolean onMediaButtonEvent(Intent intent) {
Santos Cordondeb8c892014-05-30 01:38:03 -070047 KeyEvent event = (KeyEvent) intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT);
48 Log.v(this, "SessionCallback.onMediaButton()... event = %s.", event);
49 if ((event != null) && (event.getKeyCode() == KeyEvent.KEYCODE_HEADSETHOOK)) {
Ihab Awad731369c2015-05-19 09:23:21 -070050 synchronized (mLock) {
51 Log.v(this, "SessionCallback: HEADSETHOOK");
52 boolean consumed = handleHeadsetHook(event);
53 Log.v(this, "==> handleHeadsetHook(): consumed = %b.", consumed);
54 return consumed;
55 }
Santos Cordondeb8c892014-05-30 01:38:03 -070056 }
RoboErika6e9d752014-08-07 16:10:08 -070057 return true;
Santos Cordondeb8c892014-05-30 01:38:03 -070058 }
59 };
60
Ihab Awad731369c2015-05-19 09:23:21 -070061 private final Handler mMediaSessionHandler = new Handler(Looper.getMainLooper()) {
62 @Override
63 public void handleMessage(Message msg) {
64 switch (msg.what) {
65 case MSG_MEDIA_SESSION_INITIALIZE: {
66 MediaSession session = new MediaSession(
67 mContext,
68 HeadsetMediaButton.class.getSimpleName());
69 session.setCallback(mSessionCallback);
70 session.setFlags(MediaSession.FLAG_EXCLUSIVE_GLOBAL_PRIORITY
71 | MediaSession.FLAG_HANDLES_MEDIA_BUTTONS);
72 session.setPlaybackToLocal(AUDIO_ATTRIBUTES);
73 mSession = session;
74 break;
75 }
76 case MSG_MEDIA_SESSION_SET_ACTIVE: {
77 if (mSession != null) {
78 boolean activate = msg.arg1 != 0;
79 if (activate != mSession.isActive()) {
80 mSession.setActive(activate);
81 }
82 }
83 break;
84 }
85 default:
86 break;
87 }
88 }
89 };
90
91 private final Context mContext;
Santos Cordondeb8c892014-05-30 01:38:03 -070092 private final CallsManager mCallsManager;
Ihab Awad731369c2015-05-19 09:23:21 -070093 private final TelecomSystem.SyncRoot mLock;
94 private MediaSession mSession;
yifan.baic73ca8d2016-04-21 16:19:12 +080095 private KeyEvent mLastHookEvent;
Santos Cordondeb8c892014-05-30 01:38:03 -070096
Ihab Awad731369c2015-05-19 09:23:21 -070097 public HeadsetMediaButton(
98 Context context,
99 CallsManager callsManager,
100 TelecomSystem.SyncRoot lock) {
101 mContext = context;
Santos Cordondeb8c892014-05-30 01:38:03 -0700102 mCallsManager = callsManager;
Ihab Awad731369c2015-05-19 09:23:21 -0700103 mLock = lock;
Santos Cordondeb8c892014-05-30 01:38:03 -0700104
RoboErikb771c432014-07-15 12:44:33 -0700105 // Create a MediaSession but don't enable it yet. This is a
Santos Cordondeb8c892014-05-30 01:38:03 -0700106 // replacement for MediaButtonReceiver
Ihab Awad731369c2015-05-19 09:23:21 -0700107 mMediaSessionHandler.obtainMessage(MSG_MEDIA_SESSION_INITIALIZE).sendToTarget();
Santos Cordondeb8c892014-05-30 01:38:03 -0700108 }
109
110 /**
111 * Handles the wired headset button while in-call.
112 *
113 * @return true if we consumed the event.
114 */
115 private boolean handleHeadsetHook(KeyEvent event) {
116 Log.d(this, "handleHeadsetHook()...%s %s", event.getAction(), event.getRepeatCount());
117
yifan.baic73ca8d2016-04-21 16:19:12 +0800118 // Save ACTION_DOWN Event temporarily.
119 if (event.getAction() == KeyEvent.ACTION_DOWN) {
120 mLastHookEvent = event;
121 }
122
Santos Cordondeb8c892014-05-30 01:38:03 -0700123 if (event.isLongPress()) {
124 return mCallsManager.onMediaButton(LONG_PRESS);
yifan.baic73ca8d2016-04-21 16:19:12 +0800125 } else if (event.getAction() == KeyEvent.ACTION_UP) {
126 // We should not judge SHORT_PRESS by ACTION_UP event repeatCount, because it always
127 // return 0.
128 // Actually ACTION_DOWN event repeatCount only increases when LONG_PRESS performed.
129 if (mLastHookEvent != null && mLastHookEvent.getRepeatCount() == 0) {
130 return mCallsManager.onMediaButton(SHORT_PRESS);
131 }
132 }
133
134 if (event.getAction() != KeyEvent.ACTION_DOWN) {
135 mLastHookEvent = null;
Santos Cordondeb8c892014-05-30 01:38:03 -0700136 }
137
138 return true;
139 }
Santos Cordon81289982014-06-03 16:03:08 -0700140
141 /** ${inheritDoc} */
142 @Override
143 public void onCallAdded(Call call) {
Tyler Gunn1a40c4f2016-04-14 14:29:45 -0700144 if (call.isExternalCall()) {
145 return;
146 }
Ihab Awad731369c2015-05-19 09:23:21 -0700147 mMediaSessionHandler.obtainMessage(MSG_MEDIA_SESSION_SET_ACTIVE, 1, 0).sendToTarget();
Santos Cordon81289982014-06-03 16:03:08 -0700148 }
149
150 /** ${inheritDoc} */
151 @Override
152 public void onCallRemoved(Call call) {
Tyler Gunnf15dc332016-06-07 16:01:41 -0700153 if (call.isExternalCall()) {
154 return;
155 }
Santos Cordon81289982014-06-03 16:03:08 -0700156 if (!mCallsManager.hasAnyCalls()) {
Ihab Awad731369c2015-05-19 09:23:21 -0700157 mMediaSessionHandler.obtainMessage(MSG_MEDIA_SESSION_SET_ACTIVE, 0, 0).sendToTarget();
Santos Cordon81289982014-06-03 16:03:08 -0700158 }
159 }
Tyler Gunn1a40c4f2016-04-14 14:29:45 -0700160
161 /** ${inheritDoc} */
162 @Override
163 public void onExternalCallChanged(Call call, boolean isExternalCall) {
164 if (isExternalCall) {
165 onCallRemoved(call);
166 } else {
167 onCallAdded(call);
168 }
169 }
Santos Cordondeb8c892014-05-30 01:38:03 -0700170}