blob: f49eb2b4c2af8028d7267aa1d4ae8278e81089cf [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;
Sekine Yasuakida2c0ae2017-07-07 17:01:56 +090045import com.android.incallui.call.DialerCallListener;
Eric Erfanianccca3152017-02-22 16:32:36 -080046import com.android.incallui.call.TelecomAdapter;
wangqibb94ca62018-04-27 14:34:04 -070047import com.android.incallui.call.state.DialerCallState;
Eric Erfanianccca3152017-02-22 16:32:36 -080048import com.android.incallui.incall.protocol.InCallButtonIds;
49import com.android.incallui.incall.protocol.InCallButtonUi;
50import com.android.incallui.incall.protocol.InCallButtonUiDelegate;
twyen00623aa2017-10-10 12:15:08 -070051import com.android.incallui.multisim.SwapSimWorker;
Eric Erfanian90508232017-03-24 09:31:16 -070052import com.android.incallui.videotech.utils.VideoUtils;
Eric Erfanianccca3152017-02-22 16:32:36 -080053
54/** Logic for call buttons. */
55public class CallButtonPresenter
56 implements InCallStateListener,
57 AudioModeListener,
58 IncomingCallListener,
59 InCallDetailsListener,
60 CanAddCallListener,
61 Listener,
Sekine Yasuakida2c0ae2017-07-07 17:01:56 +090062 InCallButtonUiDelegate,
63 DialerCallListener {
Eric Erfanianccca3152017-02-22 16:32:36 -080064
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;
Sekine Yasuakida2c0ae2017-07-07 17:01:56 +0900111
112 if (call != null) {
113 call.removeListener(this);
114 }
Eric Erfanianccca3152017-02-22 16:32:36 -0800115 }
116
117 @Override
118 public void onStateChange(InCallState oldState, InCallState newState, CallList callList) {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700119 Trace.beginSection("CallButtonPresenter.onStateChange");
Sekine Yasuakida2c0ae2017-07-07 17:01:56 +0900120 if (call != null) {
121 call.removeListener(this);
122 }
Eric Erfanianccca3152017-02-22 16:32:36 -0800123 if (newState == InCallState.OUTGOING) {
linyuh183cb712017-12-27 17:02:37 -0800124 call = callList.getOutgoingCall();
Eric Erfanianccca3152017-02-22 16:32:36 -0800125 } else if (newState == InCallState.INCALL) {
linyuh183cb712017-12-27 17:02:37 -0800126 call = callList.getActiveOrBackgroundCall();
Eric Erfanianccca3152017-02-22 16:32:36 -0800127
128 // When connected to voice mail, automatically shows the dialpad.
129 // (On previous releases we showed it when in-call shows up, before waiting for
130 // OUTGOING. We may want to do that once we start showing "Voice mail" label on
131 // the dialpad too.)
linyuh183cb712017-12-27 17:02:37 -0800132 if (oldState == InCallState.OUTGOING && call != null) {
133 if (call.isVoiceMailNumber() && getActivity() != null) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800134 getActivity().showDialpadFragment(true /* show */, true /* animate */);
135 }
136 }
137 } else if (newState == InCallState.INCOMING) {
138 if (getActivity() != null) {
139 getActivity().showDialpadFragment(false /* show */, true /* animate */);
140 }
linyuh183cb712017-12-27 17:02:37 -0800141 call = callList.getIncomingCall();
Eric Erfanianccca3152017-02-22 16:32:36 -0800142 } else {
linyuh183cb712017-12-27 17:02:37 -0800143 call = null;
Eric Erfanianccca3152017-02-22 16:32:36 -0800144 }
Sekine Yasuakida2c0ae2017-07-07 17:01:56 +0900145
146 if (call != null) {
147 call.addListener(this);
148 }
linyuh183cb712017-12-27 17:02:37 -0800149 updateUi(newState, call);
Eric Erfanian2ca43182017-08-31 06:57:16 -0700150 Trace.endSection();
Eric Erfanianccca3152017-02-22 16:32:36 -0800151 }
152
153 /**
154 * Updates the user interface in response to a change in the details of a call. Currently handles
155 * changes to the call buttons in response to a change in the details for a call. This is
156 * important to ensure changes to the active call are reflected in the available buttons.
157 *
158 * @param call The active call.
159 * @param details The call details.
160 */
161 @Override
162 public void onDetailsChanged(DialerCall call, android.telecom.Call.Details details) {
163 // Only update if the changes are for the currently active call
linyuh183cb712017-12-27 17:02:37 -0800164 if (inCallButtonUi != null && call != null && call.equals(this.call)) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800165 updateButtonsState(call);
166 }
167 }
168
169 @Override
170 public void onIncomingCall(InCallState oldState, InCallState newState, DialerCall call) {
171 onStateChange(oldState, newState, CallList.getInstance());
172 }
173
174 @Override
175 public void onCanAddCallChanged(boolean canAddCall) {
linyuh183cb712017-12-27 17:02:37 -0800176 if (inCallButtonUi != null && call != null) {
177 updateButtonsState(call);
Eric Erfanianccca3152017-02-22 16:32:36 -0800178 }
179 }
180
181 @Override
182 public void onAudioStateChanged(CallAudioState audioState) {
linyuh183cb712017-12-27 17:02:37 -0800183 if (inCallButtonUi != null) {
184 inCallButtonUi.setAudioState(audioState);
Eric Erfanianccca3152017-02-22 16:32:36 -0800185 }
186 }
187
188 @Override
189 public CallAudioState getCurrentAudioState() {
190 return AudioModeProvider.getInstance().getAudioState();
191 }
192
193 @Override
194 public void setAudioRoute(int route) {
195 LogUtil.i(
196 "CallButtonPresenter.setAudioRoute",
197 "sending new audio route: " + CallAudioState.audioRouteToString(route));
198 TelecomAdapter.getInstance().setAudioRoute(route);
199 }
200
201 /** Function assumes that bluetooth is not supported. */
202 @Override
203 public void toggleSpeakerphone() {
204 // This function should not be called if bluetooth is available.
205 CallAudioState audioState = getCurrentAudioState();
206 if (0 != (CallAudioState.ROUTE_BLUETOOTH & audioState.getSupportedRouteMask())) {
207 // It's clear the UI is wrong, so update the supported mode once again.
208 LogUtil.e(
209 "CallButtonPresenter", "toggling speakerphone not allowed when bluetooth supported.");
linyuh183cb712017-12-27 17:02:37 -0800210 inCallButtonUi.setAudioState(audioState);
Eric Erfanianccca3152017-02-22 16:32:36 -0800211 return;
212 }
213
214 int newRoute;
215 if (audioState.getRoute() == CallAudioState.ROUTE_SPEAKER) {
216 newRoute = CallAudioState.ROUTE_WIRED_OR_EARPIECE;
linyuh183cb712017-12-27 17:02:37 -0800217 Logger.get(context)
Eric Erfanianccca3152017-02-22 16:32:36 -0800218 .logCallImpression(
219 DialerImpression.Type.IN_CALL_SCREEN_TURN_ON_WIRED_OR_EARPIECE,
linyuh183cb712017-12-27 17:02:37 -0800220 call.getUniqueCallId(),
221 call.getTimeAddedMs());
Eric Erfanianccca3152017-02-22 16:32:36 -0800222 } else {
223 newRoute = CallAudioState.ROUTE_SPEAKER;
linyuh183cb712017-12-27 17:02:37 -0800224 Logger.get(context)
Eric Erfanianccca3152017-02-22 16:32:36 -0800225 .logCallImpression(
226 DialerImpression.Type.IN_CALL_SCREEN_TURN_ON_SPEAKERPHONE,
linyuh183cb712017-12-27 17:02:37 -0800227 call.getUniqueCallId(),
228 call.getTimeAddedMs());
Eric Erfanianccca3152017-02-22 16:32:36 -0800229 }
230
231 setAudioRoute(newRoute);
232 }
233
234 @Override
Eric Erfanian9a090c82017-03-16 19:22:24 -0700235 public void muteClicked(boolean checked, boolean clickedByUser) {
236 LogUtil.i(
237 "CallButtonPresenter", "turning on mute: %s, clicked by user: %s", checked, clickedByUser);
238 if (clickedByUser) {
linyuh183cb712017-12-27 17:02:37 -0800239 Logger.get(context)
Eric Erfanian9a090c82017-03-16 19:22:24 -0700240 .logCallImpression(
241 checked
242 ? DialerImpression.Type.IN_CALL_SCREEN_TURN_ON_MUTE
243 : DialerImpression.Type.IN_CALL_SCREEN_TURN_OFF_MUTE,
linyuh183cb712017-12-27 17:02:37 -0800244 call.getUniqueCallId(),
245 call.getTimeAddedMs());
Eric Erfanian9a090c82017-03-16 19:22:24 -0700246 }
Eric Erfanianccca3152017-02-22 16:32:36 -0800247 TelecomAdapter.getInstance().mute(checked);
248 }
249
250 @Override
251 public void holdClicked(boolean checked) {
linyuh183cb712017-12-27 17:02:37 -0800252 if (call == null) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800253 return;
254 }
255 if (checked) {
linyuh183cb712017-12-27 17:02:37 -0800256 LogUtil.i("CallButtonPresenter", "putting the call on hold: " + call);
257 call.hold();
Eric Erfanianccca3152017-02-22 16:32:36 -0800258 } else {
linyuh183cb712017-12-27 17:02:37 -0800259 LogUtil.i("CallButtonPresenter", "removing the call from hold: " + call);
260 call.unhold();
Eric Erfanianccca3152017-02-22 16:32:36 -0800261 }
262 }
263
264 @Override
265 public void swapClicked() {
linyuh183cb712017-12-27 17:02:37 -0800266 if (call == null) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800267 return;
268 }
269
linyuh183cb712017-12-27 17:02:37 -0800270 LogUtil.i("CallButtonPresenter", "swapping the call: " + call);
271 TelecomAdapter.getInstance().swap(call.getId());
Eric Erfanianccca3152017-02-22 16:32:36 -0800272 }
273
274 @Override
275 public void mergeClicked() {
linyuh183cb712017-12-27 17:02:37 -0800276 Logger.get(context)
Eric Erfanian2ca43182017-08-31 06:57:16 -0700277 .logCallImpression(
278 DialerImpression.Type.IN_CALL_MERGE_BUTTON_PRESSED,
linyuh183cb712017-12-27 17:02:37 -0800279 call.getUniqueCallId(),
280 call.getTimeAddedMs());
281 TelecomAdapter.getInstance().merge(call.getId());
Eric Erfanianccca3152017-02-22 16:32:36 -0800282 }
283
284 @Override
285 public void addCallClicked() {
linyuh183cb712017-12-27 17:02:37 -0800286 Logger.get(context)
Eric Erfanian2ca43182017-08-31 06:57:16 -0700287 .logCallImpression(
288 DialerImpression.Type.IN_CALL_ADD_CALL_BUTTON_PRESSED,
linyuh183cb712017-12-27 17:02:37 -0800289 call.getUniqueCallId(),
290 call.getTimeAddedMs());
yuegd1a269b2018-05-14 16:57:37 -0700291 if (automaticallyMutedByAddCall) {
292 // Since clicking add call button brings user to MainActivity and coming back refreshes mute
293 // state, add call button should only be clicked once during InCallActivity shows. Otherwise,
294 // we set previousMuteState wrong.
295 return;
296 }
Eric Erfanianccca3152017-02-22 16:32:36 -0800297 // Automatically mute the current call
yuegd1a269b2018-05-14 16:57:37 -0700298 automaticallyMutedByAddCall = true;
linyuh183cb712017-12-27 17:02:37 -0800299 previousMuteState = AudioModeProvider.getInstance().getAudioState().isMuted();
Eric Erfanianccca3152017-02-22 16:32:36 -0800300 // Simulate a click on the mute button
Eric Erfanian9a090c82017-03-16 19:22:24 -0700301 muteClicked(true /* checked */, false /* clickedByUser */);
Eric Erfanianccca3152017-02-22 16:32:36 -0800302 TelecomAdapter.getInstance().addCall();
303 }
304
305 @Override
306 public void showDialpadClicked(boolean checked) {
linyuh183cb712017-12-27 17:02:37 -0800307 Logger.get(context)
Eric Erfanian2ca43182017-08-31 06:57:16 -0700308 .logCallImpression(
309 DialerImpression.Type.IN_CALL_SHOW_DIALPAD_BUTTON_PRESSED,
linyuh183cb712017-12-27 17:02:37 -0800310 call.getUniqueCallId(),
311 call.getTimeAddedMs());
Eric Erfanianccca3152017-02-22 16:32:36 -0800312 LogUtil.v("CallButtonPresenter", "show dialpad " + String.valueOf(checked));
313 getActivity().showDialpadFragment(checked /* show */, true /* animate */);
314 }
315
316 @Override
317 public void changeToVideoClicked() {
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700318 LogUtil.enterBlock("CallButtonPresenter.changeToVideoClicked");
linyuh183cb712017-12-27 17:02:37 -0800319 Logger.get(context)
Eric Erfanian8369df02017-05-03 10:27:13 -0700320 .logCallImpression(
321 DialerImpression.Type.VIDEO_CALL_UPGRADE_REQUESTED,
linyuh183cb712017-12-27 17:02:37 -0800322 call.getUniqueCallId(),
323 call.getTimeAddedMs());
324 call.getVideoTech().upgradeToVideo(context);
Eric Erfanianccca3152017-02-22 16:32:36 -0800325 }
326
327 @Override
wangqif6be6172018-03-30 15:57:56 -0700328 public void changeToRttClicked() {
329 LogUtil.enterBlock("CallButtonPresenter.changeToRttClicked");
330 call.sendRttUpgradeRequest();
331 }
332
333 @Override
Eric Erfanianccca3152017-02-22 16:32:36 -0800334 public void onEndCallClicked() {
linyuh183cb712017-12-27 17:02:37 -0800335 LogUtil.i("CallButtonPresenter.onEndCallClicked", "call: " + call);
336 if (call != null) {
337 call.disconnect();
Eric Erfanianccca3152017-02-22 16:32:36 -0800338 }
339 }
340
341 @Override
342 public void showAudioRouteSelector() {
linyuh183cb712017-12-27 17:02:37 -0800343 inCallButtonUi.showAudioRouteSelector();
Eric Erfanianccca3152017-02-22 16:32:36 -0800344 }
345
twyen00623aa2017-10-10 12:15:08 -0700346 @Override
347 public void swapSimClicked() {
348 LogUtil.enterBlock("CallButtonPresenter.swapSimClicked");
twyenfc1f9cf2017-11-27 13:22:48 -0800349 Logger.get(getContext()).logImpression(Type.DUAL_SIM_CHANGE_SIM_PRESSED);
twyen00623aa2017-10-10 12:15:08 -0700350 SwapSimWorker worker =
351 new SwapSimWorker(
352 getContext(),
linyuh183cb712017-12-27 17:02:37 -0800353 call,
twyen00623aa2017-10-10 12:15:08 -0700354 InCallPresenter.getInstance().getCallList(),
linyuh183cb712017-12-27 17:02:37 -0800355 otherAccount,
twyen00623aa2017-10-10 12:15:08 -0700356 InCallPresenter.getInstance().acquireInCallUiLock("swapSim"));
357 DialerExecutorComponent.get(getContext())
358 .dialerExecutorFactory()
359 .createNonUiTaskBuilder(worker)
360 .build()
361 .executeParallel(null);
362 }
363
Eric Erfanianccca3152017-02-22 16:32:36 -0800364 /**
365 * Switches the camera between the front-facing and back-facing camera.
366 *
367 * @param useFrontFacingCamera True if we should switch to using the front-facing camera, or false
368 * if we should switch to using the back-facing camera.
369 */
370 @Override
371 public void switchCameraClicked(boolean useFrontFacingCamera) {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700372 updateCamera(useFrontFacingCamera);
Eric Erfanianccca3152017-02-22 16:32:36 -0800373 }
374
375 @Override
376 public void toggleCameraClicked() {
377 LogUtil.i("CallButtonPresenter.toggleCameraClicked", "");
linyuh183cb712017-12-27 17:02:37 -0800378 if (call == null) {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700379 return;
380 }
linyuh183cb712017-12-27 17:02:37 -0800381 Logger.get(context)
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700382 .logCallImpression(
383 DialerImpression.Type.IN_CALL_SCREEN_SWAP_CAMERA,
linyuh183cb712017-12-27 17:02:37 -0800384 call.getUniqueCallId(),
385 call.getTimeAddedMs());
Eric Erfanianccca3152017-02-22 16:32:36 -0800386 switchCameraClicked(
387 !InCallPresenter.getInstance().getInCallCameraManager().isUsingFrontFacingCamera());
388 }
389
390 /**
391 * Stop or start client's video transmission.
392 *
393 * @param pause True if pausing the local user's video, or false if starting the local user's
394 * video.
395 */
396 @Override
397 public void pauseVideoClicked(boolean pause) {
398 LogUtil.i("CallButtonPresenter.pauseVideoClicked", "%s", pause ? "pause" : "unpause");
Eric Erfanianccca3152017-02-22 16:32:36 -0800399
linyuh183cb712017-12-27 17:02:37 -0800400 Logger.get(context)
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700401 .logCallImpression(
402 pause
403 ? DialerImpression.Type.IN_CALL_SCREEN_TURN_OFF_VIDEO
404 : DialerImpression.Type.IN_CALL_SCREEN_TURN_ON_VIDEO,
linyuh183cb712017-12-27 17:02:37 -0800405 call.getUniqueCallId(),
406 call.getTimeAddedMs());
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700407
Eric Erfanianccca3152017-02-22 16:32:36 -0800408 if (pause) {
linyuh183cb712017-12-27 17:02:37 -0800409 call.getVideoTech().stopTransmission();
Eric Erfanianccca3152017-02-22 16:32:36 -0800410 } else {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700411 updateCamera(
412 InCallPresenter.getInstance().getInCallCameraManager().isUsingFrontFacingCamera());
linyuh183cb712017-12-27 17:02:37 -0800413 call.getVideoTech().resumeTransmission(context);
Eric Erfanianccca3152017-02-22 16:32:36 -0800414 }
415
linyuh183cb712017-12-27 17:02:37 -0800416 inCallButtonUi.setVideoPaused(pause);
417 inCallButtonUi.enableButton(InCallButtonIds.BUTTON_PAUSE_VIDEO, false);
Eric Erfanianccca3152017-02-22 16:32:36 -0800418 }
419
Eric Erfanian2ca43182017-08-31 06:57:16 -0700420 private void updateCamera(boolean useFrontFacingCamera) {
421 InCallCameraManager cameraManager = InCallPresenter.getInstance().getInCallCameraManager();
422 cameraManager.setUseFrontFacingCamera(useFrontFacingCamera);
423
424 String cameraId = cameraManager.getActiveCameraId();
425 if (cameraId != null) {
426 final int cameraDir =
427 cameraManager.isUsingFrontFacingCamera()
428 ? CameraDirection.CAMERA_DIRECTION_FRONT_FACING
429 : CameraDirection.CAMERA_DIRECTION_BACK_FACING;
linyuh183cb712017-12-27 17:02:37 -0800430 call.setCameraDir(cameraDir);
431 call.getVideoTech().setCamera(cameraId);
Eric Erfanian2ca43182017-08-31 06:57:16 -0700432 }
433 }
434
Eric Erfanianccca3152017-02-22 16:32:36 -0800435 private void updateUi(InCallState state, DialerCall call) {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700436 LogUtil.v("CallButtonPresenter", "updating call UI for call: %s", call);
Eric Erfanianccca3152017-02-22 16:32:36 -0800437
linyuh183cb712017-12-27 17:02:37 -0800438 if (inCallButtonUi == null) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800439 return;
440 }
441
Eric Erfanian92854e02018-07-10 18:37:33 +0000442 if (call != null) {
443 inCallButtonUi.updateInCallButtonUiColors(
444 InCallPresenter.getInstance().getThemeColorManager().getSecondaryColor());
445 }
446
Eric Erfanianccca3152017-02-22 16:32:36 -0800447 final boolean isEnabled =
448 state.isConnectingOrConnected() && !state.isIncoming() && call != null;
linyuh183cb712017-12-27 17:02:37 -0800449 inCallButtonUi.setEnabled(isEnabled);
Eric Erfanianccca3152017-02-22 16:32:36 -0800450
451 if (call == null) {
452 return;
453 }
454
455 updateButtonsState(call);
456 }
457
458 /**
459 * Updates the buttons applicable for the UI.
460 *
461 * @param call The active call.
462 */
linyuh60c1e572018-06-28 20:13:52 -0700463 @SuppressWarnings(value = {"MissingPermission"})
Eric Erfanianccca3152017-02-22 16:32:36 -0800464 private void updateButtonsState(DialerCall call) {
465 LogUtil.v("CallButtonPresenter.updateButtonsState", "");
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700466 final boolean isVideo = call.isVideoCall();
Eric Erfanianccca3152017-02-22 16:32:36 -0800467
468 // Common functionality (audio, hold, etc).
469 // Show either HOLD or SWAP, but not both. If neither HOLD or SWAP is available:
470 // (1) If the device normally can hold, show HOLD in a disabled state.
471 // (2) If the device doesn't have the concept of hold/swap, remove the button.
472 final boolean showSwap = call.can(android.telecom.Call.Details.CAPABILITY_SWAP_CONFERENCE);
473 final boolean showHold =
474 !showSwap
475 && call.can(android.telecom.Call.Details.CAPABILITY_SUPPORT_HOLD)
476 && call.can(android.telecom.Call.Details.CAPABILITY_HOLD);
wangqibb94ca62018-04-27 14:34:04 -0700477 final boolean isCallOnHold = call.getState() == DialerCallState.ONHOLD;
Eric Erfanianccca3152017-02-22 16:32:36 -0800478
479 final boolean showAddCall =
linyuh183cb712017-12-27 17:02:37 -0800480 TelecomAdapter.getInstance().canAddCall() && UserManagerCompat.isUserUnlocked(context);
erfanian9dfd6a42018-04-17 15:01:17 -0700481 // There can only be two calls so don't show the ability to merge when one of them
482 // is a speak easy call.
483 final boolean showMerge =
484 InCallPresenter.getInstance()
485 .getCallList()
486 .getAllCalls()
487 .stream()
488 .noneMatch(c -> c != null && c.isSpeakEasyCall())
489 && call.can(android.telecom.Call.Details.CAPABILITY_MERGE_CONFERENCE);
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700490 final boolean showUpgradeToVideo = !isVideo && (hasVideoCallCapabilities(call));
Eric Erfanianccca3152017-02-22 16:32:36 -0800491 final boolean showDowngradeToAudio = isVideo && isDowngradeToAudioSupported(call);
492 final boolean showMute = call.can(android.telecom.Call.Details.CAPABILITY_MUTE);
493
494 final boolean hasCameraPermission =
linyuh183cb712017-12-27 17:02:37 -0800495 isVideo && VideoUtils.hasCameraPermissionAndShownPrivacyToast(context);
Eric Erfanian938468d2017-10-24 14:05:52 -0700496 // Disabling local video doesn't seem to work when dialing. See a bug.
Eric Erfanianccca3152017-02-22 16:32:36 -0800497 final boolean showPauseVideo =
498 isVideo
wangqibb94ca62018-04-27 14:34:04 -0700499 && call.getState() != DialerCallState.DIALING
500 && call.getState() != DialerCallState.CONNECTING;
Eric Erfanianccca3152017-02-22 16:32:36 -0800501
linyuh183cb712017-12-27 17:02:37 -0800502 otherAccount = TelecomUtil.getOtherAccount(getContext(), call.getAccountHandle());
twyen38af6512017-11-22 17:12:49 -0800503 boolean showSwapSim =
twyenea3b1622018-04-24 10:45:08 -0700504 !call.isEmergencyCall()
505 && otherAccount != null
twyen2c6803d2017-11-28 11:41:49 -0800506 && !call.isVoiceMailNumber()
wangqibb94ca62018-04-27 14:34:04 -0700507 && DialerCallState.isDialing(call.getState())
twyen38af6512017-11-22 17:12:49 -0800508 // Most devices cannot make calls on 2 SIMs at the same time.
509 && InCallPresenter.getInstance().getCallList().getAllCalls().size() == 1;
twyen00623aa2017-10-10 12:15:08 -0700510
wangqi01b78272018-04-03 17:02:44 -0700511 boolean showUpgradeToRtt = call.canUpgradeToRttCall();
wangqibb94ca62018-04-27 14:34:04 -0700512 boolean enableUpgradeToRtt = showUpgradeToRtt && call.getState() == DialerCallState.ACTIVE;
wangqif6be6172018-03-30 15:57:56 -0700513
linyuh183cb712017-12-27 17:02:37 -0800514 inCallButtonUi.showButton(InCallButtonIds.BUTTON_AUDIO, true);
515 inCallButtonUi.showButton(InCallButtonIds.BUTTON_SWAP, showSwap);
516 inCallButtonUi.showButton(InCallButtonIds.BUTTON_HOLD, showHold);
517 inCallButtonUi.setHold(isCallOnHold);
518 inCallButtonUi.showButton(InCallButtonIds.BUTTON_MUTE, showMute);
519 inCallButtonUi.showButton(InCallButtonIds.BUTTON_SWAP_SIM, showSwapSim);
520 inCallButtonUi.showButton(InCallButtonIds.BUTTON_ADD_CALL, true);
521 inCallButtonUi.enableButton(InCallButtonIds.BUTTON_ADD_CALL, showAddCall);
522 inCallButtonUi.showButton(InCallButtonIds.BUTTON_UPGRADE_TO_VIDEO, showUpgradeToVideo);
wangqif6be6172018-03-30 15:57:56 -0700523 inCallButtonUi.showButton(InCallButtonIds.BUTTON_UPGRADE_TO_RTT, showUpgradeToRtt);
wangqi15514ef2018-04-04 10:50:13 -0700524 inCallButtonUi.enableButton(InCallButtonIds.BUTTON_UPGRADE_TO_RTT, enableUpgradeToRtt);
linyuh183cb712017-12-27 17:02:37 -0800525 inCallButtonUi.showButton(InCallButtonIds.BUTTON_DOWNGRADE_TO_AUDIO, showDowngradeToAudio);
526 inCallButtonUi.showButton(
roldenburgf15085f2017-11-06 12:02:06 -0800527 InCallButtonIds.BUTTON_SWITCH_CAMERA,
528 isVideo && hasCameraPermission && call.getVideoTech().isTransmitting());
linyuh183cb712017-12-27 17:02:37 -0800529 inCallButtonUi.showButton(InCallButtonIds.BUTTON_PAUSE_VIDEO, showPauseVideo);
Eric Erfanianccca3152017-02-22 16:32:36 -0800530 if (isVideo) {
linyuh183cb712017-12-27 17:02:37 -0800531 inCallButtonUi.setVideoPaused(!call.getVideoTech().isTransmitting() || !hasCameraPermission);
Eric Erfanianccca3152017-02-22 16:32:36 -0800532 }
linyuh183cb712017-12-27 17:02:37 -0800533 inCallButtonUi.showButton(InCallButtonIds.BUTTON_DIALPAD, true);
534 inCallButtonUi.showButton(InCallButtonIds.BUTTON_MERGE, showMerge);
Eric Erfanianccca3152017-02-22 16:32:36 -0800535
linyuh183cb712017-12-27 17:02:37 -0800536 inCallButtonUi.updateButtonStates();
Eric Erfanianccca3152017-02-22 16:32:36 -0800537 }
538
539 private boolean hasVideoCallCapabilities(DialerCall call) {
roldenburg6bd612f2018-01-18 12:57:19 -0800540 return call.getVideoTech().isAvailable(context, call.getAccountHandle());
Eric Erfanianccca3152017-02-22 16:32:36 -0800541 }
542
543 /**
544 * Determines if downgrading from a video call to an audio-only call is supported. In order to
545 * support downgrade to audio, the SDK version must be >= N and the call should NOT have the
546 * {@link android.telecom.Call.Details#CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO}.
547 *
548 * @param call The call.
549 * @return {@code true} if downgrading to an audio-only call from a video call is supported.
550 */
551 private boolean isDowngradeToAudioSupported(DialerCall call) {
Eric Erfanian938468d2017-10-24 14:05:52 -0700552 // TODO(a bug): If there is an RCS video share session, return true here
Eric Erfanianccca3152017-02-22 16:32:36 -0800553 return !call.can(CallCompat.Details.CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO);
554 }
555
556 @Override
557 public void refreshMuteState() {
558 // Restore the previous mute state
yuegd1a269b2018-05-14 16:57:37 -0700559 if (automaticallyMutedByAddCall
linyuh183cb712017-12-27 17:02:37 -0800560 && AudioModeProvider.getInstance().getAudioState().isMuted() != previousMuteState) {
561 if (inCallButtonUi == null) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800562 return;
563 }
linyuh183cb712017-12-27 17:02:37 -0800564 muteClicked(previousMuteState, false /* clickedByUser */);
Eric Erfanianccca3152017-02-22 16:32:36 -0800565 }
yuegd1a269b2018-05-14 16:57:37 -0700566 automaticallyMutedByAddCall = false;
Eric Erfanianccca3152017-02-22 16:32:36 -0800567 }
568
569 @Override
570 public void onSaveInstanceState(Bundle outState) {
yuegd1a269b2018-05-14 16:57:37 -0700571 outState.putBoolean(KEY_AUTOMATICALLY_MUTED_BY_ADD_CALL, automaticallyMutedByAddCall);
linyuh183cb712017-12-27 17:02:37 -0800572 outState.putBoolean(KEY_PREVIOUS_MUTE_STATE, previousMuteState);
Eric Erfanianccca3152017-02-22 16:32:36 -0800573 }
574
575 @Override
576 public void onRestoreInstanceState(Bundle savedInstanceState) {
yuegd1a269b2018-05-14 16:57:37 -0700577 automaticallyMutedByAddCall =
578 savedInstanceState.getBoolean(
579 KEY_AUTOMATICALLY_MUTED_BY_ADD_CALL, automaticallyMutedByAddCall);
linyuh183cb712017-12-27 17:02:37 -0800580 previousMuteState = savedInstanceState.getBoolean(KEY_PREVIOUS_MUTE_STATE, previousMuteState);
Eric Erfanianccca3152017-02-22 16:32:36 -0800581 }
582
583 @Override
584 public void onCameraPermissionGranted() {
linyuh183cb712017-12-27 17:02:37 -0800585 if (call != null) {
586 updateButtonsState(call);
Eric Erfanianccca3152017-02-22 16:32:36 -0800587 }
588 }
589
590 @Override
591 public void onActiveCameraSelectionChanged(boolean isUsingFrontFacingCamera) {
linyuh183cb712017-12-27 17:02:37 -0800592 if (inCallButtonUi == null) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800593 return;
594 }
linyuh183cb712017-12-27 17:02:37 -0800595 inCallButtonUi.setCameraSwitched(!isUsingFrontFacingCamera);
Eric Erfanianccca3152017-02-22 16:32:36 -0800596 }
597
598 @Override
Sekine Yasuakida2c0ae2017-07-07 17:01:56 +0900599 public void onDialerCallSessionModificationStateChange() {
600 if (inCallButtonUi != null && call != null) {
601 inCallButtonUi.enableButton(InCallButtonIds.BUTTON_PAUSE_VIDEO, true);
602 updateButtonsState(call);
603 }
604 }
605
606 @Override
607 public void onDialerCallDisconnect() {}
608
609 @Override
610 public void onDialerCallUpdate() {}
611
612 @Override
613 public void onDialerCallChildNumberChange() {}
614
615 @Override
616 public void onDialerCallLastForwardedNumberChange() {}
617
618 @Override
619 public void onDialerCallUpgradeToVideo() {}
620
621 @Override
622 public void onWiFiToLteHandover() {}
623
624 @Override
625 public void onHandoverToWifiFailure() {}
626
627 @Override
628 public void onInternationalCallOnWifi() {}
629
630 @Override
631 public void onEnrichedCallSessionUpdate() {}
632
633 @Override
Eric Erfanianccca3152017-02-22 16:32:36 -0800634 public Context getContext() {
linyuh183cb712017-12-27 17:02:37 -0800635 return context;
Eric Erfanianccca3152017-02-22 16:32:36 -0800636 }
637
638 private InCallActivity getActivity() {
linyuh183cb712017-12-27 17:02:37 -0800639 if (inCallButtonUi != null) {
640 Fragment fragment = inCallButtonUi.getInCallButtonUiFragment();
Eric Erfanianccca3152017-02-22 16:32:36 -0800641 if (fragment != null) {
642 return (InCallActivity) fragment.getActivity();
643 }
644 }
645 return null;
646 }
Eric Erfanianccca3152017-02-22 16:32:36 -0800647}