1 /*
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 
17 package com.android.incallui;
18 
19 import android.content.Context;
20 import android.os.Bundle;
21 import android.os.Trace;
22 import android.support.v4.app.Fragment;
23 import android.support.v4.os.UserManagerCompat;
24 import android.telecom.CallAudioState;
25 import android.telecom.PhoneAccountHandle;
26 import com.android.contacts.common.compat.CallCompat;
27 import com.android.dialer.common.Assert;
28 import com.android.dialer.common.LogUtil;
29 import com.android.dialer.common.concurrent.DialerExecutorComponent;
30 import com.android.dialer.logging.DialerImpression;
31 import com.android.dialer.logging.DialerImpression.Type;
32 import com.android.dialer.logging.Logger;
33 import com.android.dialer.telecom.TelecomUtil;
34 import com.android.incallui.InCallCameraManager.Listener;
35 import com.android.incallui.InCallPresenter.CanAddCallListener;
36 import com.android.incallui.InCallPresenter.InCallDetailsListener;
37 import com.android.incallui.InCallPresenter.InCallState;
38 import com.android.incallui.InCallPresenter.InCallStateListener;
39 import com.android.incallui.InCallPresenter.IncomingCallListener;
40 import com.android.incallui.audiomode.AudioModeProvider;
41 import com.android.incallui.audiomode.AudioModeProvider.AudioModeListener;
42 import com.android.incallui.call.CallList;
43 import com.android.incallui.call.DialerCall;
44 import com.android.incallui.call.DialerCall.CameraDirection;
45 import com.android.incallui.call.DialerCallListener;
46 import com.android.incallui.call.TelecomAdapter;
47 import com.android.incallui.call.state.DialerCallState;
48 import com.android.incallui.incall.protocol.InCallButtonIds;
49 import com.android.incallui.incall.protocol.InCallButtonUi;
50 import com.android.incallui.incall.protocol.InCallButtonUiDelegate;
51 import com.android.incallui.multisim.SwapSimWorker;
52 import com.android.incallui.videotech.utils.VideoUtils;
53 
54 /** Logic for call buttons. */
55 public class CallButtonPresenter
56     implements InCallStateListener,
57         AudioModeListener,
58         IncomingCallListener,
59         InCallDetailsListener,
60         CanAddCallListener,
61         Listener,
62         InCallButtonUiDelegate,
63         DialerCallListener {
64 
65   private final Context context;
66   private InCallButtonUi inCallButtonUi;
67   private DialerCall call;
68   private boolean isInCallButtonUiReady;
69   private PhoneAccountHandle otherAccount;
70 
CallButtonPresenter(Context context)71   public CallButtonPresenter(Context context) {
72     this.context = context.getApplicationContext();
73   }
74 
75   @Override
onInCallButtonUiReady(InCallButtonUi ui)76   public void onInCallButtonUiReady(InCallButtonUi ui) {
77     Assert.checkState(!isInCallButtonUiReady);
78     inCallButtonUi = ui;
79     AudioModeProvider.getInstance().addListener(this);
80 
81     // register for call state changes last
82     final InCallPresenter inCallPresenter = InCallPresenter.getInstance();
83     inCallPresenter.addListener(this);
84     inCallPresenter.addIncomingCallListener(this);
85     inCallPresenter.addDetailsListener(this);
86     inCallPresenter.addCanAddCallListener(this);
87     inCallPresenter.getInCallCameraManager().addCameraSelectionListener(this);
88 
89     // Update the buttons state immediately for the current call
90     onStateChange(InCallState.NO_CALLS, inCallPresenter.getInCallState(), CallList.getInstance());
91     isInCallButtonUiReady = true;
92   }
93 
94   @Override
onInCallButtonUiUnready()95   public void onInCallButtonUiUnready() {
96     Assert.checkState(isInCallButtonUiReady);
97     inCallButtonUi = null;
98     InCallPresenter.getInstance().removeListener(this);
99     AudioModeProvider.getInstance().removeListener(this);
100     InCallPresenter.getInstance().removeIncomingCallListener(this);
101     InCallPresenter.getInstance().removeDetailsListener(this);
102     InCallPresenter.getInstance().getInCallCameraManager().removeCameraSelectionListener(this);
103     InCallPresenter.getInstance().removeCanAddCallListener(this);
104     isInCallButtonUiReady = false;
105 
106     if (call != null) {
107       call.removeListener(this);
108     }
109   }
110 
111   @Override
onStateChange(InCallState oldState, InCallState newState, CallList callList)112   public void onStateChange(InCallState oldState, InCallState newState, CallList callList) {
113     Trace.beginSection("CallButtonPresenter.onStateChange");
114     if (call != null) {
115       call.removeListener(this);
116     }
117     if (newState == InCallState.OUTGOING) {
118       call = callList.getOutgoingCall();
119     } else if (newState == InCallState.INCALL) {
120       call = callList.getActiveOrBackgroundCall();
121 
122       // When connected to voice mail, automatically shows the dialpad.
123       // (On previous releases we showed it when in-call shows up, before waiting for
124       // OUTGOING.  We may want to do that once we start showing "Voice mail" label on
125       // the dialpad too.)
126       if (oldState == InCallState.OUTGOING && call != null) {
127         if (call.isVoiceMailNumber() && getActivity() != null) {
128           getActivity().showDialpadFragment(true /* show */, true /* animate */);
129         }
130       }
131     } else if (newState == InCallState.INCOMING) {
132       if (getActivity() != null) {
133         getActivity().showDialpadFragment(false /* show */, true /* animate */);
134       }
135       call = callList.getIncomingCall();
136     } else {
137       call = null;
138     }
139 
140     if (call != null) {
141       call.addListener(this);
142     }
143     updateUi(newState, call);
144     Trace.endSection();
145   }
146 
147   /**
148    * Updates the user interface in response to a change in the details of a call. Currently handles
149    * changes to the call buttons in response to a change in the details for a call. This is
150    * important to ensure changes to the active call are reflected in the available buttons.
151    *
152    * @param call The active call.
153    * @param details The call details.
154    */
155   @Override
onDetailsChanged(DialerCall call, android.telecom.Call.Details details)156   public void onDetailsChanged(DialerCall call, android.telecom.Call.Details details) {
157     // Only update if the changes are for the currently active call
158     if (inCallButtonUi != null && call != null && call.equals(this.call)) {
159       updateButtonsState(call);
160     }
161   }
162 
163   @Override
onIncomingCall(InCallState oldState, InCallState newState, DialerCall call)164   public void onIncomingCall(InCallState oldState, InCallState newState, DialerCall call) {
165     onStateChange(oldState, newState, CallList.getInstance());
166   }
167 
168   @Override
onCanAddCallChanged(boolean canAddCall)169   public void onCanAddCallChanged(boolean canAddCall) {
170     if (inCallButtonUi != null && call != null) {
171       updateButtonsState(call);
172     }
173   }
174 
175   @Override
onAudioStateChanged(CallAudioState audioState)176   public void onAudioStateChanged(CallAudioState audioState) {
177     if (inCallButtonUi != null) {
178       inCallButtonUi.setAudioState(audioState);
179     }
180   }
181 
182   @Override
getCurrentAudioState()183   public CallAudioState getCurrentAudioState() {
184     return AudioModeProvider.getInstance().getAudioState();
185   }
186 
187   @Override
setAudioRoute(int route)188   public void setAudioRoute(int route) {
189     LogUtil.i(
190         "CallButtonPresenter.setAudioRoute",
191         "sending new audio route: " + CallAudioState.audioRouteToString(route));
192     TelecomAdapter.getInstance().setAudioRoute(route);
193   }
194 
195   /** Function assumes that bluetooth is not supported. */
196   @Override
toggleSpeakerphone()197   public void toggleSpeakerphone() {
198     // This function should not be called if bluetooth is available.
199     CallAudioState audioState = getCurrentAudioState();
200     if (0 != (CallAudioState.ROUTE_BLUETOOTH & audioState.getSupportedRouteMask())) {
201       // It's clear the UI is wrong, so update the supported mode once again.
202       LogUtil.e(
203           "CallButtonPresenter", "toggling speakerphone not allowed when bluetooth supported.");
204       inCallButtonUi.setAudioState(audioState);
205       return;
206     }
207 
208     int newRoute;
209     if (audioState.getRoute() == CallAudioState.ROUTE_SPEAKER) {
210       newRoute = CallAudioState.ROUTE_WIRED_OR_EARPIECE;
211       Logger.get(context)
212           .logCallImpression(
213               DialerImpression.Type.IN_CALL_SCREEN_TURN_ON_WIRED_OR_EARPIECE,
214               call.getUniqueCallId(),
215               call.getTimeAddedMs());
216     } else {
217       newRoute = CallAudioState.ROUTE_SPEAKER;
218       Logger.get(context)
219           .logCallImpression(
220               DialerImpression.Type.IN_CALL_SCREEN_TURN_ON_SPEAKERPHONE,
221               call.getUniqueCallId(),
222               call.getTimeAddedMs());
223     }
224 
225     setAudioRoute(newRoute);
226   }
227 
228   @Override
muteClicked(boolean checked, boolean clickedByUser)229   public void muteClicked(boolean checked, boolean clickedByUser) {
230     LogUtil.i(
231         "CallButtonPresenter", "turning on mute: %s, clicked by user: %s", checked, clickedByUser);
232     if (clickedByUser) {
233       Logger.get(context)
234           .logCallImpression(
235               checked
236                   ? DialerImpression.Type.IN_CALL_SCREEN_TURN_ON_MUTE
237                   : DialerImpression.Type.IN_CALL_SCREEN_TURN_OFF_MUTE,
238               call.getUniqueCallId(),
239               call.getTimeAddedMs());
240     }
241     TelecomAdapter.getInstance().mute(checked);
242   }
243 
244   @Override
holdClicked(boolean checked)245   public void holdClicked(boolean checked) {
246     if (call == null) {
247       return;
248     }
249     if (checked) {
250       LogUtil.i("CallButtonPresenter", "putting the call on hold: " + call);
251       call.hold();
252     } else {
253       LogUtil.i("CallButtonPresenter", "removing the call from hold: " + call);
254       call.unhold();
255     }
256   }
257 
258   @Override
swapClicked()259   public void swapClicked() {
260     if (call == null) {
261       return;
262     }
263 
264     LogUtil.i("CallButtonPresenter", "swapping the call: " + call);
265     TelecomAdapter.getInstance().swap(call.getId());
266   }
267 
268   @Override
mergeClicked()269   public void mergeClicked() {
270     Logger.get(context)
271         .logCallImpression(
272             DialerImpression.Type.IN_CALL_MERGE_BUTTON_PRESSED,
273             call.getUniqueCallId(),
274             call.getTimeAddedMs());
275     TelecomAdapter.getInstance().merge(call.getId());
276   }
277 
278   @Override
addCallClicked()279   public void addCallClicked() {
280     Logger.get(context)
281         .logCallImpression(
282             DialerImpression.Type.IN_CALL_ADD_CALL_BUTTON_PRESSED,
283             call.getUniqueCallId(),
284             call.getTimeAddedMs());
285     InCallPresenter.getInstance().addCallClicked();
286   }
287 
288   @Override
showDialpadClicked(boolean checked)289   public void showDialpadClicked(boolean checked) {
290     Logger.get(context)
291         .logCallImpression(
292             DialerImpression.Type.IN_CALL_SHOW_DIALPAD_BUTTON_PRESSED,
293             call.getUniqueCallId(),
294             call.getTimeAddedMs());
295     LogUtil.v("CallButtonPresenter", "show dialpad " + String.valueOf(checked));
296     getActivity().showDialpadFragment(checked /* show */, true /* animate */);
297   }
298 
299   @Override
changeToVideoClicked()300   public void changeToVideoClicked() {
301     LogUtil.enterBlock("CallButtonPresenter.changeToVideoClicked");
302     Logger.get(context)
303         .logCallImpression(
304             DialerImpression.Type.VIDEO_CALL_UPGRADE_REQUESTED,
305             call.getUniqueCallId(),
306             call.getTimeAddedMs());
307     call.getVideoTech().upgradeToVideo(context);
308   }
309 
310   @Override
changeToRttClicked()311   public void changeToRttClicked() {
312     LogUtil.enterBlock("CallButtonPresenter.changeToRttClicked");
313     call.sendRttUpgradeRequest();
314   }
315 
316   @Override
onEndCallClicked()317   public void onEndCallClicked() {
318     LogUtil.i("CallButtonPresenter.onEndCallClicked", "call: " + call);
319     if (call != null) {
320       call.disconnect();
321     }
322   }
323 
324   @Override
showAudioRouteSelector()325   public void showAudioRouteSelector() {
326     inCallButtonUi.showAudioRouteSelector();
327   }
328 
329   @Override
swapSimClicked()330   public void swapSimClicked() {
331     LogUtil.enterBlock("CallButtonPresenter.swapSimClicked");
332     Logger.get(getContext()).logImpression(Type.DUAL_SIM_CHANGE_SIM_PRESSED);
333     SwapSimWorker worker =
334         new SwapSimWorker(
335             getContext(),
336             call,
337             InCallPresenter.getInstance().getCallList(),
338             otherAccount,
339             InCallPresenter.getInstance().acquireInCallUiLock("swapSim"));
340     DialerExecutorComponent.get(getContext())
341         .dialerExecutorFactory()
342         .createNonUiTaskBuilder(worker)
343         .build()
344         .executeParallel(null);
345   }
346 
347   /**
348    * Switches the camera between the front-facing and back-facing camera.
349    *
350    * @param useFrontFacingCamera True if we should switch to using the front-facing camera, or false
351    *     if we should switch to using the back-facing camera.
352    */
353   @Override
switchCameraClicked(boolean useFrontFacingCamera)354   public void switchCameraClicked(boolean useFrontFacingCamera) {
355     updateCamera(useFrontFacingCamera);
356   }
357 
358   @Override
toggleCameraClicked()359   public void toggleCameraClicked() {
360     LogUtil.i("CallButtonPresenter.toggleCameraClicked", "");
361     if (call == null) {
362       return;
363     }
364     Logger.get(context)
365         .logCallImpression(
366             DialerImpression.Type.IN_CALL_SCREEN_SWAP_CAMERA,
367             call.getUniqueCallId(),
368             call.getTimeAddedMs());
369     switchCameraClicked(
370         !InCallPresenter.getInstance().getInCallCameraManager().isUsingFrontFacingCamera());
371   }
372 
373   /**
374    * Stop or start client's video transmission.
375    *
376    * @param pause True if pausing the local user's video, or false if starting the local user's
377    *     video.
378    */
379   @Override
pauseVideoClicked(boolean pause)380   public void pauseVideoClicked(boolean pause) {
381     LogUtil.i("CallButtonPresenter.pauseVideoClicked", "%s", pause ? "pause" : "unpause");
382 
383     Logger.get(context)
384         .logCallImpression(
385             pause
386                 ? DialerImpression.Type.IN_CALL_SCREEN_TURN_OFF_VIDEO
387                 : DialerImpression.Type.IN_CALL_SCREEN_TURN_ON_VIDEO,
388             call.getUniqueCallId(),
389             call.getTimeAddedMs());
390 
391     if (pause) {
392       call.getVideoTech().stopTransmission();
393     } else {
394       updateCamera(
395           InCallPresenter.getInstance().getInCallCameraManager().isUsingFrontFacingCamera());
396       call.getVideoTech().resumeTransmission(context);
397     }
398 
399     inCallButtonUi.setVideoPaused(pause);
400     inCallButtonUi.enableButton(InCallButtonIds.BUTTON_PAUSE_VIDEO, false);
401   }
402 
updateCamera(boolean useFrontFacingCamera)403   private void updateCamera(boolean useFrontFacingCamera) {
404     InCallCameraManager cameraManager = InCallPresenter.getInstance().getInCallCameraManager();
405     cameraManager.setUseFrontFacingCamera(useFrontFacingCamera);
406 
407     String cameraId = cameraManager.getActiveCameraId();
408     if (cameraId != null) {
409       final int cameraDir =
410           cameraManager.isUsingFrontFacingCamera()
411               ? CameraDirection.CAMERA_DIRECTION_FRONT_FACING
412               : CameraDirection.CAMERA_DIRECTION_BACK_FACING;
413       call.setCameraDir(cameraDir);
414       call.getVideoTech().setCamera(cameraId);
415     }
416   }
417 
updateUi(InCallState state, DialerCall call)418   private void updateUi(InCallState state, DialerCall call) {
419     LogUtil.v("CallButtonPresenter", "updating call UI for call: %s", call);
420 
421     if (inCallButtonUi == null) {
422       return;
423     }
424 
425     if (call != null) {
426       inCallButtonUi.updateInCallButtonUiColors(
427           InCallPresenter.getInstance().getThemeColorManager().getSecondaryColor());
428     }
429 
430     final boolean isEnabled =
431         state.isConnectingOrConnected() && !state.isIncoming() && call != null;
432     inCallButtonUi.setEnabled(isEnabled);
433 
434     if (call == null) {
435       return;
436     }
437 
438     updateButtonsState(call);
439   }
440 
441   /**
442    * Updates the buttons applicable for the UI.
443    *
444    * @param call The active call.
445    */
446   @SuppressWarnings(value = {"MissingPermission"})
updateButtonsState(DialerCall call)447   private void updateButtonsState(DialerCall call) {
448     LogUtil.v("CallButtonPresenter.updateButtonsState", "");
449     final boolean isVideo = call.isVideoCall();
450 
451     // Common functionality (audio, hold, etc).
452     // Show either HOLD or SWAP, but not both. If neither HOLD or SWAP is available:
453     //     (1) If the device normally can hold, show HOLD in a disabled state.
454     //     (2) If the device doesn't have the concept of hold/swap, remove the button.
455     final boolean showSwap = call.can(android.telecom.Call.Details.CAPABILITY_SWAP_CONFERENCE);
456     final boolean showHold =
457         !showSwap
458             && call.can(android.telecom.Call.Details.CAPABILITY_SUPPORT_HOLD)
459             && call.can(android.telecom.Call.Details.CAPABILITY_HOLD);
460     final boolean isCallOnHold = call.getState() == DialerCallState.ONHOLD;
461 
462     final boolean showAddCall =
463         TelecomAdapter.getInstance().canAddCall() && UserManagerCompat.isUserUnlocked(context);
464     // There can only be two calls so don't show the ability to merge when one of them
465     // is a speak easy call.
466     final boolean showMerge =
467         InCallPresenter.getInstance()
468                 .getCallList()
469                 .getAllCalls()
470                 .stream()
471                 .noneMatch(c -> c != null && c.isSpeakEasyCall())
472             && call.can(android.telecom.Call.Details.CAPABILITY_MERGE_CONFERENCE);
473     final boolean showUpgradeToVideo = !isVideo && (hasVideoCallCapabilities(call));
474     final boolean showDowngradeToAudio = isVideo && isDowngradeToAudioSupported(call);
475     final boolean showMute = call.can(android.telecom.Call.Details.CAPABILITY_MUTE);
476 
477     final boolean hasCameraPermission =
478         isVideo && VideoUtils.hasCameraPermissionAndShownPrivacyToast(context);
479     // Disabling local video doesn't seem to work when dialing. See a bug.
480     final boolean showPauseVideo =
481         isVideo
482             && call.getState() != DialerCallState.DIALING
483             && call.getState() != DialerCallState.CONNECTING;
484 
485     otherAccount = TelecomUtil.getOtherAccount(getContext(), call.getAccountHandle());
486     boolean showSwapSim =
487         !call.isEmergencyCall()
488             && otherAccount != null
489             && !call.isVoiceMailNumber()
490             && DialerCallState.isDialing(call.getState())
491             // Most devices cannot make calls on 2 SIMs at the same time.
492             && InCallPresenter.getInstance().getCallList().getAllCalls().size() == 1;
493 
494     boolean showUpgradeToRtt = call.canUpgradeToRttCall();
495     boolean enableUpgradeToRtt = showUpgradeToRtt && call.getState() == DialerCallState.ACTIVE;
496 
497     inCallButtonUi.showButton(InCallButtonIds.BUTTON_AUDIO, true);
498     inCallButtonUi.showButton(InCallButtonIds.BUTTON_SWAP, showSwap);
499     inCallButtonUi.showButton(InCallButtonIds.BUTTON_HOLD, showHold);
500     inCallButtonUi.setHold(isCallOnHold);
501     inCallButtonUi.showButton(InCallButtonIds.BUTTON_MUTE, showMute);
502     inCallButtonUi.showButton(InCallButtonIds.BUTTON_SWAP_SIM, showSwapSim);
503     inCallButtonUi.showButton(InCallButtonIds.BUTTON_ADD_CALL, true);
504     inCallButtonUi.enableButton(InCallButtonIds.BUTTON_ADD_CALL, showAddCall);
505     inCallButtonUi.showButton(InCallButtonIds.BUTTON_UPGRADE_TO_VIDEO, showUpgradeToVideo);
506     inCallButtonUi.showButton(InCallButtonIds.BUTTON_UPGRADE_TO_RTT, showUpgradeToRtt);
507     inCallButtonUi.enableButton(InCallButtonIds.BUTTON_UPGRADE_TO_RTT, enableUpgradeToRtt);
508     inCallButtonUi.showButton(InCallButtonIds.BUTTON_DOWNGRADE_TO_AUDIO, showDowngradeToAudio);
509     inCallButtonUi.showButton(
510         InCallButtonIds.BUTTON_SWITCH_CAMERA,
511         isVideo && hasCameraPermission && call.getVideoTech().isTransmitting());
512     inCallButtonUi.showButton(InCallButtonIds.BUTTON_PAUSE_VIDEO, showPauseVideo);
513     if (isVideo) {
514       inCallButtonUi.setVideoPaused(!call.getVideoTech().isTransmitting() || !hasCameraPermission);
515     }
516     inCallButtonUi.showButton(InCallButtonIds.BUTTON_DIALPAD, true);
517     inCallButtonUi.showButton(InCallButtonIds.BUTTON_MERGE, showMerge);
518 
519     inCallButtonUi.updateButtonStates();
520   }
521 
hasVideoCallCapabilities(DialerCall call)522   private boolean hasVideoCallCapabilities(DialerCall call) {
523     return call.getVideoTech().isAvailable(context, call.getAccountHandle());
524   }
525 
526   /**
527    * Determines if downgrading from a video call to an audio-only call is supported. In order to
528    * support downgrade to audio, the SDK version must be >= N and the call should NOT have the
529    * {@link android.telecom.Call.Details#CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO}.
530    *
531    * @param call The call.
532    * @return {@code true} if downgrading to an audio-only call from a video call is supported.
533    */
isDowngradeToAudioSupported(DialerCall call)534   private boolean isDowngradeToAudioSupported(DialerCall call) {
535     // TODO(a bug): If there is an RCS video share session, return true here
536     return !call.can(CallCompat.Details.CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO);
537   }
538 
539   @Override
onSaveInstanceState(Bundle outState)540   public void onSaveInstanceState(Bundle outState) {}
541 
542   @Override
onRestoreInstanceState(Bundle savedInstanceState)543   public void onRestoreInstanceState(Bundle savedInstanceState) {}
544 
545   @Override
onCameraPermissionGranted()546   public void onCameraPermissionGranted() {
547     if (call != null) {
548       updateButtonsState(call);
549     }
550   }
551 
552   @Override
onActiveCameraSelectionChanged(boolean isUsingFrontFacingCamera)553   public void onActiveCameraSelectionChanged(boolean isUsingFrontFacingCamera) {
554     if (inCallButtonUi == null) {
555       return;
556     }
557     inCallButtonUi.setCameraSwitched(!isUsingFrontFacingCamera);
558   }
559 
560   @Override
onDialerCallSessionModificationStateChange()561   public void onDialerCallSessionModificationStateChange() {
562     if (inCallButtonUi != null && call != null) {
563       inCallButtonUi.enableButton(InCallButtonIds.BUTTON_PAUSE_VIDEO, true);
564       updateButtonsState(call);
565     }
566   }
567 
568   @Override
onDialerCallDisconnect()569   public void onDialerCallDisconnect() {}
570 
571   @Override
onDialerCallUpdate()572   public void onDialerCallUpdate() {}
573 
574   @Override
onDialerCallChildNumberChange()575   public void onDialerCallChildNumberChange() {}
576 
577   @Override
onDialerCallLastForwardedNumberChange()578   public void onDialerCallLastForwardedNumberChange() {}
579 
580   @Override
onDialerCallUpgradeToVideo()581   public void onDialerCallUpgradeToVideo() {}
582 
583   @Override
onWiFiToLteHandover()584   public void onWiFiToLteHandover() {}
585 
586   @Override
onHandoverToWifiFailure()587   public void onHandoverToWifiFailure() {}
588 
589   @Override
onInternationalCallOnWifi()590   public void onInternationalCallOnWifi() {}
591 
592   @Override
onEnrichedCallSessionUpdate()593   public void onEnrichedCallSessionUpdate() {}
594 
595   @Override
getContext()596   public Context getContext() {
597     return context;
598   }
599 
getActivity()600   private InCallActivity getActivity() {
601     if (inCallButtonUi != null) {
602       Fragment fragment = inCallButtonUi.getInCallButtonUiFragment();
603       if (fragment != null) {
604         return (InCallActivity) fragment.getActivity();
605       }
606     }
607     return null;
608   }
609 }
610