Home | History | Annotate | Download | only in incallui
      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
     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
     77     CallButtonPresenter.CallButtonUi getUi() {
     78         return this;
     79     }
     80 
     81     @Override
     82     public void onCreate(Bundle savedInstanceState) {
     83         super.onCreate(savedInstanceState);
     84     }
     85 
     86     @Override
     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
    120     public void onActivityCreated(Bundle savedInstanceState) {
    121         super.onActivityCreated(savedInstanceState);
    122 
    123         // set the buttons
    124         updateAudioButtons(getPresenter().getSupportedAudio());
    125     }
    126 
    127     @Override
    128     public void onResume() {
    129         if (getPresenter() != null) {
    130             getPresenter().refreshMuteState();
    131         }
    132         super.onResume();
    133     }
    134 
    135     @Override
    136     public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
    137     }
    138 
    139     @Override
    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
    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
    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
    225     public void showAudioButton(boolean show) {
    226         mAudioButton.setVisibility(show ? View.VISIBLE : View.GONE);
    227     }
    228 
    229     @Override
    230     public void showChangeToVoiceButton(boolean show) {
    231         mChangeToVoiceButton.setVisibility(show ? View.VISIBLE : View.GONE);
    232     }
    233 
    234     @Override
    235     public void enableMute(boolean enabled) {
    236         mMuteButton.setEnabled(enabled);
    237     }
    238 
    239     @Override
    240     public void showDialpadButton(boolean show) {
    241         mShowDialpadButton.setVisibility(show ? View.VISIBLE : View.GONE);
    242     }
    243 
    244     @Override
    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
    255     public void showHoldButton(boolean show) {
    256         mHoldButton.setVisibility(show ? View.VISIBLE : View.GONE);
    257     }
    258 
    259     @Override
    260     public void enableHold(boolean enabled) {
    261         mHoldButton.setEnabled(enabled);
    262     }
    263 
    264     @Override
    265     public void showSwapButton(boolean show) {
    266         mSwapButton.setVisibility(show ? View.VISIBLE : View.GONE);
    267     }
    268 
    269     @Override
    270     public void showChangeToVideoButton(boolean show) {
    271         mChangeToVideoButton.setVisibility(show ? View.VISIBLE : View.GONE);
    272     }
    273 
    274     @Override
    275     public void showSwitchCameraButton(boolean show) {
    276         mSwitchCameraButton.setVisibility(show ? View.VISIBLE : View.GONE);
    277     }
    278 
    279     @Override
    280     public void setSwitchCameraButton(boolean isBackFacingCamera) {
    281         mSwitchCameraButton.setSelected(isBackFacingCamera);
    282     }
    283 
    284     @Override
    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
    291     public void showMergeButton(boolean show) {
    292         mMergeButton.setVisibility(show ? View.VISIBLE : View.GONE);
    293     }
    294 
    295     @Override
    296     public void showPauseVideoButton(boolean show) {
    297         mPauseVideoButton.setVisibility(show ? View.VISIBLE : View.GONE);
    298     }
    299 
    300     @Override
    301     public void setPauseVideoButton(boolean isPaused) {
    302         mPauseVideoButton.setSelected(isPaused);
    303     }
    304 
    305     @Override
    306     public void showOverflowButton(boolean show) {
    307         mOverflowButton.setVisibility(show ? View.VISIBLE : View.GONE);
    308     }
    309 
    310     @Override
    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
    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
    396     public void setSupportedAudio(int modeMask) {
    397         updateAudioButtons(modeMask);
    398         refreshAudioModePopup();
    399     }
    400 
    401     @Override
    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
    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      */
    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      */
    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      */
    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 
    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 
    623     private boolean isSupported(int mode) {
    624         return (mode == (getPresenter().getSupportedAudio() & mode));
    625     }
    626 
    627     private boolean isAudio(int mode) {
    628         return (mode == getPresenter().getAudioMode());
    629     }
    630 
    631     @Override
    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
    640     public boolean isDialpadVisible() {
    641         if (getActivity() != null && getActivity() instanceof InCallActivity) {
    642             return ((InCallActivity) getActivity()).isDialpadVisible();
    643         }
    644         return false;
    645     }
    646 
    647     @Override
    648     public Context getContext() {
    649         return getActivity();
    650     }
    651 
    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