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