• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.graphics.drawable.LayerDrawable;
21 import android.os.Bundle;
22 import android.telecom.AudioState;
23 import android.view.ContextThemeWrapper;
24 import android.view.LayoutInflater;
25 import android.view.Menu;
26 import android.view.MenuItem;
27 import android.view.View;
28 import android.view.ViewGroup;
29 import android.view.accessibility.AccessibilityEvent;
30 import android.view.accessibility.AccessibilityManager;
31 import android.widget.CompoundButton;
32 import android.widget.ImageButton;
33 import android.widget.PopupMenu;
34 import android.widget.PopupMenu.OnDismissListener;
35 import android.widget.PopupMenu.OnMenuItemClickListener;
36 
37 /**
38  * Fragment for call control buttons
39  */
40 public class CallButtonFragment
41         extends BaseFragment<CallButtonPresenter, CallButtonPresenter.CallButtonUi>
42         implements CallButtonPresenter.CallButtonUi, OnMenuItemClickListener, OnDismissListener,
43         View.OnClickListener, CompoundButton.OnCheckedChangeListener {
44 
45     private ImageButton mAudioButton;
46     private ImageButton mChangeToVoiceButton;
47     private ImageButton mMuteButton;
48     private ImageButton mShowDialpadButton;
49     private ImageButton mHoldButton;
50     private ImageButton mSwapButton;
51     private ImageButton mChangeToVideoButton;
52     private ImageButton mSwitchCameraButton;
53     private ImageButton mAddCallButton;
54     private ImageButton mMergeButton;
55     private ImageButton mPauseVideoButton;
56     private ImageButton mOverflowButton;
57 
58     private PopupMenu mAudioModePopup;
59     private boolean mAudioModePopupVisible;
60     private PopupMenu mOverflowPopup;
61 
62     private int mPrevAudioMode = 0;
63 
64     // Constants for Drawable.setAlpha()
65     private static final int HIDDEN = 0;
66     private static final int VISIBLE = 255;
67 
68     private boolean mIsEnabled;
69 
70     @Override
createPresenter()71     CallButtonPresenter createPresenter() {
72         // TODO: find a cleaner way to include audio mode provider than having a singleton instance.
73         return new CallButtonPresenter();
74     }
75 
76     @Override
getUi()77     CallButtonPresenter.CallButtonUi getUi() {
78         return this;
79     }
80 
81     @Override
onCreate(Bundle savedInstanceState)82     public void onCreate(Bundle savedInstanceState) {
83         super.onCreate(savedInstanceState);
84     }
85 
86     @Override
onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)87     public View onCreateView(LayoutInflater inflater, ViewGroup container,
88             Bundle savedInstanceState) {
89         final View parent = inflater.inflate(R.layout.call_button_fragment, container, false);
90 
91         mAudioButton = (ImageButton) parent.findViewById(R.id.audioButton);
92         mAudioButton.setOnClickListener(this);
93         mChangeToVoiceButton = (ImageButton) parent.findViewById(R.id.changeToVoiceButton);
94         mChangeToVoiceButton. setOnClickListener(this);
95         mMuteButton = (ImageButton) parent.findViewById(R.id.muteButton);
96         mMuteButton.setOnClickListener(this);
97         mShowDialpadButton = (ImageButton) parent.findViewById(R.id.dialpadButton);
98         mShowDialpadButton.setOnClickListener(this);
99         mHoldButton = (ImageButton) parent.findViewById(R.id.holdButton);
100         mHoldButton.setOnClickListener(this);
101         mSwapButton = (ImageButton) parent.findViewById(R.id.swapButton);
102         mSwapButton.setOnClickListener(this);
103         mChangeToVideoButton = (ImageButton) parent.findViewById(R.id.changeToVideoButton);
104         mChangeToVideoButton.setOnClickListener(this);
105         mSwitchCameraButton = (ImageButton) parent.findViewById(R.id.switchCameraButton);
106         mSwitchCameraButton.setOnClickListener(this);
107         mAddCallButton = (ImageButton) parent.findViewById(R.id.addButton);
108         mAddCallButton.setOnClickListener(this);
109         mMergeButton = (ImageButton) parent.findViewById(R.id.mergeButton);
110         mMergeButton.setOnClickListener(this);
111         mPauseVideoButton = (ImageButton) parent.findViewById(R.id.pauseVideoButton);
112         mPauseVideoButton.setOnClickListener(this);
113         mOverflowButton = (ImageButton) parent.findViewById(R.id.overflowButton);
114         mOverflowButton.setOnClickListener(this);
115 
116         return parent;
117     }
118 
119     @Override
onActivityCreated(Bundle savedInstanceState)120     public void onActivityCreated(Bundle savedInstanceState) {
121         super.onActivityCreated(savedInstanceState);
122 
123         // set the buttons
124         updateAudioButtons(getPresenter().getSupportedAudio());
125     }
126 
127     @Override
onResume()128     public void onResume() {
129         if (getPresenter() != null) {
130             getPresenter().refreshMuteState();
131         }
132         super.onResume();
133     }
134 
135     @Override
onCheckedChanged(CompoundButton buttonView, boolean isChecked)136     public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
137     }
138 
139     @Override
onClick(View view)140     public void onClick(View view) {
141         int id = view.getId();
142         Log.d(this, "onClick(View " + view + ", id " + id + ")...");
143 
144         switch(id) {
145             case R.id.audioButton:
146                 onAudioButtonClicked();
147                 break;
148             case R.id.addButton:
149                 getPresenter().addCallClicked();
150                 break;
151             case R.id.changeToVoiceButton:
152                 getPresenter().changeToVoiceClicked();
153                 break;
154             case R.id.muteButton: {
155                 final ImageButton button = (ImageButton) view;
156                 getPresenter().muteClicked(!button.isSelected());
157                 break;
158             }
159             case R.id.mergeButton:
160                 getPresenter().mergeClicked();
161                 break;
162             case R.id.holdButton: {
163                 final ImageButton button = (ImageButton) view;
164                 getPresenter().holdClicked(!button.isSelected());
165                 break;
166             }
167             case R.id.swapButton:
168                 getPresenter().swapClicked();
169                 break;
170             case R.id.dialpadButton:
171                 getPresenter().showDialpadClicked(!mShowDialpadButton.isSelected());
172                 break;
173             case R.id.changeToVideoButton:
174                 getPresenter().changeToVideoClicked();
175                 break;
176             case R.id.switchCameraButton:
177                 getPresenter().switchCameraClicked(
178                         mSwitchCameraButton.isSelected() /* useFrontFacingCamera */);
179                 break;
180             case R.id.pauseVideoButton:
181                 getPresenter().pauseVideoClicked(
182                         !mPauseVideoButton.isSelected() /* pause */);
183                 break;
184             case R.id.overflowButton:
185                 mOverflowPopup.show();
186                 break;
187             default:
188                 Log.wtf(this, "onClick: unexpected");
189                 break;
190         }
191     }
192 
193     @Override
setEnabled(boolean isEnabled)194     public void setEnabled(boolean isEnabled) {
195         mIsEnabled = isEnabled;
196         View view = getView();
197         if (view.getVisibility() != View.VISIBLE) {
198             view.setVisibility(View.VISIBLE);
199         }
200 
201         mAudioButton.setEnabled(isEnabled);
202         mChangeToVoiceButton.setEnabled(isEnabled);
203         mMuteButton.setEnabled(isEnabled);
204         mShowDialpadButton.setEnabled(isEnabled);
205         mHoldButton.setEnabled(isEnabled);
206         mSwapButton.setEnabled(isEnabled);
207         mChangeToVideoButton.setEnabled(isEnabled);
208         mSwitchCameraButton.setEnabled(isEnabled);
209         mAddCallButton.setEnabled(isEnabled);
210         mMergeButton.setEnabled(isEnabled);
211         mPauseVideoButton.setEnabled(isEnabled);
212         mOverflowButton.setEnabled(isEnabled);
213     }
214 
215     @Override
setMute(boolean value)216     public void setMute(boolean value) {
217         if (mMuteButton.isSelected() != value) {
218             mMuteButton.setSelected(value);
219             maybeSendAccessibilityEvent(mMuteButton, value ? R.string.accessibility_call_muted
220                     : R.string.accessibility_call_unmuted);
221         }
222     }
223 
224     @Override
showAudioButton(boolean show)225     public void showAudioButton(boolean show) {
226         mAudioButton.setVisibility(show ? View.VISIBLE : View.GONE);
227     }
228 
229     @Override
showChangeToVoiceButton(boolean show)230     public void showChangeToVoiceButton(boolean show) {
231         mChangeToVoiceButton.setVisibility(show ? View.VISIBLE : View.GONE);
232     }
233 
234     @Override
enableMute(boolean enabled)235     public void enableMute(boolean enabled) {
236         mMuteButton.setEnabled(enabled);
237     }
238 
239     @Override
showDialpadButton(boolean show)240     public void showDialpadButton(boolean show) {
241         mShowDialpadButton.setVisibility(show ? View.VISIBLE : View.GONE);
242     }
243 
244     @Override
setHold(boolean value)245     public void setHold(boolean value) {
246         if (mHoldButton.isSelected() != value) {
247             mHoldButton.setSelected(value);
248             maybeSendAccessibilityEvent(mHoldButton,
249                     value ? R.string.accessibility_call_put_on_hold :
250                             R.string.accessibility_call_removed_from_hold);
251         }
252     }
253 
254     @Override
showHoldButton(boolean show)255     public void showHoldButton(boolean show) {
256         mHoldButton.setVisibility(show ? View.VISIBLE : View.GONE);
257     }
258 
259     @Override
enableHold(boolean enabled)260     public void enableHold(boolean enabled) {
261         mHoldButton.setEnabled(enabled);
262     }
263 
264     @Override
showSwapButton(boolean show)265     public void showSwapButton(boolean show) {
266         mSwapButton.setVisibility(show ? View.VISIBLE : View.GONE);
267     }
268 
269     @Override
showChangeToVideoButton(boolean show)270     public void showChangeToVideoButton(boolean show) {
271         mChangeToVideoButton.setVisibility(show ? View.VISIBLE : View.GONE);
272     }
273 
274     @Override
showSwitchCameraButton(boolean show)275     public void showSwitchCameraButton(boolean show) {
276         mSwitchCameraButton.setVisibility(show ? View.VISIBLE : View.GONE);
277     }
278 
279     @Override
setSwitchCameraButton(boolean isBackFacingCamera)280     public void setSwitchCameraButton(boolean isBackFacingCamera) {
281         mSwitchCameraButton.setSelected(isBackFacingCamera);
282     }
283 
284     @Override
showAddCallButton(boolean show)285     public void showAddCallButton(boolean show) {
286         Log.d(this, "show Add call button: " + show);
287         mAddCallButton.setVisibility(show ? View.VISIBLE : View.GONE);
288     }
289 
290     @Override
showMergeButton(boolean show)291     public void showMergeButton(boolean show) {
292         mMergeButton.setVisibility(show ? View.VISIBLE : View.GONE);
293     }
294 
295     @Override
showPauseVideoButton(boolean show)296     public void showPauseVideoButton(boolean show) {
297         mPauseVideoButton.setVisibility(show ? View.VISIBLE : View.GONE);
298     }
299 
300     @Override
setPauseVideoButton(boolean isPaused)301     public void setPauseVideoButton(boolean isPaused) {
302         mPauseVideoButton.setSelected(isPaused);
303     }
304 
305     @Override
showOverflowButton(boolean show)306     public void showOverflowButton(boolean show) {
307         mOverflowButton.setVisibility(show ? View.VISIBLE : View.GONE);
308     }
309 
310     @Override
configureOverflowMenu(boolean showMergeMenuOption, boolean showAddMenuOption, boolean showHoldMenuOption, boolean showSwapMenuOption)311     public void configureOverflowMenu(boolean showMergeMenuOption, boolean showAddMenuOption,
312             boolean showHoldMenuOption, boolean showSwapMenuOption) {
313         if (mOverflowPopup == null) {
314             final ContextThemeWrapper contextWrapper = new ContextThemeWrapper(getActivity(),
315                     R.style.InCallPopupMenuStyle);
316             mOverflowPopup = new PopupMenu(contextWrapper, mOverflowButton);
317             mOverflowPopup.getMenuInflater().inflate(R.menu.incall_overflow_menu,
318                     mOverflowPopup.getMenu());
319             mOverflowPopup.setOnMenuItemClickListener(new OnMenuItemClickListener() {
320                 @Override
321                 public boolean onMenuItemClick(MenuItem item) {
322                     switch (item.getItemId()) {
323                         case R.id.overflow_merge_menu_item:
324                             getPresenter().mergeClicked();
325                             break;
326                         case R.id.overflow_add_menu_item:
327                             getPresenter().addCallClicked();
328                             break;
329                         case R.id.overflow_hold_menu_item:
330                             getPresenter().holdClicked(true /* checked */);
331                             break;
332                         case R.id.overflow_resume_menu_item:
333                             getPresenter().holdClicked(false /* checked */);
334                             break;
335                         case R.id.overflow_swap_menu_item:
336                             getPresenter().addCallClicked();
337                             break;
338                         default:
339                             Log.wtf(this, "onMenuItemClick: unexpected overflow menu click");
340                             break;
341                     }
342                     return true;
343                 }
344             });
345             mOverflowPopup.setOnDismissListener(new OnDismissListener() {
346                 @Override
347                 public void onDismiss(PopupMenu popupMenu) {
348                     popupMenu.dismiss();
349                 }
350             });
351         }
352 
353         final Menu menu = mOverflowPopup.getMenu();
354         menu.findItem(R.id.overflow_merge_menu_item).setVisible(showMergeMenuOption);
355         menu.findItem(R.id.overflow_add_menu_item).setVisible(showAddMenuOption);
356         menu.findItem(R.id.overflow_hold_menu_item).setVisible(
357                 showHoldMenuOption && !mHoldButton.isSelected());
358         menu.findItem(R.id.overflow_resume_menu_item).setVisible(
359                 showHoldMenuOption && mHoldButton.isSelected());
360         menu.findItem(R.id.overflow_swap_menu_item).setVisible(showSwapMenuOption);
361 
362         mOverflowButton.setEnabled(menu.hasVisibleItems());
363     }
364 
365     @Override
setAudio(int mode)366     public void setAudio(int mode) {
367         updateAudioButtons(getPresenter().getSupportedAudio());
368         refreshAudioModePopup();
369 
370         if (mPrevAudioMode != mode) {
371             if (mPrevAudioMode != 0) {
372                 int stringId = 0;
373                 switch (mode) {
374                     case AudioState.ROUTE_EARPIECE:
375                         stringId = R.string.accessibility_earpiece_selected;
376                         break;
377                     case AudioState.ROUTE_BLUETOOTH:
378                         stringId = R.string.accessibility_bluetooth_headset_selected;
379                         break;
380                     case AudioState.ROUTE_WIRED_HEADSET:
381                         stringId = R.string.accessibility_wired_headset_selected;
382                         break;
383                     case AudioState.ROUTE_SPEAKER:
384                         stringId = R.string.accessibility_speakerphone_selected;
385                         break;
386                 }
387                 if (stringId != 0) {
388                     maybeSendAccessibilityEvent(mAudioButton, stringId);
389                 }
390             }
391             mPrevAudioMode = mode;
392         }
393     }
394 
395     @Override
setSupportedAudio(int modeMask)396     public void setSupportedAudio(int modeMask) {
397         updateAudioButtons(modeMask);
398         refreshAudioModePopup();
399     }
400 
401     @Override
onMenuItemClick(MenuItem item)402     public boolean onMenuItemClick(MenuItem item) {
403         Log.d(this, "- onMenuItemClick: " + item);
404         Log.d(this, "  id: " + item.getItemId());
405         Log.d(this, "  title: '" + item.getTitle() + "'");
406 
407         int mode = AudioState.ROUTE_WIRED_OR_EARPIECE;
408 
409         switch (item.getItemId()) {
410             case R.id.audio_mode_speaker:
411                 mode = AudioState.ROUTE_SPEAKER;
412                 break;
413             case R.id.audio_mode_earpiece:
414             case R.id.audio_mode_wired_headset:
415                 // InCallAudioState.ROUTE_EARPIECE means either the handset earpiece,
416                 // or the wired headset (if connected.)
417                 mode = AudioState.ROUTE_WIRED_OR_EARPIECE;
418                 break;
419             case R.id.audio_mode_bluetooth:
420                 mode = AudioState.ROUTE_BLUETOOTH;
421                 break;
422             default:
423                 Log.e(this, "onMenuItemClick:  unexpected View ID " + item.getItemId()
424                         + " (MenuItem = '" + item + "')");
425                 break;
426         }
427 
428         getPresenter().setAudioMode(mode);
429 
430         return true;
431     }
432 
433     // PopupMenu.OnDismissListener implementation; see showAudioModePopup().
434     // This gets called when the PopupMenu gets dismissed for *any* reason, like
435     // the user tapping outside its bounds, or pressing Back, or selecting one
436     // of the menu items.
437     @Override
onDismiss(PopupMenu menu)438     public void onDismiss(PopupMenu menu) {
439         Log.d(this, "- onDismiss: " + menu);
440         mAudioModePopupVisible = false;
441     }
442 
443     /**
444      * Checks for supporting modes.  If bluetooth is supported, it uses the audio
445      * pop up menu.  Otherwise, it toggles the speakerphone.
446      */
onAudioButtonClicked()447     private void onAudioButtonClicked() {
448         Log.d(this, "onAudioButtonClicked: " +
449                 AudioState.audioRouteToString(getPresenter().getSupportedAudio()));
450 
451         if (isSupported(AudioState.ROUTE_BLUETOOTH)) {
452             showAudioModePopup();
453         } else {
454             getPresenter().toggleSpeakerphone();
455         }
456     }
457 
458     /**
459      * Refreshes the "Audio mode" popup if it's visible.  This is useful
460      * (for example) when a wired headset is plugged or unplugged,
461      * since we need to switch back and forth between the "earpiece"
462      * and "wired headset" items.
463      *
464      * This is safe to call even if the popup is already dismissed, or even if
465      * you never called showAudioModePopup() in the first place.
466      */
refreshAudioModePopup()467     public void refreshAudioModePopup() {
468         if (mAudioModePopup != null && mAudioModePopupVisible) {
469             // Dismiss the previous one
470             mAudioModePopup.dismiss();  // safe even if already dismissed
471             // And bring up a fresh PopupMenu
472             showAudioModePopup();
473         }
474     }
475 
476     /**
477      * Updates the audio button so that the appriopriate visual layers
478      * are visible based on the supported audio formats.
479      */
updateAudioButtons(int supportedModes)480     private void updateAudioButtons(int supportedModes) {
481         final boolean bluetoothSupported = isSupported(AudioState.ROUTE_BLUETOOTH);
482         final boolean speakerSupported = isSupported(AudioState.ROUTE_SPEAKER);
483 
484         boolean audioButtonEnabled = false;
485         boolean audioButtonChecked = false;
486         boolean showMoreIndicator = false;
487 
488         boolean showBluetoothIcon = false;
489         boolean showSpeakerphoneIcon = false;
490         boolean showHandsetIcon = false;
491 
492         boolean showToggleIndicator = false;
493 
494         if (bluetoothSupported) {
495             Log.d(this, "updateAudioButtons - popup menu mode");
496 
497             audioButtonEnabled = true;
498             showMoreIndicator = true;
499             // The audio button is NOT a toggle in this state.  (And its
500             // setChecked() state is irrelevant since we completely hide the
501             // btn_compound_background layer anyway.)
502 
503             // Update desired layers:
504             if (isAudio(AudioState.ROUTE_BLUETOOTH)) {
505                 showBluetoothIcon = true;
506             } else if (isAudio(AudioState.ROUTE_SPEAKER)) {
507                 showSpeakerphoneIcon = true;
508             } else {
509                 showHandsetIcon = true;
510                 // TODO: if a wired headset is plugged in, that takes precedence
511                 // over the handset earpiece.  If so, maybe we should show some
512                 // sort of "wired headset" icon here instead of the "handset
513                 // earpiece" icon.  (Still need an asset for that, though.)
514             }
515         } else if (speakerSupported) {
516             Log.d(this, "updateAudioButtons - speaker toggle mode");
517 
518             audioButtonEnabled = true;
519 
520             // The audio button *is* a toggle in this state, and indicated the
521             // current state of the speakerphone.
522             audioButtonChecked = isAudio(AudioState.ROUTE_SPEAKER);
523 
524             // update desired layers:
525             showToggleIndicator = true;
526             showSpeakerphoneIcon = true;
527         } else {
528             Log.d(this, "updateAudioButtons - disabled...");
529 
530             // The audio button is a toggle in this state, but that's mostly
531             // irrelevant since it's always disabled and unchecked.
532             audioButtonEnabled = false;
533             audioButtonChecked = false;
534 
535             // update desired layers:
536             showToggleIndicator = true;
537             showSpeakerphoneIcon = true;
538         }
539 
540         // Finally, update it all!
541 
542         Log.v(this, "audioButtonEnabled: " + audioButtonEnabled);
543         Log.v(this, "audioButtonChecked: " + audioButtonChecked);
544         Log.v(this, "showMoreIndicator: " + showMoreIndicator);
545         Log.v(this, "showBluetoothIcon: " + showBluetoothIcon);
546         Log.v(this, "showSpeakerphoneIcon: " + showSpeakerphoneIcon);
547         Log.v(this, "showHandsetIcon: " + showHandsetIcon);
548 
549         // Only enable the audio button if the fragment is enabled.
550         mAudioButton.setEnabled(audioButtonEnabled && mIsEnabled);
551         mAudioButton.setSelected(audioButtonChecked);
552 
553         final LayerDrawable layers = (LayerDrawable) mAudioButton.getBackground();
554         Log.d(this, "'layers' drawable: " + layers);
555 
556         layers.findDrawableByLayerId(R.id.compoundBackgroundItem)
557                 .setAlpha(showToggleIndicator ? VISIBLE : HIDDEN);
558 
559         layers.findDrawableByLayerId(R.id.moreIndicatorItem)
560                 .setAlpha(showMoreIndicator ? VISIBLE : HIDDEN);
561 
562         layers.findDrawableByLayerId(R.id.bluetoothItem)
563                 .setAlpha(showBluetoothIcon ? VISIBLE : HIDDEN);
564 
565         layers.findDrawableByLayerId(R.id.handsetItem)
566                 .setAlpha(showHandsetIcon ? VISIBLE : HIDDEN);
567 
568         layers.findDrawableByLayerId(R.id.speakerphoneItem)
569                 .setAlpha(showSpeakerphoneIcon ? VISIBLE : HIDDEN);
570 
571     }
572 
showAudioModePopup()573     private void showAudioModePopup() {
574         Log.d(this, "showAudioPopup()...");
575 
576         final ContextThemeWrapper contextWrapper = new ContextThemeWrapper(getActivity(),
577                 R.style.InCallPopupMenuStyle);
578         mAudioModePopup = new PopupMenu(contextWrapper, mAudioButton /* anchorView */);
579         mAudioModePopup.getMenuInflater().inflate(R.menu.incall_audio_mode_menu,
580                 mAudioModePopup.getMenu());
581         mAudioModePopup.setOnMenuItemClickListener(this);
582         mAudioModePopup.setOnDismissListener(this);
583 
584         final Menu menu = mAudioModePopup.getMenu();
585 
586         // TODO: Still need to have the "currently active" audio mode come
587         // up pre-selected (or focused?) with a blue highlight.  Still
588         // need exact visual design, and possibly framework support for this.
589         // See comments below for the exact logic.
590 
591         final MenuItem speakerItem = menu.findItem(R.id.audio_mode_speaker);
592         speakerItem.setEnabled(isSupported(AudioState.ROUTE_SPEAKER));
593         // TODO: Show speakerItem as initially "selected" if
594         // speaker is on.
595 
596         // We display *either* "earpiece" or "wired headset", never both,
597         // depending on whether a wired headset is physically plugged in.
598         final MenuItem earpieceItem = menu.findItem(R.id.audio_mode_earpiece);
599         final MenuItem wiredHeadsetItem = menu.findItem(R.id.audio_mode_wired_headset);
600 
601         final boolean usingHeadset = isSupported(AudioState.ROUTE_WIRED_HEADSET);
602         earpieceItem.setVisible(!usingHeadset);
603         earpieceItem.setEnabled(!usingHeadset);
604         wiredHeadsetItem.setVisible(usingHeadset);
605         wiredHeadsetItem.setEnabled(usingHeadset);
606         // TODO: Show the above item (either earpieceItem or wiredHeadsetItem)
607         // as initially "selected" if speakerOn and
608         // bluetoothIndicatorOn are both false.
609 
610         final MenuItem bluetoothItem = menu.findItem(R.id.audio_mode_bluetooth);
611         bluetoothItem.setEnabled(isSupported(AudioState.ROUTE_BLUETOOTH));
612         // TODO: Show bluetoothItem as initially "selected" if
613         // bluetoothIndicatorOn is true.
614 
615         mAudioModePopup.show();
616 
617         // Unfortunately we need to manually keep track of the popup menu's
618         // visiblity, since PopupMenu doesn't have an isShowing() method like
619         // Dialogs do.
620         mAudioModePopupVisible = true;
621     }
622 
isSupported(int mode)623     private boolean isSupported(int mode) {
624         return (mode == (getPresenter().getSupportedAudio() & mode));
625     }
626 
isAudio(int mode)627     private boolean isAudio(int mode) {
628         return (mode == getPresenter().getAudioMode());
629     }
630 
631     @Override
displayDialpad(boolean value, boolean animate)632     public void displayDialpad(boolean value, boolean animate) {
633         mShowDialpadButton.setSelected(value);
634         if (getActivity() != null && getActivity() instanceof InCallActivity) {
635             ((InCallActivity) getActivity()).displayDialpad(value, animate);
636         }
637     }
638 
639     @Override
isDialpadVisible()640     public boolean isDialpadVisible() {
641         if (getActivity() != null && getActivity() instanceof InCallActivity) {
642             return ((InCallActivity) getActivity()).isDialpadVisible();
643         }
644         return false;
645     }
646 
647     @Override
getContext()648     public Context getContext() {
649         return getActivity();
650     }
651 
maybeSendAccessibilityEvent(View view, int stringId)652     private void maybeSendAccessibilityEvent(View view, int stringId) {
653         final Context context = getActivity();
654         AccessibilityManager manager =
655                 (AccessibilityManager) context.getSystemService(Context.ACCESSIBILITY_SERVICE);
656         if (manager != null && manager.isEnabled()) {
657             AccessibilityEvent e = AccessibilityEvent.obtain();
658             e.setSource(view);
659             e.setEventType(AccessibilityEvent.TYPE_ANNOUNCEMENT);
660             e.setClassName(getClass().getName());
661             e.setPackageName(context.getPackageName());
662             e.getText().add(context.getResources().getString(stringId));
663             manager.sendAccessibilityEvent(e);
664         }
665     }
666 }
667