Add more boilerplate to the incallui.

Bug: 73774242
Test: unit tests where they exist
PiperOrigin-RevId: 189853302
Change-Id: Ibf3431cc0d7716c2c53c7ea165dfc130102a1bc7
diff --git a/java/com/android/incallui/AnswerScreenPresenter.java b/java/com/android/incallui/AnswerScreenPresenter.java
index b9a84ae..a0c76f3 100644
--- a/java/com/android/incallui/AnswerScreenPresenter.java
+++ b/java/com/android/incallui/AnswerScreenPresenter.java
@@ -132,6 +132,18 @@
   }
 
   @Override
+  public void onSpeakEasyCall() {
+    LogUtil.enterBlock("AnswerScreenPresenter.onSpeakEasyCall");
+    DialerCall incomingCall = CallList.getInstance().getIncomingCall();
+    if (incomingCall == null) {
+      LogUtil.i("AnswerScreenPresenter.onSpeakEasyCall", "incomingCall == null");
+      return;
+    }
+    incomingCall.setIsSpeakEasyCall(true);
+    incomingCall.answer();
+  }
+
+  @Override
   public void onAnswerAndReleaseCall() {
     LogUtil.enterBlock("AnswerScreenPresenter.onAnswerAndReleaseCall");
     DialerCall activeCall = CallList.getInstance().getActiveCall();
diff --git a/java/com/android/incallui/AnswerScreenPresenterStub.java b/java/com/android/incallui/AnswerScreenPresenterStub.java
index 99f1f2c..e85fdaa 100644
--- a/java/com/android/incallui/AnswerScreenPresenterStub.java
+++ b/java/com/android/incallui/AnswerScreenPresenterStub.java
@@ -38,6 +38,9 @@
   public void onReject() {}
 
   @Override
+  public void onSpeakEasyCall() {}
+
+  @Override
   public void onAnswerAndReleaseCall() {}
 
   @Override
diff --git a/java/com/android/incallui/InCallActivity.java b/java/com/android/incallui/InCallActivity.java
index 6f2ad8b..be36509 100644
--- a/java/com/android/incallui/InCallActivity.java
+++ b/java/com/android/incallui/InCallActivity.java
@@ -35,6 +35,7 @@
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
 import android.support.annotation.VisibleForTesting;
+import android.support.v4.app.Fragment;
 import android.support.v4.app.FragmentManager;
 import android.support.v4.app.FragmentTransaction;
 import android.support.v4.content.res.ResourcesCompat;
@@ -94,6 +95,7 @@
 import com.android.incallui.rtt.protocol.RttCallScreen;
 import com.android.incallui.rtt.protocol.RttCallScreenDelegate;
 import com.android.incallui.rtt.protocol.RttCallScreenDelegateFactory;
+import com.android.incallui.speakeasy.SpeakEasyCallManager;
 import com.android.incallui.telecomeventui.InternationalCallOnWifiDialogFragment;
 import com.android.incallui.video.bindings.VideoBindings;
 import com.android.incallui.video.protocol.VideoCallScreen;
@@ -149,6 +151,7 @@
   private boolean didShowInCallScreen;
   private boolean didShowVideoCallScreen;
   private boolean didShowRttCallScreen;
+  private boolean didShowSpeakEasyScreen;
   private boolean dismissKeyguard;
   private boolean isInShowMainInCallFragment;
   private boolean isRecreating; // whether the activity is going to be recreated
@@ -158,6 +161,7 @@
   private boolean touchDownWhenPseudoScreenOff;
   private int[] backgroundDrawableColors;
   @DialpadRequestType private int showDialpadRequest = DIALPAD_REQUEST_NONE;
+  private SpeakEasyCallManager speakEasyCallManager;
 
   public static Intent getIntent(
       Context context, boolean showDialpad, boolean newOutgoingCall, boolean isForFullScreen) {
@@ -437,6 +441,7 @@
     out.putBoolean(KeysForSavedInstance.DID_SHOW_IN_CALL_SCREEN, didShowInCallScreen);
     out.putBoolean(KeysForSavedInstance.DID_SHOW_VIDEO_CALL_SCREEN, didShowVideoCallScreen);
     out.putBoolean(KeysForSavedInstance.DID_SHOW_RTT_CALL_SCREEN, didShowRttCallScreen);
+    out.putBoolean(KeysForSavedInstance.DID_SHOW_SPEAK_EASY_SCREEN, didShowSpeakEasyScreen);
 
     super.onSaveInstanceState(out);
     isVisible = false;
@@ -971,7 +976,10 @@
   }
 
   public boolean getCallCardFragmentVisible() {
-    return didShowInCallScreen || didShowVideoCallScreen || didShowRttCallScreen;
+    return didShowInCallScreen
+        || didShowVideoCallScreen
+        || didShowRttCallScreen
+        || didShowSpeakEasyScreen;
   }
 
   public void dismissKeyguard(boolean dismiss) {
@@ -1282,18 +1290,21 @@
     ShouldShowUiResult shouldShowAnswerUi = getShouldShowAnswerUi();
     ShouldShowUiResult shouldShowVideoUi = getShouldShowVideoUi();
     ShouldShowUiResult shouldShowRttUi = getShouldShowRttUi();
+    ShouldShowUiResult shouldShowSpeakEasyUi = getShouldShowSpeakEasyUi();
     LogUtil.i(
         "InCallActivity.showMainInCallFragment",
         "shouldShowAnswerUi: %b, shouldShowRttUi: %b, shouldShowVideoUi: %b "
             + "didShowAnswerScreen: %b, didShowInCallScreen: %b, didShowRttCallScreen: %b, "
-            + "didShowVideoCallScreen: %b",
+            + "didShowVideoCallScreen: %b"
+            + "didShowSpeakEasyScreen: %b",
         shouldShowAnswerUi.shouldShow,
         shouldShowRttUi.shouldShow,
         shouldShowVideoUi.shouldShow,
         didShowAnswerScreen,
         didShowInCallScreen,
         didShowRttCallScreen,
-        didShowVideoCallScreen);
+        didShowVideoCallScreen,
+        didShowSpeakEasyScreen);
     // Only video call ui allows orientation change.
     setAllowOrientationChange(shouldShowVideoUi.shouldShow);
 
@@ -1303,21 +1314,31 @@
       didChange = hideInCallScreenFragment(transaction);
       didChange |= hideVideoCallScreenFragment(transaction);
       didChange |= hideRttCallScreenFragment(transaction);
+      didChange |= hideSpeakEasyFragment(transaction);
       didChange |= showAnswerScreenFragment(transaction, shouldShowAnswerUi.call);
     } else if (shouldShowVideoUi.shouldShow) {
       didChange = hideInCallScreenFragment(transaction);
       didChange |= showVideoCallScreenFragment(transaction, shouldShowVideoUi.call);
       didChange |= hideRttCallScreenFragment(transaction);
+      didChange |= hideSpeakEasyFragment(transaction);
       didChange |= hideAnswerScreenFragment(transaction);
     } else if (shouldShowRttUi.shouldShow) {
       didChange = hideInCallScreenFragment(transaction);
       didChange |= hideVideoCallScreenFragment(transaction);
       didChange |= hideAnswerScreenFragment(transaction);
+      didChange |= hideSpeakEasyFragment(transaction);
       didChange |= showRttCallScreenFragment(transaction, shouldShowRttUi.call);
+    } else if (shouldShowSpeakEasyUi.shouldShow) {
+      didChange = hideInCallScreenFragment(transaction);
+      didChange |= hideVideoCallScreenFragment(transaction);
+      didChange |= hideAnswerScreenFragment(transaction);
+      didChange |= hideRttCallScreenFragment(transaction);
+      didChange |= showSpeakEasyFragment(transaction, shouldShowSpeakEasyUi.call);
     } else {
       didChange = showInCallScreenFragment(transaction);
       didChange |= hideVideoCallScreenFragment(transaction);
       didChange |= hideRttCallScreenFragment(transaction);
+      didChange |= hideSpeakEasyFragment(transaction);
       didChange |= hideAnswerScreenFragment(transaction);
     }
 
@@ -1331,6 +1352,75 @@
     Trace.endSection();
   }
 
+  private boolean showSpeakEasyFragment(FragmentTransaction transaction, DialerCall call) {
+
+    // TODO(erfanian): Support multiple speakeasy screens.
+    if (didShowSpeakEasyScreen) {
+      return false;
+    }
+
+    Optional<Fragment> speakEasyFragment = speakEasyCallManager.getSpeakEasyFragment(call);
+    if (speakEasyFragment.isPresent()) {
+      transaction.add(R.id.main, speakEasyFragment.get(), Tags.SPEAK_EASY_SCREEN);
+      didShowSpeakEasyScreen = true;
+      return true;
+    }
+    return false;
+  }
+
+  private Fragment getSpeakEasyScreen() {
+    return getSupportFragmentManager().findFragmentByTag(Tags.SPEAK_EASY_SCREEN);
+  }
+
+  private boolean hideSpeakEasyFragment(FragmentTransaction transaction) {
+    if (!didShowSpeakEasyScreen) {
+      return false;
+    }
+
+    Fragment speakEasyFragment = getSpeakEasyScreen();
+
+    if (speakEasyFragment != null) {
+      transaction.remove(speakEasyFragment);
+      didShowSpeakEasyScreen = false;
+      return true;
+    }
+    return false;
+  }
+
+  public void setSpeakEasyCallManager(SpeakEasyCallManager speakEasyCallManager) {
+    this.speakEasyCallManager = Assert.isNotNull(speakEasyCallManager);
+  }
+
+  public SpeakEasyCallManager getSpeakEasyCallManager() {
+    return speakEasyCallManager;
+  }
+
+  private ShouldShowUiResult getShouldShowSpeakEasyUi() {
+    SpeakEasyCallManager speakEasyCallManager = getSpeakEasyCallManager();
+
+    if (speakEasyCallManager == null) {
+      return new ShouldShowUiResult(false, null);
+    }
+
+    // TODO(erfanian): Get a better call?
+    DialerCall call = CallList.getInstance().getActiveCall();
+
+    if (call == null) {
+      return new ShouldShowUiResult(false, call);
+    }
+
+    if (!call.isSpeakEasyCall() || !call.isSpeakEasyEligible()) {
+      return new ShouldShowUiResult(false, call);
+    }
+
+    Optional<Fragment> speakEasyFragment = speakEasyCallManager.getSpeakEasyFragment(call);
+
+    if (!speakEasyFragment.isPresent()) {
+      return new ShouldShowUiResult(false, call);
+    }
+    return new ShouldShowUiResult(true, call);
+  }
+
   private ShouldShowUiResult getShouldShowAnswerUi() {
     DialerCall call = CallList.getInstance().getIncomingCall();
     if (call != null) {
@@ -1443,7 +1533,8 @@
             isVideoUpgradeRequest,
             call.getVideoTech().isSelfManagedCamera(),
             shouldAllowAnswerAndRelease(call),
-            CallList.getInstance().getBackgroundCall() != null);
+            CallList.getInstance().getBackgroundCall() != null,
+            call.isSpeakEasyEligible());
     transaction.add(R.id.main, answerScreen.getAnswerScreenFragment(), Tags.ANSWER_SCREEN);
 
     Logger.get(this).logScreenView(ScreenEvent.Type.INCOMING_CALL, this);
@@ -1660,6 +1751,7 @@
     static final String DID_SHOW_IN_CALL_SCREEN = "did_show_in_call_screen";
     static final String DID_SHOW_VIDEO_CALL_SCREEN = "did_show_video_call_screen";
     static final String DID_SHOW_RTT_CALL_SCREEN = "did_show_rtt_call_screen";
+    static final String DID_SHOW_SPEAK_EASY_SCREEN = "did_show_speak_easy_screen";
   }
 
   /** Request codes for pending intents. */
@@ -1678,6 +1770,7 @@
     static final String VIDEO_CALL_SCREEN = "tag_video_call_screen";
     static final String RTT_CALL_SCREEN = "tag_rtt_call_screen";
     static final String POST_CHAR_DIALOG_FRAGMENT = "tag_post_char_dialog_fragment";
+    static final String SPEAK_EASY_SCREEN = "tag_speak_easy_screen";
   }
 
   private static final class ConfigNames {
diff --git a/java/com/android/incallui/InCallPresenter.java b/java/com/android/incallui/InCallPresenter.java
index 558ca33..a4b90f1 100644
--- a/java/com/android/incallui/InCallPresenter.java
+++ b/java/com/android/incallui/InCallPresenter.java
@@ -67,6 +67,7 @@
 import com.android.incallui.latencyreport.LatencyReport;
 import com.android.incallui.legacyblocking.BlockedNumberContentObserver;
 import com.android.incallui.spam.SpamCallListListener;
+import com.android.incallui.speakeasy.SpeakEasyCallManager;
 import com.android.incallui.videosurface.bindings.VideoSurfaceBindings;
 import com.android.incallui.videosurface.protocol.VideoSurfaceTexture;
 import com.android.incallui.videotech.utils.VideoUtils;
@@ -262,6 +263,8 @@
 
   private MotorolaInCallUiNotifier motorolaInCallUiNotifier;
 
+  private SpeakEasyCallManager speakEasyCallManager;
+
   /** Inaccessible constructor. Must use getRunningInstance() to get this singleton. */
   @VisibleForTesting
   InCallPresenter() {}
@@ -326,7 +329,8 @@
       ExternalCallNotifier externalCallNotifier,
       ContactInfoCache contactInfoCache,
       ProximitySensor proximitySensor,
-      FilteredNumberAsyncQueryHandler filteredNumberQueryHandler) {
+      FilteredNumberAsyncQueryHandler filteredNumberQueryHandler,
+      @NonNull SpeakEasyCallManager speakEasyCallManager) {
     Trace.beginSection("InCallPresenter.setUp");
     if (serviceConnected) {
       LogUtil.i("InCallPresenter.setUp", "New service connection replacing existing one.");
@@ -378,6 +382,7 @@
     VideoPauseController.getInstance().setUp(this);
 
     filteredQueryHandler = filteredNumberQueryHandler;
+    this.speakEasyCallManager = speakEasyCallManager;
     this.context
         .getSystemService(TelephonyManager.class)
         .listen(phoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
@@ -474,6 +479,7 @@
 
       this.inCallActivity = inCallActivity;
       this.inCallActivity.setExcludeFromRecents(false);
+      this.inCallActivity.setSpeakEasyCallManager(this.speakEasyCallManager);
 
       // By the time the UI finally comes up, the call may already be disconnected.
       // If that's the case, we may need to show an error dialog.
diff --git a/java/com/android/incallui/InCallServiceImpl.java b/java/com/android/incallui/InCallServiceImpl.java
index 2b147e7..b9d0ecc 100644
--- a/java/com/android/incallui/InCallServiceImpl.java
+++ b/java/com/android/incallui/InCallServiceImpl.java
@@ -107,7 +107,8 @@
             contactInfoCache,
             new ProximitySensor(
                 context, AudioModeProvider.getInstance(), new AccelerometerListener(context)),
-            new FilteredNumberAsyncQueryHandler(context));
+            new FilteredNumberAsyncQueryHandler(context),
+            speakEasyCallManager);
     InCallPresenter.getInstance().onServiceBind();
     InCallPresenter.getInstance().maybeStartRevealAnimation(intent);
     TelecomAdapter.getInstance().setInCallService(this);
diff --git a/java/com/android/incallui/answer/bindings/AnswerBindings.java b/java/com/android/incallui/answer/bindings/AnswerBindings.java
index 9f4199b..c370d74 100644
--- a/java/com/android/incallui/answer/bindings/AnswerBindings.java
+++ b/java/com/android/incallui/answer/bindings/AnswerBindings.java
@@ -29,7 +29,8 @@
       boolean isVideoUpgradeRequest,
       boolean isSelfManagedCamera,
       boolean allowAnswerAndRelease,
-      boolean hasCallOnHold) {
+      boolean hasCallOnHold,
+      boolean allowSpeakEasy) {
     return AnswerFragment.newInstance(
         callId,
         isRttCall,
@@ -37,6 +38,7 @@
         isVideoUpgradeRequest,
         isSelfManagedCamera,
         allowAnswerAndRelease,
-        hasCallOnHold);
+        hasCallOnHold,
+        allowSpeakEasy);
   }
 }
diff --git a/java/com/android/incallui/answer/impl/AnswerFragment.java b/java/com/android/incallui/answer/impl/AnswerFragment.java
index 4729b41..05fd4f6 100644
--- a/java/com/android/incallui/answer/impl/AnswerFragment.java
+++ b/java/com/android/incallui/answer/impl/AnswerFragment.java
@@ -118,6 +118,8 @@
   @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
   static final String ARG_IS_SELF_MANAGED_CAMERA = "is_self_managed_camera";
 
+  static final String ARG_ALLOW_SPEAK_EASY = "allow_speak_easy";
+
   private static final String STATE_HAS_ANIMATED_ENTRY = "hasAnimated";
 
   private static final int HINT_SECONDARY_SHOW_DURATION_MILLIS = 5000;
@@ -192,6 +194,17 @@
       public void performAction(AnswerFragment fragment) {
         fragment.performAnswerAndRelease();
       }
+    },
+
+    SPEAKEASY(
+        R.drawable.quantum_ic_rtt_vd_theme_24,
+        R.string.speakeasy_secondary_button_hint,
+        R.string.speakeasy_secondary_button_hint,
+        R.string.speakeasy_secondary_button_hint) {
+      @Override
+      public void performAction(AnswerFragment fragment) {
+        fragment.performSpeakEasy();
+      }
     };
 
     @DrawableRes public final int icon;
@@ -218,6 +231,12 @@
     }
   }
 
+  private void performSpeakEasy() {
+    restoreAnswerAndReleaseButtonAnimation();
+    answerScreenDelegate.onSpeakEasyCall();
+    buttonAcceptClicked = true;
+  }
+
   private void performAnswerAndRelease() {
     restoreAnswerAndReleaseButtonAnimation();
     answerScreenDelegate.onAnswerAndReleaseCall();
@@ -351,7 +370,8 @@
       boolean isVideoUpgradeRequest,
       boolean isSelfManagedCamera,
       boolean allowAnswerAndRelease,
-      boolean hasCallOnHold) {
+      boolean hasCallOnHold,
+      boolean allowSpeakEasy) {
     Bundle bundle = new Bundle();
     bundle.putString(ARG_CALL_ID, Assert.isNotNull(callId));
     bundle.putBoolean(ARG_IS_RTT_CALL, isRttCall);
@@ -360,6 +380,7 @@
     bundle.putBoolean(ARG_IS_SELF_MANAGED_CAMERA, isSelfManagedCamera);
     bundle.putBoolean(ARG_ALLOW_ANSWER_AND_RELEASE, allowAnswerAndRelease);
     bundle.putBoolean(ARG_HAS_CALL_ON_HOLD, hasCallOnHold);
+    bundle.putBoolean(ARG_ALLOW_SPEAK_EASY, allowSpeakEasy);
 
     AnswerFragment instance = new AnswerFragment();
     instance.setArguments(bundle);
@@ -427,13 +448,7 @@
 
     answerAndReleaseBehavior = SecondaryBehavior.ANSWER_AND_RELEASE;
     answerAndReleaseBehavior.applyToView(answerAndReleaseButton);
-    answerAndReleaseButton.setOnClickListener(
-        new OnClickListener() {
-          @Override
-          public void onClick(View v) {
-            performAnswerAndReleaseButtonAction();
-          }
-        });
+
     answerAndReleaseButton.setClickable(AccessibilityUtil.isAccessibilityEnabled(getContext()));
     answerAndReleaseButton.setFocusable(AccessibilityUtil.isAccessibilityEnabled(getContext()));
     answerAndReleaseButton.setAccessibilityDelegate(accessibilityDelegate);
@@ -441,10 +456,22 @@
     if (allowAnswerAndRelease()) {
       answerAndReleaseButton.setVisibility(View.VISIBLE);
       answerScreenDelegate.onAnswerAndReleaseButtonEnabled();
+    } else if (allowSpeakEasy()) {
+      answerAndReleaseBehavior = SecondaryBehavior.SPEAKEASY;
+      answerAndReleaseBehavior.applyToView(answerAndReleaseButton);
+      answerAndReleaseButton.setVisibility(View.VISIBLE);
+      answerScreenDelegate.onAnswerAndReleaseButtonEnabled();
     } else {
       answerAndReleaseButton.setVisibility(View.INVISIBLE);
       answerScreenDelegate.onAnswerAndReleaseButtonDisabled();
     }
+    answerAndReleaseButton.setOnClickListener(
+        new OnClickListener() {
+          @Override
+          public void onClick(View v) {
+            performAnswerAndReleaseButtonAction();
+          }
+        });
   }
 
   @Override
@@ -452,6 +479,11 @@
     return getArguments().getBoolean(ARG_ALLOW_ANSWER_AND_RELEASE);
   }
 
+  @Override
+  public boolean allowSpeakEasy() {
+    return getArguments().getBoolean(ARG_ALLOW_SPEAK_EASY);
+  }
+
   private boolean hasCallOnHold() {
     return getArguments().getBoolean(ARG_HAS_CALL_ON_HOLD);
   }
diff --git a/java/com/android/incallui/answer/impl/res/values/strings.xml b/java/com/android/incallui/answer/impl/res/values/strings.xml
index 2bc9ca0..61400bd 100644
--- a/java/com/android/incallui/answer/impl/res/values/strings.xml
+++ b/java/com/android/incallui/answer/impl/res/values/strings.xml
@@ -40,4 +40,6 @@
   <string description="The message announced to accessibility assistance on incoming call."
     name="a11y_incoming_call_swipe_gesture_prompt">Two finger swipe up to answer. Two finger swipe down to decline.</string>
   <string name="call_incoming_important">Important</string>
+
+  <string name="speakeasy_secondary_button_hint" translatable="false">Answer as RTT</string>
 </resources>
diff --git a/java/com/android/incallui/answer/protocol/AnswerScreen.java b/java/com/android/incallui/answer/protocol/AnswerScreen.java
index f030ce9..38e4dc5 100644
--- a/java/com/android/incallui/answer/protocol/AnswerScreen.java
+++ b/java/com/android/incallui/answer/protocol/AnswerScreen.java
@@ -32,6 +32,8 @@
 
   boolean allowAnswerAndRelease();
 
+  boolean allowSpeakEasy();
+
   boolean isActionTimeout();
 
   void setTextResponses(List<String> textResponses);
diff --git a/java/com/android/incallui/answer/protocol/AnswerScreenDelegate.java b/java/com/android/incallui/answer/protocol/AnswerScreenDelegate.java
index 5710922..172d964 100644
--- a/java/com/android/incallui/answer/protocol/AnswerScreenDelegate.java
+++ b/java/com/android/incallui/answer/protocol/AnswerScreenDelegate.java
@@ -30,6 +30,8 @@
 
   void onReject();
 
+  void onSpeakEasyCall();
+
   void onAnswerAndReleaseCall();
 
   void onAnswerAndReleaseButtonEnabled();
diff --git a/java/com/android/incallui/call/DialerCall.java b/java/com/android/incallui/call/DialerCall.java
index 5d2b147..2ce4550 100644
--- a/java/com/android/incallui/call/DialerCall.java
+++ b/java/com/android/incallui/call/DialerCall.java
@@ -136,6 +136,7 @@
       new CopyOnWriteArrayList<>();
   private final VideoTechManager videoTechManager;
 
+  private boolean isSpeakEasyCall;
   private boolean isEmergencyCall;
   private Uri handle;
   private int state = State.INVALID;
@@ -1544,6 +1545,25 @@
     this.preferredAccountRecorder = preferredAccountRecorder;
   }
 
+  /** Indicates the call is eligible for SpeakEasy */
+  public boolean isSpeakEasyEligible() {
+    // TODO(erfanian): refactor key location
+    return ConfigProviderBindings.get(context).getBoolean("speak_easy_enabled", false);
+  }
+
+  /** Indicates the user has selected SpeakEasy */
+  public boolean isSpeakEasyCall() {
+    if (!isSpeakEasyEligible()) {
+      return false;
+    }
+    return isSpeakEasyCall;
+  }
+
+  /** Sets the user preference for SpeakEasy */
+  public void setIsSpeakEasyCall(boolean isSpeakEasyCall) {
+    this.isSpeakEasyCall = isSpeakEasyCall;
+  }
+
   /**
    * Specifies whether a number is in the call history or not. {@link #CALL_HISTORY_STATUS_UNKNOWN}
    * means there is no result.