Implement suggested SIM

This CL adds a module that can query providers to aid users choosing which SIM to call with.

Bug: 64214592
Test: MotoSuggestionProviderTest
PiperOrigin-RevId: 176129303
Change-Id: Idb6314ad7f5a1bcd20b5b9173d3bfd873383cc84
diff --git a/java/com/android/dialer/binary/aosp/AospDialerRootComponent.java b/java/com/android/dialer/binary/aosp/AospDialerRootComponent.java
index a02727e..a0fb604 100644
--- a/java/com/android/dialer/binary/aosp/AospDialerRootComponent.java
+++ b/java/com/android/dialer/binary/aosp/AospDialerRootComponent.java
@@ -26,6 +26,7 @@
 import com.android.dialer.phonelookup.PhoneLookupModule;
 import com.android.dialer.phonenumbergeoutil.impl.PhoneNumberGeoUtilModule;
 import com.android.dialer.precall.impl.PreCallModule;
+import com.android.dialer.preferredsim.suggestion.stub.StubSimSuggestionModule;
 import com.android.dialer.simulator.impl.SimulatorModule;
 import com.android.dialer.storage.StorageModule;
 import com.android.dialer.strictmode.impl.SystemStrictModeModule;
@@ -53,7 +54,8 @@
     StubDuoModule.class,
     StubEnrichedCallModule.class,
     StubMapsModule.class,
-    VoicemailModule.class
+    VoicemailModule.class,
+    StubSimSuggestionModule.class
   }
 )
 public interface AospDialerRootComponent extends BaseDialerRootComponent {}
diff --git a/java/com/android/dialer/binary/basecomponent/BaseDialerRootComponent.java b/java/com/android/dialer/binary/basecomponent/BaseDialerRootComponent.java
index 387fca5..d5c91c9 100644
--- a/java/com/android/dialer/binary/basecomponent/BaseDialerRootComponent.java
+++ b/java/com/android/dialer/binary/basecomponent/BaseDialerRootComponent.java
@@ -26,6 +26,7 @@
 import com.android.dialer.phonelookup.PhoneLookupComponent;
 import com.android.dialer.phonenumbergeoutil.PhoneNumberGeoUtilComponent;
 import com.android.dialer.precall.PreCallComponent;
+import com.android.dialer.preferredsim.suggestion.SimSuggestionComponent;
 import com.android.dialer.simulator.SimulatorComponent;
 import com.android.dialer.storage.StorageComponent;
 import com.android.dialer.strictmode.StrictModeComponent;
@@ -50,6 +51,7 @@
         PhoneLookupComponent.HasComponent,
         PhoneNumberGeoUtilComponent.HasComponent,
         PreCallComponent.HasComponent,
+        SimSuggestionComponent.HasComponent,
         SimulatorComponent.HasComponent,
         StorageComponent.HasComponent,
         StrictModeComponent.HasComponent,
diff --git a/java/com/android/dialer/binary/google/GoogleStubDialerRootComponent.java b/java/com/android/dialer/binary/google/GoogleStubDialerRootComponent.java
index 273d1e4..1ae80f4 100644
--- a/java/com/android/dialer/binary/google/GoogleStubDialerRootComponent.java
+++ b/java/com/android/dialer/binary/google/GoogleStubDialerRootComponent.java
@@ -26,6 +26,7 @@
 import com.android.dialer.phonelookup.PhoneLookupModule;
 import com.android.dialer.phonenumbergeoutil.impl.PhoneNumberGeoUtilModule;
 import com.android.dialer.precall.impl.PreCallModule;
+import com.android.dialer.preferredsim.suggestion.stub.StubSimSuggestionModule;
 import com.android.dialer.simulator.impl.SimulatorModule;
 import com.android.dialer.storage.StorageModule;
 import com.android.dialer.strictmode.impl.SystemStrictModeModule;
@@ -49,6 +50,7 @@
     PhoneLookupModule.class, // TODO(zachh): Module which uses APDL?
     PhoneNumberGeoUtilModule.class,
     PreCallModule.class,
+    StubSimSuggestionModule.class,
     SharedPrefConfigProviderModule.class,
     SimulatorModule.class,
     StorageModule.class,
diff --git a/java/com/android/dialer/precall/impl/CallingAccountSelector.java b/java/com/android/dialer/precall/impl/CallingAccountSelector.java
index d763c7a..e0fe0c4 100644
--- a/java/com/android/dialer/precall/impl/CallingAccountSelector.java
+++ b/java/com/android/dialer/precall/impl/CallingAccountSelector.java
@@ -44,6 +44,8 @@
 import com.android.dialer.precall.PreCallCoordinator.PendingAction;
 import com.android.dialer.preferredsim.PreferredSimFallbackContract;
 import com.android.dialer.preferredsim.PreferredSimFallbackContract.PreferredSim;
+import com.android.dialer.preferredsim.suggestion.SimSuggestionComponent;
+import com.android.dialer.preferredsim.suggestion.SuggestionProvider.Suggestion;
 import com.google.common.base.Optional;
 import java.util.List;
 import java.util.Set;
@@ -84,7 +86,7 @@
     }
     switch (builder.getUri().getScheme()) {
       case PhoneAccount.SCHEME_VOICEMAIL:
-        showDialog(coordinator, coordinator.startPendingAction(), null);
+        showDialog(coordinator, coordinator.startPendingAction(), null, null, null);
         break;
       case PhoneAccount.SCHEME_TEL:
         processPreferredAccount(coordinator);
@@ -128,7 +130,17 @@
                 pendingAction.finish();
                 return;
               }
-              showDialog(coordinator, pendingAction, result.dataId.orNull());
+              if (result.suggestion.isPresent()) {
+                LogUtil.i(
+                    "CallingAccountSelector.processPreferredAccount",
+                    "SIM suggested: " + result.suggestion.get().reason);
+              }
+              showDialog(
+                  coordinator,
+                  pendingAction,
+                  result.dataId.orNull(),
+                  phoneNumber,
+                  result.suggestion.orNull());
             }))
         .build()
         .executeParallel(activity);
@@ -136,7 +148,11 @@
 
   @MainThread
   private void showDialog(
-      PreCallCoordinator coordinator, PendingAction pendingAction, @Nullable String dataId) {
+      PreCallCoordinator coordinator,
+      PendingAction pendingAction,
+      @Nullable String dataId,
+      @Nullable String number,
+      @Nullable Suggestion unusedSuggestion) { // TODO(twyen): incoporate suggestion in dialog
     Assert.isMainThread();
     selectPhoneAccountDialogFragment =
         SelectPhoneAccountDialogFragment.newInstance(
@@ -146,7 +162,7 @@
                 .getActivity()
                 .getSystemService(TelecomManager.class)
                 .getCallCapablePhoneAccounts(),
-            new SelectedListener(coordinator, pendingAction, dataId),
+            new SelectedListener(coordinator, pendingAction, dataId, number),
             null /* call ID */);
     selectPhoneAccountDialogFragment.show(
         coordinator.getActivity().getFragmentManager(), TAG_CALLING_ACCOUNT_SELECTOR);
@@ -169,6 +185,8 @@
      * preferred account is to be set it should be stored in this row
      */
     Optional<String> dataId = Optional.absent();
+
+    Optional<Suggestion> suggestion = Optional.absent();
   }
 
   private static class PreferredAccountWorker
@@ -189,6 +207,12 @@
       if (result.dataId.isPresent()) {
         result.phoneAccountHandle = getPreferredAccount(context, result.dataId.get());
       }
+      if (!result.phoneAccountHandle.isPresent()) {
+        result.suggestion =
+            SimSuggestionComponent.get(context)
+                .getSuggestionProvider()
+                .getSuggestion(context, phoneNumber);
+      }
       return result;
     }
   }
@@ -257,14 +281,17 @@
     private final PreCallCoordinator coordinator;
     private final PreCallCoordinator.PendingAction listener;
     private final String dataId;
+    private final String number;
 
     public SelectedListener(
         @NonNull PreCallCoordinator builder,
         @NonNull PreCallCoordinator.PendingAction listener,
-        @Nullable String dataId) {
+        @Nullable String dataId,
+        @Nullable String number) {
       this.coordinator = Assert.isNotNull(builder);
       this.listener = Assert.isNotNull(listener);
       this.dataId = dataId;
+      this.number = number;
     }
 
     @MainThread
@@ -282,7 +309,11 @@
                 new WritePreferredAccountWorkerInput(
                     coordinator.getActivity(), dataId, selectedAccountHandle));
       }
-
+      DialerExecutorComponent.get(coordinator.getActivity())
+          .dialerExecutorFactory()
+          .createNonUiTaskBuilder(new UserSelectionReporter(selectedAccountHandle, number))
+          .build()
+          .executeParallel(coordinator.getActivity());
       listener.finish();
     }
 
@@ -297,6 +328,27 @@
     }
   }
 
+  private static class UserSelectionReporter implements Worker<Context, Void> {
+
+    private final String number;
+    private final PhoneAccountHandle phoneAccountHandle;
+
+    public UserSelectionReporter(
+        @NonNull PhoneAccountHandle phoneAccountHandle, @Nullable String number) {
+      this.phoneAccountHandle = Assert.isNotNull(phoneAccountHandle);
+      this.number = Assert.isNotNull(number);
+    }
+
+    @Nullable
+    @Override
+    public Void doInBackground(@NonNull Context context) throws Throwable {
+      SimSuggestionComponent.get(context)
+          .getSuggestionProvider()
+          .reportUserSelection(context, number, phoneAccountHandle);
+      return null;
+    }
+  }
+
   private static class WritePreferredAccountWorkerInput {
     private final Context context;
     private final String dataId;
diff --git a/java/com/android/dialer/preferredsim/suggestion/SimSuggestionComponent.java b/java/com/android/dialer/preferredsim/suggestion/SimSuggestionComponent.java
new file mode 100644
index 0000000..4b3f7b2
--- /dev/null
+++ b/java/com/android/dialer/preferredsim/suggestion/SimSuggestionComponent.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.dialer.preferredsim.suggestion;
+
+import android.content.Context;
+import android.support.annotation.WorkerThread;
+import com.android.dialer.common.Assert;
+import com.android.dialer.inject.HasRootComponent;
+import dagger.Subcomponent;
+
+/** Dagger component for {@link SuggestionProvider} */
+@Subcomponent
+public abstract class SimSuggestionComponent {
+  public abstract SuggestionProvider getSuggestionProvider();
+
+  @WorkerThread
+  public static SimSuggestionComponent get(Context context) {
+    Assert.isWorkerThread();
+    return ((HasComponent) ((HasRootComponent) context.getApplicationContext()).component())
+        .simSuggestionComponent();
+  }
+
+  /** Used to refer to the root application component. */
+  public interface HasComponent {
+    SimSuggestionComponent simSuggestionComponent();
+  }
+}
diff --git a/java/com/android/dialer/preferredsim/suggestion/SuggestionProvider.java b/java/com/android/dialer/preferredsim/suggestion/SuggestionProvider.java
new file mode 100644
index 0000000..61a831b
--- /dev/null
+++ b/java/com/android/dialer/preferredsim/suggestion/SuggestionProvider.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.dialer.preferredsim.suggestion;
+
+import android.content.Context;
+import android.support.annotation.NonNull;
+import android.support.annotation.WorkerThread;
+import android.telecom.PhoneAccountHandle;
+import com.android.dialer.common.Assert;
+import com.google.common.base.Optional;
+
+/** Provides hints to the user when selecting a SIM to make a call. */
+public interface SuggestionProvider {
+
+  /** The reason the suggestion is made. */
+  enum Reason {
+    UNKNOWN,
+    // The SIM has the same carrier as the callee.
+    INTRA_CARRIER,
+    // The user has selected the SIM for the callee multiple times.
+    FREQUENT
+  }
+
+  /** The suggestion. */
+  class Suggestion {
+    @NonNull public final PhoneAccountHandle phoneAccountHandle;
+    @NonNull public final Reason reason;
+
+    public Suggestion(@NonNull PhoneAccountHandle phoneAccountHandle, @NonNull Reason reason) {
+      this.phoneAccountHandle = Assert.isNotNull(phoneAccountHandle);
+      this.reason = Assert.isNotNull(reason);
+    }
+  }
+
+  @WorkerThread
+  @NonNull
+  Optional<Suggestion> getSuggestion(@NonNull Context context, @NonNull String number);
+
+  @WorkerThread
+  void reportUserSelection(
+      @NonNull Context context,
+      @NonNull String number,
+      @NonNull PhoneAccountHandle phoneAccountHandle);
+}
diff --git a/java/com/android/dialer/preferredsim/suggestion/stub/StubSimSuggestionModule.java b/java/com/android/dialer/preferredsim/suggestion/stub/StubSimSuggestionModule.java
new file mode 100644
index 0000000..2f0e9b2
--- /dev/null
+++ b/java/com/android/dialer/preferredsim/suggestion/stub/StubSimSuggestionModule.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.dialer.preferredsim.suggestion.stub;
+
+import com.android.dialer.preferredsim.suggestion.SuggestionProvider;
+import dagger.Binds;
+import dagger.Module;
+import javax.inject.Singleton;
+
+/** Stub module for {@link com.android.dialer.preferredsim.suggestion.SimSuggestionComponent} */
+@Module
+public abstract class StubSimSuggestionModule {
+
+  @Binds
+  @Singleton
+  public abstract SuggestionProvider bindSuggestionProvider(
+      StubSuggestionProvider suggestionProvider);
+}
diff --git a/java/com/android/dialer/preferredsim/suggestion/stub/StubSuggestionProvider.java b/java/com/android/dialer/preferredsim/suggestion/stub/StubSuggestionProvider.java
new file mode 100644
index 0000000..e324044
--- /dev/null
+++ b/java/com/android/dialer/preferredsim/suggestion/stub/StubSuggestionProvider.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.dialer.preferredsim.suggestion.stub;
+
+import android.content.Context;
+import android.support.annotation.NonNull;
+import android.support.annotation.WorkerThread;
+import android.telecom.PhoneAccountHandle;
+import com.android.dialer.preferredsim.suggestion.SuggestionProvider;
+import com.google.common.base.Optional;
+import javax.inject.Inject;
+
+/** {@link SuggestionProvider} that does nothing. */
+public class StubSuggestionProvider implements SuggestionProvider {
+
+  @Inject
+  public StubSuggestionProvider() {}
+
+  @WorkerThread
+  @Override
+  public Optional<Suggestion> getSuggestion(Context context, String number) {
+    return Optional.absent();
+  }
+
+  @Override
+  public void reportUserSelection(
+      @NonNull Context context,
+      @NonNull String number,
+      @NonNull PhoneAccountHandle phoneAccountHandle) {}
+}