blob: 7e05badfeb91f98fe6f8ba5c96a7799f29ce7260 [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;
wangqi15514ef2018-04-04 10:50:13 -070045import com.android.incallui.call.DialerCall.State;
Eric Erfanianccca3152017-02-22 16:32:36 -080046import com.android.incallui.call.TelecomAdapter;
Eric Erfanianccca3152017-02-22 16:32:36 -080047import com.android.incallui.incall.protocol.InCallButtonIds;
48import com.android.incallui.incall.protocol.InCallButtonUi;
49import com.android.incallui.incall.protocol.InCallButtonUiDelegate;
twyen00623aa2017-10-10 12:15:08 -070050import com.android.incallui.multisim.SwapSimWorker;
Eric Erfanian90508232017-03-24 09:31:16 -070051import com.android.incallui.videotech.utils.VideoUtils;
Eric Erfanianccca3152017-02-22 16:32:36 -080052
53/** Logic for call buttons. */
54public class CallButtonPresenter
55 implements InCallStateListener,
56 AudioModeListener,
57 IncomingCallListener,
58 InCallDetailsListener,
59 CanAddCallListener,
60 Listener,
61 InCallButtonUiDelegate {
62
63 private static final String KEY_AUTOMATICALLY_MUTED = "incall_key_automatically_muted";
64 private static final String KEY_PREVIOUS_MUTE_STATE = "incall_key_previous_mute_state";
65
linyuh183cb712017-12-27 17:02:37 -080066 private final Context context;
67 private InCallButtonUi inCallButtonUi;
68 private DialerCall call;
69 private boolean automaticallyMuted = false;
70 private boolean previousMuteState = false;
Eric Erfanianccca3152017-02-22 16:32:36 -080071 private boolean isInCallButtonUiReady;
linyuh183cb712017-12-27 17:02:37 -080072 private PhoneAccountHandle otherAccount;
Eric Erfanianccca3152017-02-22 16:32:36 -080073
74 public CallButtonPresenter(Context context) {
linyuh183cb712017-12-27 17:02:37 -080075 this.context = context.getApplicationContext();
Eric Erfanianccca3152017-02-22 16:32:36 -080076 }
77
78 @Override
79 public void onInCallButtonUiReady(InCallButtonUi ui) {
80 Assert.checkState(!isInCallButtonUiReady);
linyuh183cb712017-12-27 17:02:37 -080081 inCallButtonUi = ui;
Eric Erfanianccca3152017-02-22 16:32:36 -080082 AudioModeProvider.getInstance().addListener(this);
83
84 // register for call state changes last
85 final InCallPresenter inCallPresenter = InCallPresenter.getInstance();
86 inCallPresenter.addListener(this);
87 inCallPresenter.addIncomingCallListener(this);
88 inCallPresenter.addDetailsListener(this);
89 inCallPresenter.addCanAddCallListener(this);
90 inCallPresenter.getInCallCameraManager().addCameraSelectionListener(this);
91
92 // Update the buttons state immediately for the current call
93 onStateChange(InCallState.NO_CALLS, inCallPresenter.getInCallState(), CallList.getInstance());
94 isInCallButtonUiReady = true;
95 }
96
97 @Override
98 public void onInCallButtonUiUnready() {
99 Assert.checkState(isInCallButtonUiReady);
linyuh183cb712017-12-27 17:02:37 -0800100 inCallButtonUi = null;
Eric Erfanianccca3152017-02-22 16:32:36 -0800101 InCallPresenter.getInstance().removeListener(this);
102 AudioModeProvider.getInstance().removeListener(this);
103 InCallPresenter.getInstance().removeIncomingCallListener(this);
104 InCallPresenter.getInstance().removeDetailsListener(this);
105 InCallPresenter.getInstance().getInCallCameraManager().removeCameraSelectionListener(this);
106 InCallPresenter.getInstance().removeCanAddCallListener(this);
107 isInCallButtonUiReady = false;
108 }
109
110 @Override
111 public void onStateChange(InCallState oldState, InCallState newState, CallList callList) {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700112 Trace.beginSection("CallButtonPresenter.onStateChange");
Eric Erfanianccca3152017-02-22 16:32:36 -0800113 if (newState == InCallState.OUTGOING) {
linyuh183cb712017-12-27 17:02:37 -0800114 call = callList.getOutgoingCall();
Eric Erfanianccca3152017-02-22 16:32:36 -0800115 } else if (newState == InCallState.INCALL) {
linyuh183cb712017-12-27 17:02:37 -0800116 call = callList.getActiveOrBackgroundCall();
Eric Erfanianccca3152017-02-22 16:32:36 -0800117
118 // When connected to voice mail, automatically shows the dialpad.
119 // (On previous releases we showed it when in-call shows up, before waiting for
120 // OUTGOING. We may want to do that once we start showing "Voice mail" label on
121 // the dialpad too.)
linyuh183cb712017-12-27 17:02:37 -0800122 if (oldState == InCallState.OUTGOING && call != null) {
123 if (call.isVoiceMailNumber() && getActivity() != null) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800124 getActivity().showDialpadFragment(true /* show */, true /* animate */);
125 }
126 }
127 } else if (newState == InCallState.INCOMING) {
128 if (getActivity() != null) {
129 getActivity().showDialpadFragment(false /* show */, true /* animate */);
130 }
linyuh183cb712017-12-27 17:02:37 -0800131 call = callList.getIncomingCall();
Eric Erfanianccca3152017-02-22 16:32:36 -0800132 } else {
linyuh183cb712017-12-27 17:02:37 -0800133 call = null;
Eric Erfanianccca3152017-02-22 16:32:36 -0800134 }
linyuh183cb712017-12-27 17:02:37 -0800135 updateUi(newState, call);
Eric Erfanian2ca43182017-08-31 06:57:16 -0700136 Trace.endSection();
Eric Erfanianccca3152017-02-22 16:32:36 -0800137 }
138
139 /**
140 * Updates the user interface in response to a change in the details of a call. Currently handles
141 * changes to the call buttons in response to a change in the details for a call. This is
142 * important to ensure changes to the active call are reflected in the available buttons.
143 *
144 * @param call The active call.
145 * @param details The call details.
146 */
147 @Override
148 public void onDetailsChanged(DialerCall call, android.telecom.Call.Details details) {
149 // Only update if the changes are for the currently active call
linyuh183cb712017-12-27 17:02:37 -0800150 if (inCallButtonUi != null && call != null && call.equals(this.call)) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800151 updateButtonsState(call);
152 }
153 }
154
155 @Override
156 public void onIncomingCall(InCallState oldState, InCallState newState, DialerCall call) {
157 onStateChange(oldState, newState, CallList.getInstance());
158 }
159
160 @Override
161 public void onCanAddCallChanged(boolean canAddCall) {
linyuh183cb712017-12-27 17:02:37 -0800162 if (inCallButtonUi != null && call != null) {
163 updateButtonsState(call);
Eric Erfanianccca3152017-02-22 16:32:36 -0800164 }
165 }
166
167 @Override
168 public void onAudioStateChanged(CallAudioState audioState) {
linyuh183cb712017-12-27 17:02:37 -0800169 if (inCallButtonUi != null) {
170 inCallButtonUi.setAudioState(audioState);
Eric Erfanianccca3152017-02-22 16:32:36 -0800171 }
172 }
173
174 @Override
175 public CallAudioState getCurrentAudioState() {
176 return AudioModeProvider.getInstance().getAudioState();
177 }
178
179 @Override
180 public void setAudioRoute(int route) {
181 LogUtil.i(
182 "CallButtonPresenter.setAudioRoute",
183 "sending new audio route: " + CallAudioState.audioRouteToString(route));
184 TelecomAdapter.getInstance().setAudioRoute(route);
185 }
186
187 /** Function assumes that bluetooth is not supported. */
188 @Override
189 public void toggleSpeakerphone() {
190 // This function should not be called if bluetooth is available.
191 CallAudioState audioState = getCurrentAudioState();
192 if (0 != (CallAudioState.ROUTE_BLUETOOTH & audioState.getSupportedRouteMask())) {
193 // It's clear the UI is wrong, so update the supported mode once again.
194 LogUtil.e(
195 "CallButtonPresenter", "toggling speakerphone not allowed when bluetooth supported.");
linyuh183cb712017-12-27 17:02:37 -0800196 inCallButtonUi.setAudioState(audioState);
Eric Erfanianccca3152017-02-22 16:32:36 -0800197 return;
198 }
199
200 int newRoute;
201 if (audioState.getRoute() == CallAudioState.ROUTE_SPEAKER) {
202 newRoute = CallAudioState.ROUTE_WIRED_OR_EARPIECE;
linyuh183cb712017-12-27 17:02:37 -0800203 Logger.get(context)
Eric Erfanianccca3152017-02-22 16:32:36 -0800204 .logCallImpression(
205 DialerImpression.Type.IN_CALL_SCREEN_TURN_ON_WIRED_OR_EARPIECE,
linyuh183cb712017-12-27 17:02:37 -0800206 call.getUniqueCallId(),
207 call.getTimeAddedMs());
Eric Erfanianccca3152017-02-22 16:32:36 -0800208 } else {
209 newRoute = CallAudioState.ROUTE_SPEAKER;
linyuh183cb712017-12-27 17:02:37 -0800210 Logger.get(context)
Eric Erfanianccca3152017-02-22 16:32:36 -0800211 .logCallImpression(
212 DialerImpression.Type.IN_CALL_SCREEN_TURN_ON_SPEAKERPHONE,
linyuh183cb712017-12-27 17:02:37 -0800213 call.getUniqueCallId(),
214 call.getTimeAddedMs());
Eric Erfanianccca3152017-02-22 16:32:36 -0800215 }
216
217 setAudioRoute(newRoute);
218 }
219
220 @Override
Eric Erfanian9a090c82017-03-16 19:22:24 -0700221 public void muteClicked(boolean checked, boolean clickedByUser) {
222 LogUtil.i(
223 "CallButtonPresenter", "turning on mute: %s, clicked by user: %s", checked, clickedByUser);
224 if (clickedByUser) {
linyuh183cb712017-12-27 17:02:37 -0800225 Logger.get(context)
Eric Erfanian9a090c82017-03-16 19:22:24 -0700226 .logCallImpression(
227 checked
228 ? DialerImpression.Type.IN_CALL_SCREEN_TURN_ON_MUTE
229 : DialerImpression.Type.IN_CALL_SCREEN_TURN_OFF_MUTE,
linyuh183cb712017-12-27 17:02:37 -0800230 call.getUniqueCallId(),
231 call.getTimeAddedMs());
Eric Erfanian9a090c82017-03-16 19:22:24 -0700232 }
Eric Erfanianccca3152017-02-22 16:32:36 -0800233 TelecomAdapter.getInstance().mute(checked);
234 }
235
236 @Override
237 public void holdClicked(boolean checked) {
linyuh183cb712017-12-27 17:02:37 -0800238 if (call == null) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800239 return;
240 }
241 if (checked) {
linyuh183cb712017-12-27 17:02:37 -0800242 LogUtil.i("CallButtonPresenter", "putting the call on hold: " + call);
243 call.hold();
Eric Erfanianccca3152017-02-22 16:32:36 -0800244 } else {
linyuh183cb712017-12-27 17:02:37 -0800245 LogUtil.i("CallButtonPresenter", "removing the call from hold: " + call);
246 call.unhold();
Eric Erfanianccca3152017-02-22 16:32:36 -0800247 }
248 }
249
250 @Override
251 public void swapClicked() {
linyuh183cb712017-12-27 17:02:37 -0800252 if (call == null) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800253 return;
254 }
255
linyuh183cb712017-12-27 17:02:37 -0800256 LogUtil.i("CallButtonPresenter", "swapping the call: " + call);
257 TelecomAdapter.getInstance().swap(call.getId());
Eric Erfanianccca3152017-02-22 16:32:36 -0800258 }
259
260 @Override
261 public void mergeClicked() {
linyuh183cb712017-12-27 17:02:37 -0800262 Logger.get(context)
Eric Erfanian2ca43182017-08-31 06:57:16 -0700263 .logCallImpression(
264 DialerImpression.Type.IN_CALL_MERGE_BUTTON_PRESSED,
linyuh183cb712017-12-27 17:02:37 -0800265 call.getUniqueCallId(),
266 call.getTimeAddedMs());
267 TelecomAdapter.getInstance().merge(call.getId());
Eric Erfanianccca3152017-02-22 16:32:36 -0800268 }
269
270 @Override
271 public void addCallClicked() {
linyuh183cb712017-12-27 17:02:37 -0800272 Logger.get(context)
Eric Erfanian2ca43182017-08-31 06:57:16 -0700273 .logCallImpression(
274 DialerImpression.Type.IN_CALL_ADD_CALL_BUTTON_PRESSED,
linyuh183cb712017-12-27 17:02:37 -0800275 call.getUniqueCallId(),
276 call.getTimeAddedMs());
Eric Erfanianccca3152017-02-22 16:32:36 -0800277 // Automatically mute the current call
linyuh183cb712017-12-27 17:02:37 -0800278 automaticallyMuted = true;
279 previousMuteState = AudioModeProvider.getInstance().getAudioState().isMuted();
Eric Erfanianccca3152017-02-22 16:32:36 -0800280 // Simulate a click on the mute button
Eric Erfanian9a090c82017-03-16 19:22:24 -0700281 muteClicked(true /* checked */, false /* clickedByUser */);
Eric Erfanianccca3152017-02-22 16:32:36 -0800282 TelecomAdapter.getInstance().addCall();
283 }
284
285 @Override
286 public void showDialpadClicked(boolean checked) {
linyuh183cb712017-12-27 17:02:37 -0800287 Logger.get(context)
Eric Erfanian2ca43182017-08-31 06:57:16 -0700288 .logCallImpression(
289 DialerImpression.Type.IN_CALL_SHOW_DIALPAD_BUTTON_PRESSED,
linyuh183cb712017-12-27 17:02:37 -0800290 call.getUniqueCallId(),
291 call.getTimeAddedMs());
Eric Erfanianccca3152017-02-22 16:32:36 -0800292 LogUtil.v("CallButtonPresenter", "show dialpad " + String.valueOf(checked));
293 getActivity().showDialpadFragment(checked /* show */, true /* animate */);
294 }
295
296 @Override
297 public void changeToVideoClicked() {
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700298 LogUtil.enterBlock("CallButtonPresenter.changeToVideoClicked");
linyuh183cb712017-12-27 17:02:37 -0800299 Logger.get(context)
Eric Erfanian8369df02017-05-03 10:27:13 -0700300 .logCallImpression(
301 DialerImpression.Type.VIDEO_CALL_UPGRADE_REQUESTED,
linyuh183cb712017-12-27 17:02:37 -0800302 call.getUniqueCallId(),
303 call.getTimeAddedMs());
304 call.getVideoTech().upgradeToVideo(context);
Eric Erfanianccca3152017-02-22 16:32:36 -0800305 }
306
307 @Override
wangqif6be6172018-03-30 15:57:56 -0700308 public void changeToRttClicked() {
309 LogUtil.enterBlock("CallButtonPresenter.changeToRttClicked");
310 call.sendRttUpgradeRequest();
311 }
312
313 @Override
Eric Erfanianccca3152017-02-22 16:32:36 -0800314 public void onEndCallClicked() {
linyuh183cb712017-12-27 17:02:37 -0800315 LogUtil.i("CallButtonPresenter.onEndCallClicked", "call: " + call);
316 if (call != null) {
317 call.disconnect();
Eric Erfanianccca3152017-02-22 16:32:36 -0800318 }
319 }
320
321 @Override
322 public void showAudioRouteSelector() {
linyuh183cb712017-12-27 17:02:37 -0800323 inCallButtonUi.showAudioRouteSelector();
Eric Erfanianccca3152017-02-22 16:32:36 -0800324 }
325
twyen00623aa2017-10-10 12:15:08 -0700326 @Override
327 public void swapSimClicked() {
328 LogUtil.enterBlock("CallButtonPresenter.swapSimClicked");
twyenfc1f9cf2017-11-27 13:22:48 -0800329 Logger.get(getContext()).logImpression(Type.DUAL_SIM_CHANGE_SIM_PRESSED);
twyen00623aa2017-10-10 12:15:08 -0700330 SwapSimWorker worker =
331 new SwapSimWorker(
332 getContext(),
linyuh183cb712017-12-27 17:02:37 -0800333 call,
twyen00623aa2017-10-10 12:15:08 -0700334 InCallPresenter.getInstance().getCallList(),
linyuh183cb712017-12-27 17:02:37 -0800335 otherAccount,
twyen00623aa2017-10-10 12:15:08 -0700336 InCallPresenter.getInstance().acquireInCallUiLock("swapSim"));
337 DialerExecutorComponent.get(getContext())
338 .dialerExecutorFactory()
339 .createNonUiTaskBuilder(worker)
340 .build()
341 .executeParallel(null);
342 }
343
Eric Erfanianccca3152017-02-22 16:32:36 -0800344 /**
345 * Switches the camera between the front-facing and back-facing camera.
346 *
347 * @param useFrontFacingCamera True if we should switch to using the front-facing camera, or false
348 * if we should switch to using the back-facing camera.
349 */
350 @Override
351 public void switchCameraClicked(boolean useFrontFacingCamera) {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700352 updateCamera(useFrontFacingCamera);
Eric Erfanianccca3152017-02-22 16:32:36 -0800353 }
354
355 @Override
356 public void toggleCameraClicked() {
357 LogUtil.i("CallButtonPresenter.toggleCameraClicked", "");
linyuh183cb712017-12-27 17:02:37 -0800358 if (call == null) {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700359 return;
360 }
linyuh183cb712017-12-27 17:02:37 -0800361 Logger.get(context)
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700362 .logCallImpression(
363 DialerImpression.Type.IN_CALL_SCREEN_SWAP_CAMERA,
linyuh183cb712017-12-27 17:02:37 -0800364 call.getUniqueCallId(),
365 call.getTimeAddedMs());
Eric Erfanianccca3152017-02-22 16:32:36 -0800366 switchCameraClicked(
367 !InCallPresenter.getInstance().getInCallCameraManager().isUsingFrontFacingCamera());
368 }
369
370 /**
371 * Stop or start client's video transmission.
372 *
373 * @param pause True if pausing the local user's video, or false if starting the local user's
374 * video.
375 */
376 @Override
377 public void pauseVideoClicked(boolean pause) {
378 LogUtil.i("CallButtonPresenter.pauseVideoClicked", "%s", pause ? "pause" : "unpause");
Eric Erfanianccca3152017-02-22 16:32:36 -0800379
linyuh183cb712017-12-27 17:02:37 -0800380 Logger.get(context)
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700381 .logCallImpression(
382 pause
383 ? DialerImpression.Type.IN_CALL_SCREEN_TURN_OFF_VIDEO
384 : DialerImpression.Type.IN_CALL_SCREEN_TURN_ON_VIDEO,
linyuh183cb712017-12-27 17:02:37 -0800385 call.getUniqueCallId(),
386 call.getTimeAddedMs());
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700387
Eric Erfanianccca3152017-02-22 16:32:36 -0800388 if (pause) {
linyuh183cb712017-12-27 17:02:37 -0800389 call.getVideoTech().setCamera(null);
390 call.getVideoTech().stopTransmission();
Eric Erfanianccca3152017-02-22 16:32:36 -0800391 } else {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700392 updateCamera(
393 InCallPresenter.getInstance().getInCallCameraManager().isUsingFrontFacingCamera());
linyuh183cb712017-12-27 17:02:37 -0800394 call.getVideoTech().resumeTransmission(context);
Eric Erfanianccca3152017-02-22 16:32:36 -0800395 }
396
linyuh183cb712017-12-27 17:02:37 -0800397 inCallButtonUi.setVideoPaused(pause);
398 inCallButtonUi.enableButton(InCallButtonIds.BUTTON_PAUSE_VIDEO, false);
Eric Erfanianccca3152017-02-22 16:32:36 -0800399 }
400
Eric Erfanian2ca43182017-08-31 06:57:16 -0700401 private void updateCamera(boolean useFrontFacingCamera) {
402 InCallCameraManager cameraManager = InCallPresenter.getInstance().getInCallCameraManager();
403 cameraManager.setUseFrontFacingCamera(useFrontFacingCamera);
404
405 String cameraId = cameraManager.getActiveCameraId();
406 if (cameraId != null) {
407 final int cameraDir =
408 cameraManager.isUsingFrontFacingCamera()
409 ? CameraDirection.CAMERA_DIRECTION_FRONT_FACING
410 : CameraDirection.CAMERA_DIRECTION_BACK_FACING;
linyuh183cb712017-12-27 17:02:37 -0800411 call.setCameraDir(cameraDir);
412 call.getVideoTech().setCamera(cameraId);
Eric Erfanian2ca43182017-08-31 06:57:16 -0700413 }
414 }
415
Eric Erfanianccca3152017-02-22 16:32:36 -0800416 private void updateUi(InCallState state, DialerCall call) {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700417 LogUtil.v("CallButtonPresenter", "updating call UI for call: %s", call);
Eric Erfanianccca3152017-02-22 16:32:36 -0800418
linyuh183cb712017-12-27 17:02:37 -0800419 if (inCallButtonUi == null) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800420 return;
421 }
422
423 if (call != null) {
linyuh183cb712017-12-27 17:02:37 -0800424 inCallButtonUi.updateInCallButtonUiColors(
wangqi8d662ca2017-10-26 11:27:19 -0700425 InCallPresenter.getInstance().getThemeColorManager().getSecondaryColor());
Eric Erfanianccca3152017-02-22 16:32:36 -0800426 }
427
428 final boolean isEnabled =
429 state.isConnectingOrConnected() && !state.isIncoming() && call != null;
linyuh183cb712017-12-27 17:02:37 -0800430 inCallButtonUi.setEnabled(isEnabled);
Eric Erfanianccca3152017-02-22 16:32:36 -0800431
432 if (call == null) {
433 return;
434 }
435
436 updateButtonsState(call);
437 }
438
439 /**
440 * Updates the buttons applicable for the UI.
441 *
442 * @param call The active call.
443 */
twyen00623aa2017-10-10 12:15:08 -0700444 @SuppressWarnings("MissingPermission")
Eric Erfanianccca3152017-02-22 16:32:36 -0800445 private void updateButtonsState(DialerCall call) {
446 LogUtil.v("CallButtonPresenter.updateButtonsState", "");
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700447 final boolean isVideo = call.isVideoCall();
Eric Erfanianccca3152017-02-22 16:32:36 -0800448
449 // Common functionality (audio, hold, etc).
450 // Show either HOLD or SWAP, but not both. If neither HOLD or SWAP is available:
451 // (1) If the device normally can hold, show HOLD in a disabled state.
452 // (2) If the device doesn't have the concept of hold/swap, remove the button.
453 final boolean showSwap = call.can(android.telecom.Call.Details.CAPABILITY_SWAP_CONFERENCE);
454 final boolean showHold =
455 !showSwap
456 && call.can(android.telecom.Call.Details.CAPABILITY_SUPPORT_HOLD)
457 && call.can(android.telecom.Call.Details.CAPABILITY_HOLD);
458 final boolean isCallOnHold = call.getState() == DialerCall.State.ONHOLD;
459
460 final boolean showAddCall =
linyuh183cb712017-12-27 17:02:37 -0800461 TelecomAdapter.getInstance().canAddCall() && UserManagerCompat.isUserUnlocked(context);
Eric Erfanianccca3152017-02-22 16:32:36 -0800462 final boolean showMerge = call.can(android.telecom.Call.Details.CAPABILITY_MERGE_CONFERENCE);
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700463 final boolean showUpgradeToVideo = !isVideo && (hasVideoCallCapabilities(call));
Eric Erfanianccca3152017-02-22 16:32:36 -0800464 final boolean showDowngradeToAudio = isVideo && isDowngradeToAudioSupported(call);
465 final boolean showMute = call.can(android.telecom.Call.Details.CAPABILITY_MUTE);
466
467 final boolean hasCameraPermission =
linyuh183cb712017-12-27 17:02:37 -0800468 isVideo && VideoUtils.hasCameraPermissionAndShownPrivacyToast(context);
Eric Erfanian938468d2017-10-24 14:05:52 -0700469 // Disabling local video doesn't seem to work when dialing. See a bug.
Eric Erfanianccca3152017-02-22 16:32:36 -0800470 final boolean showPauseVideo =
471 isVideo
472 && call.getState() != DialerCall.State.DIALING
473 && call.getState() != DialerCall.State.CONNECTING;
474
linyuh183cb712017-12-27 17:02:37 -0800475 otherAccount = TelecomUtil.getOtherAccount(getContext(), call.getAccountHandle());
twyen38af6512017-11-22 17:12:49 -0800476 boolean showSwapSim =
linyuh183cb712017-12-27 17:02:37 -0800477 otherAccount != null
twyen2c6803d2017-11-28 11:41:49 -0800478 && !call.isVoiceMailNumber()
twyen38af6512017-11-22 17:12:49 -0800479 && DialerCall.State.isDialing(call.getState())
480 // Most devices cannot make calls on 2 SIMs at the same time.
481 && InCallPresenter.getInstance().getCallList().getAllCalls().size() == 1;
twyen00623aa2017-10-10 12:15:08 -0700482
wangqi01b78272018-04-03 17:02:44 -0700483 boolean showUpgradeToRtt = call.canUpgradeToRttCall();
wangqi15514ef2018-04-04 10:50:13 -0700484 boolean enableUpgradeToRtt = showUpgradeToRtt && call.getState() == State.ACTIVE;
wangqif6be6172018-03-30 15:57:56 -0700485
linyuh183cb712017-12-27 17:02:37 -0800486 inCallButtonUi.showButton(InCallButtonIds.BUTTON_AUDIO, true);
487 inCallButtonUi.showButton(InCallButtonIds.BUTTON_SWAP, showSwap);
488 inCallButtonUi.showButton(InCallButtonIds.BUTTON_HOLD, showHold);
489 inCallButtonUi.setHold(isCallOnHold);
490 inCallButtonUi.showButton(InCallButtonIds.BUTTON_MUTE, showMute);
491 inCallButtonUi.showButton(InCallButtonIds.BUTTON_SWAP_SIM, showSwapSim);
492 inCallButtonUi.showButton(InCallButtonIds.BUTTON_ADD_CALL, true);
493 inCallButtonUi.enableButton(InCallButtonIds.BUTTON_ADD_CALL, showAddCall);
494 inCallButtonUi.showButton(InCallButtonIds.BUTTON_UPGRADE_TO_VIDEO, showUpgradeToVideo);
wangqif6be6172018-03-30 15:57:56 -0700495 inCallButtonUi.showButton(InCallButtonIds.BUTTON_UPGRADE_TO_RTT, showUpgradeToRtt);
wangqi15514ef2018-04-04 10:50:13 -0700496 inCallButtonUi.enableButton(InCallButtonIds.BUTTON_UPGRADE_TO_RTT, enableUpgradeToRtt);
linyuh183cb712017-12-27 17:02:37 -0800497 inCallButtonUi.showButton(InCallButtonIds.BUTTON_DOWNGRADE_TO_AUDIO, showDowngradeToAudio);
498 inCallButtonUi.showButton(
roldenburgf15085f2017-11-06 12:02:06 -0800499 InCallButtonIds.BUTTON_SWITCH_CAMERA,
500 isVideo && hasCameraPermission && call.getVideoTech().isTransmitting());
linyuh183cb712017-12-27 17:02:37 -0800501 inCallButtonUi.showButton(InCallButtonIds.BUTTON_PAUSE_VIDEO, showPauseVideo);
Eric Erfanianccca3152017-02-22 16:32:36 -0800502 if (isVideo) {
linyuh183cb712017-12-27 17:02:37 -0800503 inCallButtonUi.setVideoPaused(!call.getVideoTech().isTransmitting() || !hasCameraPermission);
Eric Erfanianccca3152017-02-22 16:32:36 -0800504 }
linyuh183cb712017-12-27 17:02:37 -0800505 inCallButtonUi.showButton(InCallButtonIds.BUTTON_DIALPAD, true);
506 inCallButtonUi.showButton(InCallButtonIds.BUTTON_MERGE, showMerge);
Eric Erfanianccca3152017-02-22 16:32:36 -0800507
linyuh183cb712017-12-27 17:02:37 -0800508 inCallButtonUi.updateButtonStates();
Eric Erfanianccca3152017-02-22 16:32:36 -0800509 }
510
511 private boolean hasVideoCallCapabilities(DialerCall call) {
roldenburg6bd612f2018-01-18 12:57:19 -0800512 return call.getVideoTech().isAvailable(context, call.getAccountHandle());
Eric Erfanianccca3152017-02-22 16:32:36 -0800513 }
514
515 /**
516 * Determines if downgrading from a video call to an audio-only call is supported. In order to
517 * support downgrade to audio, the SDK version must be >= N and the call should NOT have the
518 * {@link android.telecom.Call.Details#CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO}.
519 *
520 * @param call The call.
521 * @return {@code true} if downgrading to an audio-only call from a video call is supported.
522 */
523 private boolean isDowngradeToAudioSupported(DialerCall call) {
Eric Erfanian938468d2017-10-24 14:05:52 -0700524 // TODO(a bug): If there is an RCS video share session, return true here
Eric Erfanianccca3152017-02-22 16:32:36 -0800525 return !call.can(CallCompat.Details.CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO);
526 }
527
528 @Override
529 public void refreshMuteState() {
530 // Restore the previous mute state
linyuh183cb712017-12-27 17:02:37 -0800531 if (automaticallyMuted
532 && AudioModeProvider.getInstance().getAudioState().isMuted() != previousMuteState) {
533 if (inCallButtonUi == null) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800534 return;
535 }
linyuh183cb712017-12-27 17:02:37 -0800536 muteClicked(previousMuteState, false /* clickedByUser */);
Eric Erfanianccca3152017-02-22 16:32:36 -0800537 }
linyuh183cb712017-12-27 17:02:37 -0800538 automaticallyMuted = false;
Eric Erfanianccca3152017-02-22 16:32:36 -0800539 }
540
541 @Override
542 public void onSaveInstanceState(Bundle outState) {
linyuh183cb712017-12-27 17:02:37 -0800543 outState.putBoolean(KEY_AUTOMATICALLY_MUTED, automaticallyMuted);
544 outState.putBoolean(KEY_PREVIOUS_MUTE_STATE, previousMuteState);
Eric Erfanianccca3152017-02-22 16:32:36 -0800545 }
546
547 @Override
548 public void onRestoreInstanceState(Bundle savedInstanceState) {
linyuh183cb712017-12-27 17:02:37 -0800549 automaticallyMuted = savedInstanceState.getBoolean(KEY_AUTOMATICALLY_MUTED, automaticallyMuted);
550 previousMuteState = savedInstanceState.getBoolean(KEY_PREVIOUS_MUTE_STATE, previousMuteState);
Eric Erfanianccca3152017-02-22 16:32:36 -0800551 }
552
553 @Override
554 public void onCameraPermissionGranted() {
linyuh183cb712017-12-27 17:02:37 -0800555 if (call != null) {
556 updateButtonsState(call);
Eric Erfanianccca3152017-02-22 16:32:36 -0800557 }
558 }
559
560 @Override
561 public void onActiveCameraSelectionChanged(boolean isUsingFrontFacingCamera) {
linyuh183cb712017-12-27 17:02:37 -0800562 if (inCallButtonUi == null) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800563 return;
564 }
linyuh183cb712017-12-27 17:02:37 -0800565 inCallButtonUi.setCameraSwitched(!isUsingFrontFacingCamera);
Eric Erfanianccca3152017-02-22 16:32:36 -0800566 }
567
568 @Override
569 public Context getContext() {
linyuh183cb712017-12-27 17:02:37 -0800570 return context;
Eric Erfanianccca3152017-02-22 16:32:36 -0800571 }
572
573 private InCallActivity getActivity() {
linyuh183cb712017-12-27 17:02:37 -0800574 if (inCallButtonUi != null) {
575 Fragment fragment = inCallButtonUi.getInCallButtonUiFragment();
Eric Erfanianccca3152017-02-22 16:32:36 -0800576 if (fragment != null) {
577 return (InCallActivity) fragment.getActivity();
578 }
579 }
580 return null;
581 }
Eric Erfanianccca3152017-02-22 16:32:36 -0800582}