blob: 7d12d52812d9128e3b4aa00d1ea58510407e635a [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;
wangqibb94ca62018-04-27 14:34:04 -070046import com.android.incallui.call.state.DialerCallState;
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
yuegd1a269b2018-05-14 16:57:37 -070063 private static final String KEY_AUTOMATICALLY_MUTED_BY_ADD_CALL =
64 "incall_key_automatically_muted_by_add_call";
Eric Erfanianccca3152017-02-22 16:32:36 -080065 private static final String KEY_PREVIOUS_MUTE_STATE = "incall_key_previous_mute_state";
66
linyuh183cb712017-12-27 17:02:37 -080067 private final Context context;
68 private InCallButtonUi inCallButtonUi;
69 private DialerCall call;
yuegd1a269b2018-05-14 16:57:37 -070070 private boolean automaticallyMutedByAddCall = false;
linyuh183cb712017-12-27 17:02:37 -080071 private boolean previousMuteState = false;
Eric Erfanianccca3152017-02-22 16:32:36 -080072 private boolean isInCallButtonUiReady;
linyuh183cb712017-12-27 17:02:37 -080073 private PhoneAccountHandle otherAccount;
Eric Erfanianccca3152017-02-22 16:32:36 -080074
75 public CallButtonPresenter(Context context) {
linyuh183cb712017-12-27 17:02:37 -080076 this.context = context.getApplicationContext();
Eric Erfanianccca3152017-02-22 16:32:36 -080077 }
78
79 @Override
80 public void onInCallButtonUiReady(InCallButtonUi ui) {
81 Assert.checkState(!isInCallButtonUiReady);
linyuh183cb712017-12-27 17:02:37 -080082 inCallButtonUi = ui;
Eric Erfanianccca3152017-02-22 16:32:36 -080083 AudioModeProvider.getInstance().addListener(this);
84
85 // register for call state changes last
86 final InCallPresenter inCallPresenter = InCallPresenter.getInstance();
87 inCallPresenter.addListener(this);
88 inCallPresenter.addIncomingCallListener(this);
89 inCallPresenter.addDetailsListener(this);
90 inCallPresenter.addCanAddCallListener(this);
91 inCallPresenter.getInCallCameraManager().addCameraSelectionListener(this);
92
93 // Update the buttons state immediately for the current call
94 onStateChange(InCallState.NO_CALLS, inCallPresenter.getInCallState(), CallList.getInstance());
95 isInCallButtonUiReady = true;
96 }
97
98 @Override
99 public void onInCallButtonUiUnready() {
100 Assert.checkState(isInCallButtonUiReady);
linyuh183cb712017-12-27 17:02:37 -0800101 inCallButtonUi = null;
Eric Erfanianccca3152017-02-22 16:32:36 -0800102 InCallPresenter.getInstance().removeListener(this);
103 AudioModeProvider.getInstance().removeListener(this);
104 InCallPresenter.getInstance().removeIncomingCallListener(this);
105 InCallPresenter.getInstance().removeDetailsListener(this);
106 InCallPresenter.getInstance().getInCallCameraManager().removeCameraSelectionListener(this);
107 InCallPresenter.getInstance().removeCanAddCallListener(this);
108 isInCallButtonUiReady = false;
109 }
110
111 @Override
112 public void onStateChange(InCallState oldState, InCallState newState, CallList callList) {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700113 Trace.beginSection("CallButtonPresenter.onStateChange");
Eric Erfanianccca3152017-02-22 16:32:36 -0800114 if (newState == InCallState.OUTGOING) {
linyuh183cb712017-12-27 17:02:37 -0800115 call = callList.getOutgoingCall();
Eric Erfanianccca3152017-02-22 16:32:36 -0800116 } else if (newState == InCallState.INCALL) {
linyuh183cb712017-12-27 17:02:37 -0800117 call = callList.getActiveOrBackgroundCall();
Eric Erfanianccca3152017-02-22 16:32:36 -0800118
119 // When connected to voice mail, automatically shows the dialpad.
120 // (On previous releases we showed it when in-call shows up, before waiting for
121 // OUTGOING. We may want to do that once we start showing "Voice mail" label on
122 // the dialpad too.)
linyuh183cb712017-12-27 17:02:37 -0800123 if (oldState == InCallState.OUTGOING && call != null) {
124 if (call.isVoiceMailNumber() && getActivity() != null) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800125 getActivity().showDialpadFragment(true /* show */, true /* animate */);
126 }
127 }
128 } else if (newState == InCallState.INCOMING) {
129 if (getActivity() != null) {
130 getActivity().showDialpadFragment(false /* show */, true /* animate */);
131 }
linyuh183cb712017-12-27 17:02:37 -0800132 call = callList.getIncomingCall();
Eric Erfanianccca3152017-02-22 16:32:36 -0800133 } else {
linyuh183cb712017-12-27 17:02:37 -0800134 call = null;
Eric Erfanianccca3152017-02-22 16:32:36 -0800135 }
linyuh183cb712017-12-27 17:02:37 -0800136 updateUi(newState, call);
Eric Erfanian2ca43182017-08-31 06:57:16 -0700137 Trace.endSection();
Eric Erfanianccca3152017-02-22 16:32:36 -0800138 }
139
140 /**
141 * Updates the user interface in response to a change in the details of a call. Currently handles
142 * changes to the call buttons in response to a change in the details for a call. This is
143 * important to ensure changes to the active call are reflected in the available buttons.
144 *
145 * @param call The active call.
146 * @param details The call details.
147 */
148 @Override
149 public void onDetailsChanged(DialerCall call, android.telecom.Call.Details details) {
150 // Only update if the changes are for the currently active call
linyuh183cb712017-12-27 17:02:37 -0800151 if (inCallButtonUi != null && call != null && call.equals(this.call)) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800152 updateButtonsState(call);
153 }
154 }
155
156 @Override
157 public void onIncomingCall(InCallState oldState, InCallState newState, DialerCall call) {
158 onStateChange(oldState, newState, CallList.getInstance());
159 }
160
161 @Override
162 public void onCanAddCallChanged(boolean canAddCall) {
linyuh183cb712017-12-27 17:02:37 -0800163 if (inCallButtonUi != null && call != null) {
164 updateButtonsState(call);
Eric Erfanianccca3152017-02-22 16:32:36 -0800165 }
166 }
167
168 @Override
169 public void onAudioStateChanged(CallAudioState audioState) {
linyuh183cb712017-12-27 17:02:37 -0800170 if (inCallButtonUi != null) {
171 inCallButtonUi.setAudioState(audioState);
Eric Erfanianccca3152017-02-22 16:32:36 -0800172 }
173 }
174
175 @Override
176 public CallAudioState getCurrentAudioState() {
177 return AudioModeProvider.getInstance().getAudioState();
178 }
179
180 @Override
181 public void setAudioRoute(int route) {
182 LogUtil.i(
183 "CallButtonPresenter.setAudioRoute",
184 "sending new audio route: " + CallAudioState.audioRouteToString(route));
185 TelecomAdapter.getInstance().setAudioRoute(route);
186 }
187
188 /** Function assumes that bluetooth is not supported. */
189 @Override
190 public void toggleSpeakerphone() {
191 // This function should not be called if bluetooth is available.
192 CallAudioState audioState = getCurrentAudioState();
193 if (0 != (CallAudioState.ROUTE_BLUETOOTH & audioState.getSupportedRouteMask())) {
194 // It's clear the UI is wrong, so update the supported mode once again.
195 LogUtil.e(
196 "CallButtonPresenter", "toggling speakerphone not allowed when bluetooth supported.");
linyuh183cb712017-12-27 17:02:37 -0800197 inCallButtonUi.setAudioState(audioState);
Eric Erfanianccca3152017-02-22 16:32:36 -0800198 return;
199 }
200
201 int newRoute;
202 if (audioState.getRoute() == CallAudioState.ROUTE_SPEAKER) {
203 newRoute = CallAudioState.ROUTE_WIRED_OR_EARPIECE;
linyuh183cb712017-12-27 17:02:37 -0800204 Logger.get(context)
Eric Erfanianccca3152017-02-22 16:32:36 -0800205 .logCallImpression(
206 DialerImpression.Type.IN_CALL_SCREEN_TURN_ON_WIRED_OR_EARPIECE,
linyuh183cb712017-12-27 17:02:37 -0800207 call.getUniqueCallId(),
208 call.getTimeAddedMs());
Eric Erfanianccca3152017-02-22 16:32:36 -0800209 } else {
210 newRoute = CallAudioState.ROUTE_SPEAKER;
linyuh183cb712017-12-27 17:02:37 -0800211 Logger.get(context)
Eric Erfanianccca3152017-02-22 16:32:36 -0800212 .logCallImpression(
213 DialerImpression.Type.IN_CALL_SCREEN_TURN_ON_SPEAKERPHONE,
linyuh183cb712017-12-27 17:02:37 -0800214 call.getUniqueCallId(),
215 call.getTimeAddedMs());
Eric Erfanianccca3152017-02-22 16:32:36 -0800216 }
217
218 setAudioRoute(newRoute);
219 }
220
221 @Override
Eric Erfanian9a090c82017-03-16 19:22:24 -0700222 public void muteClicked(boolean checked, boolean clickedByUser) {
223 LogUtil.i(
224 "CallButtonPresenter", "turning on mute: %s, clicked by user: %s", checked, clickedByUser);
225 if (clickedByUser) {
linyuh183cb712017-12-27 17:02:37 -0800226 Logger.get(context)
Eric Erfanian9a090c82017-03-16 19:22:24 -0700227 .logCallImpression(
228 checked
229 ? DialerImpression.Type.IN_CALL_SCREEN_TURN_ON_MUTE
230 : DialerImpression.Type.IN_CALL_SCREEN_TURN_OFF_MUTE,
linyuh183cb712017-12-27 17:02:37 -0800231 call.getUniqueCallId(),
232 call.getTimeAddedMs());
Eric Erfanian9a090c82017-03-16 19:22:24 -0700233 }
Eric Erfanianccca3152017-02-22 16:32:36 -0800234 TelecomAdapter.getInstance().mute(checked);
235 }
236
237 @Override
238 public void holdClicked(boolean checked) {
linyuh183cb712017-12-27 17:02:37 -0800239 if (call == null) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800240 return;
241 }
242 if (checked) {
linyuh183cb712017-12-27 17:02:37 -0800243 LogUtil.i("CallButtonPresenter", "putting the call on hold: " + call);
244 call.hold();
Eric Erfanianccca3152017-02-22 16:32:36 -0800245 } else {
linyuh183cb712017-12-27 17:02:37 -0800246 LogUtil.i("CallButtonPresenter", "removing the call from hold: " + call);
247 call.unhold();
Eric Erfanianccca3152017-02-22 16:32:36 -0800248 }
249 }
250
251 @Override
252 public void swapClicked() {
linyuh183cb712017-12-27 17:02:37 -0800253 if (call == null) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800254 return;
255 }
256
linyuh183cb712017-12-27 17:02:37 -0800257 LogUtil.i("CallButtonPresenter", "swapping the call: " + call);
258 TelecomAdapter.getInstance().swap(call.getId());
Eric Erfanianccca3152017-02-22 16:32:36 -0800259 }
260
261 @Override
262 public void mergeClicked() {
linyuh183cb712017-12-27 17:02:37 -0800263 Logger.get(context)
Eric Erfanian2ca43182017-08-31 06:57:16 -0700264 .logCallImpression(
265 DialerImpression.Type.IN_CALL_MERGE_BUTTON_PRESSED,
linyuh183cb712017-12-27 17:02:37 -0800266 call.getUniqueCallId(),
267 call.getTimeAddedMs());
268 TelecomAdapter.getInstance().merge(call.getId());
Eric Erfanianccca3152017-02-22 16:32:36 -0800269 }
270
271 @Override
272 public void addCallClicked() {
linyuh183cb712017-12-27 17:02:37 -0800273 Logger.get(context)
Eric Erfanian2ca43182017-08-31 06:57:16 -0700274 .logCallImpression(
275 DialerImpression.Type.IN_CALL_ADD_CALL_BUTTON_PRESSED,
linyuh183cb712017-12-27 17:02:37 -0800276 call.getUniqueCallId(),
277 call.getTimeAddedMs());
yuegd1a269b2018-05-14 16:57:37 -0700278 if (automaticallyMutedByAddCall) {
279 // Since clicking add call button brings user to MainActivity and coming back refreshes mute
280 // state, add call button should only be clicked once during InCallActivity shows. Otherwise,
281 // we set previousMuteState wrong.
282 return;
283 }
Eric Erfanianccca3152017-02-22 16:32:36 -0800284 // Automatically mute the current call
yuegd1a269b2018-05-14 16:57:37 -0700285 automaticallyMutedByAddCall = true;
linyuh183cb712017-12-27 17:02:37 -0800286 previousMuteState = AudioModeProvider.getInstance().getAudioState().isMuted();
Eric Erfanianccca3152017-02-22 16:32:36 -0800287 // Simulate a click on the mute button
Eric Erfanian9a090c82017-03-16 19:22:24 -0700288 muteClicked(true /* checked */, false /* clickedByUser */);
Eric Erfanianccca3152017-02-22 16:32:36 -0800289 TelecomAdapter.getInstance().addCall();
290 }
291
292 @Override
293 public void showDialpadClicked(boolean checked) {
linyuh183cb712017-12-27 17:02:37 -0800294 Logger.get(context)
Eric Erfanian2ca43182017-08-31 06:57:16 -0700295 .logCallImpression(
296 DialerImpression.Type.IN_CALL_SHOW_DIALPAD_BUTTON_PRESSED,
linyuh183cb712017-12-27 17:02:37 -0800297 call.getUniqueCallId(),
298 call.getTimeAddedMs());
Eric Erfanianccca3152017-02-22 16:32:36 -0800299 LogUtil.v("CallButtonPresenter", "show dialpad " + String.valueOf(checked));
300 getActivity().showDialpadFragment(checked /* show */, true /* animate */);
301 }
302
303 @Override
304 public void changeToVideoClicked() {
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700305 LogUtil.enterBlock("CallButtonPresenter.changeToVideoClicked");
linyuh183cb712017-12-27 17:02:37 -0800306 Logger.get(context)
Eric Erfanian8369df02017-05-03 10:27:13 -0700307 .logCallImpression(
308 DialerImpression.Type.VIDEO_CALL_UPGRADE_REQUESTED,
linyuh183cb712017-12-27 17:02:37 -0800309 call.getUniqueCallId(),
310 call.getTimeAddedMs());
311 call.getVideoTech().upgradeToVideo(context);
Eric Erfanianccca3152017-02-22 16:32:36 -0800312 }
313
314 @Override
wangqif6be6172018-03-30 15:57:56 -0700315 public void changeToRttClicked() {
316 LogUtil.enterBlock("CallButtonPresenter.changeToRttClicked");
317 call.sendRttUpgradeRequest();
318 }
319
320 @Override
Eric Erfanianccca3152017-02-22 16:32:36 -0800321 public void onEndCallClicked() {
linyuh183cb712017-12-27 17:02:37 -0800322 LogUtil.i("CallButtonPresenter.onEndCallClicked", "call: " + call);
323 if (call != null) {
324 call.disconnect();
Eric Erfanianccca3152017-02-22 16:32:36 -0800325 }
326 }
327
328 @Override
329 public void showAudioRouteSelector() {
linyuh183cb712017-12-27 17:02:37 -0800330 inCallButtonUi.showAudioRouteSelector();
Eric Erfanianccca3152017-02-22 16:32:36 -0800331 }
332
twyen00623aa2017-10-10 12:15:08 -0700333 @Override
334 public void swapSimClicked() {
335 LogUtil.enterBlock("CallButtonPresenter.swapSimClicked");
twyenfc1f9cf2017-11-27 13:22:48 -0800336 Logger.get(getContext()).logImpression(Type.DUAL_SIM_CHANGE_SIM_PRESSED);
twyen00623aa2017-10-10 12:15:08 -0700337 SwapSimWorker worker =
338 new SwapSimWorker(
339 getContext(),
linyuh183cb712017-12-27 17:02:37 -0800340 call,
twyen00623aa2017-10-10 12:15:08 -0700341 InCallPresenter.getInstance().getCallList(),
linyuh183cb712017-12-27 17:02:37 -0800342 otherAccount,
twyen00623aa2017-10-10 12:15:08 -0700343 InCallPresenter.getInstance().acquireInCallUiLock("swapSim"));
344 DialerExecutorComponent.get(getContext())
345 .dialerExecutorFactory()
346 .createNonUiTaskBuilder(worker)
347 .build()
348 .executeParallel(null);
349 }
350
Eric Erfanianccca3152017-02-22 16:32:36 -0800351 /**
352 * Switches the camera between the front-facing and back-facing camera.
353 *
354 * @param useFrontFacingCamera True if we should switch to using the front-facing camera, or false
355 * if we should switch to using the back-facing camera.
356 */
357 @Override
358 public void switchCameraClicked(boolean useFrontFacingCamera) {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700359 updateCamera(useFrontFacingCamera);
Eric Erfanianccca3152017-02-22 16:32:36 -0800360 }
361
362 @Override
363 public void toggleCameraClicked() {
364 LogUtil.i("CallButtonPresenter.toggleCameraClicked", "");
linyuh183cb712017-12-27 17:02:37 -0800365 if (call == null) {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700366 return;
367 }
linyuh183cb712017-12-27 17:02:37 -0800368 Logger.get(context)
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700369 .logCallImpression(
370 DialerImpression.Type.IN_CALL_SCREEN_SWAP_CAMERA,
linyuh183cb712017-12-27 17:02:37 -0800371 call.getUniqueCallId(),
372 call.getTimeAddedMs());
Eric Erfanianccca3152017-02-22 16:32:36 -0800373 switchCameraClicked(
374 !InCallPresenter.getInstance().getInCallCameraManager().isUsingFrontFacingCamera());
375 }
376
377 /**
378 * Stop or start client's video transmission.
379 *
380 * @param pause True if pausing the local user's video, or false if starting the local user's
381 * video.
382 */
383 @Override
384 public void pauseVideoClicked(boolean pause) {
385 LogUtil.i("CallButtonPresenter.pauseVideoClicked", "%s", pause ? "pause" : "unpause");
Eric Erfanianccca3152017-02-22 16:32:36 -0800386
linyuh183cb712017-12-27 17:02:37 -0800387 Logger.get(context)
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700388 .logCallImpression(
389 pause
390 ? DialerImpression.Type.IN_CALL_SCREEN_TURN_OFF_VIDEO
391 : DialerImpression.Type.IN_CALL_SCREEN_TURN_ON_VIDEO,
linyuh183cb712017-12-27 17:02:37 -0800392 call.getUniqueCallId(),
393 call.getTimeAddedMs());
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700394
Eric Erfanianccca3152017-02-22 16:32:36 -0800395 if (pause) {
linyuh183cb712017-12-27 17:02:37 -0800396 call.getVideoTech().setCamera(null);
397 call.getVideoTech().stopTransmission();
Eric Erfanianccca3152017-02-22 16:32:36 -0800398 } else {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700399 updateCamera(
400 InCallPresenter.getInstance().getInCallCameraManager().isUsingFrontFacingCamera());
linyuh183cb712017-12-27 17:02:37 -0800401 call.getVideoTech().resumeTransmission(context);
Eric Erfanianccca3152017-02-22 16:32:36 -0800402 }
403
linyuh183cb712017-12-27 17:02:37 -0800404 inCallButtonUi.setVideoPaused(pause);
405 inCallButtonUi.enableButton(InCallButtonIds.BUTTON_PAUSE_VIDEO, false);
Eric Erfanianccca3152017-02-22 16:32:36 -0800406 }
407
Eric Erfanian2ca43182017-08-31 06:57:16 -0700408 private void updateCamera(boolean useFrontFacingCamera) {
409 InCallCameraManager cameraManager = InCallPresenter.getInstance().getInCallCameraManager();
410 cameraManager.setUseFrontFacingCamera(useFrontFacingCamera);
411
412 String cameraId = cameraManager.getActiveCameraId();
413 if (cameraId != null) {
414 final int cameraDir =
415 cameraManager.isUsingFrontFacingCamera()
416 ? CameraDirection.CAMERA_DIRECTION_FRONT_FACING
417 : CameraDirection.CAMERA_DIRECTION_BACK_FACING;
linyuh183cb712017-12-27 17:02:37 -0800418 call.setCameraDir(cameraDir);
419 call.getVideoTech().setCamera(cameraId);
Eric Erfanian2ca43182017-08-31 06:57:16 -0700420 }
421 }
422
Eric Erfanianccca3152017-02-22 16:32:36 -0800423 private void updateUi(InCallState state, DialerCall call) {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700424 LogUtil.v("CallButtonPresenter", "updating call UI for call: %s", call);
Eric Erfanianccca3152017-02-22 16:32:36 -0800425
linyuh183cb712017-12-27 17:02:37 -0800426 if (inCallButtonUi == null) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800427 return;
428 }
429
Eric Erfanian92854e02018-07-10 18:37:33 +0000430 if (call != null) {
431 inCallButtonUi.updateInCallButtonUiColors(
432 InCallPresenter.getInstance().getThemeColorManager().getSecondaryColor());
433 }
434
Eric Erfanianccca3152017-02-22 16:32:36 -0800435 final boolean isEnabled =
436 state.isConnectingOrConnected() && !state.isIncoming() && call != null;
linyuh183cb712017-12-27 17:02:37 -0800437 inCallButtonUi.setEnabled(isEnabled);
Eric Erfanianccca3152017-02-22 16:32:36 -0800438
439 if (call == null) {
440 return;
441 }
442
443 updateButtonsState(call);
444 }
445
446 /**
447 * Updates the buttons applicable for the UI.
448 *
449 * @param call The active call.
450 */
linyuh60c1e572018-06-28 20:13:52 -0700451 @SuppressWarnings(value = {"MissingPermission"})
Eric Erfanianccca3152017-02-22 16:32:36 -0800452 private void updateButtonsState(DialerCall call) {
453 LogUtil.v("CallButtonPresenter.updateButtonsState", "");
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700454 final boolean isVideo = call.isVideoCall();
Eric Erfanianccca3152017-02-22 16:32:36 -0800455
456 // Common functionality (audio, hold, etc).
457 // Show either HOLD or SWAP, but not both. If neither HOLD or SWAP is available:
458 // (1) If the device normally can hold, show HOLD in a disabled state.
459 // (2) If the device doesn't have the concept of hold/swap, remove the button.
460 final boolean showSwap = call.can(android.telecom.Call.Details.CAPABILITY_SWAP_CONFERENCE);
461 final boolean showHold =
462 !showSwap
463 && call.can(android.telecom.Call.Details.CAPABILITY_SUPPORT_HOLD)
464 && call.can(android.telecom.Call.Details.CAPABILITY_HOLD);
wangqibb94ca62018-04-27 14:34:04 -0700465 final boolean isCallOnHold = call.getState() == DialerCallState.ONHOLD;
Eric Erfanianccca3152017-02-22 16:32:36 -0800466
467 final boolean showAddCall =
linyuh183cb712017-12-27 17:02:37 -0800468 TelecomAdapter.getInstance().canAddCall() && UserManagerCompat.isUserUnlocked(context);
erfanian9dfd6a42018-04-17 15:01:17 -0700469 // There can only be two calls so don't show the ability to merge when one of them
470 // is a speak easy call.
471 final boolean showMerge =
472 InCallPresenter.getInstance()
473 .getCallList()
474 .getAllCalls()
475 .stream()
476 .noneMatch(c -> c != null && c.isSpeakEasyCall())
477 && call.can(android.telecom.Call.Details.CAPABILITY_MERGE_CONFERENCE);
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700478 final boolean showUpgradeToVideo = !isVideo && (hasVideoCallCapabilities(call));
Eric Erfanianccca3152017-02-22 16:32:36 -0800479 final boolean showDowngradeToAudio = isVideo && isDowngradeToAudioSupported(call);
480 final boolean showMute = call.can(android.telecom.Call.Details.CAPABILITY_MUTE);
481
482 final boolean hasCameraPermission =
linyuh183cb712017-12-27 17:02:37 -0800483 isVideo && VideoUtils.hasCameraPermissionAndShownPrivacyToast(context);
Eric Erfanian938468d2017-10-24 14:05:52 -0700484 // Disabling local video doesn't seem to work when dialing. See a bug.
Eric Erfanianccca3152017-02-22 16:32:36 -0800485 final boolean showPauseVideo =
486 isVideo
wangqibb94ca62018-04-27 14:34:04 -0700487 && call.getState() != DialerCallState.DIALING
488 && call.getState() != DialerCallState.CONNECTING;
Eric Erfanianccca3152017-02-22 16:32:36 -0800489
linyuh183cb712017-12-27 17:02:37 -0800490 otherAccount = TelecomUtil.getOtherAccount(getContext(), call.getAccountHandle());
twyen38af6512017-11-22 17:12:49 -0800491 boolean showSwapSim =
twyenea3b1622018-04-24 10:45:08 -0700492 !call.isEmergencyCall()
493 && otherAccount != null
twyen2c6803d2017-11-28 11:41:49 -0800494 && !call.isVoiceMailNumber()
wangqibb94ca62018-04-27 14:34:04 -0700495 && DialerCallState.isDialing(call.getState())
twyen38af6512017-11-22 17:12:49 -0800496 // Most devices cannot make calls on 2 SIMs at the same time.
497 && InCallPresenter.getInstance().getCallList().getAllCalls().size() == 1;
twyen00623aa2017-10-10 12:15:08 -0700498
wangqi01b78272018-04-03 17:02:44 -0700499 boolean showUpgradeToRtt = call.canUpgradeToRttCall();
wangqibb94ca62018-04-27 14:34:04 -0700500 boolean enableUpgradeToRtt = showUpgradeToRtt && call.getState() == DialerCallState.ACTIVE;
wangqif6be6172018-03-30 15:57:56 -0700501
linyuh183cb712017-12-27 17:02:37 -0800502 inCallButtonUi.showButton(InCallButtonIds.BUTTON_AUDIO, true);
503 inCallButtonUi.showButton(InCallButtonIds.BUTTON_SWAP, showSwap);
504 inCallButtonUi.showButton(InCallButtonIds.BUTTON_HOLD, showHold);
505 inCallButtonUi.setHold(isCallOnHold);
506 inCallButtonUi.showButton(InCallButtonIds.BUTTON_MUTE, showMute);
507 inCallButtonUi.showButton(InCallButtonIds.BUTTON_SWAP_SIM, showSwapSim);
508 inCallButtonUi.showButton(InCallButtonIds.BUTTON_ADD_CALL, true);
509 inCallButtonUi.enableButton(InCallButtonIds.BUTTON_ADD_CALL, showAddCall);
510 inCallButtonUi.showButton(InCallButtonIds.BUTTON_UPGRADE_TO_VIDEO, showUpgradeToVideo);
wangqif6be6172018-03-30 15:57:56 -0700511 inCallButtonUi.showButton(InCallButtonIds.BUTTON_UPGRADE_TO_RTT, showUpgradeToRtt);
wangqi15514ef2018-04-04 10:50:13 -0700512 inCallButtonUi.enableButton(InCallButtonIds.BUTTON_UPGRADE_TO_RTT, enableUpgradeToRtt);
linyuh183cb712017-12-27 17:02:37 -0800513 inCallButtonUi.showButton(InCallButtonIds.BUTTON_DOWNGRADE_TO_AUDIO, showDowngradeToAudio);
514 inCallButtonUi.showButton(
roldenburgf15085f2017-11-06 12:02:06 -0800515 InCallButtonIds.BUTTON_SWITCH_CAMERA,
516 isVideo && hasCameraPermission && call.getVideoTech().isTransmitting());
linyuh183cb712017-12-27 17:02:37 -0800517 inCallButtonUi.showButton(InCallButtonIds.BUTTON_PAUSE_VIDEO, showPauseVideo);
Eric Erfanianccca3152017-02-22 16:32:36 -0800518 if (isVideo) {
linyuh183cb712017-12-27 17:02:37 -0800519 inCallButtonUi.setVideoPaused(!call.getVideoTech().isTransmitting() || !hasCameraPermission);
Eric Erfanianccca3152017-02-22 16:32:36 -0800520 }
linyuh183cb712017-12-27 17:02:37 -0800521 inCallButtonUi.showButton(InCallButtonIds.BUTTON_DIALPAD, true);
522 inCallButtonUi.showButton(InCallButtonIds.BUTTON_MERGE, showMerge);
Eric Erfanianccca3152017-02-22 16:32:36 -0800523
linyuh183cb712017-12-27 17:02:37 -0800524 inCallButtonUi.updateButtonStates();
Eric Erfanianccca3152017-02-22 16:32:36 -0800525 }
526
527 private boolean hasVideoCallCapabilities(DialerCall call) {
roldenburg6bd612f2018-01-18 12:57:19 -0800528 return call.getVideoTech().isAvailable(context, call.getAccountHandle());
Eric Erfanianccca3152017-02-22 16:32:36 -0800529 }
530
531 /**
532 * Determines if downgrading from a video call to an audio-only call is supported. In order to
533 * support downgrade to audio, the SDK version must be >= N and the call should NOT have the
534 * {@link android.telecom.Call.Details#CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO}.
535 *
536 * @param call The call.
537 * @return {@code true} if downgrading to an audio-only call from a video call is supported.
538 */
539 private boolean isDowngradeToAudioSupported(DialerCall call) {
Eric Erfanian938468d2017-10-24 14:05:52 -0700540 // TODO(a bug): If there is an RCS video share session, return true here
Eric Erfanianccca3152017-02-22 16:32:36 -0800541 return !call.can(CallCompat.Details.CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO);
542 }
543
544 @Override
545 public void refreshMuteState() {
546 // Restore the previous mute state
yuegd1a269b2018-05-14 16:57:37 -0700547 if (automaticallyMutedByAddCall
linyuh183cb712017-12-27 17:02:37 -0800548 && AudioModeProvider.getInstance().getAudioState().isMuted() != previousMuteState) {
549 if (inCallButtonUi == null) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800550 return;
551 }
linyuh183cb712017-12-27 17:02:37 -0800552 muteClicked(previousMuteState, false /* clickedByUser */);
Eric Erfanianccca3152017-02-22 16:32:36 -0800553 }
yuegd1a269b2018-05-14 16:57:37 -0700554 automaticallyMutedByAddCall = false;
Eric Erfanianccca3152017-02-22 16:32:36 -0800555 }
556
557 @Override
558 public void onSaveInstanceState(Bundle outState) {
yuegd1a269b2018-05-14 16:57:37 -0700559 outState.putBoolean(KEY_AUTOMATICALLY_MUTED_BY_ADD_CALL, automaticallyMutedByAddCall);
linyuh183cb712017-12-27 17:02:37 -0800560 outState.putBoolean(KEY_PREVIOUS_MUTE_STATE, previousMuteState);
Eric Erfanianccca3152017-02-22 16:32:36 -0800561 }
562
563 @Override
564 public void onRestoreInstanceState(Bundle savedInstanceState) {
yuegd1a269b2018-05-14 16:57:37 -0700565 automaticallyMutedByAddCall =
566 savedInstanceState.getBoolean(
567 KEY_AUTOMATICALLY_MUTED_BY_ADD_CALL, automaticallyMutedByAddCall);
linyuh183cb712017-12-27 17:02:37 -0800568 previousMuteState = savedInstanceState.getBoolean(KEY_PREVIOUS_MUTE_STATE, previousMuteState);
Eric Erfanianccca3152017-02-22 16:32:36 -0800569 }
570
571 @Override
572 public void onCameraPermissionGranted() {
linyuh183cb712017-12-27 17:02:37 -0800573 if (call != null) {
574 updateButtonsState(call);
Eric Erfanianccca3152017-02-22 16:32:36 -0800575 }
576 }
577
578 @Override
579 public void onActiveCameraSelectionChanged(boolean isUsingFrontFacingCamera) {
linyuh183cb712017-12-27 17:02:37 -0800580 if (inCallButtonUi == null) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800581 return;
582 }
linyuh183cb712017-12-27 17:02:37 -0800583 inCallButtonUi.setCameraSwitched(!isUsingFrontFacingCamera);
Eric Erfanianccca3152017-02-22 16:32:36 -0800584 }
585
586 @Override
587 public Context getContext() {
linyuh183cb712017-12-27 17:02:37 -0800588 return context;
Eric Erfanianccca3152017-02-22 16:32:36 -0800589 }
590
591 private InCallActivity getActivity() {
linyuh183cb712017-12-27 17:02:37 -0800592 if (inCallButtonUi != null) {
593 Fragment fragment = inCallButtonUi.getInCallButtonUiFragment();
Eric Erfanianccca3152017-02-22 16:32:36 -0800594 if (fragment != null) {
595 return (InCallActivity) fragment.getActivity();
596 }
597 }
598 return null;
599 }
Eric Erfanianccca3152017-02-22 16:32:36 -0800600}