InCallActivity.java revision 8bf9040cfb63181c3655b3cb111a342058037d6f
1/* 2 * Copyright (C) 2006 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 17package com.android.incallui; 18 19import android.app.ActionBar; 20import android.app.Activity; 21import android.app.AlertDialog; 22import android.app.FragmentManager; 23import android.app.FragmentTransaction; 24import android.content.Context; 25import android.content.DialogInterface; 26import android.content.DialogInterface.OnClickListener; 27import android.content.DialogInterface.OnCancelListener; 28import android.content.Intent; 29import android.content.res.Configuration; 30import android.content.res.Resources; 31import android.graphics.Point; 32import android.net.Uri; 33import android.os.Bundle; 34import android.telecom.DisconnectCause; 35import android.telecom.PhoneAccountHandle; 36import android.telecom.TelecomManager; 37import android.telephony.PhoneNumberUtils; 38import android.text.TextUtils; 39import android.view.MenuItem; 40import android.view.animation.Animation; 41import android.view.animation.AnimationUtils; 42import android.view.KeyEvent; 43import android.view.View; 44import android.view.Window; 45import android.view.WindowManager; 46import android.view.accessibility.AccessibilityEvent; 47 48import com.android.phone.common.animation.AnimUtils; 49import com.android.phone.common.animation.AnimationListenerAdapter; 50import com.android.contacts.common.interactions.TouchPointManager; 51import com.android.contacts.common.util.MaterialColorMapUtils; 52import com.android.contacts.common.util.MaterialColorMapUtils.MaterialPalette; 53import com.android.contacts.common.widget.SelectPhoneAccountDialogFragment; 54import com.android.contacts.common.widget.SelectPhoneAccountDialogFragment.SelectPhoneAccountListener; 55import com.android.incallui.Call.State; 56 57import java.util.ArrayList; 58import java.util.List; 59import java.util.Locale; 60 61/** 62 * Main activity that the user interacts with while in a live call. 63 */ 64public class InCallActivity extends Activity { 65 66 public static final String SHOW_DIALPAD_EXTRA = "InCallActivity.show_dialpad"; 67 public static final String DIALPAD_TEXT_EXTRA = "InCallActivity.dialpad_text"; 68 public static final String NEW_OUTGOING_CALL_EXTRA = "InCallActivity.new_outgoing_call"; 69 public static final String SHOW_CIRCULAR_REVEAL_EXTRA = "InCallActivity.show_circular_reveal"; 70 71 private CallButtonFragment mCallButtonFragment; 72 private CallCardFragment mCallCardFragment; 73 private AnswerFragment mAnswerFragment; 74 private DialpadFragment mDialpadFragment; 75 private ConferenceManagerFragment mConferenceManagerFragment; 76 private FragmentManager mChildFragmentManager; 77 78 private boolean mIsForegroundActivity; 79 private AlertDialog mDialog; 80 81 /** Use to pass 'showDialpad' from {@link #onNewIntent} to {@link #onResume} */ 82 private boolean mShowDialpadRequested; 83 84 /** Use to determine if the dialpad should be animated on show. */ 85 private boolean mAnimateDialpadOnShow; 86 87 /** Use to determine the DTMF Text which should be pre-populated in the dialpad. */ 88 private String mDtmfText; 89 90 /** Use to pass parameters for showing the PostCharDialog to {@link #onResume} */ 91 private boolean mShowPostCharWaitDialogOnResume; 92 private String mShowPostCharWaitDialogCallId; 93 private String mShowPostCharWaitDialogChars; 94 95 private boolean mIsLandscape; 96 private Animation mSlideIn; 97 private Animation mSlideOut; 98 private boolean mDismissKeyguard = false; 99 100 AnimationListenerAdapter mSlideOutListener = new AnimationListenerAdapter() { 101 @Override 102 public void onAnimationEnd(Animation animation) { 103 showDialpad(false); 104 } 105 }; 106 107 /** 108 * Stores the current orientation of the activity. Used to determine if a change in orientation 109 * has occurred. 110 */ 111 private int mCurrentOrientation; 112 113 @Override 114 protected void onCreate(Bundle icicle) { 115 Log.d(this, "onCreate()... this = " + this); 116 117 super.onCreate(icicle); 118 119 // set this flag so this activity will stay in front of the keyguard 120 // Have the WindowManager filter out touch events that are "too fat". 121 int flags = WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED 122 | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON 123 | WindowManager.LayoutParams.FLAG_IGNORE_CHEEK_PRESSES; 124 125 getWindow().addFlags(flags); 126 127 // Setup action bar for the conference call manager. 128 requestWindowFeature(Window.FEATURE_ACTION_BAR_OVERLAY); 129 ActionBar actionBar = getActionBar(); 130 if (actionBar != null) { 131 actionBar.setDisplayHomeAsUpEnabled(true); 132 actionBar.setDisplayShowTitleEnabled(true); 133 actionBar.hide(); 134 } 135 136 // TODO(klp): Do we need to add this back when prox sensor is not available? 137 // lp.inputFeatures |= WindowManager.LayoutParams.INPUT_FEATURE_DISABLE_USER_ACTIVITY; 138 139 setContentView(R.layout.incall_screen); 140 141 initializeInCall(); 142 143 internalResolveIntent(getIntent()); 144 145 mCurrentOrientation = getResources().getConfiguration().orientation; 146 mIsLandscape = getResources().getConfiguration().orientation 147 == Configuration.ORIENTATION_LANDSCAPE; 148 149 final boolean isRtl = TextUtils.getLayoutDirectionFromLocale(Locale.getDefault()) == 150 View.LAYOUT_DIRECTION_RTL; 151 152 if (mIsLandscape) { 153 mSlideIn = AnimationUtils.loadAnimation(this, 154 isRtl ? R.anim.dialpad_slide_in_left : R.anim.dialpad_slide_in_right); 155 mSlideOut = AnimationUtils.loadAnimation(this, 156 isRtl ? R.anim.dialpad_slide_out_left : R.anim.dialpad_slide_out_right); 157 } else { 158 mSlideIn = AnimationUtils.loadAnimation(this, R.anim.dialpad_slide_in_bottom); 159 mSlideOut = AnimationUtils.loadAnimation(this, R.anim.dialpad_slide_out_bottom); 160 } 161 162 mSlideIn.setInterpolator(AnimUtils.EASE_IN); 163 mSlideOut.setInterpolator(AnimUtils.EASE_OUT); 164 165 mSlideOut.setAnimationListener(mSlideOutListener); 166 167 if (icicle != null) { 168 // If the dialpad was shown before, set variables indicating it should be shown and 169 // populated with the previous DTMF text. The dialpad is actually shown and populated 170 // in onResume() to ensure the hosting CallCardFragment has been inflated and is ready 171 // to receive it. 172 mShowDialpadRequested = icicle.getBoolean(SHOW_DIALPAD_EXTRA); 173 mAnimateDialpadOnShow = false; 174 mDtmfText = icicle.getString(DIALPAD_TEXT_EXTRA); 175 } 176 Log.d(this, "onCreate(): exit"); 177 } 178 179 @Override 180 protected void onSaveInstanceState(Bundle out) { 181 out.putBoolean(SHOW_DIALPAD_EXTRA, mCallButtonFragment.isDialpadVisible()); 182 if (mDialpadFragment != null) { 183 out.putString(DIALPAD_TEXT_EXTRA, mDialpadFragment.getDtmfText()); 184 } 185 super.onSaveInstanceState(out); 186 } 187 188 @Override 189 protected void onStart() { 190 Log.d(this, "onStart()..."); 191 super.onStart(); 192 193 // setting activity should be last thing in setup process 194 InCallPresenter.getInstance().setActivity(this); 195 } 196 197 @Override 198 protected void onResume() { 199 Log.i(this, "onResume()..."); 200 super.onResume(); 201 202 mIsForegroundActivity = true; 203 204 InCallPresenter.getInstance().setThemeColors(); 205 InCallPresenter.getInstance().onUiShowing(true); 206 207 if (mShowDialpadRequested) { 208 mCallButtonFragment.displayDialpad(true /* show */, 209 mAnimateDialpadOnShow /* animate */); 210 mShowDialpadRequested = false; 211 mAnimateDialpadOnShow = false; 212 213 if (mDialpadFragment != null) { 214 mDialpadFragment.setDtmfText(mDtmfText); 215 mDtmfText = null; 216 } 217 } 218 219 if (mShowPostCharWaitDialogOnResume) { 220 showPostCharWaitDialog(mShowPostCharWaitDialogCallId, mShowPostCharWaitDialogChars); 221 } 222 } 223 224 // onPause is guaranteed to be called when the InCallActivity goes 225 // in the background. 226 @Override 227 protected void onPause() { 228 Log.d(this, "onPause()..."); 229 super.onPause(); 230 231 mIsForegroundActivity = false; 232 233 if (mDialpadFragment != null ) { 234 mDialpadFragment.onDialerKeyUp(null); 235 } 236 237 InCallPresenter.getInstance().onUiShowing(false); 238 if (isFinishing()) { 239 InCallPresenter.getInstance().unsetActivity(this); 240 } 241 } 242 243 @Override 244 protected void onStop() { 245 Log.d(this, "onStop()..."); 246 super.onStop(); 247 } 248 249 @Override 250 protected void onDestroy() { 251 Log.d(this, "onDestroy()... this = " + this); 252 InCallPresenter.getInstance().unsetActivity(this); 253 super.onDestroy(); 254 } 255 256 /** 257 * Returns true when theActivity is in foreground (between onResume and onPause). 258 */ 259 /* package */ boolean isForegroundActivity() { 260 return mIsForegroundActivity; 261 } 262 263 private boolean hasPendingErrorDialog() { 264 return mDialog != null; 265 } 266 267 @Override 268 public void finish() { 269 Log.i(this, "finish(). Dialog showing: " + (mDialog != null)); 270 271 // skip finish if we are still showing a dialog. 272 if (!hasPendingErrorDialog() && !mAnswerFragment.hasPendingDialogs()) { 273 super.finish(); 274 } 275 } 276 277 @Override 278 protected void onNewIntent(Intent intent) { 279 Log.d(this, "onNewIntent: intent = " + intent); 280 281 // We're being re-launched with a new Intent. Since it's possible for a 282 // single InCallActivity instance to persist indefinitely (even if we 283 // finish() ourselves), this sequence can potentially happen any time 284 // the InCallActivity needs to be displayed. 285 286 // Stash away the new intent so that we can get it in the future 287 // by calling getIntent(). (Otherwise getIntent() will return the 288 // original Intent from when we first got created!) 289 setIntent(intent); 290 291 // Activities are always paused before receiving a new intent, so 292 // we can count on our onResume() method being called next. 293 294 // Just like in onCreate(), handle the intent. 295 internalResolveIntent(intent); 296 } 297 298 @Override 299 public void onBackPressed() { 300 Log.i(this, "onBackPressed"); 301 302 // BACK is also used to exit out of any "special modes" of the 303 // in-call UI: 304 305 if (!mConferenceManagerFragment.isVisible() && !mCallCardFragment.isVisible()) { 306 return; 307 } 308 309 if (mDialpadFragment != null && mDialpadFragment.isVisible()) { 310 mCallButtonFragment.displayDialpad(false /* show */, true /* animate */); 311 return; 312 } else if (mConferenceManagerFragment.isVisible()) { 313 showConferenceCallManager(false); 314 return; 315 } 316 317 // Always disable the Back key while an incoming call is ringing 318 final Call call = CallList.getInstance().getIncomingCall(); 319 if (call != null) { 320 Log.i(this, "Consume Back press for an incoming call"); 321 return; 322 } 323 324 // Nothing special to do. Fall back to the default behavior. 325 super.onBackPressed(); 326 } 327 328 @Override 329 public boolean onOptionsItemSelected(MenuItem item) { 330 final int itemId = item.getItemId(); 331 if (itemId == android.R.id.home) { 332 onBackPressed(); 333 return true; 334 } 335 return super.onOptionsItemSelected(item); 336 } 337 338 @Override 339 public boolean onKeyUp(int keyCode, KeyEvent event) { 340 // push input to the dialer. 341 if (mDialpadFragment != null && (mDialpadFragment.isVisible()) && 342 (mDialpadFragment.onDialerKeyUp(event))){ 343 return true; 344 } else if (keyCode == KeyEvent.KEYCODE_CALL) { 345 // Always consume CALL to be sure the PhoneWindow won't do anything with it 346 return true; 347 } 348 return super.onKeyUp(keyCode, event); 349 } 350 351 @Override 352 public boolean onKeyDown(int keyCode, KeyEvent event) { 353 switch (keyCode) { 354 case KeyEvent.KEYCODE_CALL: 355 boolean handled = InCallPresenter.getInstance().handleCallKey(); 356 if (!handled) { 357 Log.w(this, "InCallActivity should always handle KEYCODE_CALL in onKeyDown"); 358 } 359 // Always consume CALL to be sure the PhoneWindow won't do anything with it 360 return true; 361 362 // Note there's no KeyEvent.KEYCODE_ENDCALL case here. 363 // The standard system-wide handling of the ENDCALL key 364 // (see PhoneWindowManager's handling of KEYCODE_ENDCALL) 365 // already implements exactly what the UI spec wants, 366 // namely (1) "hang up" if there's a current active call, 367 // or (2) "don't answer" if there's a current ringing call. 368 369 case KeyEvent.KEYCODE_CAMERA: 370 // Disable the CAMERA button while in-call since it's too 371 // easy to press accidentally. 372 return true; 373 374 case KeyEvent.KEYCODE_VOLUME_UP: 375 case KeyEvent.KEYCODE_VOLUME_DOWN: 376 case KeyEvent.KEYCODE_VOLUME_MUTE: 377 // Ringer silencing handled by PhoneWindowManager. 378 break; 379 380 case KeyEvent.KEYCODE_MUTE: 381 // toggle mute 382 TelecomAdapter.getInstance().mute(!AudioModeProvider.getInstance().getMute()); 383 return true; 384 385 // Various testing/debugging features, enabled ONLY when VERBOSE == true. 386 case KeyEvent.KEYCODE_SLASH: 387 if (Log.VERBOSE) { 388 Log.v(this, "----------- InCallActivity View dump --------------"); 389 // Dump starting from the top-level view of the entire activity: 390 Window w = this.getWindow(); 391 View decorView = w.getDecorView(); 392 Log.d(this, "View dump:" + decorView); 393 return true; 394 } 395 break; 396 case KeyEvent.KEYCODE_EQUALS: 397 // TODO: Dump phone state? 398 break; 399 } 400 401 if (event.getRepeatCount() == 0 && handleDialerKeyDown(keyCode, event)) { 402 return true; 403 } 404 405 return super.onKeyDown(keyCode, event); 406 } 407 408 private boolean handleDialerKeyDown(int keyCode, KeyEvent event) { 409 Log.v(this, "handleDialerKeyDown: keyCode " + keyCode + ", event " + event + "..."); 410 411 // As soon as the user starts typing valid dialable keys on the 412 // keyboard (presumably to type DTMF tones) we start passing the 413 // key events to the DTMFDialer's onDialerKeyDown. 414 if (mDialpadFragment != null && mDialpadFragment.isVisible()) { 415 return mDialpadFragment.onDialerKeyDown(event); 416 } 417 418 return false; 419 } 420 421 @Override 422 public void onConfigurationChanged(Configuration config) { 423 InCallPresenter.getInstance().getProximitySensor().onConfigurationChanged(config); 424 Log.d(this, "onConfigurationChanged "+config.orientation); 425 426 // Check to see if the orientation changed to prevent triggering orientation change events 427 // for other configuration changes. 428 if (config.orientation != mCurrentOrientation) { 429 mCurrentOrientation = config.orientation; 430 InCallPresenter.getInstance().onDeviceRotationChange( 431 getWindowManager().getDefaultDisplay().getRotation()); 432 InCallPresenter.getInstance().onDeviceOrientationChange(mCurrentOrientation); 433 } 434 super.onConfigurationChanged(config); 435 } 436 437 public CallButtonFragment getCallButtonFragment() { 438 return mCallButtonFragment; 439 } 440 441 public CallCardFragment getCallCardFragment() { 442 return mCallCardFragment; 443 } 444 445 private void internalResolveIntent(Intent intent) { 446 final String action = intent.getAction(); 447 448 if (action.equals(intent.ACTION_MAIN)) { 449 // This action is the normal way to bring up the in-call UI. 450 // 451 // But we do check here for one extra that can come along with the 452 // ACTION_MAIN intent: 453 454 if (intent.hasExtra(SHOW_DIALPAD_EXTRA)) { 455 // SHOW_DIALPAD_EXTRA can be used here to specify whether the DTMF 456 // dialpad should be initially visible. If the extra isn't 457 // present at all, we just leave the dialpad in its previous state. 458 459 final boolean showDialpad = intent.getBooleanExtra(SHOW_DIALPAD_EXTRA, false); 460 Log.d(this, "- internalResolveIntent: SHOW_DIALPAD_EXTRA: " + showDialpad); 461 462 relaunchedFromDialer(showDialpad); 463 } 464 465 if (intent.getBooleanExtra(NEW_OUTGOING_CALL_EXTRA, false)) { 466 intent.removeExtra(NEW_OUTGOING_CALL_EXTRA); 467 Call call = CallList.getInstance().getOutgoingCall(); 468 if (call == null) { 469 call = CallList.getInstance().getPendingOutgoingCall(); 470 } 471 472 Bundle extras = null; 473 if (call != null) { 474 extras = call.getTelecommCall().getDetails().getExtras(); 475 } 476 if (extras == null) { 477 // Initialize the extras bundle to avoid NPE 478 extras = new Bundle(); 479 } 480 481 Point touchPoint = null; 482 if (TouchPointManager.getInstance().hasValidPoint()) { 483 // Use the most immediate touch point in the InCallUi if available 484 touchPoint = TouchPointManager.getInstance().getPoint(); 485 } else { 486 // Otherwise retrieve the touch point from the call intent 487 if (call != null) { 488 touchPoint = (Point) extras.getParcelable(TouchPointManager.TOUCH_POINT); 489 } 490 } 491 492 // This is only true in the case where an outgoing call is initiated by tapping 493 // on the "Select account dialog", in which case we skip the initial animation. In 494 // most other cases the circular reveal is done by OutgoingCallAnimationActivity. 495 final boolean showCircularReveal = 496 intent.getBooleanExtra(SHOW_CIRCULAR_REVEAL_EXTRA, false); 497 mCallCardFragment.animateForNewOutgoingCall(touchPoint, showCircularReveal); 498 499 // InCallActivity is responsible for disconnecting a new outgoing call if there 500 // is no way of making it (i.e. no valid call capable accounts) 501 if (InCallPresenter.isCallWithNoValidAccounts(call)) { 502 TelecomAdapter.getInstance().disconnectCall(call.getId()); 503 } 504 505 dismissKeyguard(true); 506 } 507 508 Call pendingAccountSelectionCall = CallList.getInstance().getWaitingForAccountCall(); 509 if (pendingAccountSelectionCall != null) { 510 mCallCardFragment.setVisible(false); 511 Bundle extras = pendingAccountSelectionCall 512 .getTelecommCall().getDetails().getExtras(); 513 514 final List<PhoneAccountHandle> phoneAccountHandles; 515 if (extras != null) { 516 phoneAccountHandles = extras.getParcelableArrayList( 517 android.telecom.Call.AVAILABLE_PHONE_ACCOUNTS); 518 } else { 519 phoneAccountHandles = new ArrayList<>(); 520 } 521 522 SelectPhoneAccountListener listener = new SelectPhoneAccountListener() { 523 @Override 524 public void onPhoneAccountSelected(PhoneAccountHandle selectedAccountHandle, 525 boolean setDefault) { 526 InCallPresenter.getInstance().handleAccountSelection(selectedAccountHandle, 527 setDefault); 528 } 529 @Override 530 public void onDialogDismissed() { 531 InCallPresenter.getInstance().cancelAccountSelection(); 532 } 533 }; 534 535 SelectPhoneAccountDialogFragment.showAccountDialog(getFragmentManager(), 536 R.string.select_phone_account_for_calls, true, phoneAccountHandles, 537 listener); 538 } else { 539 mCallCardFragment.setVisible(true); 540 } 541 542 return; 543 } 544 } 545 546 private void relaunchedFromDialer(boolean showDialpad) { 547 mShowDialpadRequested = showDialpad; 548 mAnimateDialpadOnShow = true; 549 550 if (mShowDialpadRequested) { 551 // If there's only one line in use, AND it's on hold, then we're sure the user 552 // wants to use the dialpad toward the exact line, so un-hold the holding line. 553 final Call call = CallList.getInstance().getActiveOrBackgroundCall(); 554 if (call != null && call.getState() == State.ONHOLD) { 555 TelecomAdapter.getInstance().unholdCall(call.getId()); 556 } 557 } 558 } 559 560 private void initializeInCall() { 561 if (mCallCardFragment == null) { 562 mCallCardFragment = (CallCardFragment) getFragmentManager() 563 .findFragmentById(R.id.callCardFragment); 564 } 565 566 mChildFragmentManager = mCallCardFragment.getChildFragmentManager(); 567 568 if (mCallButtonFragment == null) { 569 mCallButtonFragment = (CallButtonFragment) mChildFragmentManager 570 .findFragmentById(R.id.callButtonFragment); 571 mCallButtonFragment.getView().setVisibility(View.INVISIBLE); 572 } 573 574 if (mAnswerFragment == null) { 575 mAnswerFragment = (AnswerFragment) mChildFragmentManager 576 .findFragmentById(R.id.answerFragment); 577 } 578 579 if (mConferenceManagerFragment == null) { 580 mConferenceManagerFragment = (ConferenceManagerFragment) getFragmentManager() 581 .findFragmentById(R.id.conferenceManagerFragment); 582 mConferenceManagerFragment.getView().setVisibility(View.INVISIBLE); 583 } 584 } 585 586 public void dismissKeyguard(boolean dismiss) { 587 if (mDismissKeyguard == dismiss) { 588 return; 589 } 590 mDismissKeyguard = dismiss; 591 if (dismiss) { 592 getWindow().addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD); 593 } else { 594 getWindow().clearFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD); 595 } 596 } 597 598 private void showDialpad(boolean showDialpad) { 599 // If the dialpad is being shown and it has not already been loaded, replace the dialpad 600 // placeholder with the actual fragment before continuing. 601 if (mDialpadFragment == null && showDialpad) { 602 final FragmentTransaction loadTransaction = mChildFragmentManager.beginTransaction(); 603 View fragmentContainer = findViewById(R.id.dialpadFragmentContainer); 604 mDialpadFragment = new DialpadFragment(); 605 loadTransaction.replace(fragmentContainer.getId(), mDialpadFragment, 606 DialpadFragment.class.getName()); 607 loadTransaction.commitAllowingStateLoss(); 608 mChildFragmentManager.executePendingTransactions(); 609 } 610 611 final FragmentTransaction ft = mChildFragmentManager.beginTransaction(); 612 if (showDialpad) { 613 ft.show(mDialpadFragment); 614 } else { 615 ft.hide(mDialpadFragment); 616 } 617 ft.commitAllowingStateLoss(); 618 } 619 620 public void displayDialpad(boolean showDialpad, boolean animate) { 621 // If the dialpad is already visible, don't animate in. If it's gone, don't animate out. 622 if ((showDialpad && isDialpadVisible()) || (!showDialpad && !isDialpadVisible())) { 623 return; 624 } 625 // We don't do a FragmentTransaction on the hide case because it will be dealt with when 626 // the listener is fired after an animation finishes. 627 if (!animate) { 628 showDialpad(showDialpad); 629 } else { 630 if (showDialpad) { 631 showDialpad(true); 632 mDialpadFragment.animateShowDialpad(); 633 } 634 mCallCardFragment.onDialpadVisiblityChange(showDialpad); 635 mDialpadFragment.getView().startAnimation(showDialpad ? mSlideIn : mSlideOut); 636 } 637 638 InCallPresenter.getInstance().getProximitySensor().onDialpadVisible(showDialpad); 639 } 640 641 public boolean isDialpadVisible() { 642 return mDialpadFragment != null && mDialpadFragment.isVisible(); 643 } 644 645 /** 646 * Hides or shows the conference manager fragment. 647 * 648 * @param show {@code true} if the conference manager should be shown, {@code false} if it 649 * should be hidden. 650 */ 651 public void showConferenceCallManager(boolean show) { 652 mConferenceManagerFragment.setVisible(show); 653 654 // Need to hide the call card fragment to ensure that accessibility service does not try to 655 // give focus to the call card when the conference manager is visible. 656 mCallCardFragment.getView().setVisibility(show ? View.GONE : View.VISIBLE); 657 } 658 659 public void showPostCharWaitDialog(String callId, String chars) { 660 if (isForegroundActivity()) { 661 final PostCharDialogFragment fragment = new PostCharDialogFragment(callId, chars); 662 fragment.show(getFragmentManager(), "postCharWait"); 663 664 mShowPostCharWaitDialogOnResume = false; 665 mShowPostCharWaitDialogCallId = null; 666 mShowPostCharWaitDialogChars = null; 667 } else { 668 mShowPostCharWaitDialogOnResume = true; 669 mShowPostCharWaitDialogCallId = callId; 670 mShowPostCharWaitDialogChars = chars; 671 } 672 } 673 674 @Override 675 public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) { 676 if (mCallCardFragment != null) { 677 mCallCardFragment.dispatchPopulateAccessibilityEvent(event); 678 } 679 return super.dispatchPopulateAccessibilityEvent(event); 680 } 681 682 public void maybeShowErrorDialogOnDisconnect(DisconnectCause disconnectCause) { 683 Log.d(this, "maybeShowErrorDialogOnDisconnect"); 684 685 if (!isFinishing() && !TextUtils.isEmpty(disconnectCause.getDescription()) 686 && (disconnectCause.getCode() == DisconnectCause.ERROR || 687 disconnectCause.getCode() == DisconnectCause.RESTRICTED)) { 688 showErrorDialog(disconnectCause.getDescription()); 689 } 690 } 691 692 public void dismissPendingDialogs() { 693 if (mDialog != null) { 694 mDialog.dismiss(); 695 mDialog = null; 696 } 697 mAnswerFragment.dismissPendingDialogues(); 698 } 699 700 /** 701 * Utility function to bring up a generic "error" dialog. 702 */ 703 private void showErrorDialog(CharSequence msg) { 704 Log.i(this, "Show Dialog: " + msg); 705 706 dismissPendingDialogs(); 707 708 mDialog = new AlertDialog.Builder(this) 709 .setMessage(msg) 710 .setPositiveButton(android.R.string.ok, new OnClickListener() { 711 @Override 712 public void onClick(DialogInterface dialog, int which) { 713 onDialogDismissed(); 714 }}) 715 .setOnCancelListener(new OnCancelListener() { 716 @Override 717 public void onCancel(DialogInterface dialog) { 718 onDialogDismissed(); 719 }}) 720 .create(); 721 722 mDialog.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND); 723 mDialog.show(); 724 } 725 726 private void onDialogDismissed() { 727 mDialog = null; 728 InCallPresenter.getInstance().onDismissDialog(); 729 } 730} 731