blob: a22a2581fc81a5c7f9d5e7851d08d917d8fed64d [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;
wangqi15514ef2018-04-04 10:50:13 -070047import com.android.incallui.call.DialerCall.State;
Eric Erfanianccca3152017-02-22 16:32:36 -080048import com.android.incallui.call.TelecomAdapter;
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
65 private static final String KEY_AUTOMATICALLY_MUTED = "incall_key_automatically_muted";
66 private static final String KEY_PREVIOUS_MUTE_STATE = "incall_key_previous_mute_state";
67
linyuh183cb712017-12-27 17:02:37 -080068 private final Context context;
69 private InCallButtonUi inCallButtonUi;
70 private DialerCall call;
71 private boolean automaticallyMuted = false;
72 private boolean previousMuteState = false;
Eric Erfanianccca3152017-02-22 16:32:36 -080073 private boolean isInCallButtonUiReady;
linyuh183cb712017-12-27 17:02:37 -080074 private PhoneAccountHandle otherAccount;
Eric Erfanianccca3152017-02-22 16:32:36 -080075
76 public CallButtonPresenter(Context context) {
linyuh183cb712017-12-27 17:02:37 -080077 this.context = context.getApplicationContext();
Eric Erfanianccca3152017-02-22 16:32:36 -080078 }
79
80 @Override
81 public void onInCallButtonUiReady(InCallButtonUi ui) {
82 Assert.checkState(!isInCallButtonUiReady);
linyuh183cb712017-12-27 17:02:37 -080083 inCallButtonUi = ui;
Eric Erfanianccca3152017-02-22 16:32:36 -080084 AudioModeProvider.getInstance().addListener(this);
85
86 // register for call state changes last
87 final InCallPresenter inCallPresenter = InCallPresenter.getInstance();
88 inCallPresenter.addListener(this);
89 inCallPresenter.addIncomingCallListener(this);
90 inCallPresenter.addDetailsListener(this);
91 inCallPresenter.addCanAddCallListener(this);
92 inCallPresenter.getInCallCameraManager().addCameraSelectionListener(this);
93
94 // Update the buttons state immediately for the current call
95 onStateChange(InCallState.NO_CALLS, inCallPresenter.getInCallState(), CallList.getInstance());
96 isInCallButtonUiReady = true;
97 }
98
99 @Override
100 public void onInCallButtonUiUnready() {
101 Assert.checkState(isInCallButtonUiReady);
linyuh183cb712017-12-27 17:02:37 -0800102 inCallButtonUi = null;
Eric Erfanianccca3152017-02-22 16:32:36 -0800103 InCallPresenter.getInstance().removeListener(this);
104 AudioModeProvider.getInstance().removeListener(this);
105 InCallPresenter.getInstance().removeIncomingCallListener(this);
106 InCallPresenter.getInstance().removeDetailsListener(this);
107 InCallPresenter.getInstance().getInCallCameraManager().removeCameraSelectionListener(this);
108 InCallPresenter.getInstance().removeCanAddCallListener(this);
109 isInCallButtonUiReady = false;
110 }
111
112 @Override
113 public void onStateChange(InCallState oldState, InCallState newState, CallList callList) {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700114 Trace.beginSection("CallButtonPresenter.onStateChange");
Eric Erfanianccca3152017-02-22 16:32:36 -0800115 if (newState == InCallState.OUTGOING) {
linyuh183cb712017-12-27 17:02:37 -0800116 call = callList.getOutgoingCall();
Eric Erfanianccca3152017-02-22 16:32:36 -0800117 } else if (newState == InCallState.INCALL) {
linyuh183cb712017-12-27 17:02:37 -0800118 call = callList.getActiveOrBackgroundCall();
Eric Erfanianccca3152017-02-22 16:32:36 -0800119
120 // When connected to voice mail, automatically shows the dialpad.
121 // (On previous releases we showed it when in-call shows up, before waiting for
122 // OUTGOING. We may want to do that once we start showing "Voice mail" label on
123 // the dialpad too.)
linyuh183cb712017-12-27 17:02:37 -0800124 if (oldState == InCallState.OUTGOING && call != null) {
125 if (call.isVoiceMailNumber() && getActivity() != null) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800126 getActivity().showDialpadFragment(true /* show */, true /* animate */);
127 }
128 }
129 } else if (newState == InCallState.INCOMING) {
130 if (getActivity() != null) {
131 getActivity().showDialpadFragment(false /* show */, true /* animate */);
132 }
linyuh183cb712017-12-27 17:02:37 -0800133 call = callList.getIncomingCall();
Eric Erfanianccca3152017-02-22 16:32:36 -0800134 } else {
linyuh183cb712017-12-27 17:02:37 -0800135 call = null;
Eric Erfanianccca3152017-02-22 16:32:36 -0800136 }
linyuh183cb712017-12-27 17:02:37 -0800137 updateUi(newState, call);
Eric Erfanian2ca43182017-08-31 06:57:16 -0700138 Trace.endSection();
Eric Erfanianccca3152017-02-22 16:32:36 -0800139 }
140
141 /**
142 * Updates the user interface in response to a change in the details of a call. Currently handles
143 * changes to the call buttons in response to a change in the details for a call. This is
144 * important to ensure changes to the active call are reflected in the available buttons.
145 *
146 * @param call The active call.
147 * @param details The call details.
148 */
149 @Override
150 public void onDetailsChanged(DialerCall call, android.telecom.Call.Details details) {
151 // Only update if the changes are for the currently active call
linyuh183cb712017-12-27 17:02:37 -0800152 if (inCallButtonUi != null && call != null && call.equals(this.call)) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800153 updateButtonsState(call);
154 }
155 }
156
157 @Override
158 public void onIncomingCall(InCallState oldState, InCallState newState, DialerCall call) {
159 onStateChange(oldState, newState, CallList.getInstance());
160 }
161
162 @Override
163 public void onCanAddCallChanged(boolean canAddCall) {
linyuh183cb712017-12-27 17:02:37 -0800164 if (inCallButtonUi != null && call != null) {
165 updateButtonsState(call);
Eric Erfanianccca3152017-02-22 16:32:36 -0800166 }
167 }
168
169 @Override
170 public void onAudioStateChanged(CallAudioState audioState) {
linyuh183cb712017-12-27 17:02:37 -0800171 if (inCallButtonUi != null) {
172 inCallButtonUi.setAudioState(audioState);
Eric Erfanianccca3152017-02-22 16:32:36 -0800173 }
174 }
175
176 @Override
177 public CallAudioState getCurrentAudioState() {
178 return AudioModeProvider.getInstance().getAudioState();
179 }
180
181 @Override
182 public void setAudioRoute(int route) {
183 LogUtil.i(
184 "CallButtonPresenter.setAudioRoute",
185 "sending new audio route: " + CallAudioState.audioRouteToString(route));
186 TelecomAdapter.getInstance().setAudioRoute(route);
187 }
188
189 /** Function assumes that bluetooth is not supported. */
190 @Override
191 public void toggleSpeakerphone() {
192 // This function should not be called if bluetooth is available.
193 CallAudioState audioState = getCurrentAudioState();
194 if (0 != (CallAudioState.ROUTE_BLUETOOTH & audioState.getSupportedRouteMask())) {
195 // It's clear the UI is wrong, so update the supported mode once again.
196 LogUtil.e(
197 "CallButtonPresenter", "toggling speakerphone not allowed when bluetooth supported.");
linyuh183cb712017-12-27 17:02:37 -0800198 inCallButtonUi.setAudioState(audioState);
Eric Erfanianccca3152017-02-22 16:32:36 -0800199 return;
200 }
201
202 int newRoute;
203 if (audioState.getRoute() == CallAudioState.ROUTE_SPEAKER) {
204 newRoute = CallAudioState.ROUTE_WIRED_OR_EARPIECE;
linyuh183cb712017-12-27 17:02:37 -0800205 Logger.get(context)
Eric Erfanianccca3152017-02-22 16:32:36 -0800206 .logCallImpression(
207 DialerImpression.Type.IN_CALL_SCREEN_TURN_ON_WIRED_OR_EARPIECE,
linyuh183cb712017-12-27 17:02:37 -0800208 call.getUniqueCallId(),
209 call.getTimeAddedMs());
Eric Erfanianccca3152017-02-22 16:32:36 -0800210 } else {
211 newRoute = CallAudioState.ROUTE_SPEAKER;
linyuh183cb712017-12-27 17:02:37 -0800212 Logger.get(context)
Eric Erfanianccca3152017-02-22 16:32:36 -0800213 .logCallImpression(
214 DialerImpression.Type.IN_CALL_SCREEN_TURN_ON_SPEAKERPHONE,
linyuh183cb712017-12-27 17:02:37 -0800215 call.getUniqueCallId(),
216 call.getTimeAddedMs());
Eric Erfanianccca3152017-02-22 16:32:36 -0800217 }
218
219 setAudioRoute(newRoute);
220 }
221
222 @Override
Eric Erfanian9a090c82017-03-16 19:22:24 -0700223 public void muteClicked(boolean checked, boolean clickedByUser) {
224 LogUtil.i(
225 "CallButtonPresenter", "turning on mute: %s, clicked by user: %s", checked, clickedByUser);
226 if (clickedByUser) {
linyuh183cb712017-12-27 17:02:37 -0800227 Logger.get(context)
Eric Erfanian9a090c82017-03-16 19:22:24 -0700228 .logCallImpression(
229 checked
230 ? DialerImpression.Type.IN_CALL_SCREEN_TURN_ON_MUTE
231 : DialerImpression.Type.IN_CALL_SCREEN_TURN_OFF_MUTE,
linyuh183cb712017-12-27 17:02:37 -0800232 call.getUniqueCallId(),
233 call.getTimeAddedMs());
Eric Erfanian9a090c82017-03-16 19:22:24 -0700234 }
Eric Erfanianccca3152017-02-22 16:32:36 -0800235 TelecomAdapter.getInstance().mute(checked);
236 }
237
238 @Override
239 public void holdClicked(boolean checked) {
linyuh183cb712017-12-27 17:02:37 -0800240 if (call == null) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800241 return;
242 }
243 if (checked) {
linyuh183cb712017-12-27 17:02:37 -0800244 LogUtil.i("CallButtonPresenter", "putting the call on hold: " + call);
245 call.hold();
Eric Erfanianccca3152017-02-22 16:32:36 -0800246 } else {
linyuh183cb712017-12-27 17:02:37 -0800247 LogUtil.i("CallButtonPresenter", "removing the call from hold: " + call);
248 call.unhold();
Eric Erfanianccca3152017-02-22 16:32:36 -0800249 }
250 }
251
252 @Override
253 public void swapClicked() {
linyuh183cb712017-12-27 17:02:37 -0800254 if (call == null) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800255 return;
256 }
257
linyuh183cb712017-12-27 17:02:37 -0800258 LogUtil.i("CallButtonPresenter", "swapping the call: " + call);
259 TelecomAdapter.getInstance().swap(call.getId());
Eric Erfanianccca3152017-02-22 16:32:36 -0800260 }
261
262 @Override
263 public void mergeClicked() {
linyuh183cb712017-12-27 17:02:37 -0800264 Logger.get(context)
Eric Erfanian2ca43182017-08-31 06:57:16 -0700265 .logCallImpression(
266 DialerImpression.Type.IN_CALL_MERGE_BUTTON_PRESSED,
linyuh183cb712017-12-27 17:02:37 -0800267 call.getUniqueCallId(),
268 call.getTimeAddedMs());
269 TelecomAdapter.getInstance().merge(call.getId());
Eric Erfanianccca3152017-02-22 16:32:36 -0800270 }
271
272 @Override
273 public void addCallClicked() {
linyuh183cb712017-12-27 17:02:37 -0800274 Logger.get(context)
Eric Erfanian2ca43182017-08-31 06:57:16 -0700275 .logCallImpression(
276 DialerImpression.Type.IN_CALL_ADD_CALL_BUTTON_PRESSED,
linyuh183cb712017-12-27 17:02:37 -0800277 call.getUniqueCallId(),
278 call.getTimeAddedMs());
Eric Erfanianccca3152017-02-22 16:32:36 -0800279 // Automatically mute the current call
linyuh183cb712017-12-27 17:02:37 -0800280 automaticallyMuted = true;
281 previousMuteState = AudioModeProvider.getInstance().getAudioState().isMuted();
Eric Erfanianccca3152017-02-22 16:32:36 -0800282 // Simulate a click on the mute button
Eric Erfanian9a090c82017-03-16 19:22:24 -0700283 muteClicked(true /* checked */, false /* clickedByUser */);
Eric Erfanianccca3152017-02-22 16:32:36 -0800284 TelecomAdapter.getInstance().addCall();
285 }
286
287 @Override
288 public void showDialpadClicked(boolean checked) {
linyuh183cb712017-12-27 17:02:37 -0800289 Logger.get(context)
Eric Erfanian2ca43182017-08-31 06:57:16 -0700290 .logCallImpression(
291 DialerImpression.Type.IN_CALL_SHOW_DIALPAD_BUTTON_PRESSED,
linyuh183cb712017-12-27 17:02:37 -0800292 call.getUniqueCallId(),
293 call.getTimeAddedMs());
Eric Erfanianccca3152017-02-22 16:32:36 -0800294 LogUtil.v("CallButtonPresenter", "show dialpad " + String.valueOf(checked));
295 getActivity().showDialpadFragment(checked /* show */, true /* animate */);
296 }
297
298 @Override
299 public void changeToVideoClicked() {
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700300 LogUtil.enterBlock("CallButtonPresenter.changeToVideoClicked");
linyuh183cb712017-12-27 17:02:37 -0800301 Logger.get(context)
Eric Erfanian8369df02017-05-03 10:27:13 -0700302 .logCallImpression(
303 DialerImpression.Type.VIDEO_CALL_UPGRADE_REQUESTED,
linyuh183cb712017-12-27 17:02:37 -0800304 call.getUniqueCallId(),
305 call.getTimeAddedMs());
306 call.getVideoTech().upgradeToVideo(context);
Eric Erfanianccca3152017-02-22 16:32:36 -0800307 }
308
309 @Override
wangqif6be6172018-03-30 15:57:56 -0700310 public void changeToRttClicked() {
311 LogUtil.enterBlock("CallButtonPresenter.changeToRttClicked");
312 call.sendRttUpgradeRequest();
313 }
314
315 @Override
Eric Erfanianccca3152017-02-22 16:32:36 -0800316 public void onEndCallClicked() {
linyuh183cb712017-12-27 17:02:37 -0800317 LogUtil.i("CallButtonPresenter.onEndCallClicked", "call: " + call);
318 if (call != null) {
319 call.disconnect();
Eric Erfanianccca3152017-02-22 16:32:36 -0800320 }
321 }
322
323 @Override
324 public void showAudioRouteSelector() {
linyuh183cb712017-12-27 17:02:37 -0800325 inCallButtonUi.showAudioRouteSelector();
Eric Erfanianccca3152017-02-22 16:32:36 -0800326 }
327
twyen00623aa2017-10-10 12:15:08 -0700328 @Override
329 public void swapSimClicked() {
330 LogUtil.enterBlock("CallButtonPresenter.swapSimClicked");
twyenfc1f9cf2017-11-27 13:22:48 -0800331 Logger.get(getContext()).logImpression(Type.DUAL_SIM_CHANGE_SIM_PRESSED);
twyen00623aa2017-10-10 12:15:08 -0700332 SwapSimWorker worker =
333 new SwapSimWorker(
334 getContext(),
linyuh183cb712017-12-27 17:02:37 -0800335 call,
twyen00623aa2017-10-10 12:15:08 -0700336 InCallPresenter.getInstance().getCallList(),
linyuh183cb712017-12-27 17:02:37 -0800337 otherAccount,
twyen00623aa2017-10-10 12:15:08 -0700338 InCallPresenter.getInstance().acquireInCallUiLock("swapSim"));
339 DialerExecutorComponent.get(getContext())
340 .dialerExecutorFactory()
341 .createNonUiTaskBuilder(worker)
342 .build()
343 .executeParallel(null);
344 }
345
Eric Erfanianccca3152017-02-22 16:32:36 -0800346 /**
347 * Switches the camera between the front-facing and back-facing camera.
348 *
349 * @param useFrontFacingCamera True if we should switch to using the front-facing camera, or false
350 * if we should switch to using the back-facing camera.
351 */
352 @Override
353 public void switchCameraClicked(boolean useFrontFacingCamera) {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700354 updateCamera(useFrontFacingCamera);
Eric Erfanianccca3152017-02-22 16:32:36 -0800355 }
356
357 @Override
358 public void toggleCameraClicked() {
359 LogUtil.i("CallButtonPresenter.toggleCameraClicked", "");
linyuh183cb712017-12-27 17:02:37 -0800360 if (call == null) {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700361 return;
362 }
linyuh183cb712017-12-27 17:02:37 -0800363 Logger.get(context)
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700364 .logCallImpression(
365 DialerImpression.Type.IN_CALL_SCREEN_SWAP_CAMERA,
linyuh183cb712017-12-27 17:02:37 -0800366 call.getUniqueCallId(),
367 call.getTimeAddedMs());
Eric Erfanianccca3152017-02-22 16:32:36 -0800368 switchCameraClicked(
369 !InCallPresenter.getInstance().getInCallCameraManager().isUsingFrontFacingCamera());
370 }
371
372 /**
373 * Stop or start client's video transmission.
374 *
375 * @param pause True if pausing the local user's video, or false if starting the local user's
376 * video.
377 */
378 @Override
379 public void pauseVideoClicked(boolean pause) {
380 LogUtil.i("CallButtonPresenter.pauseVideoClicked", "%s", pause ? "pause" : "unpause");
Eric Erfanianccca3152017-02-22 16:32:36 -0800381
linyuh183cb712017-12-27 17:02:37 -0800382 Logger.get(context)
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700383 .logCallImpression(
384 pause
385 ? DialerImpression.Type.IN_CALL_SCREEN_TURN_OFF_VIDEO
386 : DialerImpression.Type.IN_CALL_SCREEN_TURN_ON_VIDEO,
linyuh183cb712017-12-27 17:02:37 -0800387 call.getUniqueCallId(),
388 call.getTimeAddedMs());
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700389
Eric Erfanianccca3152017-02-22 16:32:36 -0800390 if (pause) {
linyuh183cb712017-12-27 17:02:37 -0800391 call.getVideoTech().setCamera(null);
392 call.getVideoTech().stopTransmission();
Eric Erfanianccca3152017-02-22 16:32:36 -0800393 } else {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700394 updateCamera(
395 InCallPresenter.getInstance().getInCallCameraManager().isUsingFrontFacingCamera());
linyuh183cb712017-12-27 17:02:37 -0800396 call.getVideoTech().resumeTransmission(context);
Eric Erfanianccca3152017-02-22 16:32:36 -0800397 }
398
linyuh183cb712017-12-27 17:02:37 -0800399 inCallButtonUi.setVideoPaused(pause);
400 inCallButtonUi.enableButton(InCallButtonIds.BUTTON_PAUSE_VIDEO, false);
Eric Erfanianccca3152017-02-22 16:32:36 -0800401 }
402
Eric Erfanian2ca43182017-08-31 06:57:16 -0700403 private void updateCamera(boolean useFrontFacingCamera) {
404 InCallCameraManager cameraManager = InCallPresenter.getInstance().getInCallCameraManager();
405 cameraManager.setUseFrontFacingCamera(useFrontFacingCamera);
406
407 String cameraId = cameraManager.getActiveCameraId();
408 if (cameraId != null) {
409 final int cameraDir =
410 cameraManager.isUsingFrontFacingCamera()
411 ? CameraDirection.CAMERA_DIRECTION_FRONT_FACING
412 : CameraDirection.CAMERA_DIRECTION_BACK_FACING;
linyuh183cb712017-12-27 17:02:37 -0800413 call.setCameraDir(cameraDir);
414 call.getVideoTech().setCamera(cameraId);
Eric Erfanian2ca43182017-08-31 06:57:16 -0700415 }
416 }
417
Eric Erfanianccca3152017-02-22 16:32:36 -0800418 private void updateUi(InCallState state, DialerCall call) {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700419 LogUtil.v("CallButtonPresenter", "updating call UI for call: %s", call);
Eric Erfanianccca3152017-02-22 16:32:36 -0800420
linyuh183cb712017-12-27 17:02:37 -0800421 if (inCallButtonUi == null) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800422 return;
423 }
424
425 if (call != null) {
linyuh183cb712017-12-27 17:02:37 -0800426 inCallButtonUi.updateInCallButtonUiColors(
wangqi8d662ca2017-10-26 11:27:19 -0700427 InCallPresenter.getInstance().getThemeColorManager().getSecondaryColor());
Eric Erfanianccca3152017-02-22 16:32:36 -0800428 }
429
430 final boolean isEnabled =
431 state.isConnectingOrConnected() && !state.isIncoming() && call != null;
linyuh183cb712017-12-27 17:02:37 -0800432 inCallButtonUi.setEnabled(isEnabled);
Eric Erfanianccca3152017-02-22 16:32:36 -0800433
434 if (call == null) {
435 return;
436 }
437
438 updateButtonsState(call);
439 }
440
441 /**
442 * Updates the buttons applicable for the UI.
443 *
444 * @param call The active call.
445 */
erfanian9dfd6a42018-04-17 15:01:17 -0700446 @TargetApi(VERSION_CODES.N)
447 @SuppressWarnings(value = {"MissingPermission", "AndroidApiChecker"}) // Java 8 APIs.
Eric Erfanianccca3152017-02-22 16:32:36 -0800448 private void updateButtonsState(DialerCall call) {
449 LogUtil.v("CallButtonPresenter.updateButtonsState", "");
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700450 final boolean isVideo = call.isVideoCall();
Eric Erfanianccca3152017-02-22 16:32:36 -0800451
452 // Common functionality (audio, hold, etc).
453 // Show either HOLD or SWAP, but not both. If neither HOLD or SWAP is available:
454 // (1) If the device normally can hold, show HOLD in a disabled state.
455 // (2) If the device doesn't have the concept of hold/swap, remove the button.
456 final boolean showSwap = call.can(android.telecom.Call.Details.CAPABILITY_SWAP_CONFERENCE);
457 final boolean showHold =
458 !showSwap
459 && call.can(android.telecom.Call.Details.CAPABILITY_SUPPORT_HOLD)
460 && call.can(android.telecom.Call.Details.CAPABILITY_HOLD);
461 final boolean isCallOnHold = call.getState() == DialerCall.State.ONHOLD;
462
463 final boolean showAddCall =
linyuh183cb712017-12-27 17:02:37 -0800464 TelecomAdapter.getInstance().canAddCall() && UserManagerCompat.isUserUnlocked(context);
erfanian9dfd6a42018-04-17 15:01:17 -0700465 // There can only be two calls so don't show the ability to merge when one of them
466 // is a speak easy call.
467 final boolean showMerge =
468 InCallPresenter.getInstance()
469 .getCallList()
470 .getAllCalls()
471 .stream()
472 .noneMatch(c -> c != null && c.isSpeakEasyCall())
473 && call.can(android.telecom.Call.Details.CAPABILITY_MERGE_CONFERENCE);
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700474 final boolean showUpgradeToVideo = !isVideo && (hasVideoCallCapabilities(call));
Eric Erfanianccca3152017-02-22 16:32:36 -0800475 final boolean showDowngradeToAudio = isVideo && isDowngradeToAudioSupported(call);
476 final boolean showMute = call.can(android.telecom.Call.Details.CAPABILITY_MUTE);
477
478 final boolean hasCameraPermission =
linyuh183cb712017-12-27 17:02:37 -0800479 isVideo && VideoUtils.hasCameraPermissionAndShownPrivacyToast(context);
Eric Erfanian938468d2017-10-24 14:05:52 -0700480 // Disabling local video doesn't seem to work when dialing. See a bug.
Eric Erfanianccca3152017-02-22 16:32:36 -0800481 final boolean showPauseVideo =
482 isVideo
483 && call.getState() != DialerCall.State.DIALING
484 && call.getState() != DialerCall.State.CONNECTING;
485
linyuh183cb712017-12-27 17:02:37 -0800486 otherAccount = TelecomUtil.getOtherAccount(getContext(), call.getAccountHandle());
twyen38af6512017-11-22 17:12:49 -0800487 boolean showSwapSim =
linyuh183cb712017-12-27 17:02:37 -0800488 otherAccount != null
twyen2c6803d2017-11-28 11:41:49 -0800489 && !call.isVoiceMailNumber()
twyen38af6512017-11-22 17:12:49 -0800490 && DialerCall.State.isDialing(call.getState())
491 // Most devices cannot make calls on 2 SIMs at the same time.
492 && InCallPresenter.getInstance().getCallList().getAllCalls().size() == 1;
twyen00623aa2017-10-10 12:15:08 -0700493
wangqi01b78272018-04-03 17:02:44 -0700494 boolean showUpgradeToRtt = call.canUpgradeToRttCall();
wangqi15514ef2018-04-04 10:50:13 -0700495 boolean enableUpgradeToRtt = showUpgradeToRtt && call.getState() == State.ACTIVE;
wangqif6be6172018-03-30 15:57:56 -0700496
linyuh183cb712017-12-27 17:02:37 -0800497 inCallButtonUi.showButton(InCallButtonIds.BUTTON_AUDIO, true);
498 inCallButtonUi.showButton(InCallButtonIds.BUTTON_SWAP, showSwap);
499 inCallButtonUi.showButton(InCallButtonIds.BUTTON_HOLD, showHold);
500 inCallButtonUi.setHold(isCallOnHold);
501 inCallButtonUi.showButton(InCallButtonIds.BUTTON_MUTE, showMute);
502 inCallButtonUi.showButton(InCallButtonIds.BUTTON_SWAP_SIM, showSwapSim);
503 inCallButtonUi.showButton(InCallButtonIds.BUTTON_ADD_CALL, true);
504 inCallButtonUi.enableButton(InCallButtonIds.BUTTON_ADD_CALL, showAddCall);
505 inCallButtonUi.showButton(InCallButtonIds.BUTTON_UPGRADE_TO_VIDEO, showUpgradeToVideo);
wangqif6be6172018-03-30 15:57:56 -0700506 inCallButtonUi.showButton(InCallButtonIds.BUTTON_UPGRADE_TO_RTT, showUpgradeToRtt);
wangqi15514ef2018-04-04 10:50:13 -0700507 inCallButtonUi.enableButton(InCallButtonIds.BUTTON_UPGRADE_TO_RTT, enableUpgradeToRtt);
linyuh183cb712017-12-27 17:02:37 -0800508 inCallButtonUi.showButton(InCallButtonIds.BUTTON_DOWNGRADE_TO_AUDIO, showDowngradeToAudio);
509 inCallButtonUi.showButton(
roldenburgf15085f2017-11-06 12:02:06 -0800510 InCallButtonIds.BUTTON_SWITCH_CAMERA,
511 isVideo && hasCameraPermission && call.getVideoTech().isTransmitting());
linyuh183cb712017-12-27 17:02:37 -0800512 inCallButtonUi.showButton(InCallButtonIds.BUTTON_PAUSE_VIDEO, showPauseVideo);
Eric Erfanianccca3152017-02-22 16:32:36 -0800513 if (isVideo) {
linyuh183cb712017-12-27 17:02:37 -0800514 inCallButtonUi.setVideoPaused(!call.getVideoTech().isTransmitting() || !hasCameraPermission);
Eric Erfanianccca3152017-02-22 16:32:36 -0800515 }
linyuh183cb712017-12-27 17:02:37 -0800516 inCallButtonUi.showButton(InCallButtonIds.BUTTON_DIALPAD, true);
517 inCallButtonUi.showButton(InCallButtonIds.BUTTON_MERGE, showMerge);
Eric Erfanianccca3152017-02-22 16:32:36 -0800518
linyuh183cb712017-12-27 17:02:37 -0800519 inCallButtonUi.updateButtonStates();
Eric Erfanianccca3152017-02-22 16:32:36 -0800520 }
521
522 private boolean hasVideoCallCapabilities(DialerCall call) {
roldenburg6bd612f2018-01-18 12:57:19 -0800523 return call.getVideoTech().isAvailable(context, call.getAccountHandle());
Eric Erfanianccca3152017-02-22 16:32:36 -0800524 }
525
526 /**
527 * Determines if downgrading from a video call to an audio-only call is supported. In order to
528 * support downgrade to audio, the SDK version must be >= N and the call should NOT have the
529 * {@link android.telecom.Call.Details#CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO}.
530 *
531 * @param call The call.
532 * @return {@code true} if downgrading to an audio-only call from a video call is supported.
533 */
534 private boolean isDowngradeToAudioSupported(DialerCall call) {
Eric Erfanian938468d2017-10-24 14:05:52 -0700535 // TODO(a bug): If there is an RCS video share session, return true here
Eric Erfanianccca3152017-02-22 16:32:36 -0800536 return !call.can(CallCompat.Details.CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO);
537 }
538
539 @Override
540 public void refreshMuteState() {
541 // Restore the previous mute state
linyuh183cb712017-12-27 17:02:37 -0800542 if (automaticallyMuted
543 && AudioModeProvider.getInstance().getAudioState().isMuted() != previousMuteState) {
544 if (inCallButtonUi == null) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800545 return;
546 }
linyuh183cb712017-12-27 17:02:37 -0800547 muteClicked(previousMuteState, false /* clickedByUser */);
Eric Erfanianccca3152017-02-22 16:32:36 -0800548 }
linyuh183cb712017-12-27 17:02:37 -0800549 automaticallyMuted = false;
Eric Erfanianccca3152017-02-22 16:32:36 -0800550 }
551
552 @Override
553 public void onSaveInstanceState(Bundle outState) {
linyuh183cb712017-12-27 17:02:37 -0800554 outState.putBoolean(KEY_AUTOMATICALLY_MUTED, automaticallyMuted);
555 outState.putBoolean(KEY_PREVIOUS_MUTE_STATE, previousMuteState);
Eric Erfanianccca3152017-02-22 16:32:36 -0800556 }
557
558 @Override
559 public void onRestoreInstanceState(Bundle savedInstanceState) {
linyuh183cb712017-12-27 17:02:37 -0800560 automaticallyMuted = savedInstanceState.getBoolean(KEY_AUTOMATICALLY_MUTED, automaticallyMuted);
561 previousMuteState = savedInstanceState.getBoolean(KEY_PREVIOUS_MUTE_STATE, previousMuteState);
Eric Erfanianccca3152017-02-22 16:32:36 -0800562 }
563
564 @Override
565 public void onCameraPermissionGranted() {
linyuh183cb712017-12-27 17:02:37 -0800566 if (call != null) {
567 updateButtonsState(call);
Eric Erfanianccca3152017-02-22 16:32:36 -0800568 }
569 }
570
571 @Override
572 public void onActiveCameraSelectionChanged(boolean isUsingFrontFacingCamera) {
linyuh183cb712017-12-27 17:02:37 -0800573 if (inCallButtonUi == null) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800574 return;
575 }
linyuh183cb712017-12-27 17:02:37 -0800576 inCallButtonUi.setCameraSwitched(!isUsingFrontFacingCamera);
Eric Erfanianccca3152017-02-22 16:32:36 -0800577 }
578
579 @Override
580 public Context getContext() {
linyuh183cb712017-12-27 17:02:37 -0800581 return context;
Eric Erfanianccca3152017-02-22 16:32:36 -0800582 }
583
584 private InCallActivity getActivity() {
linyuh183cb712017-12-27 17:02:37 -0800585 if (inCallButtonUi != null) {
586 Fragment fragment = inCallButtonUi.getInCallButtonUiFragment();
Eric Erfanianccca3152017-02-22 16:32:36 -0800587 if (fragment != null) {
588 return (InCallActivity) fragment.getActivity();
589 }
590 }
591 return null;
592 }
Eric Erfanianccca3152017-02-22 16:32:36 -0800593}