Enable and disable the call log framework on flag changes.

Bug: 74821995
Test: unit
PiperOrigin-RevId: 189969399
Change-Id: I8e287cc7884dde7640721bd385fe383a4635f3c8
diff --git a/java/com/android/dialer/binary/common/DialerApplication.java b/java/com/android/dialer/binary/common/DialerApplication.java
index 58829b8..9e9b03b 100644
--- a/java/com/android/dialer/binary/common/DialerApplication.java
+++ b/java/com/android/dialer/binary/common/DialerApplication.java
@@ -48,7 +48,7 @@
             new FilteredNumberAsyncQueryHandler(this),
             DialerExecutorComponent.get(this).dialerExecutorFactory())
         .asyncAutoMigrate();
-    CallLogComponent.get(this).callLogFramework().onApplicationCreate(getApplicationContext());
+    CallLogComponent.get(this).callLogFramework().onApplicationCreate();
     Futures.addCallback(
         CallLogComponent.get(this).getAnnotatedCallLogMigrator().migrate(),
         new DefaultFutureCallback<>(),
diff --git a/java/com/android/dialer/calllog/AnnotatedCallLogMigrator.java b/java/com/android/dialer/calllog/AnnotatedCallLogMigrator.java
index e2e112c..a12a98f 100644
--- a/java/com/android/dialer/calllog/AnnotatedCallLogMigrator.java
+++ b/java/com/android/dialer/calllog/AnnotatedCallLogMigrator.java
@@ -90,4 +90,17 @@
           return true;
         });
   }
+
+  /**
+   * Clears data that indicates if migration happened or not. This is necessary if migration needs
+   * to happen again, for example because the call log framework was disabled via flags due to a
+   * problem.
+   */
+  ListenableFuture<Void> clearData() {
+    return backgroundExecutor.submit(
+        () -> {
+          sharedPreferences.edit().remove(PREF_MIGRATED).apply();
+          return null;
+        });
+  }
 }
diff --git a/java/com/android/dialer/calllog/CallLogConfig.java b/java/com/android/dialer/calllog/CallLogConfig.java
index e6fa9c7..12056c7 100644
--- a/java/com/android/dialer/calllog/CallLogConfig.java
+++ b/java/com/android/dialer/calllog/CallLogConfig.java
@@ -28,6 +28,7 @@
 import com.android.dialer.common.Assert;
 import com.android.dialer.common.LogUtil;
 import com.android.dialer.common.concurrent.Annotations.BackgroundExecutor;
+import com.android.dialer.common.concurrent.Annotations.LightweightExecutor;
 import com.android.dialer.common.concurrent.ThreadUtil;
 import com.android.dialer.configprovider.ConfigProvider;
 import com.android.dialer.constants.ScheduledJobIds;
@@ -59,18 +60,27 @@
   private static final String NEW_CALL_LOG_FRAMEWORK_ENABLED_PREF_KEY =
       "newCallLogFrameworkEnabled";
 
+  private final CallLogFramework callLogFramework;
   private final SharedPreferences sharedPreferences;
   private final ConfigProvider configProvider;
+  private final AnnotatedCallLogMigrator annotatedCallLogMigrator;
   private final ListeningExecutorService backgroundExecutor;
+  private final ListeningExecutorService lightweightExecutor;
 
   @Inject
   public CallLogConfig(
+      CallLogFramework callLogFramework,
       @Unencrypted SharedPreferences sharedPreferences,
       ConfigProvider configProvider,
-      @BackgroundExecutor ListeningExecutorService backgroundExecutor) {
+      AnnotatedCallLogMigrator annotatedCallLogMigrator,
+      @BackgroundExecutor ListeningExecutorService backgroundExecutor,
+      @LightweightExecutor ListeningExecutorService lightweightExecutor) {
+    this.callLogFramework = callLogFramework;
     this.sharedPreferences = sharedPreferences;
     this.configProvider = configProvider;
+    this.annotatedCallLogMigrator = annotatedCallLogMigrator;
     this.backgroundExecutor = backgroundExecutor;
+    this.lightweightExecutor = lightweightExecutor;
   }
 
   /**
@@ -78,24 +88,22 @@
    * example by a scheduled job or broadcast receiver which rarely fires.
    */
   public ListenableFuture<Void> update() {
-    return backgroundExecutor.submit(
-        () -> {
-          boolean newCallLogFragmentEnabledInConfigProvider =
-              configProvider.getBoolean("new_call_log_fragment_enabled", false);
-          boolean newVoicemailFragmentEnabledInConfigProvider =
-              configProvider.getBoolean("new_voicemail_fragment_enabled", false);
-          boolean newPeerEnabledInConfigProvider =
-              configProvider.getBoolean("nui_peer_enabled", false);
+    boolean newCallLogFragmentEnabledInConfigProvider =
+        configProvider.getBoolean("new_call_log_fragment_enabled", false);
+    boolean newVoicemailFragmentEnabledInConfigProvider =
+        configProvider.getBoolean("new_voicemail_fragment_enabled", false);
+    boolean newPeerEnabledInConfigProvider = configProvider.getBoolean("nui_peer_enabled", false);
 
-          boolean isCallLogFrameworkEnabled = isCallLogFrameworkEnabled();
-          boolean callLogFrameworkShouldBeEnabled =
-              newCallLogFragmentEnabledInConfigProvider
-                  || newVoicemailFragmentEnabledInConfigProvider
-                  || newPeerEnabledInConfigProvider;
+    boolean isCallLogFrameworkEnabled = isCallLogFrameworkEnabled();
+    boolean callLogFrameworkShouldBeEnabled =
+        newCallLogFragmentEnabledInConfigProvider
+            || newVoicemailFragmentEnabledInConfigProvider
+            || newPeerEnabledInConfigProvider;
 
-          if (callLogFrameworkShouldBeEnabled && !isCallLogFrameworkEnabled) {
-            enableFramework();
-
+    if (callLogFrameworkShouldBeEnabled && !isCallLogFrameworkEnabled) {
+      return Futures.transform(
+          enableFramework(),
+          unused -> {
             // Reflect the flag changes only after the framework is enabled.
             sharedPreferences
                 .edit()
@@ -108,21 +116,30 @@
                 .putBoolean(NEW_PEER_ENABLED_PREF_KEY, newPeerEnabledInConfigProvider)
                 .putBoolean(NEW_CALL_LOG_FRAMEWORK_ENABLED_PREF_KEY, true)
                 .apply();
-
-          } else if (!callLogFrameworkShouldBeEnabled && isCallLogFrameworkEnabled) {
-            // Reflect the flag changes before disabling the framework.
-            sharedPreferences
-                .edit()
-                .putBoolean(NEW_CALL_LOG_FRAGMENT_ENABLED_PREF_KEY, false)
-                .putBoolean(NEW_VOICEMAIL_FRAGMENT_ENABLED_PREF_KEY, false)
-                .putBoolean(NEW_PEER_ENABLED_PREF_KEY, false)
-                .putBoolean(NEW_CALL_LOG_FRAMEWORK_ENABLED_PREF_KEY, false)
-                .apply();
-
-            disableFramework();
-          } else {
-            // We didn't need to enable/disable the framework, but we still need to update the
-            // individual flags.
+            return null;
+          },
+          backgroundExecutor);
+    } else if (!callLogFrameworkShouldBeEnabled && isCallLogFrameworkEnabled) {
+      // Reflect the flag changes before disabling the framework.
+      ListenableFuture<Void> writeSharedPrefsFuture =
+          backgroundExecutor.submit(
+              () -> {
+                sharedPreferences
+                    .edit()
+                    .putBoolean(NEW_CALL_LOG_FRAGMENT_ENABLED_PREF_KEY, false)
+                    .putBoolean(NEW_VOICEMAIL_FRAGMENT_ENABLED_PREF_KEY, false)
+                    .putBoolean(NEW_PEER_ENABLED_PREF_KEY, false)
+                    .putBoolean(NEW_CALL_LOG_FRAMEWORK_ENABLED_PREF_KEY, false)
+                    .apply();
+                return null;
+              });
+      return Futures.transformAsync(
+          writeSharedPrefsFuture, unused -> disableFramework(), MoreExecutors.directExecutor());
+    } else {
+      // We didn't need to enable/disable the framework, but we still need to update the
+      // individual flags.
+      return backgroundExecutor.submit(
+          () -> {
             sharedPreferences
                 .edit()
                 .putBoolean(
@@ -133,17 +150,30 @@
                     newVoicemailFragmentEnabledInConfigProvider)
                 .putBoolean(NEW_PEER_ENABLED_PREF_KEY, newPeerEnabledInConfigProvider)
                 .apply();
-          }
-          return null;
-        });
+            return null;
+          });
+    }
   }
 
-  private void enableFramework() {
-    // TODO(zachh): Register content observers, etc.
+  private ListenableFuture<Void> enableFramework() {
+    ListenableFuture<Void> registerObserversFuture =
+        lightweightExecutor.submit(
+            () -> {
+              callLogFramework.registerContentObservers();
+              return null;
+            });
+    ListenableFuture<Void> migratorFuture = annotatedCallLogMigrator.migrate();
+    return Futures.transform(
+        Futures.allAsList(registerObserversFuture, migratorFuture),
+        unused -> null,
+        MoreExecutors.directExecutor());
   }
 
-  private void disableFramework() {
-    // TODO(zachh): Unregister content observers, delete databases, etc.
+  private ListenableFuture<Void> disableFramework() {
+    return Futures.transform(
+        Futures.allAsList(callLogFramework.disable(), annotatedCallLogMigrator.clearData()),
+        unused -> null,
+        MoreExecutors.directExecutor());
   }
 
   public boolean isNewCallLogFragmentEnabled() {
diff --git a/java/com/android/dialer/calllog/CallLogFramework.java b/java/com/android/dialer/calllog/CallLogFramework.java
index 61535e6..365e4d7 100644
--- a/java/com/android/dialer/calllog/CallLogFramework.java
+++ b/java/com/android/dialer/calllog/CallLogFramework.java
@@ -21,6 +21,12 @@
 import com.android.dialer.calllog.datasources.DataSources;
 import com.android.dialer.common.LogUtil;
 import com.android.dialer.configprovider.ConfigProviderBindings;
+import com.android.dialer.inject.ApplicationContext;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.MoreExecutors;
+import java.util.ArrayList;
+import java.util.List;
 import javax.inject.Inject;
 import javax.inject.Singleton;
 
@@ -32,21 +38,23 @@
 @Singleton
 public final class CallLogFramework {
 
+  private final Context appContext;
   private final DataSources dataSources;
 
   @Inject
-  CallLogFramework(DataSources dataSources) {
+  CallLogFramework(@ApplicationContext Context appContext, DataSources dataSources) {
+    this.appContext = appContext;
     this.dataSources = dataSources;
   }
 
   /** Performs necessary setup work when the application is created. */
-  public void onApplicationCreate(Context appContext) {
-    registerContentObservers(appContext);
+  public void onApplicationCreate() {
+    registerContentObservers();
     CallLogConfig.schedulePollingJob(appContext);
   }
 
   /** Registers the content observers for all data sources. */
-  public void registerContentObservers(Context appContext) {
+  public void registerContentObservers() {
     LogUtil.enterBlock("CallLogFramework.registerContentObservers");
 
     // This is the same condition used in MainImpl#isNewUiEnabled. It means that bugfood/debug
@@ -61,4 +69,26 @@
       LogUtil.i("CallLogFramework.registerContentObservers", "not registering content observers");
     }
   }
+
+  /** Disables the framework. */
+  public ListenableFuture<Void> disable() {
+    LogUtil.enterBlock("CallLogFramework.disable");
+
+    if (!ConfigProviderBindings.get(appContext).getBoolean("is_nui_shortcut_enabled", false)) {
+      LogUtil.i("CallLogFramework.disable", "not disabling");
+      return Futures.immediateFuture(null);
+    }
+
+    for (CallLogDataSource dataSource : dataSources.getDataSourcesIncludingSystemCallLog()) {
+      dataSource.unregisterContentObservers(appContext);
+    }
+
+    // Clear data only after all content observers have been disabled.
+    List<ListenableFuture<Void>> allFutures = new ArrayList<>();
+    for (CallLogDataSource dataSource : dataSources.getDataSourcesIncludingSystemCallLog()) {
+      allFutures.add(dataSource.clearData());
+    }
+    return Futures.transform(
+        Futures.allAsList(allFutures), unused -> null, MoreExecutors.directExecutor());
+  }
 }