Home | History | Annotate | Download | only in incallui
      1 /*
      2  * Copyright (C) 2016 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.content.Intent;
     21 import android.graphics.drawable.GradientDrawable;
     22 import android.graphics.drawable.GradientDrawable.Orientation;
     23 import android.os.Bundle;
     24 import android.support.annotation.ColorInt;
     25 import android.support.annotation.FloatRange;
     26 import android.support.annotation.NonNull;
     27 import android.support.annotation.Nullable;
     28 import android.support.v4.app.FragmentManager;
     29 import android.support.v4.app.FragmentTransaction;
     30 import android.support.v4.graphics.ColorUtils;
     31 import android.telecom.DisconnectCause;
     32 import android.telephony.TelephonyManager;
     33 import android.view.KeyEvent;
     34 import android.view.MenuItem;
     35 import android.view.MotionEvent;
     36 import android.view.View;
     37 import com.android.dialer.common.Assert;
     38 import com.android.dialer.common.ConfigProviderBindings;
     39 import com.android.dialer.common.LogUtil;
     40 import com.android.dialer.compat.ActivityCompat;
     41 import com.android.dialer.logging.Logger;
     42 import com.android.dialer.logging.ScreenEvent;
     43 import com.android.incallui.answer.bindings.AnswerBindings;
     44 import com.android.incallui.answer.protocol.AnswerScreen;
     45 import com.android.incallui.answer.protocol.AnswerScreenDelegate;
     46 import com.android.incallui.answer.protocol.AnswerScreenDelegateFactory;
     47 import com.android.incallui.answerproximitysensor.PseudoScreenState;
     48 import com.android.incallui.call.CallList;
     49 import com.android.incallui.call.DialerCall;
     50 import com.android.incallui.call.DialerCall.State;
     51 import com.android.incallui.incall.bindings.InCallBindings;
     52 import com.android.incallui.incall.protocol.InCallButtonUiDelegate;
     53 import com.android.incallui.incall.protocol.InCallButtonUiDelegateFactory;
     54 import com.android.incallui.incall.protocol.InCallScreen;
     55 import com.android.incallui.incall.protocol.InCallScreenDelegate;
     56 import com.android.incallui.incall.protocol.InCallScreenDelegateFactory;
     57 import com.android.incallui.video.bindings.VideoBindings;
     58 import com.android.incallui.video.protocol.VideoCallScreen;
     59 import com.android.incallui.video.protocol.VideoCallScreenDelegate;
     60 import com.android.incallui.video.protocol.VideoCallScreenDelegateFactory;
     61 
     62 /** Version of {@link InCallActivity} that shows the new UI */
     63 public class InCallActivity extends TransactionSafeFragmentActivity
     64     implements AnswerScreenDelegateFactory,
     65         InCallScreenDelegateFactory,
     66         InCallButtonUiDelegateFactory,
     67         VideoCallScreenDelegateFactory,
     68         PseudoScreenState.StateChangedListener {
     69 
     70   private static final String TAG_IN_CALL_SCREEN = "tag_in_call_screen";
     71   private static final String TAG_ANSWER_SCREEN = "tag_answer_screen";
     72   private static final String TAG_VIDEO_CALL_SCREEN = "tag_video_call_screen";
     73 
     74   private static final String DID_SHOW_ANSWER_SCREEN_KEY = "did_show_answer_screen";
     75   private static final String DID_SHOW_IN_CALL_SCREEN_KEY = "did_show_in_call_screen";
     76   private static final String DID_SHOW_VIDEO_CALL_SCREEN_KEY = "did_show_video_call_screen";
     77 
     78   private static final String CONFIG_ANSWER_AND_RELEASE_ENABLED = "answer_and_release_enabled";
     79 
     80   private final InCallActivityCommon common;
     81   private boolean didShowAnswerScreen;
     82   private boolean didShowInCallScreen;
     83   private boolean didShowVideoCallScreen;
     84   private int[] backgroundDrawableColors;
     85   private GradientDrawable backgroundDrawable;
     86   private boolean isVisible;
     87   private View pseudoBlackScreenOverlay;
     88   private boolean touchDownWhenPseudoScreenOff;
     89   private boolean isInShowMainInCallFragment;
     90   private boolean needDismissPendingDialogs;
     91 
     92   public InCallActivity() {
     93     common = new InCallActivityCommon(this);
     94   }
     95 
     96   public static Intent getIntent(
     97       Context context, boolean showDialpad, boolean newOutgoingCall, boolean isForFullScreen) {
     98     Intent intent = new Intent(Intent.ACTION_MAIN, null);
     99     intent.setFlags(Intent.FLAG_ACTIVITY_NO_USER_ACTION | Intent.FLAG_ACTIVITY_NEW_TASK);
    100     intent.setClass(context, InCallActivity.class);
    101     InCallActivityCommon.setIntentExtras(intent, showDialpad, newOutgoingCall, isForFullScreen);
    102     return intent;
    103   }
    104 
    105   @Override
    106   protected void onResumeFragments() {
    107     super.onResumeFragments();
    108     if (needDismissPendingDialogs) {
    109       dismissPendingDialogs();
    110     }
    111   }
    112 
    113   @Override
    114   protected void onCreate(Bundle icicle) {
    115     LogUtil.i("InCallActivity.onCreate", "");
    116     super.onCreate(icicle);
    117 
    118     if (icicle != null) {
    119       didShowAnswerScreen = icicle.getBoolean(DID_SHOW_ANSWER_SCREEN_KEY);
    120       didShowInCallScreen = icicle.getBoolean(DID_SHOW_IN_CALL_SCREEN_KEY);
    121       didShowVideoCallScreen = icicle.getBoolean(DID_SHOW_VIDEO_CALL_SCREEN_KEY);
    122     }
    123 
    124     common.onCreate(icicle);
    125 
    126     getWindow()
    127         .getDecorView()
    128         .setSystemUiVisibility(
    129             View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
    130 
    131     pseudoBlackScreenOverlay = findViewById(R.id.psuedo_black_screen_overlay);
    132   }
    133 
    134   @Override
    135   protected void onSaveInstanceState(Bundle out) {
    136     LogUtil.i("InCallActivity.onSaveInstanceState", "");
    137     common.onSaveInstanceState(out);
    138     out.putBoolean(DID_SHOW_ANSWER_SCREEN_KEY, didShowAnswerScreen);
    139     out.putBoolean(DID_SHOW_IN_CALL_SCREEN_KEY, didShowInCallScreen);
    140     out.putBoolean(DID_SHOW_VIDEO_CALL_SCREEN_KEY, didShowVideoCallScreen);
    141     super.onSaveInstanceState(out);
    142     isVisible = false;
    143   }
    144 
    145   @Override
    146   protected void onStart() {
    147     LogUtil.i("InCallActivity.onStart", "");
    148     super.onStart();
    149     isVisible = true;
    150     showMainInCallFragment();
    151     common.onStart();
    152     if (ActivityCompat.isInMultiWindowMode(this)
    153         && !getResources().getBoolean(R.bool.incall_dialpad_allowed)) {
    154       // Hide the dialpad because there may not be enough room
    155       showDialpadFragment(false, false);
    156     }
    157   }
    158 
    159   @Override
    160   protected void onResume() {
    161     LogUtil.i("InCallActivity.onResume", "");
    162     super.onResume();
    163     common.onResume();
    164     PseudoScreenState pseudoScreenState = InCallPresenter.getInstance().getPseudoScreenState();
    165     pseudoScreenState.addListener(this);
    166     onPseudoScreenStateChanged(pseudoScreenState.isOn());
    167   }
    168 
    169   /** onPause is guaranteed to be called when the InCallActivity goes in the background. */
    170   @Override
    171   protected void onPause() {
    172     LogUtil.i("InCallActivity.onPause", "");
    173     super.onPause();
    174     common.onPause();
    175     InCallPresenter.getInstance().getPseudoScreenState().removeListener(this);
    176   }
    177 
    178   @Override
    179   protected void onStop() {
    180     LogUtil.i("InCallActivity.onStop", "");
    181     super.onStop();
    182     common.onStop();
    183     isVisible = false;
    184   }
    185 
    186   @Override
    187   protected void onDestroy() {
    188     LogUtil.i("InCallActivity.onDestroy", "");
    189     super.onDestroy();
    190     common.onDestroy();
    191   }
    192 
    193   @Override
    194   public void finish() {
    195     if (shouldCloseActivityOnFinish()) {
    196       // When user select incall ui from recents after the call is disconnected, it tries to launch
    197       // a new InCallActivity but InCallPresenter is already teared down at this point, which causes
    198       // crash.
    199       // By calling finishAndRemoveTask() instead of finish() the task associated with
    200       // InCallActivity is cleared completely. So system won't try to create a new InCallActivity in
    201       // this case.
    202       //
    203       // Calling finish won't clear the task and normally when an activity finishes it shouldn't
    204       // clear the task since there could be parent activity in the same task that's still alive.
    205       // But InCallActivity is special since it's singleInstance which means it's root activity and
    206       // only instance of activity in the task. So it should be safe to also remove task when
    207       // finishing.
    208       // It's also necessary in the sense of it's excluded from recents. So whenever the activity
    209       // finishes, the task should also be removed since it doesn't make sense to go back to it in
    210       // anyway anymore.
    211       super.finishAndRemoveTask();
    212     }
    213   }
    214 
    215   private boolean shouldCloseActivityOnFinish() {
    216     if (!isVisible()) {
    217       LogUtil.i(
    218           "InCallActivity.shouldCloseActivityOnFinish",
    219           "allowing activity to be closed because it's not visible");
    220       return true;
    221     }
    222 
    223     if (common.hasPendingDialogs()) {
    224       LogUtil.i(
    225           "InCallActivity.shouldCloseActivityOnFinish", "dialog is visible, not closing activity");
    226       return false;
    227     }
    228 
    229     AnswerScreen answerScreen = getAnswerScreen();
    230     if (answerScreen != null && answerScreen.hasPendingDialogs()) {
    231       LogUtil.i(
    232           "InCallActivity.shouldCloseActivityOnFinish",
    233           "answer screen dialog is visible, not closing activity");
    234       return false;
    235     }
    236 
    237     LogUtil.i(
    238         "InCallActivity.shouldCloseActivityOnFinish",
    239         "activity is visible and has no dialogs, allowing activity to close");
    240     return true;
    241   }
    242 
    243   @Override
    244   protected void onNewIntent(Intent intent) {
    245     LogUtil.i("InCallActivity.onNewIntent", "");
    246 
    247     // If the screen is off, we need to make sure it gets turned on for incoming calls.
    248     // This normally works just fine thanks to FLAG_TURN_SCREEN_ON but that only works
    249     // when the activity is first created. Therefore, to ensure the screen is turned on
    250     // for the call waiting case, we recreate() the current activity. There should be no jank from
    251     // this since the screen is already off and will remain so until our new activity is up.
    252     if (!isVisible()) {
    253       common.onNewIntent(intent, true /* isRecreating */);
    254       LogUtil.i("InCallActivity.onNewIntent", "Restarting InCallActivity to force screen on.");
    255       recreate();
    256     } else {
    257       common.onNewIntent(intent, false /* isRecreating */);
    258     }
    259   }
    260 
    261   @Override
    262   public void onBackPressed() {
    263     LogUtil.i("InCallActivity.onBackPressed", "");
    264     if (!common.onBackPressed(didShowInCallScreen || didShowVideoCallScreen)) {
    265       super.onBackPressed();
    266     }
    267   }
    268 
    269   @Override
    270   public boolean onOptionsItemSelected(MenuItem item) {
    271     LogUtil.i("InCallActivity.onOptionsItemSelected", "item: " + item);
    272     if (item.getItemId() == android.R.id.home) {
    273       onBackPressed();
    274       return true;
    275     }
    276     return super.onOptionsItemSelected(item);
    277   }
    278 
    279   @Override
    280   public boolean onKeyUp(int keyCode, KeyEvent event) {
    281     return common.onKeyUp(keyCode, event) || super.onKeyUp(keyCode, event);
    282   }
    283 
    284   @Override
    285   public boolean onKeyDown(int keyCode, KeyEvent event) {
    286     return common.onKeyDown(keyCode, event) || super.onKeyDown(keyCode, event);
    287   }
    288 
    289   public boolean isInCallScreenAnimating() {
    290     return false;
    291   }
    292 
    293   public void showConferenceFragment(boolean show) {
    294     if (show) {
    295       startActivity(new Intent(this, ManageConferenceActivity.class));
    296     }
    297   }
    298 
    299   public boolean showDialpadFragment(boolean show, boolean animate) {
    300     boolean didChange = common.showDialpadFragment(show, animate);
    301     if (didChange) {
    302       // Note:  onInCallScreenDialpadVisibilityChange is called here to ensure that the dialpad FAB
    303       // repositions itself.
    304       getInCallScreen().onInCallScreenDialpadVisibilityChange(show);
    305     }
    306     return didChange;
    307   }
    308 
    309   public boolean isDialpadVisible() {
    310     return common.isDialpadVisible();
    311   }
    312 
    313   public void onForegroundCallChanged(DialerCall newForegroundCall) {
    314     common.updateTaskDescription();
    315     if (didShowAnswerScreen && newForegroundCall != null) {
    316       if (newForegroundCall.getState() == State.DISCONNECTED
    317           || newForegroundCall.getState() == State.IDLE) {
    318         LogUtil.i(
    319             "InCallActivity.onForegroundCallChanged",
    320             "rejecting incoming call, not updating " + "window background color");
    321       }
    322     } else {
    323       LogUtil.v("InCallActivity.onForegroundCallChanged", "resetting background color");
    324       updateWindowBackgroundColor(0);
    325     }
    326   }
    327 
    328   public void updateWindowBackgroundColor(@FloatRange(from = -1f, to = 1.0f) float progress) {
    329     ThemeColorManager themeColorManager = InCallPresenter.getInstance().getThemeColorManager();
    330     @ColorInt int top;
    331     @ColorInt int middle;
    332     @ColorInt int bottom;
    333     @ColorInt int gray = 0x66000000;
    334 
    335     if (ActivityCompat.isInMultiWindowMode(this)) {
    336       top = themeColorManager.getBackgroundColorSolid();
    337       middle = themeColorManager.getBackgroundColorSolid();
    338       bottom = themeColorManager.getBackgroundColorSolid();
    339     } else {
    340       top = themeColorManager.getBackgroundColorTop();
    341       middle = themeColorManager.getBackgroundColorMiddle();
    342       bottom = themeColorManager.getBackgroundColorBottom();
    343     }
    344 
    345     if (progress < 0) {
    346       float correctedProgress = Math.abs(progress);
    347       top = ColorUtils.blendARGB(top, gray, correctedProgress);
    348       middle = ColorUtils.blendARGB(middle, gray, correctedProgress);
    349       bottom = ColorUtils.blendARGB(bottom, gray, correctedProgress);
    350     }
    351 
    352     boolean backgroundDirty = false;
    353     if (backgroundDrawable == null) {
    354       backgroundDrawableColors = new int[] {top, middle, bottom};
    355       backgroundDrawable = new GradientDrawable(Orientation.TOP_BOTTOM, backgroundDrawableColors);
    356       backgroundDirty = true;
    357     } else {
    358       if (backgroundDrawableColors[0] != top) {
    359         backgroundDrawableColors[0] = top;
    360         backgroundDirty = true;
    361       }
    362       if (backgroundDrawableColors[1] != middle) {
    363         backgroundDrawableColors[1] = middle;
    364         backgroundDirty = true;
    365       }
    366       if (backgroundDrawableColors[2] != bottom) {
    367         backgroundDrawableColors[2] = bottom;
    368         backgroundDirty = true;
    369       }
    370       if (backgroundDirty) {
    371         backgroundDrawable.setColors(backgroundDrawableColors);
    372       }
    373     }
    374 
    375     if (backgroundDirty) {
    376       getWindow().setBackgroundDrawable(backgroundDrawable);
    377     }
    378   }
    379 
    380   public boolean isVisible() {
    381     return isVisible;
    382   }
    383 
    384   public boolean getCallCardFragmentVisible() {
    385     return didShowInCallScreen || didShowVideoCallScreen;
    386   }
    387 
    388   public void dismissKeyguard(boolean dismiss) {
    389     common.dismissKeyguard(dismiss);
    390   }
    391 
    392   public void showPostCharWaitDialog(String callId, String chars) {
    393     common.showPostCharWaitDialog(callId, chars);
    394   }
    395 
    396   public void maybeShowErrorDialogOnDisconnect(DisconnectCause disconnectCause) {
    397     common.maybeShowErrorDialogOnDisconnect(disconnectCause);
    398   }
    399 
    400   public void dismissPendingDialogs() {
    401     if (isVisible) {
    402       LogUtil.i("InCallActivity.dismissPendingDialogs", "");
    403       common.dismissPendingDialogs();
    404       AnswerScreen answerScreen = getAnswerScreen();
    405       if (answerScreen != null) {
    406         answerScreen.dismissPendingDialogs();
    407       }
    408       needDismissPendingDialogs = false;
    409     } else {
    410       // The activity is not visible and onSaveInstanceState may have been called so defer the
    411       // dismissing action.
    412       LogUtil.i(
    413           "InCallActivity.dismissPendingDialogs", "defer actions since activity is not visible");
    414       needDismissPendingDialogs = true;
    415     }
    416   }
    417 
    418   private void enableInCallOrientationEventListener(boolean enable) {
    419     common.enableInCallOrientationEventListener(enable);
    420   }
    421 
    422   public void setExcludeFromRecents(boolean exclude) {
    423     common.setExcludeFromRecents(exclude);
    424   }
    425 
    426   @Nullable
    427   public FragmentManager getDialpadFragmentManager() {
    428     InCallScreen inCallScreen = getInCallScreen();
    429     if (inCallScreen != null) {
    430       return inCallScreen.getInCallScreenFragment().getChildFragmentManager();
    431     }
    432     return null;
    433   }
    434 
    435   public int getDialpadContainerId() {
    436     return getInCallScreen().getAnswerAndDialpadContainerResourceId();
    437   }
    438 
    439   @Override
    440   public AnswerScreenDelegate newAnswerScreenDelegate(AnswerScreen answerScreen) {
    441     DialerCall call = CallList.getInstance().getCallById(answerScreen.getCallId());
    442     if (call == null) {
    443       // This is a work around for a bug where we attempt to create a new delegate after the call
    444       // has already been removed. An example of when this can happen is:
    445       // 1. incoming video call in landscape mode
    446       // 2. remote party hangs up
    447       // 3. activity switches from landscape to portrait
    448       // At step #3 the answer fragment will try to create a new answer delegate but the call won't
    449       // exist. In this case we'll simply return a stub delegate that does nothing. This is ok
    450       // because this new state is transient and the activity will be destroyed soon.
    451       LogUtil.i("InCallActivity.onPrimaryCallStateChanged", "call doesn't exist, using stub");
    452       return new AnswerScreenPresenterStub();
    453     } else {
    454       return new AnswerScreenPresenter(
    455           this, answerScreen, CallList.getInstance().getCallById(answerScreen.getCallId()));
    456     }
    457   }
    458 
    459   @Override
    460   public InCallScreenDelegate newInCallScreenDelegate() {
    461     return new CallCardPresenter(this);
    462   }
    463 
    464   @Override
    465   public InCallButtonUiDelegate newInCallButtonUiDelegate() {
    466     return new CallButtonPresenter(this);
    467   }
    468 
    469   @Override
    470   public VideoCallScreenDelegate newVideoCallScreenDelegate(VideoCallScreen videoCallScreen) {
    471     DialerCall dialerCall = CallList.getInstance().getCallById(videoCallScreen.getCallId());
    472     if (dialerCall != null && dialerCall.getVideoTech().shouldUseSurfaceView()) {
    473       return dialerCall.getVideoTech().createVideoCallScreenDelegate(this, videoCallScreen);
    474     }
    475     return new VideoCallPresenter();
    476   }
    477 
    478   public void onPrimaryCallStateChanged() {
    479     LogUtil.i("InCallActivity.onPrimaryCallStateChanged", "");
    480     showMainInCallFragment();
    481   }
    482 
    483   public void onWiFiToLteHandover(DialerCall call) {
    484     common.showWifiToLteHandoverToast(call);
    485   }
    486 
    487   public void onHandoverToWifiFailed(DialerCall call) {
    488     common.showWifiFailedDialog(call);
    489   }
    490 
    491   public void onInternationalCallOnWifi(@NonNull DialerCall call) {
    492     LogUtil.enterBlock("InCallActivity.onInternationalCallOnWifi");
    493     common.showInternationalCallOnWifiDialog(call);
    494   }
    495 
    496   public void setAllowOrientationChange(boolean allowOrientationChange) {
    497     if (!allowOrientationChange) {
    498       setRequestedOrientation(InCallOrientationEventListener.ACTIVITY_PREFERENCE_DISALLOW_ROTATION);
    499     } else {
    500       setRequestedOrientation(InCallOrientationEventListener.ACTIVITY_PREFERENCE_ALLOW_ROTATION);
    501     }
    502     enableInCallOrientationEventListener(allowOrientationChange);
    503   }
    504 
    505   public void hideMainInCallFragment() {
    506     LogUtil.i("InCallActivity.hideMainInCallFragment", "");
    507     if (didShowInCallScreen || didShowVideoCallScreen) {
    508       FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
    509       hideInCallScreenFragment(transaction);
    510       hideVideoCallScreenFragment(transaction);
    511       transaction.commitAllowingStateLoss();
    512       getSupportFragmentManager().executePendingTransactions();
    513     }
    514   }
    515 
    516   private void showMainInCallFragment() {
    517     // If the activity's onStart method hasn't been called yet then defer doing any work.
    518     if (!isVisible) {
    519       LogUtil.i("InCallActivity.showMainInCallFragment", "not visible yet/anymore");
    520       return;
    521     }
    522 
    523     // Don't let this be reentrant.
    524     if (isInShowMainInCallFragment) {
    525       LogUtil.i("InCallActivity.showMainInCallFragment", "already in method, bailing");
    526       return;
    527     }
    528 
    529     isInShowMainInCallFragment = true;
    530     ShouldShowUiResult shouldShowAnswerUi = getShouldShowAnswerUi();
    531     ShouldShowUiResult shouldShowVideoUi = getShouldShowVideoUi();
    532     LogUtil.i(
    533         "InCallActivity.showMainInCallFragment",
    534         "shouldShowAnswerUi: %b, shouldShowVideoUi: %b, "
    535             + "didShowAnswerScreen: %b, didShowInCallScreen: %b, didShowVideoCallScreen: %b",
    536         shouldShowAnswerUi.shouldShow,
    537         shouldShowVideoUi.shouldShow,
    538         didShowAnswerScreen,
    539         didShowInCallScreen,
    540         didShowVideoCallScreen);
    541     // Only video call ui allows orientation change.
    542     setAllowOrientationChange(shouldShowVideoUi.shouldShow);
    543 
    544     FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
    545     boolean didChangeInCall;
    546     boolean didChangeVideo;
    547     boolean didChangeAnswer;
    548     if (shouldShowAnswerUi.shouldShow) {
    549       didChangeInCall = hideInCallScreenFragment(transaction);
    550       didChangeVideo = hideVideoCallScreenFragment(transaction);
    551       didChangeAnswer = showAnswerScreenFragment(transaction, shouldShowAnswerUi.call);
    552     } else if (shouldShowVideoUi.shouldShow) {
    553       didChangeInCall = hideInCallScreenFragment(transaction);
    554       didChangeVideo = showVideoCallScreenFragment(transaction, shouldShowVideoUi.call);
    555       didChangeAnswer = hideAnswerScreenFragment(transaction);
    556     } else {
    557       didChangeInCall = showInCallScreenFragment(transaction);
    558       didChangeVideo = hideVideoCallScreenFragment(transaction);
    559       didChangeAnswer = hideAnswerScreenFragment(transaction);
    560     }
    561 
    562     if (didChangeInCall || didChangeVideo || didChangeAnswer) {
    563       transaction.commitNow();
    564       Logger.get(this).logScreenView(ScreenEvent.Type.INCALL, this);
    565     }
    566     isInShowMainInCallFragment = false;
    567   }
    568 
    569   private ShouldShowUiResult getShouldShowAnswerUi() {
    570     DialerCall call = CallList.getInstance().getIncomingCall();
    571     if (call != null) {
    572       LogUtil.i("InCallActivity.getShouldShowAnswerUi", "found incoming call");
    573       return new ShouldShowUiResult(true, call);
    574     }
    575 
    576     call = CallList.getInstance().getVideoUpgradeRequestCall();
    577     if (call != null) {
    578       LogUtil.i("InCallActivity.getShouldShowAnswerUi", "found video upgrade request");
    579       return new ShouldShowUiResult(true, call);
    580     }
    581 
    582     // Check if we're showing the answer screen and the call is disconnected. If this condition is
    583     // true then we won't switch from the answer UI to the in call UI. This prevents flicker when
    584     // the user rejects an incoming call.
    585     call = CallList.getInstance().getFirstCall();
    586     if (call == null) {
    587       call = CallList.getInstance().getBackgroundCall();
    588     }
    589     if (didShowAnswerScreen && (call == null || call.getState() == State.DISCONNECTED)) {
    590       LogUtil.i("InCallActivity.getShouldShowAnswerUi", "found disconnecting incoming call");
    591       return new ShouldShowUiResult(true, call);
    592     }
    593 
    594     return new ShouldShowUiResult(false, null);
    595   }
    596 
    597   private static ShouldShowUiResult getShouldShowVideoUi() {
    598     DialerCall call = CallList.getInstance().getFirstCall();
    599     if (call == null) {
    600       LogUtil.i("InCallActivity.getShouldShowVideoUi", "null call");
    601       return new ShouldShowUiResult(false, null);
    602     }
    603 
    604     if (call.isVideoCall()) {
    605       LogUtil.i("InCallActivity.getShouldShowVideoUi", "found video call");
    606       return new ShouldShowUiResult(true, call);
    607     }
    608 
    609     if (call.hasSentVideoUpgradeRequest()) {
    610       LogUtil.i("InCallActivity.getShouldShowVideoUi", "upgrading to video");
    611       return new ShouldShowUiResult(true, call);
    612     }
    613 
    614     return new ShouldShowUiResult(false, null);
    615   }
    616 
    617   private boolean showAnswerScreenFragment(FragmentTransaction transaction, DialerCall call) {
    618     // When rejecting a call the active call can become null in which case we should continue
    619     // showing the answer screen.
    620     if (didShowAnswerScreen && call == null) {
    621       return false;
    622     }
    623 
    624     Assert.checkArgument(call != null, "didShowAnswerScreen was false but call was still null");
    625 
    626     boolean isVideoUpgradeRequest = call.hasReceivedVideoUpgradeRequest();
    627 
    628     // Check if we're already showing an answer screen for this call.
    629     if (didShowAnswerScreen) {
    630       AnswerScreen answerScreen = getAnswerScreen();
    631       if (answerScreen.getCallId().equals(call.getId())
    632           && answerScreen.isVideoCall() == call.isVideoCall()
    633           && answerScreen.isVideoUpgradeRequest() == isVideoUpgradeRequest) {
    634         return false;
    635       }
    636       LogUtil.i(
    637           "InCallActivity.showAnswerScreenFragment",
    638           "answer fragment exists but arguments do not match");
    639       hideAnswerScreenFragment(transaction);
    640     }
    641 
    642     // Show a new answer screen.
    643     AnswerScreen answerScreen =
    644         AnswerBindings.createAnswerScreen(
    645             call.getId(),
    646             call.isVideoCall(),
    647             isVideoUpgradeRequest,
    648             call.getVideoTech().isSelfManagedCamera(),
    649             shouldAllowAnswerAndRelease(call),
    650             CallList.getInstance().getBackgroundCall() != null);
    651     transaction.add(R.id.main, answerScreen.getAnswerScreenFragment(), TAG_ANSWER_SCREEN);
    652 
    653     Logger.get(this).logScreenView(ScreenEvent.Type.INCOMING_CALL, this);
    654     didShowAnswerScreen = true;
    655     return true;
    656   }
    657 
    658   private boolean shouldAllowAnswerAndRelease(DialerCall call) {
    659     if (CallList.getInstance().getActiveCall() == null) {
    660       LogUtil.i("InCallActivity.shouldAllowAnswerAndRelease", "no active call");
    661       return false;
    662     }
    663     if (getSystemService(TelephonyManager.class).getPhoneType()
    664         == TelephonyManager.PHONE_TYPE_CDMA) {
    665       LogUtil.i("InCallActivity.shouldAllowAnswerAndRelease", "PHONE_TYPE_CDMA not supported");
    666       return false;
    667     }
    668     if (call.isVideoCall() || call.hasReceivedVideoUpgradeRequest()) {
    669       LogUtil.i("InCallActivity.shouldAllowAnswerAndRelease", "video call");
    670       return false;
    671     }
    672     if (!ConfigProviderBindings.get(this).getBoolean(CONFIG_ANSWER_AND_RELEASE_ENABLED, true)) {
    673       LogUtil.i("InCallActivity.shouldAllowAnswerAndRelease", "disabled by config");
    674       return false;
    675     }
    676 
    677     return true;
    678   }
    679 
    680   private boolean hideAnswerScreenFragment(FragmentTransaction transaction) {
    681     if (!didShowAnswerScreen) {
    682       return false;
    683     }
    684     AnswerScreen answerScreen = getAnswerScreen();
    685     if (answerScreen != null) {
    686       transaction.remove(answerScreen.getAnswerScreenFragment());
    687     }
    688 
    689     didShowAnswerScreen = false;
    690     return true;
    691   }
    692 
    693   private boolean showInCallScreenFragment(FragmentTransaction transaction) {
    694     if (didShowInCallScreen) {
    695       return false;
    696     }
    697     InCallScreen inCallScreen = getInCallScreen();
    698     if (inCallScreen == null) {
    699       inCallScreen = InCallBindings.createInCallScreen();
    700       transaction.add(R.id.main, inCallScreen.getInCallScreenFragment(), TAG_IN_CALL_SCREEN);
    701     } else {
    702       transaction.show(inCallScreen.getInCallScreenFragment());
    703     }
    704     Logger.get(this).logScreenView(ScreenEvent.Type.INCALL, this);
    705     didShowInCallScreen = true;
    706     return true;
    707   }
    708 
    709   private boolean hideInCallScreenFragment(FragmentTransaction transaction) {
    710     if (!didShowInCallScreen) {
    711       return false;
    712     }
    713     InCallScreen inCallScreen = getInCallScreen();
    714     if (inCallScreen != null) {
    715       transaction.hide(inCallScreen.getInCallScreenFragment());
    716     }
    717     didShowInCallScreen = false;
    718     return true;
    719   }
    720 
    721   private boolean showVideoCallScreenFragment(FragmentTransaction transaction, DialerCall call) {
    722     if (didShowVideoCallScreen) {
    723       VideoCallScreen videoCallScreen = getVideoCallScreen();
    724       if (videoCallScreen.getCallId().equals(call.getId())) {
    725         return false;
    726       }
    727       LogUtil.i(
    728           "InCallActivity.showVideoCallScreenFragment",
    729           "video call fragment exists but arguments do not match");
    730       hideVideoCallScreenFragment(transaction);
    731     }
    732 
    733     LogUtil.i("InCallActivity.showVideoCallScreenFragment", "call: %s", call);
    734 
    735     VideoCallScreen videoCallScreen =
    736         VideoBindings.createVideoCallScreen(
    737             call.getId(), call.getVideoTech().shouldUseSurfaceView());
    738     transaction.add(R.id.main, videoCallScreen.getVideoCallScreenFragment(), TAG_VIDEO_CALL_SCREEN);
    739 
    740     Logger.get(this).logScreenView(ScreenEvent.Type.INCALL, this);
    741     didShowVideoCallScreen = true;
    742     return true;
    743   }
    744 
    745   private boolean hideVideoCallScreenFragment(FragmentTransaction transaction) {
    746     if (!didShowVideoCallScreen) {
    747       return false;
    748     }
    749     VideoCallScreen videoCallScreen = getVideoCallScreen();
    750     if (videoCallScreen != null) {
    751       transaction.remove(videoCallScreen.getVideoCallScreenFragment());
    752     }
    753     didShowVideoCallScreen = false;
    754     return true;
    755   }
    756 
    757   AnswerScreen getAnswerScreen() {
    758     return (AnswerScreen) getSupportFragmentManager().findFragmentByTag(TAG_ANSWER_SCREEN);
    759   }
    760 
    761   InCallScreen getInCallScreen() {
    762     return (InCallScreen) getSupportFragmentManager().findFragmentByTag(TAG_IN_CALL_SCREEN);
    763   }
    764 
    765   VideoCallScreen getVideoCallScreen() {
    766     return (VideoCallScreen) getSupportFragmentManager().findFragmentByTag(TAG_VIDEO_CALL_SCREEN);
    767   }
    768 
    769   @Override
    770   public void onPseudoScreenStateChanged(boolean isOn) {
    771     LogUtil.i("InCallActivity.onPseudoScreenStateChanged", "isOn: " + isOn);
    772     pseudoBlackScreenOverlay.setVisibility(isOn ? View.GONE : View.VISIBLE);
    773   }
    774 
    775   /**
    776    * For some touch related issue, turning off the screen can be faked by drawing a black view over
    777    * the activity. All touch events started when the screen is "off" is rejected.
    778    *
    779    * @see PseudoScreenState
    780    */
    781   @Override
    782   public boolean dispatchTouchEvent(MotionEvent event) {
    783     // Reject any gesture that started when the screen is in the fake off state.
    784     if (touchDownWhenPseudoScreenOff) {
    785       if (event.getAction() == MotionEvent.ACTION_UP) {
    786         touchDownWhenPseudoScreenOff = false;
    787       }
    788       return true;
    789     }
    790     // Reject all touch event when the screen is in the fake off state.
    791     if (!InCallPresenter.getInstance().getPseudoScreenState().isOn()) {
    792       if (event.getAction() == MotionEvent.ACTION_DOWN) {
    793         touchDownWhenPseudoScreenOff = true;
    794         LogUtil.i("InCallActivity.dispatchTouchEvent", "touchDownWhenPseudoScreenOff");
    795       }
    796       return true;
    797     }
    798     return super.dispatchTouchEvent(event);
    799   }
    800 
    801   private static class ShouldShowUiResult {
    802     public final boolean shouldShow;
    803     public final DialerCall call;
    804 
    805     ShouldShowUiResult(boolean shouldShow, DialerCall call) {
    806       this.shouldShow = shouldShow;
    807       this.call = call;
    808     }
    809   }
    810 }
    811