Loading java/com/android/dialer/binary/common/DialerApplication.java +1 −1 Original line number Original line Diff line number Diff line Loading @@ -48,7 +48,7 @@ public abstract class DialerApplication extends Application implements HasRootCo new FilteredNumberAsyncQueryHandler(this), new FilteredNumberAsyncQueryHandler(this), DialerExecutorComponent.get(this).dialerExecutorFactory()) DialerExecutorComponent.get(this).dialerExecutorFactory()) .asyncAutoMigrate(); .asyncAutoMigrate(); CallLogComponent.get(this).callLogFramework().onApplicationCreate(getApplicationContext()); CallLogComponent.get(this).callLogFramework().onApplicationCreate(); Futures.addCallback( Futures.addCallback( CallLogComponent.get(this).getAnnotatedCallLogMigrator().migrate(), CallLogComponent.get(this).getAnnotatedCallLogMigrator().migrate(), new DefaultFutureCallback<>(), new DefaultFutureCallback<>(), Loading java/com/android/dialer/calllog/AnnotatedCallLogMigrator.java +13 −0 Original line number Original line Diff line number Diff line Loading @@ -90,4 +90,17 @@ public final class AnnotatedCallLogMigrator { return true; 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; }); } } } java/com/android/dialer/calllog/CallLogConfig.java +71 −41 Original line number Original line Diff line number Diff line Loading @@ -28,6 +28,7 @@ import android.support.v4.os.UserManagerCompat; import com.android.dialer.common.Assert; import com.android.dialer.common.Assert; import com.android.dialer.common.LogUtil; import com.android.dialer.common.LogUtil; import com.android.dialer.common.concurrent.Annotations.BackgroundExecutor; 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.common.concurrent.ThreadUtil; import com.android.dialer.configprovider.ConfigProvider; import com.android.dialer.configprovider.ConfigProvider; import com.android.dialer.constants.ScheduledJobIds; import com.android.dialer.constants.ScheduledJobIds; Loading Loading @@ -59,18 +60,27 @@ public final class CallLogConfig { private static final String NEW_CALL_LOG_FRAMEWORK_ENABLED_PREF_KEY = private static final String NEW_CALL_LOG_FRAMEWORK_ENABLED_PREF_KEY = "newCallLogFrameworkEnabled"; "newCallLogFrameworkEnabled"; private final CallLogFramework callLogFramework; private final SharedPreferences sharedPreferences; private final SharedPreferences sharedPreferences; private final ConfigProvider configProvider; private final ConfigProvider configProvider; private final AnnotatedCallLogMigrator annotatedCallLogMigrator; private final ListeningExecutorService backgroundExecutor; private final ListeningExecutorService backgroundExecutor; private final ListeningExecutorService lightweightExecutor; @Inject @Inject public CallLogConfig( public CallLogConfig( CallLogFramework callLogFramework, @Unencrypted SharedPreferences sharedPreferences, @Unencrypted SharedPreferences sharedPreferences, ConfigProvider configProvider, ConfigProvider configProvider, @BackgroundExecutor ListeningExecutorService backgroundExecutor) { AnnotatedCallLogMigrator annotatedCallLogMigrator, @BackgroundExecutor ListeningExecutorService backgroundExecutor, @LightweightExecutor ListeningExecutorService lightweightExecutor) { this.callLogFramework = callLogFramework; this.sharedPreferences = sharedPreferences; this.sharedPreferences = sharedPreferences; this.configProvider = configProvider; this.configProvider = configProvider; this.annotatedCallLogMigrator = annotatedCallLogMigrator; this.backgroundExecutor = backgroundExecutor; this.backgroundExecutor = backgroundExecutor; this.lightweightExecutor = lightweightExecutor; } } /** /** Loading @@ -78,14 +88,11 @@ public final class CallLogConfig { * example by a scheduled job or broadcast receiver which rarely fires. * example by a scheduled job or broadcast receiver which rarely fires. */ */ public ListenableFuture<Void> update() { public ListenableFuture<Void> update() { return backgroundExecutor.submit( () -> { boolean newCallLogFragmentEnabledInConfigProvider = boolean newCallLogFragmentEnabledInConfigProvider = configProvider.getBoolean("new_call_log_fragment_enabled", false); configProvider.getBoolean("new_call_log_fragment_enabled", false); boolean newVoicemailFragmentEnabledInConfigProvider = boolean newVoicemailFragmentEnabledInConfigProvider = configProvider.getBoolean("new_voicemail_fragment_enabled", false); configProvider.getBoolean("new_voicemail_fragment_enabled", false); boolean newPeerEnabledInConfigProvider = boolean newPeerEnabledInConfigProvider = configProvider.getBoolean("nui_peer_enabled", false); configProvider.getBoolean("nui_peer_enabled", false); boolean isCallLogFrameworkEnabled = isCallLogFrameworkEnabled(); boolean isCallLogFrameworkEnabled = isCallLogFrameworkEnabled(); boolean callLogFrameworkShouldBeEnabled = boolean callLogFrameworkShouldBeEnabled = Loading @@ -94,8 +101,9 @@ public final class CallLogConfig { || newPeerEnabledInConfigProvider; || newPeerEnabledInConfigProvider; if (callLogFrameworkShouldBeEnabled && !isCallLogFrameworkEnabled) { if (callLogFrameworkShouldBeEnabled && !isCallLogFrameworkEnabled) { enableFramework(); return Futures.transform( enableFramework(), unused -> { // Reflect the flag changes only after the framework is enabled. // Reflect the flag changes only after the framework is enabled. sharedPreferences sharedPreferences .edit() .edit() Loading @@ -108,9 +116,14 @@ public final class CallLogConfig { .putBoolean(NEW_PEER_ENABLED_PREF_KEY, newPeerEnabledInConfigProvider) .putBoolean(NEW_PEER_ENABLED_PREF_KEY, newPeerEnabledInConfigProvider) .putBoolean(NEW_CALL_LOG_FRAMEWORK_ENABLED_PREF_KEY, true) .putBoolean(NEW_CALL_LOG_FRAMEWORK_ENABLED_PREF_KEY, true) .apply(); .apply(); return null; }, backgroundExecutor); } else if (!callLogFrameworkShouldBeEnabled && isCallLogFrameworkEnabled) { } else if (!callLogFrameworkShouldBeEnabled && isCallLogFrameworkEnabled) { // Reflect the flag changes before disabling the framework. // Reflect the flag changes before disabling the framework. ListenableFuture<Void> writeSharedPrefsFuture = backgroundExecutor.submit( () -> { sharedPreferences sharedPreferences .edit() .edit() .putBoolean(NEW_CALL_LOG_FRAGMENT_ENABLED_PREF_KEY, false) .putBoolean(NEW_CALL_LOG_FRAGMENT_ENABLED_PREF_KEY, false) Loading @@ -118,11 +131,15 @@ public final class CallLogConfig { .putBoolean(NEW_PEER_ENABLED_PREF_KEY, false) .putBoolean(NEW_PEER_ENABLED_PREF_KEY, false) .putBoolean(NEW_CALL_LOG_FRAMEWORK_ENABLED_PREF_KEY, false) .putBoolean(NEW_CALL_LOG_FRAMEWORK_ENABLED_PREF_KEY, false) .apply(); .apply(); return null; disableFramework(); }); return Futures.transformAsync( writeSharedPrefsFuture, unused -> disableFramework(), MoreExecutors.directExecutor()); } else { } else { // We didn't need to enable/disable the framework, but we still need to update the // We didn't need to enable/disable the framework, but we still need to update the // individual flags. // individual flags. return backgroundExecutor.submit( () -> { sharedPreferences sharedPreferences .edit() .edit() .putBoolean( .putBoolean( Loading @@ -133,17 +150,30 @@ public final class CallLogConfig { newVoicemailFragmentEnabledInConfigProvider) newVoicemailFragmentEnabledInConfigProvider) .putBoolean(NEW_PEER_ENABLED_PREF_KEY, newPeerEnabledInConfigProvider) .putBoolean(NEW_PEER_ENABLED_PREF_KEY, newPeerEnabledInConfigProvider) .apply(); .apply(); } return null; return null; }); }); } } } private void enableFramework() { private ListenableFuture<Void> enableFramework() { // TODO(zachh): Register content observers, etc. 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() { private ListenableFuture<Void> disableFramework() { // TODO(zachh): Unregister content observers, delete databases, etc. return Futures.transform( Futures.allAsList(callLogFramework.disable(), annotatedCallLogMigrator.clearData()), unused -> null, MoreExecutors.directExecutor()); } } public boolean isNewCallLogFragmentEnabled() { public boolean isNewCallLogFragmentEnabled() { Loading java/com/android/dialer/calllog/CallLogFramework.java +34 −4 Original line number Original line Diff line number Diff line Loading @@ -21,6 +21,12 @@ import com.android.dialer.calllog.datasources.CallLogDataSource; import com.android.dialer.calllog.datasources.DataSources; import com.android.dialer.calllog.datasources.DataSources; import com.android.dialer.common.LogUtil; import com.android.dialer.common.LogUtil; import com.android.dialer.configprovider.ConfigProviderBindings; 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.Inject; import javax.inject.Singleton; import javax.inject.Singleton; Loading @@ -32,21 +38,23 @@ import javax.inject.Singleton; @Singleton @Singleton public final class CallLogFramework { public final class CallLogFramework { private final Context appContext; private final DataSources dataSources; private final DataSources dataSources; @Inject @Inject CallLogFramework(DataSources dataSources) { CallLogFramework(@ApplicationContext Context appContext, DataSources dataSources) { this.appContext = appContext; this.dataSources = dataSources; this.dataSources = dataSources; } } /** Performs necessary setup work when the application is created. */ /** Performs necessary setup work when the application is created. */ public void onApplicationCreate(Context appContext) { public void onApplicationCreate() { registerContentObservers(appContext); registerContentObservers(); CallLogConfig.schedulePollingJob(appContext); CallLogConfig.schedulePollingJob(appContext); } } /** Registers the content observers for all data sources. */ /** Registers the content observers for all data sources. */ public void registerContentObservers(Context appContext) { public void registerContentObservers() { LogUtil.enterBlock("CallLogFramework.registerContentObservers"); LogUtil.enterBlock("CallLogFramework.registerContentObservers"); // This is the same condition used in MainImpl#isNewUiEnabled. It means that bugfood/debug // This is the same condition used in MainImpl#isNewUiEnabled. It means that bugfood/debug Loading @@ -61,4 +69,26 @@ public final class CallLogFramework { LogUtil.i("CallLogFramework.registerContentObservers", "not registering content observers"); 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()); } } } Loading
java/com/android/dialer/binary/common/DialerApplication.java +1 −1 Original line number Original line Diff line number Diff line Loading @@ -48,7 +48,7 @@ public abstract class DialerApplication extends Application implements HasRootCo new FilteredNumberAsyncQueryHandler(this), new FilteredNumberAsyncQueryHandler(this), DialerExecutorComponent.get(this).dialerExecutorFactory()) DialerExecutorComponent.get(this).dialerExecutorFactory()) .asyncAutoMigrate(); .asyncAutoMigrate(); CallLogComponent.get(this).callLogFramework().onApplicationCreate(getApplicationContext()); CallLogComponent.get(this).callLogFramework().onApplicationCreate(); Futures.addCallback( Futures.addCallback( CallLogComponent.get(this).getAnnotatedCallLogMigrator().migrate(), CallLogComponent.get(this).getAnnotatedCallLogMigrator().migrate(), new DefaultFutureCallback<>(), new DefaultFutureCallback<>(), Loading
java/com/android/dialer/calllog/AnnotatedCallLogMigrator.java +13 −0 Original line number Original line Diff line number Diff line Loading @@ -90,4 +90,17 @@ public final class AnnotatedCallLogMigrator { return true; 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; }); } } }
java/com/android/dialer/calllog/CallLogConfig.java +71 −41 Original line number Original line Diff line number Diff line Loading @@ -28,6 +28,7 @@ import android.support.v4.os.UserManagerCompat; import com.android.dialer.common.Assert; import com.android.dialer.common.Assert; import com.android.dialer.common.LogUtil; import com.android.dialer.common.LogUtil; import com.android.dialer.common.concurrent.Annotations.BackgroundExecutor; 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.common.concurrent.ThreadUtil; import com.android.dialer.configprovider.ConfigProvider; import com.android.dialer.configprovider.ConfigProvider; import com.android.dialer.constants.ScheduledJobIds; import com.android.dialer.constants.ScheduledJobIds; Loading Loading @@ -59,18 +60,27 @@ public final class CallLogConfig { private static final String NEW_CALL_LOG_FRAMEWORK_ENABLED_PREF_KEY = private static final String NEW_CALL_LOG_FRAMEWORK_ENABLED_PREF_KEY = "newCallLogFrameworkEnabled"; "newCallLogFrameworkEnabled"; private final CallLogFramework callLogFramework; private final SharedPreferences sharedPreferences; private final SharedPreferences sharedPreferences; private final ConfigProvider configProvider; private final ConfigProvider configProvider; private final AnnotatedCallLogMigrator annotatedCallLogMigrator; private final ListeningExecutorService backgroundExecutor; private final ListeningExecutorService backgroundExecutor; private final ListeningExecutorService lightweightExecutor; @Inject @Inject public CallLogConfig( public CallLogConfig( CallLogFramework callLogFramework, @Unencrypted SharedPreferences sharedPreferences, @Unencrypted SharedPreferences sharedPreferences, ConfigProvider configProvider, ConfigProvider configProvider, @BackgroundExecutor ListeningExecutorService backgroundExecutor) { AnnotatedCallLogMigrator annotatedCallLogMigrator, @BackgroundExecutor ListeningExecutorService backgroundExecutor, @LightweightExecutor ListeningExecutorService lightweightExecutor) { this.callLogFramework = callLogFramework; this.sharedPreferences = sharedPreferences; this.sharedPreferences = sharedPreferences; this.configProvider = configProvider; this.configProvider = configProvider; this.annotatedCallLogMigrator = annotatedCallLogMigrator; this.backgroundExecutor = backgroundExecutor; this.backgroundExecutor = backgroundExecutor; this.lightweightExecutor = lightweightExecutor; } } /** /** Loading @@ -78,14 +88,11 @@ public final class CallLogConfig { * example by a scheduled job or broadcast receiver which rarely fires. * example by a scheduled job or broadcast receiver which rarely fires. */ */ public ListenableFuture<Void> update() { public ListenableFuture<Void> update() { return backgroundExecutor.submit( () -> { boolean newCallLogFragmentEnabledInConfigProvider = boolean newCallLogFragmentEnabledInConfigProvider = configProvider.getBoolean("new_call_log_fragment_enabled", false); configProvider.getBoolean("new_call_log_fragment_enabled", false); boolean newVoicemailFragmentEnabledInConfigProvider = boolean newVoicemailFragmentEnabledInConfigProvider = configProvider.getBoolean("new_voicemail_fragment_enabled", false); configProvider.getBoolean("new_voicemail_fragment_enabled", false); boolean newPeerEnabledInConfigProvider = boolean newPeerEnabledInConfigProvider = configProvider.getBoolean("nui_peer_enabled", false); configProvider.getBoolean("nui_peer_enabled", false); boolean isCallLogFrameworkEnabled = isCallLogFrameworkEnabled(); boolean isCallLogFrameworkEnabled = isCallLogFrameworkEnabled(); boolean callLogFrameworkShouldBeEnabled = boolean callLogFrameworkShouldBeEnabled = Loading @@ -94,8 +101,9 @@ public final class CallLogConfig { || newPeerEnabledInConfigProvider; || newPeerEnabledInConfigProvider; if (callLogFrameworkShouldBeEnabled && !isCallLogFrameworkEnabled) { if (callLogFrameworkShouldBeEnabled && !isCallLogFrameworkEnabled) { enableFramework(); return Futures.transform( enableFramework(), unused -> { // Reflect the flag changes only after the framework is enabled. // Reflect the flag changes only after the framework is enabled. sharedPreferences sharedPreferences .edit() .edit() Loading @@ -108,9 +116,14 @@ public final class CallLogConfig { .putBoolean(NEW_PEER_ENABLED_PREF_KEY, newPeerEnabledInConfigProvider) .putBoolean(NEW_PEER_ENABLED_PREF_KEY, newPeerEnabledInConfigProvider) .putBoolean(NEW_CALL_LOG_FRAMEWORK_ENABLED_PREF_KEY, true) .putBoolean(NEW_CALL_LOG_FRAMEWORK_ENABLED_PREF_KEY, true) .apply(); .apply(); return null; }, backgroundExecutor); } else if (!callLogFrameworkShouldBeEnabled && isCallLogFrameworkEnabled) { } else if (!callLogFrameworkShouldBeEnabled && isCallLogFrameworkEnabled) { // Reflect the flag changes before disabling the framework. // Reflect the flag changes before disabling the framework. ListenableFuture<Void> writeSharedPrefsFuture = backgroundExecutor.submit( () -> { sharedPreferences sharedPreferences .edit() .edit() .putBoolean(NEW_CALL_LOG_FRAGMENT_ENABLED_PREF_KEY, false) .putBoolean(NEW_CALL_LOG_FRAGMENT_ENABLED_PREF_KEY, false) Loading @@ -118,11 +131,15 @@ public final class CallLogConfig { .putBoolean(NEW_PEER_ENABLED_PREF_KEY, false) .putBoolean(NEW_PEER_ENABLED_PREF_KEY, false) .putBoolean(NEW_CALL_LOG_FRAMEWORK_ENABLED_PREF_KEY, false) .putBoolean(NEW_CALL_LOG_FRAMEWORK_ENABLED_PREF_KEY, false) .apply(); .apply(); return null; disableFramework(); }); return Futures.transformAsync( writeSharedPrefsFuture, unused -> disableFramework(), MoreExecutors.directExecutor()); } else { } else { // We didn't need to enable/disable the framework, but we still need to update the // We didn't need to enable/disable the framework, but we still need to update the // individual flags. // individual flags. return backgroundExecutor.submit( () -> { sharedPreferences sharedPreferences .edit() .edit() .putBoolean( .putBoolean( Loading @@ -133,17 +150,30 @@ public final class CallLogConfig { newVoicemailFragmentEnabledInConfigProvider) newVoicemailFragmentEnabledInConfigProvider) .putBoolean(NEW_PEER_ENABLED_PREF_KEY, newPeerEnabledInConfigProvider) .putBoolean(NEW_PEER_ENABLED_PREF_KEY, newPeerEnabledInConfigProvider) .apply(); .apply(); } return null; return null; }); }); } } } private void enableFramework() { private ListenableFuture<Void> enableFramework() { // TODO(zachh): Register content observers, etc. 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() { private ListenableFuture<Void> disableFramework() { // TODO(zachh): Unregister content observers, delete databases, etc. return Futures.transform( Futures.allAsList(callLogFramework.disable(), annotatedCallLogMigrator.clearData()), unused -> null, MoreExecutors.directExecutor()); } } public boolean isNewCallLogFragmentEnabled() { public boolean isNewCallLogFragmentEnabled() { Loading
java/com/android/dialer/calllog/CallLogFramework.java +34 −4 Original line number Original line Diff line number Diff line Loading @@ -21,6 +21,12 @@ import com.android.dialer.calllog.datasources.CallLogDataSource; import com.android.dialer.calllog.datasources.DataSources; import com.android.dialer.calllog.datasources.DataSources; import com.android.dialer.common.LogUtil; import com.android.dialer.common.LogUtil; import com.android.dialer.configprovider.ConfigProviderBindings; 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.Inject; import javax.inject.Singleton; import javax.inject.Singleton; Loading @@ -32,21 +38,23 @@ import javax.inject.Singleton; @Singleton @Singleton public final class CallLogFramework { public final class CallLogFramework { private final Context appContext; private final DataSources dataSources; private final DataSources dataSources; @Inject @Inject CallLogFramework(DataSources dataSources) { CallLogFramework(@ApplicationContext Context appContext, DataSources dataSources) { this.appContext = appContext; this.dataSources = dataSources; this.dataSources = dataSources; } } /** Performs necessary setup work when the application is created. */ /** Performs necessary setup work when the application is created. */ public void onApplicationCreate(Context appContext) { public void onApplicationCreate() { registerContentObservers(appContext); registerContentObservers(); CallLogConfig.schedulePollingJob(appContext); CallLogConfig.schedulePollingJob(appContext); } } /** Registers the content observers for all data sources. */ /** Registers the content observers for all data sources. */ public void registerContentObservers(Context appContext) { public void registerContentObservers() { LogUtil.enterBlock("CallLogFramework.registerContentObservers"); LogUtil.enterBlock("CallLogFramework.registerContentObservers"); // This is the same condition used in MainImpl#isNewUiEnabled. It means that bugfood/debug // This is the same condition used in MainImpl#isNewUiEnabled. It means that bugfood/debug Loading @@ -61,4 +69,26 @@ public final class CallLogFramework { LogUtil.i("CallLogFramework.registerContentObservers", "not registering content observers"); 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()); } } }