blob: 274071a19a578cda6cecb5d4e98c2ce736f821ce [file] [log] [blame]
Eric Erfanianccca3152017-02-22 16:32:36 -08001/*
2 * Copyright (C) 2013 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.incallui;
18
erfanian9dfd6a42018-04-17 15:01:17 -070019import android.annotation.TargetApi;
Eric Erfanianccca3152017-02-22 16:32:36 -080020import android.content.Context;
erfanian9dfd6a42018-04-17 15:01:17 -070021import android.os.Build.VERSION_CODES;
Eric Erfanianccca3152017-02-22 16:32:36 -080022import android.os.Bundle;
Eric Erfanian2ca43182017-08-31 06:57:16 -070023import android.os.Trace;
Eric Erfanianccca3152017-02-22 16:32:36 -080024import android.support.v4.app.Fragment;
25import android.support.v4.os.UserManagerCompat;
26import android.telecom.CallAudioState;
twyen00623aa2017-10-10 12:15:08 -070027import android.telecom.PhoneAccountHandle;
Eric Erfanianccca3152017-02-22 16:32:36 -080028import com.android.contacts.common.compat.CallCompat;
29import com.android.dialer.common.Assert;
30import com.android.dialer.common.LogUtil;
twyen00623aa2017-10-10 12:15:08 -070031import com.android.dialer.common.concurrent.DialerExecutorComponent;
Eric Erfanian8369df02017-05-03 10:27:13 -070032import com.android.dialer.logging.DialerImpression;
twyenfc1f9cf2017-11-27 13:22:48 -080033import com.android.dialer.logging.DialerImpression.Type;
Eric Erfanianccca3152017-02-22 16:32:36 -080034import com.android.dialer.logging.Logger;
twyen00623aa2017-10-10 12:15:08 -070035import com.android.dialer.telecom.TelecomUtil;
Eric Erfanianccca3152017-02-22 16:32:36 -080036import com.android.incallui.InCallCameraManager.Listener;
37import com.android.incallui.InCallPresenter.CanAddCallListener;
38import com.android.incallui.InCallPresenter.InCallDetailsListener;
39import com.android.incallui.InCallPresenter.InCallState;
40import com.android.incallui.InCallPresenter.InCallStateListener;
41import com.android.incallui.InCallPresenter.IncomingCallListener;
Eric Erfanian8369df02017-05-03 10:27:13 -070042import com.android.incallui.audiomode.AudioModeProvider;
43import com.android.incallui.audiomode.AudioModeProvider.AudioModeListener;
Eric Erfanianccca3152017-02-22 16:32:36 -080044import com.android.incallui.call.CallList;
45import com.android.incallui.call.DialerCall;
Eric Erfaniand5e47f62017-03-15 14:41:07 -070046import com.android.incallui.call.DialerCall.CameraDirection;
Eric Erfanianccca3152017-02-22 16:32:36 -080047import com.android.incallui.call.TelecomAdapter;
wangqibb94ca62018-04-27 14:34:04 -070048import com.android.incallui.call.state.DialerCallState;
Eric Erfanianccca3152017-02-22 16:32:36 -080049import com.android.incallui.incall.protocol.InCallButtonIds;
50import com.android.incallui.incall.protocol.InCallButtonUi;
51import com.android.incallui.incall.protocol.InCallButtonUiDelegate;
twyen00623aa2017-10-10 12:15:08 -070052import com.android.incallui.multisim.SwapSimWorker;
Eric Erfanian90508232017-03-24 09:31:16 -070053import com.android.incallui.videotech.utils.VideoUtils;
Eric Erfanianccca3152017-02-22 16:32:36 -080054
55/** Logic for call buttons. */
56public class CallButtonPresenter
57 implements InCallStateListener,
58 AudioModeListener,
59 IncomingCallListener,
60 InCallDetailsListener,
61 CanAddCallListener,
62 Listener,
63 InCallButtonUiDelegate {
64
yuegd1a269b2018-05-14 16:57:37 -070065 private static final String KEY_AUTOMATICALLY_MUTED_BY_ADD_CALL =
66 "incall_key_automatically_muted_by_add_call";
Eric Erfanianccca3152017-02-22 16:32:36 -080067 private static final String KEY_PREVIOUS_MUTE_STATE = "incall_key_previous_mute_state";
68
linyuh183cb712017-12-27 17:02:37 -080069 private final Context context;
70 private InCallButtonUi inCallButtonUi;
71 private DialerCall call;
yuegd1a269b2018-05-14 16:57:37 -070072 private boolean automaticallyMutedByAddCall = false;
linyuh183cb712017-12-27 17:02:37 -080073 private boolean previousMuteState = false;
Eric Erfanianccca3152017-02-22 16:32:36 -080074 private boolean isInCallButtonUiReady;
linyuh183cb712017-12-27 17:02:37 -080075 private PhoneAccountHandle otherAccount;
Eric Erfanianccca3152017-02-22 16:32:36 -080076
77 public CallButtonPresenter(Context context) {
linyuh183cb712017-12-27 17:02:37 -080078 this.context = context.getApplicationContext();
Eric Erfanianccca3152017-02-22 16:32:36 -080079 }
80
81 @Override
82 public void onInCallButtonUiReady(InCallButtonUi ui) {
83 Assert.checkState(!isInCallButtonUiReady);
linyuh183cb712017-12-27 17:02:37 -080084 inCallButtonUi = ui;
Eric Erfanianccca3152017-02-22 16:32:36 -080085 AudioModeProvider.getInstance().addListener(this);
86
87 // register for call state changes last
88 final InCallPresenter inCallPresenter = InCallPresenter.getInstance();
89 inCallPresenter.addListener(this);
90 inCallPresenter.addIncomingCallListener(this);
91 inCallPresenter.addDetailsListener(this);
92 inCallPresenter.addCanAddCallListener(this);
93 inCallPresenter.getInCallCameraManager().addCameraSelectionListener(this);
94
95 // Update the buttons state immediately for the current call
96 onStateChange(InCallState.NO_CALLS, inCallPresenter.getInCallState(), CallList.getInstance());
97 isInCallButtonUiReady = true;
98 }
99
100 @Override
101 public void onInCallButtonUiUnready() {
102 Assert.checkState(isInCallButtonUiReady);
linyuh183cb712017-12-27 17:02:37 -0800103 inCallButtonUi = null;
Eric Erfanianccca3152017-02-22 16:32:36 -0800104 InCallPresenter.getInstance().removeListener(this);
105 AudioModeProvider.getInstance().removeListener(this);
106 InCallPresenter.getInstance().removeIncomingCallListener(this);
107 InCallPresenter.getInstance().removeDetailsListener(this);
108 InCallPresenter.getInstance().getInCallCameraManager().removeCameraSelectionListener(this);
109 InCallPresenter.getInstance().removeCanAddCallListener(this);
110 isInCallButtonUiReady = false;
111 }
112
113 @Override
114 public void onStateChange(InCallState oldState, InCallState newState, CallList callList) {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700115 Trace.beginSection("CallButtonPresenter.onStateChange");
Eric Erfanianccca3152017-02-22 16:32:36 -0800116 if (newState == InCallState.OUTGOING) {
linyuh183cb712017-12-27 17:02:37 -0800117 call = callList.getOutgoingCall();
Eric Erfanianccca3152017-02-22 16:32:36 -0800118 } else if (newState == InCallState.INCALL) {
linyuh183cb712017-12-27 17:02:37 -0800119 call = callList.getActiveOrBackgroundCall();
Eric Erfanianccca3152017-02-22 16:32:36 -0800120
121 // When connected to voice mail, automatically shows the dialpad.
122 // (On previous releases we showed it when in-call shows up, before waiting for
123 // OUTGOING. We may want to do that once we start showing "Voice mail" label on
124 // the dialpad too.)
linyuh183cb712017-12-27 17:02:37 -0800125 if (oldState == InCallState.OUTGOING && call != null) {
126 if (call.isVoiceMailNumber() && getActivity() != null) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800127 getActivity().showDialpadFragment(true /* show */, true /* animate */);
128 }
129 }
130 } else if (newState == InCallState.INCOMING) {
131 if (getActivity() != null) {
132 getActivity().showDialpadFragment(false /* show */, true /* animate */);
133 }
linyuh183cb712017-12-27 17:02:37 -0800134 call = callList.getIncomingCall();
Eric Erfanianccca3152017-02-22 16:32:36 -0800135 } else {
linyuh183cb712017-12-27 17:02:37 -0800136 call = null;
Eric Erfanianccca3152017-02-22 16:32:36 -0800137 }
linyuh183cb712017-12-27 17:02:37 -0800138 updateUi(newState, call);
Eric Erfanian2ca43182017-08-31 06:57:16 -0700139 Trace.endSection();
Eric Erfanianccca3152017-02-22 16:32:36 -0800140 }
141
142 /**
143 * Updates the user interface in response to a change in the details of a call. Currently handles
144 * changes to the call buttons in response to a change in the details for a call. This is
145 * important to ensure changes to the active call are reflected in the available buttons.
146 *
147 * @param call The active call.
148 * @param details The call details.
149 */
150 @Override
151 public void onDetailsChanged(DialerCall call, android.telecom.Call.Details details) {
152 // Only update if the changes are for the currently active call
linyuh183cb712017-12-27 17:02:37 -0800153 if (inCallButtonUi != null && call != null && call.equals(this.call)) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800154 updateButtonsState(call);
155 }
156 }
157
158 @Override
159 public void onIncomingCall(InCallState oldState, InCallState newState, DialerCall call) {
160 onStateChange(oldState, newState, CallList.getInstance());
161 }
162
163 @Override
164 public void onCanAddCallChanged(boolean canAddCall) {
linyuh183cb712017-12-27 17:02:37 -0800165 if (inCallButtonUi != null && call != null) {
166 updateButtonsState(call);
Eric Erfanianccca3152017-02-22 16:32:36 -0800167 }
168 }
169
170 @Override
171 public void onAudioStateChanged(CallAudioState audioState) {
linyuh183cb712017-12-27 17:02:37 -0800172 if (inCallButtonUi != null) {
173 inCallButtonUi.setAudioState(audioState);
Eric Erfanianccca3152017-02-22 16:32:36 -0800174 }
175 }
176
177 @Override
178 public CallAudioState getCurrentAudioState() {
179 return AudioModeProvider.getInstance().getAudioState();
180 }
181
182 @Override
183 public void setAudioRoute(int route) {
184 LogUtil.i(
185 "CallButtonPresenter.setAudioRoute",
186 "sending new audio route: " + CallAudioState.audioRouteToString(route));
187 TelecomAdapter.getInstance().setAudioRoute(route);
188 }
189
190 /** Function assumes that bluetooth is not supported. */
191 @Override
192 public void toggleSpeakerphone() {
193 // This function should not be called if bluetooth is available.
194 CallAudioState audioState = getCurrentAudioState();
195 if (0 != (CallAudioState.ROUTE_BLUETOOTH & audioState.getSupportedRouteMask())) {
196 // It's clear the UI is wrong, so update the supported mode once again.
197 LogUtil.e(
198 "CallButtonPresenter", "toggling speakerphone not allowed when bluetooth supported.");
linyuh183cb712017-12-27 17:02:37 -0800199 inCallButtonUi.setAudioState(audioState);
Eric Erfanianccca3152017-02-22 16:32:36 -0800200 return;
201 }
202
203 int newRoute;
204 if (audioState.getRoute() == CallAudioState.ROUTE_SPEAKER) {
205 newRoute = CallAudioState.ROUTE_WIRED_OR_EARPIECE;
linyuh183cb712017-12-27 17:02:37 -0800206 Logger.get(context)
Eric Erfanianccca3152017-02-22 16:32:36 -0800207 .logCallImpression(
208 DialerImpression.Type.IN_CALL_SCREEN_TURN_ON_WIRED_OR_EARPIECE,
linyuh183cb712017-12-27 17:02:37 -0800209 call.getUniqueCallId(),
210 call.getTimeAddedMs());
Eric Erfanianccca3152017-02-22 16:32:36 -0800211 } else {
212 newRoute = CallAudioState.ROUTE_SPEAKER;
linyuh183cb712017-12-27 17:02:37 -0800213 Logger.get(context)
Eric Erfanianccca3152017-02-22 16:32:36 -0800214 .logCallImpression(
215 DialerImpression.Type.IN_CALL_SCREEN_TURN_ON_SPEAKERPHONE,
linyuh183cb712017-12-27 17:02:37 -0800216 call.getUniqueCallId(),
217 call.getTimeAddedMs());
Eric Erfanianccca3152017-02-22 16:32:36 -0800218 }
219
220 setAudioRoute(newRoute);
221 }
222
223 @Override
Eric Erfanian9a090c82017-03-16 19:22:24 -0700224 public void muteClicked(boolean checked, boolean clickedByUser) {
225 LogUtil.i(
226 "CallButtonPresenter", "turning on mute: %s, clicked by user: %s", checked, clickedByUser);
227 if (clickedByUser) {
linyuh183cb712017-12-27 17:02:37 -0800228 Logger.get(context)
Eric Erfanian9a090c82017-03-16 19:22:24 -0700229 .logCallImpression(
230 checked
231 ? DialerImpression.Type.IN_CALL_SCREEN_TURN_ON_MUTE
232 : DialerImpression.Type.IN_CALL_SCREEN_TURN_OFF_MUTE,
linyuh183cb712017-12-27 17:02:37 -0800233 call.getUniqueCallId(),
234 call.getTimeAddedMs());
Eric Erfanian9a090c82017-03-16 19:22:24 -0700235 }
Eric Erfanianccca3152017-02-22 16:32:36 -0800236 TelecomAdapter.getInstance().mute(checked);
237 }
238
239 @Override
240 public void holdClicked(boolean checked) {
linyuh183cb712017-12-27 17:02:37 -0800241 if (call == null) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800242 return;
243 }
244 if (checked) {
linyuh183cb712017-12-27 17:02:37 -0800245 LogUtil.i("CallButtonPresenter", "putting the call on hold: " + call);
246 call.hold();
Eric Erfanianccca3152017-02-22 16:32:36 -0800247 } else {
linyuh183cb712017-12-27 17:02:37 -0800248 LogUtil.i("CallButtonPresenter", "removing the call from hold: " + call);
249 call.unhold();
Eric Erfanianccca3152017-02-22 16:32:36 -0800250 }
251 }
252
253 @Override
254 public void swapClicked() {
linyuh183cb712017-12-27 17:02:37 -0800255 if (call == null) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800256 return;
257 }
258
linyuh183cb712017-12-27 17:02:37 -0800259 LogUtil.i("CallButtonPresenter", "swapping the call: " + call);
260 TelecomAdapter.getInstance().swap(call.getId());
Eric Erfanianccca3152017-02-22 16:32:36 -0800261 }
262
263 @Override
264 public void mergeClicked() {
linyuh183cb712017-12-27 17:02:37 -0800265 Logger.get(context)
Eric Erfanian2ca43182017-08-31 06:57:16 -0700266 .logCallImpression(
267 DialerImpression.Type.IN_CALL_MERGE_BUTTON_PRESSED,
linyuh183cb712017-12-27 17:02:37 -0800268 call.getUniqueCallId(),
269 call.getTimeAddedMs());
270 TelecomAdapter.getInstance().merge(call.getId());
Eric Erfanianccca3152017-02-22 16:32:36 -0800271 }
272
273 @Override
274 public void addCallClicked() {
linyuh183cb712017-12-27 17:02:37 -0800275 Logger.get(context)
Eric Erfanian2ca43182017-08-31 06:57:16 -0700276 .logCallImpression(
277 DialerImpression.Type.IN_CALL_ADD_CALL_BUTTON_PRESSED,
linyuh183cb712017-12-27 17:02:37 -0800278 call.getUniqueCallId(),
279 call.getTimeAddedMs());
yuegd1a269b2018-05-14 16:57:37 -0700280 if (automaticallyMutedByAddCall) {
281 // Since clicking add call button brings user to MainActivity and coming back refreshes mute
282 // state, add call button should only be clicked once during InCallActivity shows. Otherwise,
283 // we set previousMuteState wrong.
284 return;
285 }
Eric Erfanianccca3152017-02-22 16:32:36 -0800286 // Automatically mute the current call
yuegd1a269b2018-05-14 16:57:37 -0700287 automaticallyMutedByAddCall = true;
linyuh183cb712017-12-27 17:02:37 -0800288 previousMuteState = AudioModeProvider.getInstance().getAudioState().isMuted();
Eric Erfanianccca3152017-02-22 16:32:36 -0800289 // Simulate a click on the mute button
Eric Erfanian9a090c82017-03-16 19:22:24 -0700290 muteClicked(true /* checked */, false /* clickedByUser */);
Eric Erfanianccca3152017-02-22 16:32:36 -0800291 TelecomAdapter.getInstance().addCall();
292 }
293
294 @Override
295 public void showDialpadClicked(boolean checked) {
linyuh183cb712017-12-27 17:02:37 -0800296 Logger.get(context)
Eric Erfanian2ca43182017-08-31 06:57:16 -0700297 .logCallImpression(
298 DialerImpression.Type.IN_CALL_SHOW_DIALPAD_BUTTON_PRESSED,
linyuh183cb712017-12-27 17:02:37 -0800299 call.getUniqueCallId(),
300 call.getTimeAddedMs());
Eric Erfanianccca3152017-02-22 16:32:36 -0800301 LogUtil.v("CallButtonPresenter", "show dialpad " + String.valueOf(checked));
302 getActivity().showDialpadFragment(checked /* show */, true /* animate */);
303 }
304
305 @Override
306 public void changeToVideoClicked() {
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700307 LogUtil.enterBlock("CallButtonPresenter.changeToVideoClicked");
linyuh183cb712017-12-27 17:02:37 -0800308 Logger.get(context)
Eric Erfanian8369df02017-05-03 10:27:13 -0700309 .logCallImpression(
310 DialerImpression.Type.VIDEO_CALL_UPGRADE_REQUESTED,
linyuh183cb712017-12-27 17:02:37 -0800311 call.getUniqueCallId(),
312 call.getTimeAddedMs());
313 call.getVideoTech().upgradeToVideo(context);
Eric Erfanianccca3152017-02-22 16:32:36 -0800314 }
315
316 @Override
wangqif6be6172018-03-30 15:57:56 -0700317 public void changeToRttClicked() {
318 LogUtil.enterBlock("CallButtonPresenter.changeToRttClicked");
319 call.sendRttUpgradeRequest();
320 }
321
322 @Override
Eric Erfanianccca3152017-02-22 16:32:36 -0800323 public void onEndCallClicked() {
linyuh183cb712017-12-27 17:02:37 -0800324 LogUtil.i("CallButtonPresenter.onEndCallClicked", "call: " + call);
325 if (call != null) {
326 call.disconnect();
Eric Erfanianccca3152017-02-22 16:32:36 -0800327 }
328 }
329
330 @Override
331 public void showAudioRouteSelector() {
linyuh183cb712017-12-27 17:02:37 -0800332 inCallButtonUi.showAudioRouteSelector();
Eric Erfanianccca3152017-02-22 16:32:36 -0800333 }
334
twyen00623aa2017-10-10 12:15:08 -0700335 @Override
336 public void swapSimClicked() {
337 LogUtil.enterBlock("CallButtonPresenter.swapSimClicked");
twyenfc1f9cf2017-11-27 13:22:48 -0800338 Logger.get(getContext()).logImpression(Type.DUAL_SIM_CHANGE_SIM_PRESSED);
twyen00623aa2017-10-10 12:15:08 -0700339 SwapSimWorker worker =
340 new SwapSimWorker(
341 getContext(),
linyuh183cb712017-12-27 17:02:37 -0800342 call,
twyen00623aa2017-10-10 12:15:08 -0700343 InCallPresenter.getInstance().getCallList(),
linyuh183cb712017-12-27 17:02:37 -0800344 otherAccount,
twyen00623aa2017-10-10 12:15:08 -0700345 InCallPresenter.getInstance().acquireInCallUiLock("swapSim"));
346 DialerExecutorComponent.get(getContext())
347 .dialerExecutorFactory()
348 .createNonUiTaskBuilder(worker)
349 .build()
350 .executeParallel(null);
351 }
352
Eric Erfanianccca3152017-02-22 16:32:36 -0800353 /**
354 * Switches the camera between the front-facing and back-facing camera.
355 *
356 * @param useFrontFacingCamera True if we should switch to using the front-facing camera, or false
357 * if we should switch to using the back-facing camera.
358 */
359 @Override
360 public void switchCameraClicked(boolean useFrontFacingCamera) {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700361 updateCamera(useFrontFacingCamera);
Eric Erfanianccca3152017-02-22 16:32:36 -0800362 }
363
364 @Override
365 public void toggleCameraClicked() {
366 LogUtil.i("CallButtonPresenter.toggleCameraClicked", "");
linyuh183cb712017-12-27 17:02:37 -0800367 if (call == null) {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700368 return;
369 }
linyuh183cb712017-12-27 17:02:37 -0800370 Logger.get(context)
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700371 .logCallImpression(
372 DialerImpression.Type.IN_CALL_SCREEN_SWAP_CAMERA,
linyuh183cb712017-12-27 17:02:37 -0800373 call.getUniqueCallId(),
374 call.getTimeAddedMs());
Eric Erfanianccca3152017-02-22 16:32:36 -0800375 switchCameraClicked(
376 !InCallPresenter.getInstance().getInCallCameraManager().isUsingFrontFacingCamera());
377 }
378
379 /**
380 * Stop or start client's video transmission.
381 *
382 * @param pause True if pausing the local user's video, or false if starting the local user's
383 * video.
384 */
385 @Override
386 public void pauseVideoClicked(boolean pause) {
387 LogUtil.i("CallButtonPresenter.pauseVideoClicked", "%s", pause ? "pause" : "unpause");
Eric Erfanianccca3152017-02-22 16:32:36 -0800388
linyuh183cb712017-12-27 17:02:37 -0800389 Logger.get(context)
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700390 .logCallImpression(
391 pause
392 ? DialerImpression.Type.IN_CALL_SCREEN_TURN_OFF_VIDEO
393 : DialerImpression.Type.IN_CALL_SCREEN_TURN_ON_VIDEO,
linyuh183cb712017-12-27 17:02:37 -0800394 call.getUniqueCallId(),
395 call.getTimeAddedMs());
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700396
Eric Erfanianccca3152017-02-22 16:32:36 -0800397 if (pause) {
linyuh183cb712017-12-27 17:02:37 -0800398 call.getVideoTech().setCamera(null);
399 call.getVideoTech().stopTransmission();
Eric Erfanianccca3152017-02-22 16:32:36 -0800400 } else {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700401 updateCamera(
402 InCallPresenter.getInstance().getInCallCameraManager().isUsingFrontFacingCamera());
linyuh183cb712017-12-27 17:02:37 -0800403 call.getVideoTech().resumeTransmission(context);
Eric Erfanianccca3152017-02-22 16:32:36 -0800404 }
405
linyuh183cb712017-12-27 17:02:37 -0800406 inCallButtonUi.setVideoPaused(pause);
407 inCallButtonUi.enableButton(InCallButtonIds.BUTTON_PAUSE_VIDEO, false);
Eric Erfanianccca3152017-02-22 16:32:36 -0800408 }
409
Eric Erfanian2ca43182017-08-31 06:57:16 -0700410 private void updateCamera(boolean useFrontFacingCamera) {
411 InCallCameraManager cameraManager = InCallPresenter.getInstance().getInCallCameraManager();
412 cameraManager.setUseFrontFacingCamera(useFrontFacingCamera);
413
414 String cameraId = cameraManager.getActiveCameraId();
415 if (cameraId != null) {
416 final int cameraDir =
417 cameraManager.isUsingFrontFacingCamera()
418 ? CameraDirection.CAMERA_DIRECTION_FRONT_FACING
419 : CameraDirection.CAMERA_DIRECTION_BACK_FACING;
linyuh183cb712017-12-27 17:02:37 -0800420 call.setCameraDir(cameraDir);
421 call.getVideoTech().setCamera(cameraId);
Eric Erfanian2ca43182017-08-31 06:57:16 -0700422 }
423 }
424
Eric Erfanianccca3152017-02-22 16:32:36 -0800425 private void updateUi(InCallState state, DialerCall call) {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700426 LogUtil.v("CallButtonPresenter", "updating call UI for call: %s", call);
Eric Erfanianccca3152017-02-22 16:32:36 -0800427
linyuh183cb712017-12-27 17:02:37 -0800428 if (inCallButtonUi == null) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800429 return;
430 }
431
Eric Erfanianccca3152017-02-22 16:32:36 -0800432 final boolean isEnabled =
433 state.isConnectingOrConnected() && !state.isIncoming() && call != null;
linyuh183cb712017-12-27 17:02:37 -0800434 inCallButtonUi.setEnabled(isEnabled);
Eric Erfanianccca3152017-02-22 16:32:36 -0800435
436 if (call == null) {
437 return;
438 }
439
440 updateButtonsState(call);
441 }
442
443 /**
444 * Updates the buttons applicable for the UI.
445 *
446 * @param call The active call.
447 */
erfanian9dfd6a42018-04-17 15:01:17 -0700448 @TargetApi(VERSION_CODES.N)
449 @SuppressWarnings(value = {"MissingPermission", "AndroidApiChecker"}) // Java 8 APIs.
Eric Erfanianccca3152017-02-22 16:32:36 -0800450 private void updateButtonsState(DialerCall call) {
451 LogUtil.v("CallButtonPresenter.updateButtonsState", "");
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700452 final boolean isVideo = call.isVideoCall();
Eric Erfanianccca3152017-02-22 16:32:36 -0800453
454 // Common functionality (audio, hold, etc).
455 // Show either HOLD or SWAP, but not both. If neither HOLD or SWAP is available:
456 // (1) If the device normally can hold, show HOLD in a disabled state.
457 // (2) If the device doesn't have the concept of hold/swap, remove the button.
458 final boolean showSwap = call.can(android.telecom.Call.Details.CAPABILITY_SWAP_CONFERENCE);
459 final boolean showHold =
460 !showSwap
461 && call.can(android.telecom.Call.Details.CAPABILITY_SUPPORT_HOLD)
462 && call.can(android.telecom.Call.Details.CAPABILITY_HOLD);
wangqibb94ca62018-04-27 14:34:04 -0700463 final boolean isCallOnHold = call.getState() == DialerCallState.ONHOLD;
Eric Erfanianccca3152017-02-22 16:32:36 -0800464
465 final boolean showAddCall =
linyuh183cb712017-12-27 17:02:37 -0800466 TelecomAdapter.getInstance().canAddCall() && UserManagerCompat.isUserUnlocked(context);
erfanian9dfd6a42018-04-17 15:01:17 -0700467 // There can only be two calls so don't show the ability to merge when one of them
468 // is a speak easy call.
469 final boolean showMerge =
470 InCallPresenter.getInstance()
471 .getCallList()
472 .getAllCalls()
473 .stream()
474 .noneMatch(c -> c != null && c.isSpeakEasyCall())
475 && call.can(android.telecom.Call.Details.CAPABILITY_MERGE_CONFERENCE);
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700476 final boolean showUpgradeToVideo = !isVideo && (hasVideoCallCapabilities(call));
Eric Erfanianccca3152017-02-22 16:32:36 -0800477 final boolean showDowngradeToAudio = isVideo && isDowngradeToAudioSupported(call);
478 final boolean showMute = call.can(android.telecom.Call.Details.CAPABILITY_MUTE);
479
480 final boolean hasCameraPermission =
linyuh183cb712017-12-27 17:02:37 -0800481 isVideo && VideoUtils.hasCameraPermissionAndShownPrivacyToast(context);
Eric Erfanian938468d2017-10-24 14:05:52 -0700482 // Disabling local video doesn't seem to work when dialing. See a bug.
Eric Erfanianccca3152017-02-22 16:32:36 -0800483 final boolean showPauseVideo =
484 isVideo
wangqibb94ca62018-04-27 14:34:04 -0700485 && call.getState() != DialerCallState.DIALING
486 && call.getState() != DialerCallState.CONNECTING;
Eric Erfanianccca3152017-02-22 16:32:36 -0800487
linyuh183cb712017-12-27 17:02:37 -0800488 otherAccount = TelecomUtil.getOtherAccount(getContext(), call.getAccountHandle());
twyen38af6512017-11-22 17:12:49 -0800489 boolean showSwapSim =
twyenea3b1622018-04-24 10:45:08 -0700490 !call.isEmergencyCall()
491 && otherAccount != null
twyen2c6803d2017-11-28 11:41:49 -0800492 && !call.isVoiceMailNumber()
wangqibb94ca62018-04-27 14:34:04 -0700493 && DialerCallState.isDialing(call.getState())
twyen38af6512017-11-22 17:12:49 -0800494 // Most devices cannot make calls on 2 SIMs at the same time.
495 && InCallPresenter.getInstance().getCallList().getAllCalls().size() == 1;
twyen00623aa2017-10-10 12:15:08 -0700496
wangqi01b78272018-04-03 17:02:44 -0700497 boolean showUpgradeToRtt = call.canUpgradeToRttCall();
wangqibb94ca62018-04-27 14:34:04 -0700498 boolean enableUpgradeToRtt = showUpgradeToRtt && call.getState() == DialerCallState.ACTIVE;
wangqif6be6172018-03-30 15:57:56 -0700499
linyuh183cb712017-12-27 17:02:37 -0800500 inCallButtonUi.showButton(InCallButtonIds.BUTTON_AUDIO, true);
501 inCallButtonUi.showButton(InCallButtonIds.BUTTON_SWAP, showSwap);
502 inCallButtonUi.showButton(InCallButtonIds.BUTTON_HOLD, showHold);
503 inCallButtonUi.setHold(isCallOnHold);
504 inCallButtonUi.showButton(InCallButtonIds.BUTTON_MUTE, showMute);
505 inCallButtonUi.showButton(InCallButtonIds.BUTTON_SWAP_SIM, showSwapSim);
506 inCallButtonUi.showButton(InCallButtonIds.BUTTON_ADD_CALL, true);
507 inCallButtonUi.enableButton(InCallButtonIds.BUTTON_ADD_CALL, showAddCall);
508 inCallButtonUi.showButton(InCallButtonIds.BUTTON_UPGRADE_TO_VIDEO, showUpgradeToVideo);
wangqif6be6172018-03-30 15:57:56 -0700509 inCallButtonUi.showButton(InCallButtonIds.BUTTON_UPGRADE_TO_RTT, showUpgradeToRtt);
wangqi15514ef2018-04-04 10:50:13 -0700510 inCallButtonUi.enableButton(InCallButtonIds.BUTTON_UPGRADE_TO_RTT, enableUpgradeToRtt);
linyuh183cb712017-12-27 17:02:37 -0800511 inCallButtonUi.showButton(InCallButtonIds.BUTTON_DOWNGRADE_TO_AUDIO, showDowngradeToAudio);
512 inCallButtonUi.showButton(
roldenburgf15085f2017-11-06 12:02:06 -0800513 InCallButtonIds.BUTTON_SWITCH_CAMERA,
514 isVideo && hasCameraPermission && call.getVideoTech().isTransmitting());
linyuh183cb712017-12-27 17:02:37 -0800515 inCallButtonUi.showButton(InCallButtonIds.BUTTON_PAUSE_VIDEO, showPauseVideo);
Eric Erfanianccca3152017-02-22 16:32:36 -0800516 if (isVideo) {
linyuh183cb712017-12-27 17:02:37 -0800517 inCallButtonUi.setVideoPaused(!call.getVideoTech().isTransmitting() || !hasCameraPermission);
Eric Erfanianccca3152017-02-22 16:32:36 -0800518 }
linyuh183cb712017-12-27 17:02:37 -0800519 inCallButtonUi.showButton(InCallButtonIds.BUTTON_DIALPAD, true);
520 inCallButtonUi.showButton(InCallButtonIds.BUTTON_MERGE, showMerge);
Eric Erfanianccca3152017-02-22 16:32:36 -0800521
linyuh183cb712017-12-27 17:02:37 -0800522 inCallButtonUi.updateButtonStates();
Eric Erfanianccca3152017-02-22 16:32:36 -0800523 }
524
525 private boolean hasVideoCallCapabilities(DialerCall call) {
roldenburg6bd612f2018-01-18 12:57:19 -0800526 return call.getVideoTech().isAvailable(context, call.getAccountHandle());
Eric Erfanianccca3152017-02-22 16:32:36 -0800527 }
528
529 /**
530 * Determines if downgrading from a video call to an audio-only call is supported. In order to
531 * support downgrade to audio, the SDK version must be >= N and the call should NOT have the
532 * {@link android.telecom.Call.Details#CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO}.
533 *
534 * @param call The call.
535 * @return {@code true} if downgrading to an audio-only call from a video call is supported.
536 */
537 private boolean isDowngradeToAudioSupported(DialerCall call) {
Eric Erfanian938468d2017-10-24 14:05:52 -0700538 // TODO(a bug): If there is an RCS video share session, return true here
Eric Erfanianccca3152017-02-22 16:32:36 -0800539 return !call.can(CallCompat.Details.CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO);
540 }
541
542 @Override
543 public void refreshMuteState() {
544 // Restore the previous mute state
yuegd1a269b2018-05-14 16:57:37 -0700545 if (automaticallyMutedByAddCall
linyuh183cb712017-12-27 17:02:37 -0800546 && AudioModeProvider.getInstance().getAudioState().isMuted() != previousMuteState) {
547 if (inCallButtonUi == null) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800548 return;
549 }
linyuh183cb712017-12-27 17:02:37 -0800550 muteClicked(previousMuteState, false /* clickedByUser */);
Eric Erfanianccca3152017-02-22 16:32:36 -0800551 }
yuegd1a269b2018-05-14 16:57:37 -0700552 automaticallyMutedByAddCall = false;
Eric Erfanianccca3152017-02-22 16:32:36 -0800553 }
554
555 @Override
556 public void onSaveInstanceState(Bundle outState) {
yuegd1a269b2018-05-14 16:57:37 -0700557 outState.putBoolean(KEY_AUTOMATICALLY_MUTED_BY_ADD_CALL, automaticallyMutedByAddCall);
linyuh183cb712017-12-27 17:02:37 -0800558 outState.putBoolean(KEY_PREVIOUS_MUTE_STATE, previousMuteState);
Eric Erfanianccca3152017-02-22 16:32:36 -0800559 }
560
561 @Override
562 public void onRestoreInstanceState(Bundle savedInstanceState) {
yuegd1a269b2018-05-14 16:57:37 -0700563 automaticallyMutedByAddCall =
564 savedInstanceState.getBoolean(
565 KEY_AUTOMATICALLY_MUTED_BY_ADD_CALL, automaticallyMutedByAddCall);
linyuh183cb712017-12-27 17:02:37 -0800566 previousMuteState = savedInstanceState.getBoolean(KEY_PREVIOUS_MUTE_STATE, previousMuteState);
Eric Erfanianccca3152017-02-22 16:32:36 -0800567 }
568
569 @Override
570 public void onCameraPermissionGranted() {
linyuh183cb712017-12-27 17:02:37 -0800571 if (call != null) {
572 updateButtonsState(call);
Eric Erfanianccca3152017-02-22 16:32:36 -0800573 }
574 }
575
576 @Override
577 public void onActiveCameraSelectionChanged(boolean isUsingFrontFacingCamera) {
linyuh183cb712017-12-27 17:02:37 -0800578 if (inCallButtonUi == null) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800579 return;
580 }
linyuh183cb712017-12-27 17:02:37 -0800581 inCallButtonUi.setCameraSwitched(!isUsingFrontFacingCamera);
Eric Erfanianccca3152017-02-22 16:32:36 -0800582 }
583
584 @Override
585 public Context getContext() {
linyuh183cb712017-12-27 17:02:37 -0800586 return context;
Eric Erfanianccca3152017-02-22 16:32:36 -0800587 }
588
589 private InCallActivity getActivity() {
linyuh183cb712017-12-27 17:02:37 -0800590 if (inCallButtonUi != null) {
591 Fragment fragment = inCallButtonUi.getInCallButtonUiFragment();
Eric Erfanianccca3152017-02-22 16:32:36 -0800592 if (fragment != null) {
593 return (InCallActivity) fragment.getActivity();
594 }
595 }
596 return null;
597 }
Eric Erfanianccca3152017-02-22 16:32:36 -0800598}