From d74e72e59795d39836a893afb60447acce9e9886 Mon Sep 17 00:00:00 2001 From: zachh Date: Fri, 23 Mar 2018 11:43:18 -0700 Subject: Removed NUI shortcut. We're not going to have a shortcut for NUI anymore. There are individual flags related to NUI that are controlled in CallLogConfig. Other related changes to help accomplish this: -Changed how the call log framework/config/migrator interact; the migrator is now only called on config changes and enabling/disabling of the framework now lives in CallLogFramework. -Move CallLogConfig an interface, and moved it to its own package and added component and module. This is to simplify tests which just need to check the config status (like PhoneLookupHistoryRecorderTest). -The "Main" package is also on longer needed since it existed to control the shortcut. Bug: 74821995 Test: existing PiperOrigin-RevId: 190251418 Change-Id: I73c8e83aee80295131443a8ffaa7dea716ea89b6 --- .../dialer/calllog/AnnotatedCallLogMigrator.java | 18 +- .../android/dialer/calllog/CallLogComponent.java | 4 - java/com/android/dialer/calllog/CallLogConfig.java | 255 --------------------- .../android/dialer/calllog/CallLogFramework.java | 46 ++-- .../dialer/calllog/config/AndroidManifest.xml | 32 +++ .../dialer/calllog/config/CallLogConfig.java | 40 ++++ .../calllog/config/CallLogConfigComponent.java | 37 +++ .../dialer/calllog/config/CallLogConfigImpl.java | 236 +++++++++++++++++++ .../dialer/calllog/config/CallLogConfigModule.java | 27 +++ 9 files changed, 391 insertions(+), 304 deletions(-) delete mode 100644 java/com/android/dialer/calllog/CallLogConfig.java create mode 100644 java/com/android/dialer/calllog/config/AndroidManifest.xml create mode 100644 java/com/android/dialer/calllog/config/CallLogConfig.java create mode 100644 java/com/android/dialer/calllog/config/CallLogConfigComponent.java create mode 100644 java/com/android/dialer/calllog/config/CallLogConfigImpl.java create mode 100644 java/com/android/dialer/calllog/config/CallLogConfigModule.java (limited to 'java/com/android/dialer/calllog') diff --git a/java/com/android/dialer/calllog/AnnotatedCallLogMigrator.java b/java/com/android/dialer/calllog/AnnotatedCallLogMigrator.java index a12a98f0e..71bfb753c 100644 --- a/java/com/android/dialer/calllog/AnnotatedCallLogMigrator.java +++ b/java/com/android/dialer/calllog/AnnotatedCallLogMigrator.java @@ -16,12 +16,9 @@ package com.android.dialer.calllog; -import android.content.Context; import android.content.SharedPreferences; import com.android.dialer.common.LogUtil; import com.android.dialer.common.concurrent.Annotations.BackgroundExecutor; -import com.android.dialer.configprovider.ConfigProviderBindings; -import com.android.dialer.inject.ApplicationContext; import com.android.dialer.storage.Unencrypted; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; @@ -37,18 +34,15 @@ public final class AnnotatedCallLogMigrator { private static final String PREF_MIGRATED = "annotatedCallLogMigratorMigrated"; - private final Context appContext; private final SharedPreferences sharedPreferences; private final RefreshAnnotatedCallLogWorker refreshAnnotatedCallLogWorker; private final ListeningExecutorService backgroundExecutor; @Inject AnnotatedCallLogMigrator( - @ApplicationContext Context appContext, @Unencrypted SharedPreferences sharedPreferences, @BackgroundExecutor ListeningExecutorService backgroundExecutor, RefreshAnnotatedCallLogWorker refreshAnnotatedCallLogWorker) { - this.appContext = appContext; this.sharedPreferences = sharedPreferences; this.backgroundExecutor = backgroundExecutor; this.refreshAnnotatedCallLogWorker = refreshAnnotatedCallLogWorker; @@ -78,17 +72,7 @@ public final class AnnotatedCallLogMigrator { } private ListenableFuture shouldMigrate() { - return backgroundExecutor.submit( - () -> { - if (!(ConfigProviderBindings.get(appContext) - .getBoolean("is_nui_shortcut_enabled", false))) { - return false; - } - if (sharedPreferences.getBoolean(PREF_MIGRATED, false)) { - return false; - } - return true; - }); + return backgroundExecutor.submit(() -> !sharedPreferences.getBoolean(PREF_MIGRATED, false)); } /** diff --git a/java/com/android/dialer/calllog/CallLogComponent.java b/java/com/android/dialer/calllog/CallLogComponent.java index c7b44a818..4f147f1a6 100644 --- a/java/com/android/dialer/calllog/CallLogComponent.java +++ b/java/com/android/dialer/calllog/CallLogComponent.java @@ -30,12 +30,8 @@ public abstract class CallLogComponent { public abstract RefreshAnnotatedCallLogWorker getRefreshAnnotatedCallLogWorker(); - public abstract AnnotatedCallLogMigrator getAnnotatedCallLogMigrator(); - public abstract ClearMissedCalls getClearMissedCalls(); - public abstract CallLogConfig callLogConfig(); - public static CallLogComponent get(Context context) { return ((HasComponent) ((HasRootComponent) context.getApplicationContext()).component()) .callLogComponent(); diff --git a/java/com/android/dialer/calllog/CallLogConfig.java b/java/com/android/dialer/calllog/CallLogConfig.java deleted file mode 100644 index 12056c758..000000000 --- a/java/com/android/dialer/calllog/CallLogConfig.java +++ /dev/null @@ -1,255 +0,0 @@ -/* - * Copyright (C) 2018 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.calllog; - -import android.annotation.SuppressLint; -import android.app.job.JobInfo; -import android.app.job.JobParameters; -import android.app.job.JobScheduler; -import android.app.job.JobService; -import android.content.ComponentName; -import android.content.Context; -import android.content.SharedPreferences; -import android.support.v4.os.UserManagerCompat; -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; -import com.android.dialer.storage.Unencrypted; -import com.google.common.util.concurrent.FutureCallback; -import com.google.common.util.concurrent.Futures; -import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.ListeningExecutorService; -import com.google.common.util.concurrent.MoreExecutors; -import java.util.concurrent.TimeUnit; -import javax.inject.Inject; - -/** - * Determines if new call log components are enabled. - * - *

When the underlying flag values from the {@link ConfigProvider} changes, it is necessary to do - * work such as registering/unregistering content observers, and this class is responsible for - * coordinating that work. - * - *

New UI application components should use this class instead of reading flags directly from the - * {@link ConfigProvider}. - */ -public final class CallLogConfig { - - private static final String NEW_CALL_LOG_FRAGMENT_ENABLED_PREF_KEY = "newCallLogFragmentEnabled"; - private static final String NEW_VOICEMAIL_FRAGMENT_ENABLED_PREF_KEY = - "newVoicemailFragmentEnabled"; - private static final String NEW_PEER_ENABLED_PREF_KEY = "newPeerEnabled"; - 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, - 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; - } - - /** - * Updates the config values. This may kick off a lot of work so should be done infrequently, for - * example by a scheduled job or broadcast receiver which rarely fires. - */ - public ListenableFuture update() { - 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; - - if (callLogFrameworkShouldBeEnabled && !isCallLogFrameworkEnabled) { - return Futures.transform( - enableFramework(), - unused -> { - // Reflect the flag changes only after the framework is enabled. - sharedPreferences - .edit() - .putBoolean( - NEW_CALL_LOG_FRAGMENT_ENABLED_PREF_KEY, - newCallLogFragmentEnabledInConfigProvider) - .putBoolean( - NEW_VOICEMAIL_FRAGMENT_ENABLED_PREF_KEY, - newVoicemailFragmentEnabledInConfigProvider) - .putBoolean(NEW_PEER_ENABLED_PREF_KEY, newPeerEnabledInConfigProvider) - .putBoolean(NEW_CALL_LOG_FRAMEWORK_ENABLED_PREF_KEY, true) - .apply(); - return null; - }, - backgroundExecutor); - } else if (!callLogFrameworkShouldBeEnabled && isCallLogFrameworkEnabled) { - // Reflect the flag changes before disabling the framework. - ListenableFuture 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( - NEW_CALL_LOG_FRAGMENT_ENABLED_PREF_KEY, - newCallLogFragmentEnabledInConfigProvider) - .putBoolean( - NEW_VOICEMAIL_FRAGMENT_ENABLED_PREF_KEY, - newVoicemailFragmentEnabledInConfigProvider) - .putBoolean(NEW_PEER_ENABLED_PREF_KEY, newPeerEnabledInConfigProvider) - .apply(); - return null; - }); - } - } - - private ListenableFuture enableFramework() { - ListenableFuture registerObserversFuture = - lightweightExecutor.submit( - () -> { - callLogFramework.registerContentObservers(); - return null; - }); - ListenableFuture migratorFuture = annotatedCallLogMigrator.migrate(); - return Futures.transform( - Futures.allAsList(registerObserversFuture, migratorFuture), - unused -> null, - MoreExecutors.directExecutor()); - } - - private ListenableFuture disableFramework() { - return Futures.transform( - Futures.allAsList(callLogFramework.disable(), annotatedCallLogMigrator.clearData()), - unused -> null, - MoreExecutors.directExecutor()); - } - - public boolean isNewCallLogFragmentEnabled() { - return sharedPreferences.getBoolean(NEW_CALL_LOG_FRAGMENT_ENABLED_PREF_KEY, false); - } - - public boolean isNewVoicemailFragmentEnabled() { - return sharedPreferences.getBoolean(NEW_VOICEMAIL_FRAGMENT_ENABLED_PREF_KEY, false); - } - - public boolean isNewPeerEnabled() { - return sharedPreferences.getBoolean(NEW_PEER_ENABLED_PREF_KEY, false); - } - - /** - * Returns true if the new call log framework is enabled, meaning that content observers are - * firing and PhoneLookupHistory is being populated, etc. - */ - public boolean isCallLogFrameworkEnabled() { - return sharedPreferences.getBoolean(NEW_CALL_LOG_FRAMEWORK_ENABLED_PREF_KEY, false); - } - - static void schedulePollingJob(Context appContext) { - if (UserManagerCompat.isUserUnlocked(appContext)) { - JobScheduler jobScheduler = Assert.isNotNull(appContext.getSystemService(JobScheduler.class)); - @SuppressLint("MissingPermission") // Dialer has RECEIVE_BOOT permission - JobInfo jobInfo = - new JobInfo.Builder( - ScheduledJobIds.CALL_LOG_CONFIG_POLLING_JOB, - new ComponentName(appContext, PollingJob.class)) - .setPeriodic(TimeUnit.HOURS.toMillis(24)) - .setPersisted(true) - .setRequiresCharging(true) - .setRequiresDeviceIdle(true) - .build(); - LogUtil.i("CallLogConfig.schedulePollingJob", "scheduling"); - jobScheduler.schedule(jobInfo); - } - } - - /** - * Job which periodically force updates the {@link CallLogConfig}. This job is necessary to - * support {@link ConfigProvider ConfigProviders} which do not provide a reliable mechanism for - * listening to changes and calling {@link CallLogConfig#update()} directly, such as the {@link - * com.android.dialer.configprovider.SharedPrefConfigProvider}. - */ - public static final class PollingJob extends JobService { - - @Override - public boolean onStartJob(JobParameters params) { - LogUtil.enterBlock("PollingJob.onStartJob"); - Futures.addCallback( - CallLogComponent.get(getApplicationContext()).callLogConfig().update(), - new FutureCallback() { - @Override - public void onSuccess(Void unused) { - jobFinished(params, false /* needsReschedule */); - } - - @Override - public void onFailure(Throwable throwable) { - ThreadUtil.getUiThreadHandler() - .post( - () -> { - throw new RuntimeException(throwable); - }); - jobFinished(params, false /* needsReschedule */); - } - }, - MoreExecutors.directExecutor()); - return true; - } - - @Override - public boolean onStopJob(JobParameters params) { - return false; - } - } -} diff --git a/java/com/android/dialer/calllog/CallLogFramework.java b/java/com/android/dialer/calllog/CallLogFramework.java index 936f2bbf6..53ff43057 100644 --- a/java/com/android/dialer/calllog/CallLogFramework.java +++ b/java/com/android/dialer/calllog/CallLogFramework.java @@ -16,12 +16,9 @@ package com.android.dialer.calllog; -import android.content.Context; import com.android.dialer.calllog.datasources.CallLogDataSource; 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; @@ -38,46 +35,39 @@ import javax.inject.Singleton; @Singleton public final class CallLogFramework { - private final Context appContext; private final DataSources dataSources; + private final AnnotatedCallLogMigrator annotatedCallLogMigrator; @Inject - CallLogFramework(@ApplicationContext Context appContext, DataSources dataSources) { - this.appContext = appContext; + CallLogFramework(DataSources dataSources, AnnotatedCallLogMigrator annotatedCallLogMigrator) { this.dataSources = dataSources; - } - - /** Performs necessary setup work when the application is created. */ - public void onApplicationCreate() { - registerContentObservers(); - CallLogConfig.schedulePollingJob(appContext); + this.annotatedCallLogMigrator = annotatedCallLogMigrator; } /** Registers the content observers for all data sources. */ public void registerContentObservers() { LogUtil.enterBlock("CallLogFramework.registerContentObservers"); - - // This is the same condition used in MainImpl#isNewUiEnabled. It means that bugfood/debug - // users will have "new call log" content observers firing. These observers usually do simple - // things like writing shared preferences. - // TODO(zachh): Find a way to access Main#isNewUiEnabled without creating a circular dependency. - if (ConfigProviderBindings.get(appContext).getBoolean("is_nui_shortcut_enabled", false)) { - for (CallLogDataSource dataSource : dataSources.getDataSourcesIncludingSystemCallLog()) { - dataSource.registerContentObservers(); - } - } else { - LogUtil.i("CallLogFramework.registerContentObservers", "not registering content observers"); + for (CallLogDataSource dataSource : dataSources.getDataSourcesIncludingSystemCallLog()) { + dataSource.registerContentObservers(); } } + /** Enables the framework. */ + public ListenableFuture enable() { + registerContentObservers(); + return annotatedCallLogMigrator.migrate(); + } + /** Disables the framework. */ public ListenableFuture disable() { - LogUtil.enterBlock("CallLogFramework.disable"); + return Futures.transform( + Futures.allAsList(disableDataSources(), annotatedCallLogMigrator.clearData()), + unused -> null, + MoreExecutors.directExecutor()); + } - if (!ConfigProviderBindings.get(appContext).getBoolean("is_nui_shortcut_enabled", false)) { - LogUtil.i("CallLogFramework.disable", "not disabling"); - return Futures.immediateFuture(null); - } + private ListenableFuture disableDataSources() { + LogUtil.enterBlock("CallLogFramework.disableDataSources"); for (CallLogDataSource dataSource : dataSources.getDataSourcesIncludingSystemCallLog()) { dataSource.unregisterContentObservers(); diff --git a/java/com/android/dialer/calllog/config/AndroidManifest.xml b/java/com/android/dialer/calllog/config/AndroidManifest.xml new file mode 100644 index 000000000..7f65fac5d --- /dev/null +++ b/java/com/android/dialer/calllog/config/AndroidManifest.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + + diff --git a/java/com/android/dialer/calllog/config/CallLogConfig.java b/java/com/android/dialer/calllog/config/CallLogConfig.java new file mode 100644 index 000000000..15fd5c1c2 --- /dev/null +++ b/java/com/android/dialer/calllog/config/CallLogConfig.java @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2018 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.calllog.config; + +import com.google.common.util.concurrent.ListenableFuture; + +/** Determines if new call log components are enabled. */ +public interface CallLogConfig { + + /** + * Updates the config values. This may kick off a lot of work so should be done infrequently, for + * example by a scheduled job or broadcast receiver which rarely fires. + */ + ListenableFuture update(); + + boolean isNewCallLogFragmentEnabled(); + + boolean isNewVoicemailFragmentEnabled(); + + boolean isNewPeerEnabled(); + + boolean isCallLogFrameworkEnabled(); + + /** Schedules a job to periodically update the config. */ + void schedulePollingJob(); +} diff --git a/java/com/android/dialer/calllog/config/CallLogConfigComponent.java b/java/com/android/dialer/calllog/config/CallLogConfigComponent.java new file mode 100644 index 000000000..c325025f3 --- /dev/null +++ b/java/com/android/dialer/calllog/config/CallLogConfigComponent.java @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2018 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.calllog.config; + +import android.content.Context; +import com.android.dialer.inject.HasRootComponent; +import dagger.Subcomponent; + +/** Dagger component for the call log config. */ +@Subcomponent +public abstract class CallLogConfigComponent { + + public abstract CallLogConfig callLogConfig(); + + public static CallLogConfigComponent get(Context context) { + return ((HasComponent) ((HasRootComponent) context.getApplicationContext()).component()) + .callLogConfigComponent(); + } + + /** Used to refer to the root application component. */ + public interface HasComponent { + CallLogConfigComponent callLogConfigComponent(); + } +} diff --git a/java/com/android/dialer/calllog/config/CallLogConfigImpl.java b/java/com/android/dialer/calllog/config/CallLogConfigImpl.java new file mode 100644 index 000000000..9c7f472d5 --- /dev/null +++ b/java/com/android/dialer/calllog/config/CallLogConfigImpl.java @@ -0,0 +1,236 @@ +/* + * Copyright (C) 2018 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.calllog.config; + +import android.annotation.SuppressLint; +import android.app.job.JobInfo; +import android.app.job.JobParameters; +import android.app.job.JobScheduler; +import android.app.job.JobService; +import android.content.ComponentName; +import android.content.Context; +import android.content.SharedPreferences; +import android.support.v4.os.UserManagerCompat; +import com.android.dialer.calllog.CallLogFramework; +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.ThreadUtil; +import com.android.dialer.configprovider.ConfigProvider; +import com.android.dialer.constants.ScheduledJobIds; +import com.android.dialer.inject.ApplicationContext; +import com.android.dialer.storage.Unencrypted; +import com.google.common.util.concurrent.FutureCallback; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.ListeningExecutorService; +import com.google.common.util.concurrent.MoreExecutors; +import java.util.concurrent.TimeUnit; +import javax.inject.Inject; + +/** + * Determines if new call log components are enabled. + * + *

When the underlying flag values from the {@link ConfigProvider} changes, it is necessary to do + * work such as registering/unregistering content observers, and this class is responsible for + * coordinating that work. + * + *

New UI application components should use this class instead of reading flags directly from the + * {@link ConfigProvider}. + */ +public final class CallLogConfigImpl implements CallLogConfig { + + private static final String NEW_CALL_LOG_FRAGMENT_ENABLED_PREF_KEY = "newCallLogFragmentEnabled"; + private static final String NEW_VOICEMAIL_FRAGMENT_ENABLED_PREF_KEY = + "newVoicemailFragmentEnabled"; + private static final String NEW_PEER_ENABLED_PREF_KEY = "newPeerEnabled"; + private static final String NEW_CALL_LOG_FRAMEWORK_ENABLED_PREF_KEY = + "newCallLogFrameworkEnabled"; + + private final Context appContext; + private final CallLogFramework callLogFramework; + private final SharedPreferences sharedPreferences; + private final ConfigProvider configProvider; + private final ListeningExecutorService backgroundExecutor; + + @Inject + public CallLogConfigImpl( + @ApplicationContext Context appContext, + CallLogFramework callLogFramework, + @Unencrypted SharedPreferences sharedPreferences, + ConfigProvider configProvider, + @BackgroundExecutor ListeningExecutorService backgroundExecutor) { + this.appContext = appContext; + this.callLogFramework = callLogFramework; + this.sharedPreferences = sharedPreferences; + this.configProvider = configProvider; + this.backgroundExecutor = backgroundExecutor; + } + + @Override + public ListenableFuture update() { + 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; + + if (callLogFrameworkShouldBeEnabled && !isCallLogFrameworkEnabled) { + return Futures.transform( + callLogFramework.enable(), + unused -> { + // Reflect the flag changes only after the framework is enabled. + sharedPreferences + .edit() + .putBoolean( + NEW_CALL_LOG_FRAGMENT_ENABLED_PREF_KEY, + newCallLogFragmentEnabledInConfigProvider) + .putBoolean( + NEW_VOICEMAIL_FRAGMENT_ENABLED_PREF_KEY, + newVoicemailFragmentEnabledInConfigProvider) + .putBoolean(NEW_PEER_ENABLED_PREF_KEY, newPeerEnabledInConfigProvider) + .putBoolean(NEW_CALL_LOG_FRAMEWORK_ENABLED_PREF_KEY, true) + .apply(); + return null; + }, + backgroundExecutor); + } else if (!callLogFrameworkShouldBeEnabled && isCallLogFrameworkEnabled) { + // Reflect the flag changes before disabling the framework. + ListenableFuture 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 -> callLogFramework.disable(), + 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( + NEW_CALL_LOG_FRAGMENT_ENABLED_PREF_KEY, + newCallLogFragmentEnabledInConfigProvider) + .putBoolean( + NEW_VOICEMAIL_FRAGMENT_ENABLED_PREF_KEY, + newVoicemailFragmentEnabledInConfigProvider) + .putBoolean(NEW_PEER_ENABLED_PREF_KEY, newPeerEnabledInConfigProvider) + .apply(); + return null; + }); + } + } + + @Override + public boolean isNewCallLogFragmentEnabled() { + return sharedPreferences.getBoolean(NEW_CALL_LOG_FRAGMENT_ENABLED_PREF_KEY, false); + } + + @Override + public boolean isNewVoicemailFragmentEnabled() { + return sharedPreferences.getBoolean(NEW_VOICEMAIL_FRAGMENT_ENABLED_PREF_KEY, false); + } + + @Override + public boolean isNewPeerEnabled() { + return sharedPreferences.getBoolean(NEW_PEER_ENABLED_PREF_KEY, false); + } + + /** + * Returns true if the new call log framework is enabled, meaning that content observers are + * firing and PhoneLookupHistory is being populated, etc. + */ + @Override + public boolean isCallLogFrameworkEnabled() { + return sharedPreferences.getBoolean(NEW_CALL_LOG_FRAMEWORK_ENABLED_PREF_KEY, false); + } + + @Override + public void schedulePollingJob() { + if (UserManagerCompat.isUserUnlocked(appContext)) { + JobScheduler jobScheduler = Assert.isNotNull(appContext.getSystemService(JobScheduler.class)); + @SuppressLint("MissingPermission") // Dialer has RECEIVE_BOOT permission + JobInfo jobInfo = + new JobInfo.Builder( + ScheduledJobIds.CALL_LOG_CONFIG_POLLING_JOB, + new ComponentName(appContext, PollingJob.class)) + .setPeriodic(TimeUnit.HOURS.toMillis(24)) + .setPersisted(true) + .setRequiresCharging(true) + .setRequiresDeviceIdle(true) + .build(); + LogUtil.i("CallLogConfigImpl.schedulePollingJob", "scheduling"); + jobScheduler.schedule(jobInfo); + } + } + + /** + * Job which periodically force updates the {@link CallLogConfig}. This job is necessary to + * support {@link ConfigProvider ConfigProviders} which do not provide a reliable mechanism for + * listening to changes and calling {@link CallLogConfig#update()} directly, such as the {@link + * com.android.dialer.configprovider.SharedPrefConfigProvider}. + */ + public static final class PollingJob extends JobService { + + @Override + public boolean onStartJob(JobParameters params) { + LogUtil.enterBlock("PollingJob.onStartJob"); + Futures.addCallback( + CallLogConfigComponent.get(getApplicationContext()).callLogConfig().update(), + new FutureCallback() { + @Override + public void onSuccess(Void unused) { + jobFinished(params, false /* needsReschedule */); + } + + @Override + public void onFailure(Throwable throwable) { + ThreadUtil.getUiThreadHandler() + .post( + () -> { + throw new RuntimeException(throwable); + }); + jobFinished(params, false /* needsReschedule */); + } + }, + MoreExecutors.directExecutor()); + return true; + } + + @Override + public boolean onStopJob(JobParameters params) { + return false; + } + } +} diff --git a/java/com/android/dialer/calllog/config/CallLogConfigModule.java b/java/com/android/dialer/calllog/config/CallLogConfigModule.java new file mode 100644 index 000000000..d982e2bd8 --- /dev/null +++ b/java/com/android/dialer/calllog/config/CallLogConfigModule.java @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2018 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.calllog.config; + +import dagger.Binds; +import dagger.Module; + +/** Binds {@link CallLogConfigImpl}. */ +@Module +public abstract class CallLogConfigModule { + @Binds + abstract CallLogConfig to(CallLogConfigImpl impl); +} -- cgit v1.2.3