• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 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.rtt.impl;
18 
19 import android.app.Activity;
20 import android.content.Context;
21 import android.os.Bundle;
22 import android.os.SystemClock;
23 import android.support.annotation.NonNull;
24 import android.support.annotation.Nullable;
25 import android.support.v4.app.Fragment;
26 import android.support.v4.app.FragmentTransaction;
27 import android.support.v7.widget.LinearLayoutManager;
28 import android.support.v7.widget.RecyclerView;
29 import android.support.v7.widget.RecyclerView.OnScrollListener;
30 import android.telecom.CallAudioState;
31 import android.text.Editable;
32 import android.text.TextUtils;
33 import android.text.TextWatcher;
34 import android.view.Gravity;
35 import android.view.KeyEvent;
36 import android.view.LayoutInflater;
37 import android.view.View;
38 import android.view.ViewGroup;
39 import android.view.Window;
40 import android.view.accessibility.AccessibilityEvent;
41 import android.view.inputmethod.EditorInfo;
42 import android.widget.Chronometer;
43 import android.widget.EditText;
44 import android.widget.ImageButton;
45 import android.widget.TextView;
46 import android.widget.TextView.OnEditorActionListener;
47 import com.android.dialer.common.Assert;
48 import com.android.dialer.common.FragmentUtils;
49 import com.android.dialer.common.LogUtil;
50 import com.android.dialer.common.UiUtil;
51 import com.android.dialer.lettertile.LetterTileDrawable;
52 import com.android.dialer.logging.DialerImpression;
53 import com.android.dialer.logging.Logger;
54 import com.android.dialer.rtt.RttTranscript;
55 import com.android.dialer.rtt.RttTranscriptMessage;
56 import com.android.dialer.util.DrawableConverter;
57 import com.android.incallui.audioroute.AudioRouteSelectorDialogFragment.AudioRouteSelectorPresenter;
58 import com.android.incallui.call.state.DialerCallState;
59 import com.android.incallui.hold.OnHoldFragment;
60 import com.android.incallui.incall.protocol.ContactPhotoType;
61 import com.android.incallui.incall.protocol.InCallButtonIds;
62 import com.android.incallui.incall.protocol.InCallButtonUi;
63 import com.android.incallui.incall.protocol.InCallButtonUiDelegate;
64 import com.android.incallui.incall.protocol.InCallButtonUiDelegateFactory;
65 import com.android.incallui.incall.protocol.InCallScreen;
66 import com.android.incallui.incall.protocol.InCallScreenDelegate;
67 import com.android.incallui.incall.protocol.InCallScreenDelegateFactory;
68 import com.android.incallui.incall.protocol.PrimaryCallState;
69 import com.android.incallui.incall.protocol.PrimaryInfo;
70 import com.android.incallui.incall.protocol.SecondaryInfo;
71 import com.android.incallui.rtt.impl.RttChatAdapter.MessageListener;
72 import com.android.incallui.rtt.protocol.Constants;
73 import com.android.incallui.rtt.protocol.RttCallScreen;
74 import com.android.incallui.rtt.protocol.RttCallScreenDelegate;
75 import com.android.incallui.rtt.protocol.RttCallScreenDelegateFactory;
76 import java.util.List;
77 
78 /** RTT chat fragment to show chat bubbles. */
79 public class RttChatFragment extends Fragment
80     implements OnEditorActionListener,
81         TextWatcher,
82         MessageListener,
83         RttCallScreen,
84         InCallScreen,
85         InCallButtonUi,
86         AudioRouteSelectorPresenter {
87 
88   private static final String ARG_CALL_ID = "call_id";
89 
90   private RecyclerView recyclerView;
91   private RttChatAdapter adapter;
92   private EditText editText;
93   private ImageButton submitButton;
94   private boolean isClearingInput;
95 
96   private InCallScreenDelegate inCallScreenDelegate;
97   private RttCallScreenDelegate rttCallScreenDelegate;
98   private InCallButtonUiDelegate inCallButtonUiDelegate;
99   private View endCallButton;
100   private TextView nameTextView;
101   private Chronometer chronometer;
102   private boolean isTimerStarted;
103   private RttOverflowMenu overflowMenu;
104   private SecondaryInfo savedSecondaryInfo;
105   private TextView statusBanner;
106   private PrimaryInfo primaryInfo = PrimaryInfo.empty();
107   private PrimaryCallState primaryCallState = PrimaryCallState.empty();
108   private boolean isUserScrolling;
109   private boolean shouldAutoScrolling;
110   private AudioSelectMenu audioSelectMenu;
111 
112   /**
113    * Create a new instance of RttChatFragment.
114    *
115    * @param callId call id of the RTT call.
116    * @return new RttChatFragment
117    */
newInstance(String callId)118   public static RttChatFragment newInstance(String callId) {
119     Bundle bundle = new Bundle();
120     bundle.putString(ARG_CALL_ID, callId);
121     RttChatFragment instance = new RttChatFragment();
122     instance.setArguments(bundle);
123     return instance;
124   }
125 
126   @Override
onCreate(@ullable Bundle savedInstanceState)127   public void onCreate(@Nullable Bundle savedInstanceState) {
128     super.onCreate(savedInstanceState);
129     LogUtil.i("RttChatFragment.onCreate", null);
130     inCallButtonUiDelegate =
131         FragmentUtils.getParent(this, InCallButtonUiDelegateFactory.class)
132             .newInCallButtonUiDelegate();
133     if (savedInstanceState != null) {
134       inCallButtonUiDelegate.onRestoreInstanceState(savedInstanceState);
135     }
136     inCallScreenDelegate =
137         FragmentUtils.getParentUnsafe(this, InCallScreenDelegateFactory.class)
138             .newInCallScreenDelegate();
139     // Prevent updating local message until UI is ready.
140     isClearingInput = true;
141   }
142 
143   @Override
onViewCreated(@onNull View view, @Nullable Bundle bundle)144   public void onViewCreated(@NonNull View view, @Nullable Bundle bundle) {
145     super.onViewCreated(view, bundle);
146     LogUtil.i("RttChatFragment.onViewCreated", null);
147 
148     rttCallScreenDelegate =
149         FragmentUtils.getParentUnsafe(this, RttCallScreenDelegateFactory.class)
150             .newRttCallScreenDelegate(this);
151 
152     rttCallScreenDelegate.initRttCallScreenDelegate(this);
153 
154     inCallScreenDelegate.onInCallScreenDelegateInit(this);
155     inCallScreenDelegate.onInCallScreenReady();
156     inCallButtonUiDelegate.onInCallButtonUiReady(this);
157   }
158 
159   @Override
getRttTranscriptMessageList()160   public List<RttTranscriptMessage> getRttTranscriptMessageList() {
161     return adapter.getRttTranscriptMessageList();
162   }
163 
164   @Nullable
165   @Override
onCreateView( LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState)166   public View onCreateView(
167       LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) {
168     View view = inflater.inflate(R.layout.frag_rtt_chat, container, false);
169     view.setSystemUiVisibility(
170         View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
171             | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
172             | View.SYSTEM_UI_FLAG_LAYOUT_STABLE
173             | View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR);
174     editText = view.findViewById(R.id.rtt_chat_input);
175     editText.setOnEditorActionListener(this);
176     editText.addTextChangedListener(this);
177 
178     editText.setOnKeyListener(
179         (v, keyCode, event) -> {
180           // This is only triggered when input method doesn't handle delete key, which usually means
181           // the current input box is empty.
182           // On non-English keyboard delete key could be passed here so we still need to check if
183           // the input box is empty.
184           if (keyCode == KeyEvent.KEYCODE_DEL
185               && event.getAction() == KeyEvent.ACTION_DOWN
186               && TextUtils.isEmpty(editText.getText())) {
187             String lastMessage = adapter.retrieveLastLocalMessage();
188             if (lastMessage != null) {
189               resumeInput(lastMessage);
190               rttCallScreenDelegate.onLocalMessage("\b");
191               return true;
192             }
193             return false;
194           }
195           return false;
196         });
197     recyclerView = view.findViewById(R.id.rtt_recycler_view);
198     LinearLayoutManager layoutManager = new LinearLayoutManager(getContext());
199     layoutManager.setStackFromEnd(true);
200     recyclerView.setLayoutManager(layoutManager);
201     recyclerView.setHasFixedSize(false);
202     adapter = new RttChatAdapter(getContext(), this);
203     recyclerView.setAdapter(adapter);
204     recyclerView.addOnScrollListener(
205         new OnScrollListener() {
206           @Override
207           public void onScrollStateChanged(RecyclerView recyclerView, int i) {
208             if (i == RecyclerView.SCROLL_STATE_DRAGGING) {
209               isUserScrolling = true;
210             } else if (i == RecyclerView.SCROLL_STATE_IDLE) {
211               isUserScrolling = false;
212               // Auto scrolling for new messages should be resumed if it's scrolled to bottom.
213               shouldAutoScrolling = !recyclerView.canScrollVertically(1);
214             }
215           }
216 
217           @Override
218           public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
219             if (dy < 0 && isUserScrolling) {
220               UiUtil.hideKeyboardFrom(getContext(), editText);
221             }
222           }
223         });
224 
225     submitButton = view.findViewById(R.id.rtt_chat_submit_button);
226     submitButton.setOnClickListener(
227         v -> {
228           Logger.get(getContext()).logImpression(DialerImpression.Type.RTT_SEND_BUTTON_CLICKED);
229           adapter.submitLocalMessage();
230           resumeInput("");
231           rttCallScreenDelegate.onLocalMessage(Constants.BUBBLE_BREAKER);
232           // Auto scrolling for new messages should be resumed since user has submit current
233           // message.
234           shouldAutoScrolling = true;
235         });
236     submitButton.setEnabled(false);
237     endCallButton = view.findViewById(R.id.rtt_end_call_button);
238     endCallButton.setOnClickListener(
239         v -> {
240           LogUtil.i("RttChatFragment.onClick", "end call button clicked");
241           inCallButtonUiDelegate.onEndCallClicked();
242         });
243 
244     overflowMenu = new RttOverflowMenu(getContext(), inCallButtonUiDelegate, inCallScreenDelegate);
245     view.findViewById(R.id.rtt_overflow_button)
246         .setOnClickListener(
247             v -> {
248               // Hide keyboard when opening overflow menu. This is alternative solution since hiding
249               // keyboard after the menu is open or dialpad is shown doesn't work.
250               UiUtil.hideKeyboardFrom(getContext(), editText);
251               overflowMenu.showAtLocation(v, Gravity.TOP | Gravity.RIGHT, 0, 0);
252             });
253 
254     nameTextView = view.findViewById(R.id.rtt_name_or_number);
255     chronometer = view.findViewById(R.id.rtt_timer);
256     statusBanner = view.findViewById(R.id.rtt_status_banner);
257     return view;
258   }
259 
260   @Override
onEditorAction(TextView v, int actionId, KeyEvent event)261   public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
262     if (actionId == EditorInfo.IME_ACTION_SEND) {
263       if (!TextUtils.isEmpty(editText.getText())) {
264         Logger.get(getContext())
265             .logImpression(DialerImpression.Type.RTT_KEYBOARD_SEND_BUTTON_CLICKED);
266         submitButton.performClick();
267       }
268       return true;
269     }
270     return false;
271   }
272 
273   @Override
beforeTextChanged(CharSequence s, int start, int count, int after)274   public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
275 
276   @Override
onTextChanged(CharSequence s, int start, int before, int count)277   public void onTextChanged(CharSequence s, int start, int before, int count) {
278     if (isClearingInput) {
279       return;
280     }
281     String messageToAppend = adapter.computeChangeOfLocalMessage(s.toString());
282     if (!TextUtils.isEmpty(messageToAppend)) {
283       adapter.addLocalMessage(messageToAppend);
284       rttCallScreenDelegate.onLocalMessage(messageToAppend);
285     }
286   }
287 
288   @Override
onRemoteMessage(String message)289   public void onRemoteMessage(String message) {
290     adapter.addRemoteMessage(message);
291   }
292 
293   @Override
onDestroyView()294   public void onDestroyView() {
295     super.onDestroyView();
296     LogUtil.enterBlock("RttChatFragment.onDestroyView");
297     inCallButtonUiDelegate.onInCallButtonUiUnready();
298     inCallScreenDelegate.onInCallScreenUnready();
299   }
300 
301   @Override
afterTextChanged(Editable s)302   public void afterTextChanged(Editable s) {
303     if (TextUtils.isEmpty(s)) {
304       submitButton.setEnabled(false);
305     } else {
306       submitButton.setEnabled(true);
307     }
308   }
309 
310   @Override
onUpdateLocalMessage(int position)311   public void onUpdateLocalMessage(int position) {
312     if (position < 0) {
313       return;
314     }
315     recyclerView.smoothScrollToPosition(position);
316   }
317 
318   @Override
onUpdateRemoteMessage(int position)319   public void onUpdateRemoteMessage(int position) {
320     if (position < 0) {
321       return;
322     }
323     if (shouldAutoScrolling) {
324       recyclerView.smoothScrollToPosition(position);
325     }
326   }
327 
328   @Override
onRestoreRttChat(RttTranscript rttTranscript)329   public void onRestoreRttChat(RttTranscript rttTranscript) {
330     String unfinishedLocalMessage = adapter.onRestoreRttChat(rttTranscript);
331     if (unfinishedLocalMessage != null) {
332       resumeInput(unfinishedLocalMessage);
333     }
334   }
335 
resumeInput(String input)336   private void resumeInput(String input) {
337     isClearingInput = true;
338     editText.setText(input);
339     editText.setSelection(input.length());
340     isClearingInput = false;
341   }
342 
343   @Override
onStart()344   public void onStart() {
345     LogUtil.enterBlock("RttChatFragment.onStart");
346     super.onStart();
347     isClearingInput = false;
348     onRttScreenStart();
349   }
350 
351   @Override
onStop()352   public void onStop() {
353     LogUtil.enterBlock("RttChatFragment.onStop");
354     super.onStop();
355     isClearingInput = true;
356     if (overflowMenu.isShowing()) {
357       overflowMenu.dismiss();
358     }
359     onRttScreenStop();
360   }
361 
362   @Override
onRttScreenStart()363   public void onRttScreenStart() {
364     inCallButtonUiDelegate.refreshMuteState();
365     rttCallScreenDelegate.onRttCallScreenUiReady();
366     Activity activity = getActivity();
367     Window window = getActivity().getWindow();
368     window.setStatusBarColor(activity.getColor(R.color.rtt_status_bar_color));
369     window.setNavigationBarColor(activity.getColor(R.color.rtt_navigation_bar_color));
370   }
371 
372   @Override
onRttScreenStop()373   public void onRttScreenStop() {
374     Activity activity = getActivity();
375     Window window = getActivity().getWindow();
376     window.setStatusBarColor(activity.getColor(android.R.color.transparent));
377     window.setNavigationBarColor(activity.getColor(android.R.color.transparent));
378     rttCallScreenDelegate.onRttCallScreenUiUnready();
379   }
380 
381   @Override
getRttCallScreenFragment()382   public Fragment getRttCallScreenFragment() {
383     return this;
384   }
385 
386   @Override
getCallId()387   public String getCallId() {
388     return Assert.isNotNull(getArguments().getString(ARG_CALL_ID));
389   }
390 
391   @Override
setPrimary(@onNull PrimaryInfo primaryInfo)392   public void setPrimary(@NonNull PrimaryInfo primaryInfo) {
393     LogUtil.i("RttChatFragment.setPrimary", primaryInfo.toString());
394     nameTextView.setText(primaryInfo.name());
395     updateAvatar(primaryInfo);
396     this.primaryInfo = primaryInfo;
397   }
398 
updateAvatar(PrimaryInfo primaryInfo)399   private void updateAvatar(PrimaryInfo primaryInfo) {
400     boolean hasPhoto =
401         primaryInfo.photo() != null && primaryInfo.photoType() == ContactPhotoType.CONTACT;
402     // Contact has a photo, don't render a letter tile.
403     if (hasPhoto) {
404       int avatarSize = getResources().getDimensionPixelSize(R.dimen.rtt_avatar_size);
405       adapter.setAvatarDrawable(
406           DrawableConverter.getRoundedDrawable(
407               getContext(), primaryInfo.photo(), avatarSize, avatarSize));
408     } else {
409       LetterTileDrawable letterTile = new LetterTileDrawable(getResources());
410       letterTile.setCanonicalDialerLetterTileDetails(
411           primaryInfo.name(),
412           primaryInfo.contactInfoLookupKey(),
413           LetterTileDrawable.SHAPE_CIRCLE,
414           LetterTileDrawable.getContactTypeFromPrimitives(
415               primaryCallState.isVoiceMailNumber(),
416               primaryInfo.isSpam(),
417               primaryCallState.isBusinessNumber(),
418               primaryInfo.numberPresentation(),
419               primaryCallState.isConference()));
420       adapter.setAvatarDrawable(letterTile);
421     }
422   }
423 
424   @Override
onAttach(Context context)425   public void onAttach(Context context) {
426     super.onAttach(context);
427     if (savedSecondaryInfo != null) {
428       setSecondary(savedSecondaryInfo);
429     }
430   }
431 
432   @Override
setSecondary(@onNull SecondaryInfo secondaryInfo)433   public void setSecondary(@NonNull SecondaryInfo secondaryInfo) {
434     LogUtil.i("RttChatFragment.setSecondary", secondaryInfo.toString());
435     if (!isAdded()) {
436       savedSecondaryInfo = secondaryInfo;
437       return;
438     }
439     savedSecondaryInfo = null;
440     FragmentTransaction transaction = getChildFragmentManager().beginTransaction();
441     Fragment oldBanner = getChildFragmentManager().findFragmentById(R.id.rtt_on_hold_banner);
442     if (secondaryInfo.shouldShow()) {
443       OnHoldFragment onHoldFragment = OnHoldFragment.newInstance(secondaryInfo);
444       onHoldFragment.setPadTopInset(false);
445       transaction.replace(R.id.rtt_on_hold_banner, onHoldFragment);
446     } else {
447       if (oldBanner != null) {
448         transaction.remove(oldBanner);
449       }
450     }
451     transaction.setCustomAnimations(R.anim.abc_slide_in_top, R.anim.abc_slide_out_top);
452     transaction.commitNowAllowingStateLoss();
453     overflowMenu.enableSwitchToSecondaryButton(secondaryInfo.shouldShow());
454   }
455 
456   @Override
setCallState(@onNull PrimaryCallState primaryCallState)457   public void setCallState(@NonNull PrimaryCallState primaryCallState) {
458     LogUtil.i("RttChatFragment.setCallState", primaryCallState.toString());
459     this.primaryCallState = primaryCallState;
460     if (!isTimerStarted && primaryCallState.state() == DialerCallState.ACTIVE) {
461       LogUtil.i(
462           "RttChatFragment.setCallState", "starting timer with base: %d", chronometer.getBase());
463       chronometer.setBase(
464           primaryCallState.connectTimeMillis()
465               - System.currentTimeMillis()
466               + SystemClock.elapsedRealtime());
467       chronometer.start();
468       isTimerStarted = true;
469       editText.setVisibility(View.VISIBLE);
470       submitButton.setVisibility(View.VISIBLE);
471       editText.setFocusableInTouchMode(true);
472       if (editText.requestFocus()) {
473         UiUtil.showKeyboardFrom(getContext(), editText);
474       }
475       adapter.showAdvisory();
476     }
477     if (primaryCallState.state() == DialerCallState.DIALING) {
478       showWaitingForJoinBanner();
479     } else {
480       hideWaitingForJoinBanner();
481     }
482     if (primaryCallState.state() == DialerCallState.DISCONNECTED) {
483       rttCallScreenDelegate.onSaveRttTranscript();
484     }
485   }
486 
showWaitingForJoinBanner()487   private void showWaitingForJoinBanner() {
488     statusBanner.setText(getString(R.string.rtt_status_banner_text, primaryInfo.name()));
489     statusBanner.setVisibility(View.VISIBLE);
490   }
491 
hideWaitingForJoinBanner()492   private void hideWaitingForJoinBanner() {
493     statusBanner.setVisibility(View.GONE);
494   }
495 
496   @Override
setEndCallButtonEnabled(boolean enabled, boolean animate)497   public void setEndCallButtonEnabled(boolean enabled, boolean animate) {}
498 
499   @Override
showManageConferenceCallButton(boolean visible)500   public void showManageConferenceCallButton(boolean visible) {}
501 
502   @Override
isManageConferenceVisible()503   public boolean isManageConferenceVisible() {
504     return false;
505   }
506 
507   @Override
dispatchPopulateAccessibilityEvent(AccessibilityEvent event)508   public void dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {}
509 
510   @Override
showNoteSentToast()511   public void showNoteSentToast() {}
512 
513   @Override
updateInCallScreenColors()514   public void updateInCallScreenColors() {}
515 
516   @Override
onInCallScreenDialpadVisibilityChange(boolean isShowing)517   public void onInCallScreenDialpadVisibilityChange(boolean isShowing) {
518     overflowMenu.setDialpadButtonChecked(isShowing);
519   }
520 
521   @Override
getAnswerAndDialpadContainerResourceId()522   public int getAnswerAndDialpadContainerResourceId() {
523     return R.id.incall_dialpad_container;
524   }
525 
526   @Override
showLocationUi(Fragment locationUi)527   public void showLocationUi(Fragment locationUi) {}
528 
529   @Override
isShowingLocationUi()530   public boolean isShowingLocationUi() {
531     return false;
532   }
533 
534   @Override
getInCallScreenFragment()535   public Fragment getInCallScreenFragment() {
536     return this;
537   }
538 
539   @Override
showButton(int buttonId, boolean show)540   public void showButton(int buttonId, boolean show) {
541     if (buttonId == InCallButtonIds.BUTTON_SWAP) {
542       overflowMenu.enableSwapCallButton(show);
543     }
544   }
545 
546   @Override
enableButton(int buttonId, boolean enable)547   public void enableButton(int buttonId, boolean enable) {}
548 
549   @Override
setEnabled(boolean on)550   public void setEnabled(boolean on) {}
551 
552   @Override
setHold(boolean on)553   public void setHold(boolean on) {}
554 
555   @Override
setCameraSwitched(boolean isBackFacingCamera)556   public void setCameraSwitched(boolean isBackFacingCamera) {}
557 
558   @Override
setVideoPaused(boolean isPaused)559   public void setVideoPaused(boolean isPaused) {}
560 
561   @Override
setAudioState(CallAudioState audioState)562   public void setAudioState(CallAudioState audioState) {
563     LogUtil.i("RttChatFragment.setAudioState", "audioState: " + audioState);
564     overflowMenu.setMuteButtonChecked(audioState.isMuted());
565     overflowMenu.setAudioState(audioState);
566     if (audioSelectMenu != null) {
567       audioSelectMenu.setAudioState(audioState);
568     }
569   }
570 
571   @Override
updateButtonStates()572   public void updateButtonStates() {}
573 
574   @Override
updateInCallButtonUiColors(int color)575   public void updateInCallButtonUiColors(int color) {}
576 
577   @Override
getInCallButtonUiFragment()578   public Fragment getInCallButtonUiFragment() {
579     return this;
580   }
581 
582   @Override
showAudioRouteSelector()583   public void showAudioRouteSelector() {
584     audioSelectMenu =
585         new AudioSelectMenu(
586             getContext(),
587             inCallButtonUiDelegate,
588             () -> overflowMenu.showAtLocation(getView(), Gravity.TOP | Gravity.RIGHT, 0, 0));
589     audioSelectMenu.showAtLocation(getView(), Gravity.TOP | Gravity.RIGHT, 0, 0);
590   }
591 
592   @Override
onAudioRouteSelected(int audioRoute)593   public void onAudioRouteSelected(int audioRoute) {
594     inCallButtonUiDelegate.setAudioRoute(audioRoute);
595   }
596 
597   @Override
onAudioRouteSelectorDismiss()598   public void onAudioRouteSelectorDismiss() {}
599 }
600