blob: 8334603985978a74be4ab6540a689ac268f52ae8 [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
19import android.content.Context;
Eric Erfanianccca3152017-02-22 16:32:36 -080020import android.os.Bundle;
Eric Erfanian2ca43182017-08-31 06:57:16 -070021import android.os.Trace;
Eric Erfanianccca3152017-02-22 16:32:36 -080022import android.support.v4.app.Fragment;
23import android.support.v4.os.UserManagerCompat;
24import android.telecom.CallAudioState;
twyen00623aa2017-10-10 12:15:08 -070025import android.telecom.PhoneAccountHandle;
Eric Erfanianccca3152017-02-22 16:32:36 -080026import com.android.contacts.common.compat.CallCompat;
27import com.android.dialer.common.Assert;
28import com.android.dialer.common.LogUtil;
twyen00623aa2017-10-10 12:15:08 -070029import com.android.dialer.common.concurrent.DialerExecutorComponent;
Eric Erfanian8369df02017-05-03 10:27:13 -070030import com.android.dialer.logging.DialerImpression;
twyenfc1f9cf2017-11-27 13:22:48 -080031import com.android.dialer.logging.DialerImpression.Type;
Eric Erfanianccca3152017-02-22 16:32:36 -080032import com.android.dialer.logging.Logger;
twyen00623aa2017-10-10 12:15:08 -070033import com.android.dialer.telecom.TelecomUtil;
Eric Erfanianccca3152017-02-22 16:32:36 -080034import com.android.incallui.InCallCameraManager.Listener;
35import com.android.incallui.InCallPresenter.CanAddCallListener;
36import com.android.incallui.InCallPresenter.InCallDetailsListener;
37import com.android.incallui.InCallPresenter.InCallState;
38import com.android.incallui.InCallPresenter.InCallStateListener;
39import com.android.incallui.InCallPresenter.IncomingCallListener;
Eric Erfanian8369df02017-05-03 10:27:13 -070040import com.android.incallui.audiomode.AudioModeProvider;
41import com.android.incallui.audiomode.AudioModeProvider.AudioModeListener;
Eric Erfanianccca3152017-02-22 16:32:36 -080042import com.android.incallui.call.CallList;
43import com.android.incallui.call.DialerCall;
Eric Erfaniand5e47f62017-03-15 14:41:07 -070044import com.android.incallui.call.DialerCall.CameraDirection;
Eric Erfanianccca3152017-02-22 16:32:36 -080045import com.android.incallui.call.TelecomAdapter;
Eric Erfanianccca3152017-02-22 16:32:36 -080046import com.android.incallui.incall.protocol.InCallButtonIds;
47import com.android.incallui.incall.protocol.InCallButtonUi;
48import com.android.incallui.incall.protocol.InCallButtonUiDelegate;
twyen00623aa2017-10-10 12:15:08 -070049import com.android.incallui.multisim.SwapSimWorker;
Eric Erfanian90508232017-03-24 09:31:16 -070050import com.android.incallui.videotech.utils.VideoUtils;
Eric Erfanianccca3152017-02-22 16:32:36 -080051
52/** Logic for call buttons. */
53public class CallButtonPresenter
54 implements InCallStateListener,
55 AudioModeListener,
56 IncomingCallListener,
57 InCallDetailsListener,
58 CanAddCallListener,
59 Listener,
60 InCallButtonUiDelegate {
61
62 private static final String KEY_AUTOMATICALLY_MUTED = "incall_key_automatically_muted";
63 private static final String KEY_PREVIOUS_MUTE_STATE = "incall_key_previous_mute_state";
64
linyuh183cb712017-12-27 17:02:37 -080065 private final Context context;
66 private InCallButtonUi inCallButtonUi;
67 private DialerCall call;
68 private boolean automaticallyMuted = false;
69 private boolean previousMuteState = false;
Eric Erfanianccca3152017-02-22 16:32:36 -080070 private boolean isInCallButtonUiReady;
linyuh183cb712017-12-27 17:02:37 -080071 private PhoneAccountHandle otherAccount;
Eric Erfanianccca3152017-02-22 16:32:36 -080072
73 public CallButtonPresenter(Context context) {
linyuh183cb712017-12-27 17:02:37 -080074 this.context = context.getApplicationContext();
Eric Erfanianccca3152017-02-22 16:32:36 -080075 }
76
77 @Override
78 public void onInCallButtonUiReady(InCallButtonUi ui) {
79 Assert.checkState(!isInCallButtonUiReady);
linyuh183cb712017-12-27 17:02:37 -080080 inCallButtonUi = ui;
Eric Erfanianccca3152017-02-22 16:32:36 -080081 AudioModeProvider.getInstance().addListener(this);
82
83 // register for call state changes last
84 final InCallPresenter inCallPresenter = InCallPresenter.getInstance();
85 inCallPresenter.addListener(this);
86 inCallPresenter.addIncomingCallListener(this);
87 inCallPresenter.addDetailsListener(this);
88 inCallPresenter.addCanAddCallListener(this);
89 inCallPresenter.getInCallCameraManager().addCameraSelectionListener(this);
90
91 // Update the buttons state immediately for the current call
92 onStateChange(InCallState.NO_CALLS, inCallPresenter.getInCallState(), CallList.getInstance());
93 isInCallButtonUiReady = true;
94 }
95
96 @Override
97 public void onInCallButtonUiUnready() {
98 Assert.checkState(isInCallButtonUiReady);
linyuh183cb712017-12-27 17:02:37 -080099 inCallButtonUi = null;
Eric Erfanianccca3152017-02-22 16:32:36 -0800100 InCallPresenter.getInstance().removeListener(this);
101 AudioModeProvider.getInstance().removeListener(this);
102 InCallPresenter.getInstance().removeIncomingCallListener(this);
103 InCallPresenter.getInstance().removeDetailsListener(this);
104 InCallPresenter.getInstance().getInCallCameraManager().removeCameraSelectionListener(this);
105 InCallPresenter.getInstance().removeCanAddCallListener(this);
106 isInCallButtonUiReady = false;
107 }
108
109 @Override
110 public void onStateChange(InCallState oldState, InCallState newState, CallList callList) {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700111 Trace.beginSection("CallButtonPresenter.onStateChange");
Eric Erfanianccca3152017-02-22 16:32:36 -0800112 if (newState == InCallState.OUTGOING) {
linyuh183cb712017-12-27 17:02:37 -0800113 call = callList.getOutgoingCall();
Eric Erfanianccca3152017-02-22 16:32:36 -0800114 } else if (newState == InCallState.INCALL) {
linyuh183cb712017-12-27 17:02:37 -0800115 call = callList.getActiveOrBackgroundCall();
Eric Erfanianccca3152017-02-22 16:32:36 -0800116
117 // When connected to voice mail, automatically shows the dialpad.
118 // (On previous releases we showed it when in-call shows up, before waiting for
119 // OUTGOING. We may want to do that once we start showing "Voice mail" label on
120 // the dialpad too.)
linyuh183cb712017-12-27 17:02:37 -0800121 if (oldState == InCallState.OUTGOING && call != null) {
122 if (call.isVoiceMailNumber() && getActivity() != null) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800123 getActivity().showDialpadFragment(true /* show */, true /* animate */);
124 }
125 }
126 } else if (newState == InCallState.INCOMING) {
127 if (getActivity() != null) {
128 getActivity().showDialpadFragment(false /* show */, true /* animate */);
129 }
linyuh183cb712017-12-27 17:02:37 -0800130 call = callList.getIncomingCall();
Eric Erfanianccca3152017-02-22 16:32:36 -0800131 } else {
linyuh183cb712017-12-27 17:02:37 -0800132 call = null;
Eric Erfanianccca3152017-02-22 16:32:36 -0800133 }
linyuh183cb712017-12-27 17:02:37 -0800134 updateUi(newState, call);
Eric Erfanian2ca43182017-08-31 06:57:16 -0700135 Trace.endSection();
Eric Erfanianccca3152017-02-22 16:32:36 -0800136 }
137
138 /**
139 * Updates the user interface in response to a change in the details of a call. Currently handles
140 * changes to the call buttons in response to a change in the details for a call. This is
141 * important to ensure changes to the active call are reflected in the available buttons.
142 *
143 * @param call The active call.
144 * @param details The call details.
145 */
146 @Override
147 public void onDetailsChanged(DialerCall call, android.telecom.Call.Details details) {
148 // Only update if the changes are for the currently active call
linyuh183cb712017-12-27 17:02:37 -0800149 if (inCallButtonUi != null && call != null && call.equals(this.call)) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800150 updateButtonsState(call);
151 }
152 }
153
154 @Override
155 public void onIncomingCall(InCallState oldState, InCallState newState, DialerCall call) {
156 onStateChange(oldState, newState, CallList.getInstance());
157 }
158
159 @Override
160 public void onCanAddCallChanged(boolean canAddCall) {
linyuh183cb712017-12-27 17:02:37 -0800161 if (inCallButtonUi != null && call != null) {
162 updateButtonsState(call);
Eric Erfanianccca3152017-02-22 16:32:36 -0800163 }
164 }
165
166 @Override
167 public void onAudioStateChanged(CallAudioState audioState) {
linyuh183cb712017-12-27 17:02:37 -0800168 if (inCallButtonUi != null) {
169 inCallButtonUi.setAudioState(audioState);
Eric Erfanianccca3152017-02-22 16:32:36 -0800170 }
171 }
172
173 @Override
174 public CallAudioState getCurrentAudioState() {
175 return AudioModeProvider.getInstance().getAudioState();
176 }
177
178 @Override
179 public void setAudioRoute(int route) {
180 LogUtil.i(
181 "CallButtonPresenter.setAudioRoute",
182 "sending new audio route: " + CallAudioState.audioRouteToString(route));
183 TelecomAdapter.getInstance().setAudioRoute(route);
184 }
185
186 /** Function assumes that bluetooth is not supported. */
187 @Override
188 public void toggleSpeakerphone() {
189 // This function should not be called if bluetooth is available.
190 CallAudioState audioState = getCurrentAudioState();
191 if (0 != (CallAudioState.ROUTE_BLUETOOTH & audioState.getSupportedRouteMask())) {
192 // It's clear the UI is wrong, so update the supported mode once again.
193 LogUtil.e(
194 "CallButtonPresenter", "toggling speakerphone not allowed when bluetooth supported.");
linyuh183cb712017-12-27 17:02:37 -0800195 inCallButtonUi.setAudioState(audioState);
Eric Erfanianccca3152017-02-22 16:32:36 -0800196 return;
197 }
198
199 int newRoute;
200 if (audioState.getRoute() == CallAudioState.ROUTE_SPEAKER) {
201 newRoute = CallAudioState.ROUTE_WIRED_OR_EARPIECE;
linyuh183cb712017-12-27 17:02:37 -0800202 Logger.get(context)
Eric Erfanianccca3152017-02-22 16:32:36 -0800203 .logCallImpression(
204 DialerImpression.Type.IN_CALL_SCREEN_TURN_ON_WIRED_OR_EARPIECE,
linyuh183cb712017-12-27 17:02:37 -0800205 call.getUniqueCallId(),
206 call.getTimeAddedMs());
Eric Erfanianccca3152017-02-22 16:32:36 -0800207 } else {
208 newRoute = CallAudioState.ROUTE_SPEAKER;
linyuh183cb712017-12-27 17:02:37 -0800209 Logger.get(context)
Eric Erfanianccca3152017-02-22 16:32:36 -0800210 .logCallImpression(
211 DialerImpression.Type.IN_CALL_SCREEN_TURN_ON_SPEAKERPHONE,
linyuh183cb712017-12-27 17:02:37 -0800212 call.getUniqueCallId(),
213 call.getTimeAddedMs());
Eric Erfanianccca3152017-02-22 16:32:36 -0800214 }
215
216 setAudioRoute(newRoute);
217 }
218
219 @Override
Eric Erfanian9a090c82017-03-16 19:22:24 -0700220 public void muteClicked(boolean checked, boolean clickedByUser) {
221 LogUtil.i(
222 "CallButtonPresenter", "turning on mute: %s, clicked by user: %s", checked, clickedByUser);
223 if (clickedByUser) {
linyuh183cb712017-12-27 17:02:37 -0800224 Logger.get(context)
Eric Erfanian9a090c82017-03-16 19:22:24 -0700225 .logCallImpression(
226 checked
227 ? DialerImpression.Type.IN_CALL_SCREEN_TURN_ON_MUTE
228 : DialerImpression.Type.IN_CALL_SCREEN_TURN_OFF_MUTE,
linyuh183cb712017-12-27 17:02:37 -0800229 call.getUniqueCallId(),
230 call.getTimeAddedMs());
Eric Erfanian9a090c82017-03-16 19:22:24 -0700231 }
Eric Erfanianccca3152017-02-22 16:32:36 -0800232 TelecomAdapter.getInstance().mute(checked);
233 }
234
235 @Override
236 public void holdClicked(boolean checked) {
linyuh183cb712017-12-27 17:02:37 -0800237 if (call == null) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800238 return;
239 }
240 if (checked) {
linyuh183cb712017-12-27 17:02:37 -0800241 LogUtil.i("CallButtonPresenter", "putting the call on hold: " + call);
242 call.hold();
Eric Erfanianccca3152017-02-22 16:32:36 -0800243 } else {
linyuh183cb712017-12-27 17:02:37 -0800244 LogUtil.i("CallButtonPresenter", "removing the call from hold: " + call);
245 call.unhold();
Eric Erfanianccca3152017-02-22 16:32:36 -0800246 }
247 }
248
249 @Override
250 public void swapClicked() {
linyuh183cb712017-12-27 17:02:37 -0800251 if (call == null) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800252 return;
253 }
254
linyuh183cb712017-12-27 17:02:37 -0800255 LogUtil.i("CallButtonPresenter", "swapping the call: " + call);
256 TelecomAdapter.getInstance().swap(call.getId());
Eric Erfanianccca3152017-02-22 16:32:36 -0800257 }
258
259 @Override
260 public void mergeClicked() {
linyuh183cb712017-12-27 17:02:37 -0800261 Logger.get(context)
Eric Erfanian2ca43182017-08-31 06:57:16 -0700262 .logCallImpression(
263 DialerImpression.Type.IN_CALL_MERGE_BUTTON_PRESSED,
linyuh183cb712017-12-27 17:02:37 -0800264 call.getUniqueCallId(),
265 call.getTimeAddedMs());
266 TelecomAdapter.getInstance().merge(call.getId());
Eric Erfanianccca3152017-02-22 16:32:36 -0800267 }
268
269 @Override
270 public void addCallClicked() {
linyuh183cb712017-12-27 17:02:37 -0800271 Logger.get(context)
Eric Erfanian2ca43182017-08-31 06:57:16 -0700272 .logCallImpression(
273 DialerImpression.Type.IN_CALL_ADD_CALL_BUTTON_PRESSED,
linyuh183cb712017-12-27 17:02:37 -0800274 call.getUniqueCallId(),
275 call.getTimeAddedMs());
Eric Erfanianccca3152017-02-22 16:32:36 -0800276 // Automatically mute the current call
linyuh183cb712017-12-27 17:02:37 -0800277 automaticallyMuted = true;
278 previousMuteState = AudioModeProvider.getInstance().getAudioState().isMuted();
Eric Erfanianccca3152017-02-22 16:32:36 -0800279 // Simulate a click on the mute button
Eric Erfanian9a090c82017-03-16 19:22:24 -0700280 muteClicked(true /* checked */, false /* clickedByUser */);
Eric Erfanianccca3152017-02-22 16:32:36 -0800281 TelecomAdapter.getInstance().addCall();
282 }
283
284 @Override
285 public void showDialpadClicked(boolean checked) {
linyuh183cb712017-12-27 17:02:37 -0800286 Logger.get(context)
Eric Erfanian2ca43182017-08-31 06:57:16 -0700287 .logCallImpression(
288 DialerImpression.Type.IN_CALL_SHOW_DIALPAD_BUTTON_PRESSED,
linyuh183cb712017-12-27 17:02:37 -0800289 call.getUniqueCallId(),
290 call.getTimeAddedMs());
Eric Erfanianccca3152017-02-22 16:32:36 -0800291 LogUtil.v("CallButtonPresenter", "show dialpad " + String.valueOf(checked));
292 getActivity().showDialpadFragment(checked /* show */, true /* animate */);
293 }
294
295 @Override
296 public void changeToVideoClicked() {
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700297 LogUtil.enterBlock("CallButtonPresenter.changeToVideoClicked");
linyuh183cb712017-12-27 17:02:37 -0800298 Logger.get(context)
Eric Erfanian8369df02017-05-03 10:27:13 -0700299 .logCallImpression(
300 DialerImpression.Type.VIDEO_CALL_UPGRADE_REQUESTED,
linyuh183cb712017-12-27 17:02:37 -0800301 call.getUniqueCallId(),
302 call.getTimeAddedMs());
303 call.getVideoTech().upgradeToVideo(context);
Eric Erfanianccca3152017-02-22 16:32:36 -0800304 }
305
306 @Override
wangqif6be6172018-03-30 15:57:56 -0700307 public void changeToRttClicked() {
308 LogUtil.enterBlock("CallButtonPresenter.changeToRttClicked");
309 call.sendRttUpgradeRequest();
310 }
311
312 @Override
Eric Erfanianccca3152017-02-22 16:32:36 -0800313 public void onEndCallClicked() {
linyuh183cb712017-12-27 17:02:37 -0800314 LogUtil.i("CallButtonPresenter.onEndCallClicked", "call: " + call);
315 if (call != null) {
316 call.disconnect();
Eric Erfanianccca3152017-02-22 16:32:36 -0800317 }
318 }
319
320 @Override
321 public void showAudioRouteSelector() {
linyuh183cb712017-12-27 17:02:37 -0800322 inCallButtonUi.showAudioRouteSelector();
Eric Erfanianccca3152017-02-22 16:32:36 -0800323 }
324
twyen00623aa2017-10-10 12:15:08 -0700325 @Override
326 public void swapSimClicked() {
327 LogUtil.enterBlock("CallButtonPresenter.swapSimClicked");
twyenfc1f9cf2017-11-27 13:22:48 -0800328 Logger.get(getContext()).logImpression(Type.DUAL_SIM_CHANGE_SIM_PRESSED);
twyen00623aa2017-10-10 12:15:08 -0700329 SwapSimWorker worker =
330 new SwapSimWorker(
331 getContext(),
linyuh183cb712017-12-27 17:02:37 -0800332 call,
twyen00623aa2017-10-10 12:15:08 -0700333 InCallPresenter.getInstance().getCallList(),
linyuh183cb712017-12-27 17:02:37 -0800334 otherAccount,
twyen00623aa2017-10-10 12:15:08 -0700335 InCallPresenter.getInstance().acquireInCallUiLock("swapSim"));
336 DialerExecutorComponent.get(getContext())
337 .dialerExecutorFactory()
338 .createNonUiTaskBuilder(worker)
339 .build()
340 .executeParallel(null);
341 }
342
Eric Erfanianccca3152017-02-22 16:32:36 -0800343 /**
344 * Switches the camera between the front-facing and back-facing camera.
345 *
346 * @param useFrontFacingCamera True if we should switch to using the front-facing camera, or false
347 * if we should switch to using the back-facing camera.
348 */
349 @Override
350 public void switchCameraClicked(boolean useFrontFacingCamera) {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700351 updateCamera(useFrontFacingCamera);
Eric Erfanianccca3152017-02-22 16:32:36 -0800352 }
353
354 @Override
355 public void toggleCameraClicked() {
356 LogUtil.i("CallButtonPresenter.toggleCameraClicked", "");
linyuh183cb712017-12-27 17:02:37 -0800357 if (call == null) {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700358 return;
359 }
linyuh183cb712017-12-27 17:02:37 -0800360 Logger.get(context)
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700361 .logCallImpression(
362 DialerImpression.Type.IN_CALL_SCREEN_SWAP_CAMERA,
linyuh183cb712017-12-27 17:02:37 -0800363 call.getUniqueCallId(),
364 call.getTimeAddedMs());
Eric Erfanianccca3152017-02-22 16:32:36 -0800365 switchCameraClicked(
366 !InCallPresenter.getInstance().getInCallCameraManager().isUsingFrontFacingCamera());
367 }
368
369 /**
370 * Stop or start client's video transmission.
371 *
372 * @param pause True if pausing the local user's video, or false if starting the local user's
373 * video.
374 */
375 @Override
376 public void pauseVideoClicked(boolean pause) {
377 LogUtil.i("CallButtonPresenter.pauseVideoClicked", "%s", pause ? "pause" : "unpause");
Eric Erfanianccca3152017-02-22 16:32:36 -0800378
linyuh183cb712017-12-27 17:02:37 -0800379 Logger.get(context)
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700380 .logCallImpression(
381 pause
382 ? DialerImpression.Type.IN_CALL_SCREEN_TURN_OFF_VIDEO
383 : DialerImpression.Type.IN_CALL_SCREEN_TURN_ON_VIDEO,
linyuh183cb712017-12-27 17:02:37 -0800384 call.getUniqueCallId(),
385 call.getTimeAddedMs());
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700386
Eric Erfanianccca3152017-02-22 16:32:36 -0800387 if (pause) {
linyuh183cb712017-12-27 17:02:37 -0800388 call.getVideoTech().setCamera(null);
389 call.getVideoTech().stopTransmission();
Eric Erfanianccca3152017-02-22 16:32:36 -0800390 } else {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700391 updateCamera(
392 InCallPresenter.getInstance().getInCallCameraManager().isUsingFrontFacingCamera());
linyuh183cb712017-12-27 17:02:37 -0800393 call.getVideoTech().resumeTransmission(context);
Eric Erfanianccca3152017-02-22 16:32:36 -0800394 }
395
linyuh183cb712017-12-27 17:02:37 -0800396 inCallButtonUi.setVideoPaused(pause);
397 inCallButtonUi.enableButton(InCallButtonIds.BUTTON_PAUSE_VIDEO, false);
Eric Erfanianccca3152017-02-22 16:32:36 -0800398 }
399
Eric Erfanian2ca43182017-08-31 06:57:16 -0700400 private void updateCamera(boolean useFrontFacingCamera) {
401 InCallCameraManager cameraManager = InCallPresenter.getInstance().getInCallCameraManager();
402 cameraManager.setUseFrontFacingCamera(useFrontFacingCamera);
403
404 String cameraId = cameraManager.getActiveCameraId();
405 if (cameraId != null) {
406 final int cameraDir =
407 cameraManager.isUsingFrontFacingCamera()
408 ? CameraDirection.CAMERA_DIRECTION_FRONT_FACING
409 : CameraDirection.CAMERA_DIRECTION_BACK_FACING;
linyuh183cb712017-12-27 17:02:37 -0800410 call.setCameraDir(cameraDir);
411 call.getVideoTech().setCamera(cameraId);
Eric Erfanian2ca43182017-08-31 06:57:16 -0700412 }
413 }
414
Eric Erfanianccca3152017-02-22 16:32:36 -0800415 private void updateUi(InCallState state, DialerCall call) {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700416 LogUtil.v("CallButtonPresenter", "updating call UI for call: %s", call);
Eric Erfanianccca3152017-02-22 16:32:36 -0800417
linyuh183cb712017-12-27 17:02:37 -0800418 if (inCallButtonUi == null) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800419 return;
420 }
421
422 if (call != null) {
linyuh183cb712017-12-27 17:02:37 -0800423 inCallButtonUi.updateInCallButtonUiColors(
wangqi8d662ca2017-10-26 11:27:19 -0700424 InCallPresenter.getInstance().getThemeColorManager().getSecondaryColor());
Eric Erfanianccca3152017-02-22 16:32:36 -0800425 }
426
427 final boolean isEnabled =
428 state.isConnectingOrConnected() && !state.isIncoming() && call != null;
linyuh183cb712017-12-27 17:02:37 -0800429 inCallButtonUi.setEnabled(isEnabled);
Eric Erfanianccca3152017-02-22 16:32:36 -0800430
431 if (call == null) {
432 return;
433 }
434
435 updateButtonsState(call);
436 }
437
438 /**
439 * Updates the buttons applicable for the UI.
440 *
441 * @param call The active call.
442 */
twyen00623aa2017-10-10 12:15:08 -0700443 @SuppressWarnings("MissingPermission")
Eric Erfanianccca3152017-02-22 16:32:36 -0800444 private void updateButtonsState(DialerCall call) {
445 LogUtil.v("CallButtonPresenter.updateButtonsState", "");
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700446 final boolean isVideo = call.isVideoCall();
Eric Erfanianccca3152017-02-22 16:32:36 -0800447
448 // Common functionality (audio, hold, etc).
449 // Show either HOLD or SWAP, but not both. If neither HOLD or SWAP is available:
450 // (1) If the device normally can hold, show HOLD in a disabled state.
451 // (2) If the device doesn't have the concept of hold/swap, remove the button.
452 final boolean showSwap = call.can(android.telecom.Call.Details.CAPABILITY_SWAP_CONFERENCE);
453 final boolean showHold =
454 !showSwap
455 && call.can(android.telecom.Call.Details.CAPABILITY_SUPPORT_HOLD)
456 && call.can(android.telecom.Call.Details.CAPABILITY_HOLD);
457 final boolean isCallOnHold = call.getState() == DialerCall.State.ONHOLD;
458
459 final boolean showAddCall =
linyuh183cb712017-12-27 17:02:37 -0800460 TelecomAdapter.getInstance().canAddCall() && UserManagerCompat.isUserUnlocked(context);
Eric Erfanianccca3152017-02-22 16:32:36 -0800461 final boolean showMerge = call.can(android.telecom.Call.Details.CAPABILITY_MERGE_CONFERENCE);
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700462 final boolean showUpgradeToVideo = !isVideo && (hasVideoCallCapabilities(call));
Eric Erfanianccca3152017-02-22 16:32:36 -0800463 final boolean showDowngradeToAudio = isVideo && isDowngradeToAudioSupported(call);
464 final boolean showMute = call.can(android.telecom.Call.Details.CAPABILITY_MUTE);
465
466 final boolean hasCameraPermission =
linyuh183cb712017-12-27 17:02:37 -0800467 isVideo && VideoUtils.hasCameraPermissionAndShownPrivacyToast(context);
Eric Erfanian938468d2017-10-24 14:05:52 -0700468 // Disabling local video doesn't seem to work when dialing. See a bug.
Eric Erfanianccca3152017-02-22 16:32:36 -0800469 final boolean showPauseVideo =
470 isVideo
471 && call.getState() != DialerCall.State.DIALING
472 && call.getState() != DialerCall.State.CONNECTING;
473
linyuh183cb712017-12-27 17:02:37 -0800474 otherAccount = TelecomUtil.getOtherAccount(getContext(), call.getAccountHandle());
twyen38af6512017-11-22 17:12:49 -0800475 boolean showSwapSim =
linyuh183cb712017-12-27 17:02:37 -0800476 otherAccount != null
twyen2c6803d2017-11-28 11:41:49 -0800477 && !call.isVoiceMailNumber()
twyen38af6512017-11-22 17:12:49 -0800478 && DialerCall.State.isDialing(call.getState())
479 // Most devices cannot make calls on 2 SIMs at the same time.
480 && InCallPresenter.getInstance().getCallList().getAllCalls().size() == 1;
twyen00623aa2017-10-10 12:15:08 -0700481
wangqi01b78272018-04-03 17:02:44 -0700482 boolean showUpgradeToRtt = call.canUpgradeToRttCall();
wangqif6be6172018-03-30 15:57:56 -0700483
linyuh183cb712017-12-27 17:02:37 -0800484 inCallButtonUi.showButton(InCallButtonIds.BUTTON_AUDIO, true);
485 inCallButtonUi.showButton(InCallButtonIds.BUTTON_SWAP, showSwap);
486 inCallButtonUi.showButton(InCallButtonIds.BUTTON_HOLD, showHold);
487 inCallButtonUi.setHold(isCallOnHold);
488 inCallButtonUi.showButton(InCallButtonIds.BUTTON_MUTE, showMute);
489 inCallButtonUi.showButton(InCallButtonIds.BUTTON_SWAP_SIM, showSwapSim);
490 inCallButtonUi.showButton(InCallButtonIds.BUTTON_ADD_CALL, true);
491 inCallButtonUi.enableButton(InCallButtonIds.BUTTON_ADD_CALL, showAddCall);
492 inCallButtonUi.showButton(InCallButtonIds.BUTTON_UPGRADE_TO_VIDEO, showUpgradeToVideo);
wangqif6be6172018-03-30 15:57:56 -0700493 inCallButtonUi.showButton(InCallButtonIds.BUTTON_UPGRADE_TO_RTT, showUpgradeToRtt);
linyuh183cb712017-12-27 17:02:37 -0800494 inCallButtonUi.showButton(InCallButtonIds.BUTTON_DOWNGRADE_TO_AUDIO, showDowngradeToAudio);
495 inCallButtonUi.showButton(
roldenburgf15085f2017-11-06 12:02:06 -0800496 InCallButtonIds.BUTTON_SWITCH_CAMERA,
497 isVideo && hasCameraPermission && call.getVideoTech().isTransmitting());
linyuh183cb712017-12-27 17:02:37 -0800498 inCallButtonUi.showButton(InCallButtonIds.BUTTON_PAUSE_VIDEO, showPauseVideo);
Eric Erfanianccca3152017-02-22 16:32:36 -0800499 if (isVideo) {
linyuh183cb712017-12-27 17:02:37 -0800500 inCallButtonUi.setVideoPaused(!call.getVideoTech().isTransmitting() || !hasCameraPermission);
Eric Erfanianccca3152017-02-22 16:32:36 -0800501 }
linyuh183cb712017-12-27 17:02:37 -0800502 inCallButtonUi.showButton(InCallButtonIds.BUTTON_DIALPAD, true);
503 inCallButtonUi.showButton(InCallButtonIds.BUTTON_MERGE, showMerge);
Eric Erfanianccca3152017-02-22 16:32:36 -0800504
linyuh183cb712017-12-27 17:02:37 -0800505 inCallButtonUi.updateButtonStates();
Eric Erfanianccca3152017-02-22 16:32:36 -0800506 }
507
508 private boolean hasVideoCallCapabilities(DialerCall call) {
roldenburg6bd612f2018-01-18 12:57:19 -0800509 return call.getVideoTech().isAvailable(context, call.getAccountHandle());
Eric Erfanianccca3152017-02-22 16:32:36 -0800510 }
511
512 /**
513 * Determines if downgrading from a video call to an audio-only call is supported. In order to
514 * support downgrade to audio, the SDK version must be >= N and the call should NOT have the
515 * {@link android.telecom.Call.Details#CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO}.
516 *
517 * @param call The call.
518 * @return {@code true} if downgrading to an audio-only call from a video call is supported.
519 */
520 private boolean isDowngradeToAudioSupported(DialerCall call) {
Eric Erfanian938468d2017-10-24 14:05:52 -0700521 // TODO(a bug): If there is an RCS video share session, return true here
Eric Erfanianccca3152017-02-22 16:32:36 -0800522 return !call.can(CallCompat.Details.CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO);
523 }
524
525 @Override
526 public void refreshMuteState() {
527 // Restore the previous mute state
linyuh183cb712017-12-27 17:02:37 -0800528 if (automaticallyMuted
529 && AudioModeProvider.getInstance().getAudioState().isMuted() != previousMuteState) {
530 if (inCallButtonUi == null) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800531 return;
532 }
linyuh183cb712017-12-27 17:02:37 -0800533 muteClicked(previousMuteState, false /* clickedByUser */);
Eric Erfanianccca3152017-02-22 16:32:36 -0800534 }
linyuh183cb712017-12-27 17:02:37 -0800535 automaticallyMuted = false;
Eric Erfanianccca3152017-02-22 16:32:36 -0800536 }
537
538 @Override
539 public void onSaveInstanceState(Bundle outState) {
linyuh183cb712017-12-27 17:02:37 -0800540 outState.putBoolean(KEY_AUTOMATICALLY_MUTED, automaticallyMuted);
541 outState.putBoolean(KEY_PREVIOUS_MUTE_STATE, previousMuteState);
Eric Erfanianccca3152017-02-22 16:32:36 -0800542 }
543
544 @Override
545 public void onRestoreInstanceState(Bundle savedInstanceState) {
linyuh183cb712017-12-27 17:02:37 -0800546 automaticallyMuted = savedInstanceState.getBoolean(KEY_AUTOMATICALLY_MUTED, automaticallyMuted);
547 previousMuteState = savedInstanceState.getBoolean(KEY_PREVIOUS_MUTE_STATE, previousMuteState);
Eric Erfanianccca3152017-02-22 16:32:36 -0800548 }
549
550 @Override
551 public void onCameraPermissionGranted() {
linyuh183cb712017-12-27 17:02:37 -0800552 if (call != null) {
553 updateButtonsState(call);
Eric Erfanianccca3152017-02-22 16:32:36 -0800554 }
555 }
556
557 @Override
558 public void onActiveCameraSelectionChanged(boolean isUsingFrontFacingCamera) {
linyuh183cb712017-12-27 17:02:37 -0800559 if (inCallButtonUi == null) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800560 return;
561 }
linyuh183cb712017-12-27 17:02:37 -0800562 inCallButtonUi.setCameraSwitched(!isUsingFrontFacingCamera);
Eric Erfanianccca3152017-02-22 16:32:36 -0800563 }
564
565 @Override
566 public Context getContext() {
linyuh183cb712017-12-27 17:02:37 -0800567 return context;
Eric Erfanianccca3152017-02-22 16:32:36 -0800568 }
569
570 private InCallActivity getActivity() {
linyuh183cb712017-12-27 17:02:37 -0800571 if (inCallButtonUi != null) {
572 Fragment fragment = inCallButtonUi.getInCallButtonUiFragment();
Eric Erfanianccca3152017-02-22 16:32:36 -0800573 if (fragment != null) {
574 return (InCallActivity) fragment.getActivity();
575 }
576 }
577 return null;
578 }
Eric Erfanianccca3152017-02-22 16:32:36 -0800579}