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