Implement headless PreCall
If no actions require an activity, instead of launching the PreCallActivity, all actions will be performed immediately, and the call intent will be sent to telecom directly.
This reduces the latency & risk when mutli SIM is not supported.
Bug: 68953196
Test: PreCallImplTest, HeadlessPreCallCoordinatorTest
PiperOrigin-RevId: 176032790
Change-Id: I03258e8643f18c65a8e5b1a84b18b71b30638044
diff --git a/java/com/android/dialer/precall/PreCallAction.java b/java/com/android/dialer/precall/PreCallAction.java
index 9434694..9ddc6f2 100644
--- a/java/com/android/dialer/precall/PreCallAction.java
+++ b/java/com/android/dialer/precall/PreCallAction.java
@@ -16,6 +16,7 @@
package com.android.dialer.precall;
+import android.content.Context;
import android.support.annotation.MainThread;
import com.android.dialer.callintent.CallIntentBuilder;
@@ -28,12 +29,29 @@
public interface PreCallAction {
/**
+ * Whether the action requires an activity to operate. This method is called on all actions before
+ * {@link #runWithUi(PreCallCoordinator)} is called. If {@link true} is returned, {@link
+ * #runWithUi(PreCallCoordinator)} will be guaranteed to be called on the execution phase.
+ * Otherwise {@link #runWithoutUi(Context, CallIntentBuilder)} may be called instead and the
+ * action will not be able to show UI, perform async task, or abort the call. This method should
+ * not make any state changes.
+ */
+ @MainThread
+ boolean requiresUi(Context context, CallIntentBuilder builder);
+
+ /**
+ * Called when all actions returned {@code false} for {@link #requiresUi(Context,
+ * CallIntentBuilder)}.
+ */
+ void runWithoutUi(Context context, CallIntentBuilder builder);
+
+ /**
* Runs the action. Should block on the main thread until the action is finished. If the action is
* not instantaneous, {@link PreCallCoordinator#startPendingAction()} should be called to release
* the thread and continue later.
*/
@MainThread
- void run(PreCallCoordinator coordinator);
+ void runWithUi(PreCallCoordinator coordinator);
/**
* Called when the UI is being paused when a {@link PreCallCoordinator.PendingAction} is started,
diff --git a/java/com/android/dialer/precall/PreCallCoordinator.java b/java/com/android/dialer/precall/PreCallCoordinator.java
index 40b909a..cb3221a 100644
--- a/java/com/android/dialer/precall/PreCallCoordinator.java
+++ b/java/com/android/dialer/precall/PreCallCoordinator.java
@@ -33,10 +33,7 @@
@NonNull
CallIntentBuilder getBuilder();
- /**
- * @return the activity to attach the UI to. Returns {@link null} if the coordinator is running on
- * headless mode. TODO(twyen): implement headless mode.
- */
+ /** @return the activity to attach the UI to. */
@NonNull
Activity getActivity();
@@ -60,10 +57,10 @@
* Called by the current running {@link PreCallAction} to release the main thread and resume
* pre-call later.
*
- * @return a {@link PendingAction} which {@link PendingAction#finish(boolean)} should be called to
- * resume pre-call. For example the action shows a dialog to the user, startPendingAction()
- * should be called as the action will not be finished immediately. When the dialog is
- * completed, {@code finish()} is then called to continue the next step.
+ * @return a {@link PendingAction} which {@link PendingAction#finish()} should be called to resume
+ * pre-call. For example the action shows a dialog to the user, startPendingAction() should be
+ * called as the action will not be finished immediately. When the dialog is completed, {@code
+ * finish()} is then called to continue the next step.
*/
@MainThread
@NonNull
diff --git a/java/com/android/dialer/precall/impl/AssistedDialAction.java b/java/com/android/dialer/precall/impl/AssistedDialAction.java
index edf97cc..c4f61d2 100644
--- a/java/com/android/dialer/precall/impl/AssistedDialAction.java
+++ b/java/com/android/dialer/precall/impl/AssistedDialAction.java
@@ -17,6 +17,7 @@
package com.android.dialer.precall.impl;
import android.annotation.TargetApi;
+import android.content.Context;
import android.os.Build;
import android.os.Bundle;
import android.telecom.PhoneAccount;
@@ -35,18 +36,21 @@
/** Rewrites the call URI with country code. TODO(erfanian): use phone account for multi SIM */
public class AssistedDialAction implements PreCallAction {
+ @Override
+ public boolean requiresUi(Context context, CallIntentBuilder builder) {
+ return false;
+ }
+
@SuppressWarnings("AndroidApiChecker") // Use of optional
@TargetApi(Build.VERSION_CODES.N)
@Override
- public void run(PreCallCoordinator coordinator) {
- CallIntentBuilder builder = coordinator.getBuilder();
+ public void runWithoutUi(Context context, CallIntentBuilder builder) {
if (!builder.isAssistedDialAllowed()) {
return;
}
AssistedDialingMediator assistedDialingMediator =
ConcreteCreator.createNewAssistedDialingMediator(
- coordinator.getActivity().getSystemService(TelephonyManager.class),
- coordinator.getActivity());
+ context.getSystemService(TelephonyManager.class), context);
if (!assistedDialingMediator.isPlatformEligible()) {
return;
}
@@ -69,5 +73,10 @@
}
@Override
+ public void runWithUi(PreCallCoordinator coordinator) {
+ runWithoutUi(coordinator.getActivity(), coordinator.getBuilder());
+ }
+
+ @Override
public void onDiscard() {}
}
diff --git a/java/com/android/dialer/precall/impl/CallingAccountSelector.java b/java/com/android/dialer/precall/impl/CallingAccountSelector.java
index ca74bef..d763c7a 100644
--- a/java/com/android/dialer/precall/impl/CallingAccountSelector.java
+++ b/java/com/android/dialer/precall/impl/CallingAccountSelector.java
@@ -59,16 +59,27 @@
private boolean isDiscarding;
@Override
- @MainThread
- public void run(PreCallCoordinator coordinator) {
- CallIntentBuilder builder = coordinator.getBuilder();
+ public boolean requiresUi(Context context, CallIntentBuilder builder) {
if (builder.getPhoneAccountHandle() != null) {
- return;
+ return false;
}
- Activity activity = coordinator.getActivity();
- TelecomManager telecomManager = activity.getSystemService(TelecomManager.class);
+ TelecomManager telecomManager = context.getSystemService(TelecomManager.class);
List<PhoneAccountHandle> accounts = telecomManager.getCallCapablePhoneAccounts();
if (accounts.size() <= 1) {
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public void runWithoutUi(Context context, CallIntentBuilder builder) {
+ // do nothing.
+ }
+
+ @Override
+ public void runWithUi(PreCallCoordinator coordinator) {
+ CallIntentBuilder builder = coordinator.getBuilder();
+ if (!requiresUi(coordinator.getActivity(), builder)) {
return;
}
switch (builder.getUri().getScheme()) {
diff --git a/java/com/android/dialer/precall/impl/PreCallCoordinatorImpl.java b/java/com/android/dialer/precall/impl/PreCallCoordinatorImpl.java
index 6302a23..485823e 100644
--- a/java/com/android/dialer/precall/impl/PreCallCoordinatorImpl.java
+++ b/java/com/android/dialer/precall/impl/PreCallCoordinatorImpl.java
@@ -93,7 +93,7 @@
}
LogUtil.i("PreCallCoordinatorImpl.runNextAction", "running " + actions.get(currentActionIndex));
currentAction = actions.get(currentActionIndex);
- actions.get(currentActionIndex).run(this);
+ actions.get(currentActionIndex).runWithUi(this);
if (pendingAction == null) {
onActionFinished();
}
diff --git a/java/com/android/dialer/precall/impl/PreCallImpl.java b/java/com/android/dialer/precall/impl/PreCallImpl.java
index 21c5dc9..1c78bb8 100644
--- a/java/com/android/dialer/precall/impl/PreCallImpl.java
+++ b/java/com/android/dialer/precall/impl/PreCallImpl.java
@@ -20,8 +20,10 @@
import android.content.Intent;
import android.support.annotation.NonNull;
import com.android.dialer.callintent.CallIntentBuilder;
+import com.android.dialer.common.LogUtil;
import com.android.dialer.precall.PreCall;
import com.android.dialer.precall.PreCallAction;
+import com.android.dialer.precall.PreCallComponent;
import com.android.dialer.precall.PreCallCoordinator;
import com.google.common.collect.ImmutableList;
import javax.inject.Inject;
@@ -40,8 +42,26 @@
@NonNull
@Override
public Intent buildIntent(Context context, CallIntentBuilder builder) {
+ if (!requiresUi(context, builder)) {
+ LogUtil.i("PreCallImpl.buildIntent", "No UI requested, running pre-call directly");
+ for (PreCallAction action : PreCallComponent.get(context).getPreCall().getActions()) {
+ action.runWithoutUi(context, builder);
+ }
+ return builder.build();
+ }
+ LogUtil.i("PreCallImpl.buildIntent", "building intent to start activity");
Intent intent = new Intent(context, PreCallActivity.class);
intent.putExtra(PreCallCoordinator.EXTRA_CALL_INTENT_BUILDER, builder);
return intent;
}
+
+ private boolean requiresUi(Context context, CallIntentBuilder builder) {
+ for (PreCallAction action : PreCallComponent.get(context).getPreCall().getActions()) {
+ if (action.requiresUi(context, builder)) {
+ LogUtil.i("PreCallImpl.requiresUi", action + " requested UI");
+ return true;
+ }
+ }
+ return false;
+ }
}