diff --git a/app/src/androidTest/java/com/duckduckgo/app/di/TestAppComponent.kt b/app/src/androidTest/java/com/duckduckgo/app/di/TestAppComponent.kt index 394869ebf6f7..15a0384da2e4 100644 --- a/app/src/androidTest/java/com/duckduckgo/app/di/TestAppComponent.kt +++ b/app/src/androidTest/java/com/duckduckgo/app/di/TestAppComponent.kt @@ -23,6 +23,7 @@ import com.duckduckgo.app.browser.di.BrowserModule import com.duckduckgo.app.browser.favicon.FaviconModule import com.duckduckgo.app.browser.rating.di.RatingModule import com.duckduckgo.app.global.exception.UncaughtExceptionModule +import com.duckduckgo.app.global.plugins.worker.WorkerPluginsModule import com.duckduckgo.app.httpsupgrade.di.HttpsUpgraderModule import com.duckduckgo.app.onboarding.di.OnboardingModule import com.duckduckgo.app.onboarding.di.WelcomePageModule @@ -48,6 +49,7 @@ import javax.inject.Singleton StubStatisticsModule::class, /* real modules */ + WorkerPluginsModule::class, ApplicationModule::class, WorkerModule::class, AndroidBindingModule::class, diff --git a/app/src/main/java/com/duckduckgo/app/di/AppComponent.kt b/app/src/main/java/com/duckduckgo/app/di/AppComponent.kt index 98bcbee97a59..a57cc2d36df0 100644 --- a/app/src/main/java/com/duckduckgo/app/di/AppComponent.kt +++ b/app/src/main/java/com/duckduckgo/app/di/AppComponent.kt @@ -24,6 +24,7 @@ import com.duckduckgo.app.browser.favicon.FaviconModule import com.duckduckgo.app.browser.rating.di.RatingModule import com.duckduckgo.app.global.DuckDuckGoApplication import com.duckduckgo.app.global.exception.UncaughtExceptionModule +import com.duckduckgo.app.global.plugins.worker.WorkerPluginsModule import com.duckduckgo.app.httpsupgrade.di.HttpsUpgraderModule import com.duckduckgo.app.onboarding.di.OnboardingModule import com.duckduckgo.app.onboarding.di.WelcomePageModule @@ -41,6 +42,7 @@ import javax.inject.Singleton @Singleton @Component( modules = [ + WorkerPluginsModule::class, ApplicationModule::class, JobsModule::class, WorkerModule::class, diff --git a/app/src/main/java/com/duckduckgo/app/di/DaggerWorkerFactory.kt b/app/src/main/java/com/duckduckgo/app/di/DaggerWorkerFactory.kt index dc0c3be3c69b..16abcf62c3d2 100644 --- a/app/src/main/java/com/duckduckgo/app/di/DaggerWorkerFactory.kt +++ b/app/src/main/java/com/duckduckgo/app/di/DaggerWorkerFactory.kt @@ -17,37 +17,14 @@ package com.duckduckgo.app.di import android.content.Context -import androidx.core.app.NotificationManagerCompat import androidx.work.ListenableWorker import androidx.work.WorkerFactory import androidx.work.WorkerParameters -import com.duckduckgo.app.fire.DataClearingWorker -import com.duckduckgo.app.global.job.AppConfigurationWorker -import com.duckduckgo.app.global.view.ClearDataAction -import com.duckduckgo.app.job.ConfigurationDownloader -import com.duckduckgo.app.notification.NotificationFactory -import com.duckduckgo.app.notification.NotificationScheduler.ClearDataNotificationWorker -import com.duckduckgo.app.notification.NotificationScheduler.PrivacyNotificationWorker -import com.duckduckgo.app.notification.db.NotificationDao -import com.duckduckgo.app.notification.model.ClearDataNotification -import com.duckduckgo.app.notification.model.PrivacyProtectionNotification -import com.duckduckgo.app.settings.db.SettingsDataStore -import com.duckduckgo.app.statistics.api.OfflinePixelScheduler -import com.duckduckgo.app.statistics.api.OfflinePixelSender -import com.duckduckgo.app.statistics.pixels.Pixel +import com.duckduckgo.app.global.plugins.worker.WorkerInjectorPluginPoint import timber.log.Timber class DaggerWorkerFactory( - private val offlinePixelSender: OfflinePixelSender, - private val settingsDataStore: SettingsDataStore, - private val clearDataAction: ClearDataAction, - private val notificationManager: NotificationManagerCompat, - private val notificationDao: NotificationDao, - private val notificationFactory: NotificationFactory, - private val clearDataNotification: ClearDataNotification, - private val privacyProtectionNotification: PrivacyProtectionNotification, - private val configurationDownloader: ConfigurationDownloader, - private val pixel: Pixel + private val workerInjectorPluginPoint: WorkerInjectorPluginPoint, ) : WorkerFactory() { override fun createWorker(appContext: Context, workerClassName: String, workerParameters: WorkerParameters): ListenableWorker? { @@ -57,13 +34,12 @@ class DaggerWorkerFactory( val constructor = workerClass.getDeclaredConstructor(Context::class.java, WorkerParameters::class.java) val instance = constructor.newInstance(appContext, workerParameters) - when (instance) { - is OfflinePixelScheduler.OfflinePixelWorker -> injectOfflinePixelWorker(instance) - is DataClearingWorker -> injectDataClearWorker(instance) - is ClearDataNotificationWorker -> injectClearDataNotificationWorker(instance) - is PrivacyNotificationWorker -> injectPrivacyNotificationWorker(instance) - is AppConfigurationWorker -> injectAppConfigurationWorker(instance) - else -> Timber.i("No injection required for worker $workerClassName") + workerInjectorPluginPoint.getPlugins().forEach { plugin -> + if (plugin.inject(instance)) { + Timber.i("Injected using plugin $workerClassName") + return@forEach + } + Timber.i("No injection required for worker $workerClassName") } return instance @@ -73,33 +49,4 @@ class DaggerWorkerFactory( } } - - private fun injectAppConfigurationWorker(worker: AppConfigurationWorker) { - worker.appConfigurationDownloader = configurationDownloader - } - - private fun injectOfflinePixelWorker(worker: OfflinePixelScheduler.OfflinePixelWorker) { - worker.offlinePixelSender = offlinePixelSender - } - - private fun injectDataClearWorker(worker: DataClearingWorker) { - worker.settingsDataStore = settingsDataStore - worker.clearDataAction = clearDataAction - } - - private fun injectClearDataNotificationWorker(worker: ClearDataNotificationWorker) { - worker.manager = notificationManager - worker.notificationDao = notificationDao - worker.factory = notificationFactory - worker.pixel = pixel - worker.notification = clearDataNotification - } - - private fun injectPrivacyNotificationWorker(worker: PrivacyNotificationWorker) { - worker.manager = notificationManager - worker.notificationDao = notificationDao - worker.factory = notificationFactory - worker.pixel = pixel - worker.notification = privacyProtectionNotification - } } diff --git a/app/src/main/java/com/duckduckgo/app/di/WorkerModule.kt b/app/src/main/java/com/duckduckgo/app/di/WorkerModule.kt index e74bdb113335..e66931ac75a4 100644 --- a/app/src/main/java/com/duckduckgo/app/di/WorkerModule.kt +++ b/app/src/main/java/com/duckduckgo/app/di/WorkerModule.kt @@ -17,19 +17,10 @@ package com.duckduckgo.app.di import android.content.Context -import androidx.core.app.NotificationManagerCompat import androidx.work.Configuration import androidx.work.WorkManager import androidx.work.WorkerFactory -import com.duckduckgo.app.global.view.ClearDataAction -import com.duckduckgo.app.job.ConfigurationDownloader -import com.duckduckgo.app.notification.NotificationFactory -import com.duckduckgo.app.notification.db.NotificationDao -import com.duckduckgo.app.notification.model.ClearDataNotification -import com.duckduckgo.app.notification.model.PrivacyProtectionNotification -import com.duckduckgo.app.settings.db.SettingsDataStore -import com.duckduckgo.app.statistics.api.OfflinePixelSender -import com.duckduckgo.app.statistics.pixels.Pixel +import com.duckduckgo.app.global.plugins.worker.WorkerInjectorPluginPoint import dagger.Module import dagger.Provides import javax.inject.Singleton @@ -50,28 +41,8 @@ class WorkerModule { @Provides @Singleton fun workerFactory( - offlinePixelSender: OfflinePixelSender, - settingsDataStore: SettingsDataStore, - clearDataAction: ClearDataAction, - notificationManager: NotificationManagerCompat, - notificationDao: NotificationDao, - notificationFactory: NotificationFactory, - clearDataNotification: ClearDataNotification, - privacyProtectionNotification: PrivacyProtectionNotification, - configurationDownloader: ConfigurationDownloader, - pixel: Pixel + workerInjectorPluginPoint: WorkerInjectorPluginPoint, ): WorkerFactory { - return DaggerWorkerFactory( - offlinePixelSender, - settingsDataStore, - clearDataAction, - notificationManager, - notificationDao, - notificationFactory, - clearDataNotification, - privacyProtectionNotification, - configurationDownloader, - pixel - ) + return DaggerWorkerFactory(workerInjectorPluginPoint) } } diff --git a/app/src/main/java/com/duckduckgo/app/fire/DataClearingWorker.kt b/app/src/main/java/com/duckduckgo/app/fire/DataClearingWorker.kt index 101e8bf3acee..e3b3758a167d 100644 --- a/app/src/main/java/com/duckduckgo/app/fire/DataClearingWorker.kt +++ b/app/src/main/java/com/duckduckgo/app/fire/DataClearingWorker.kt @@ -19,8 +19,10 @@ package com.duckduckgo.app.fire import android.content.Context import androidx.annotation.WorkerThread import androidx.work.CoroutineWorker +import androidx.work.ListenableWorker import androidx.work.ListenableWorker.Result.success import androidx.work.WorkerParameters +import com.duckduckgo.app.global.plugins.worker.WorkerInjectorPlugin import com.duckduckgo.app.global.view.ClearDataAction import com.duckduckgo.app.settings.clear.ClearWhatOption import com.duckduckgo.app.settings.db.SettingsDataStore @@ -87,3 +89,18 @@ class DataClearingWorker(context: Context, workerParams: WorkerParameters) : Cor const val WORK_REQUEST_TAG = "background-clear-data" } } + +class DataClearingWorkerInjectorPlugin( + private val settingsDataStore: SettingsDataStore, + private val clearDataAction: ClearDataAction +) : WorkerInjectorPlugin { + + override fun inject(worker: ListenableWorker): Boolean { + if (worker is DataClearingWorker) { + worker.settingsDataStore = settingsDataStore + worker.clearDataAction = clearDataAction + return true + } + return false + } +} diff --git a/app/src/main/java/com/duckduckgo/app/global/job/AppConfigurationSyncWorkRequestBuilder.kt b/app/src/main/java/com/duckduckgo/app/global/job/AppConfigurationSyncWorkRequestBuilder.kt index f393707baf61..695239990306 100644 --- a/app/src/main/java/com/duckduckgo/app/global/job/AppConfigurationSyncWorkRequestBuilder.kt +++ b/app/src/main/java/com/duckduckgo/app/global/job/AppConfigurationSyncWorkRequestBuilder.kt @@ -18,6 +18,7 @@ package com.duckduckgo.app.global.job import android.content.Context import androidx.work.* +import com.duckduckgo.app.global.plugins.worker.WorkerInjectorPlugin import com.duckduckgo.app.job.ConfigurationDownloader import io.reactivex.Single import timber.log.Timber @@ -59,3 +60,16 @@ class AppConfigurationWorker(context: Context, workerParams: WorkerParameters) : } } } + +class AppConfigurationWorkerInjectorPlugin( + private val configurationDownloader: ConfigurationDownloader +) : WorkerInjectorPlugin { + + override fun inject(worker: ListenableWorker): Boolean { + if (worker is AppConfigurationWorker) { + worker.appConfigurationDownloader = configurationDownloader + return true + } + return false + } +} diff --git a/app/src/main/java/com/duckduckgo/app/global/plugins/PluginPoint.kt b/app/src/main/java/com/duckduckgo/app/global/plugins/PluginPoint.kt new file mode 100644 index 000000000000..d3b0fc3d1e13 --- /dev/null +++ b/app/src/main/java/com/duckduckgo/app/global/plugins/PluginPoint.kt @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2018 DuckDuckGo + * + * 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.duckduckgo.app.global.plugins + +/** + * A PluginPoint provides a list of plugins of a particular type T + */ +interface PluginPoint { + /** + * @return the list of plugins of type + */ + fun getPlugins(): List +} diff --git a/app/src/main/java/com/duckduckgo/app/global/plugins/worker/WorkerInjectorPlugin.kt b/app/src/main/java/com/duckduckgo/app/global/plugins/worker/WorkerInjectorPlugin.kt new file mode 100644 index 000000000000..d2a860c0c186 --- /dev/null +++ b/app/src/main/java/com/duckduckgo/app/global/plugins/worker/WorkerInjectorPlugin.kt @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2020 DuckDuckGo + * + * 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.duckduckgo.app.global.plugins.worker + +import androidx.work.ListenableWorker +import com.duckduckgo.app.global.plugins.PluginPoint +import javax.inject.Inject +import javax.inject.Singleton + +interface WorkerInjectorPlugin { + /** + * @return whether the worker has been injected + */ + fun inject(worker: ListenableWorker): Boolean +} + +@Singleton +class WorkerInjectorPluginPoint @Inject constructor( + private val injectorPlugins: Set<@JvmSuppressWildcards WorkerInjectorPlugin> +) : PluginPoint { + override fun getPlugins(): List { + return injectorPlugins.toList() + } +} diff --git a/app/src/main/java/com/duckduckgo/app/global/plugins/worker/WorkerPluginsModule.kt b/app/src/main/java/com/duckduckgo/app/global/plugins/worker/WorkerPluginsModule.kt new file mode 100644 index 000000000000..437b404c3857 --- /dev/null +++ b/app/src/main/java/com/duckduckgo/app/global/plugins/worker/WorkerPluginsModule.kt @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2020 DuckDuckGo + * + * 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.duckduckgo.app.global.plugins.worker + +import androidx.core.app.NotificationManagerCompat +import com.duckduckgo.app.fire.DataClearingWorkerInjectorPlugin +import com.duckduckgo.app.global.job.AppConfigurationWorkerInjectorPlugin +import com.duckduckgo.app.global.view.ClearDataAction +import com.duckduckgo.app.job.ConfigurationDownloader +import com.duckduckgo.app.notification.ClearDataNotificationWorkerInjectorPlugin +import com.duckduckgo.app.notification.NotificationFactory +import com.duckduckgo.app.notification.PrivacyNotificationWorkerInjectorPlugin +import com.duckduckgo.app.notification.db.NotificationDao +import com.duckduckgo.app.notification.model.ClearDataNotification +import com.duckduckgo.app.notification.model.PrivacyProtectionNotification +import com.duckduckgo.app.settings.db.SettingsDataStore +import com.duckduckgo.app.statistics.api.OfflinePixelSender +import com.duckduckgo.app.statistics.api.OfflinePixelWorkerInjectorPlugin +import com.duckduckgo.app.statistics.pixels.Pixel +import dagger.Module +import dagger.Provides +import dagger.multibindings.IntoSet + +@Module +class WorkerPluginsModule { + + @Provides + @IntoSet + fun dataClearingWorkerInjectorPlugin( + settingsDataStore: SettingsDataStore, + clearDataAction: ClearDataAction + ): WorkerInjectorPlugin = DataClearingWorkerInjectorPlugin(settingsDataStore, clearDataAction) + + @Provides + @IntoSet + fun clearDataNotificationWorkerInjectorPlugin( + notificationManagerCompat: NotificationManagerCompat, + notificationDao: NotificationDao, + notificationFactory: NotificationFactory, + pixel: Pixel, + clearDataNotification: ClearDataNotification + ): WorkerInjectorPlugin = ClearDataNotificationWorkerInjectorPlugin( + notificationManagerCompat, + notificationDao, + notificationFactory, + pixel, + clearDataNotification + ) + + @Provides + @IntoSet + fun privacyNotificationWorkerInjectorPlugin( + notificationManagerCompat: NotificationManagerCompat, + notificationDao: NotificationDao, + notificationFactory: NotificationFactory, + pixel: Pixel, + privacyProtectionNotification: PrivacyProtectionNotification + ): WorkerInjectorPlugin = PrivacyNotificationWorkerInjectorPlugin( + notificationManagerCompat, + notificationDao, + notificationFactory, + pixel, + privacyProtectionNotification + ) + + @Provides + @IntoSet + fun appConfigurationWorkerInjectorPlugin( + configurationDownloader: ConfigurationDownloader + ): WorkerInjectorPlugin = AppConfigurationWorkerInjectorPlugin(configurationDownloader) + + @Provides + @IntoSet + fun offlinePixelWorkerInjectorPlugin( + offlinePixelSender: OfflinePixelSender + ): WorkerInjectorPlugin = OfflinePixelWorkerInjectorPlugin(offlinePixelSender) +} diff --git a/app/src/main/java/com/duckduckgo/app/notification/AndroidNotificationScheduler.kt b/app/src/main/java/com/duckduckgo/app/notification/AndroidNotificationScheduler.kt index 2cbc0651f6b1..c386db5f4c6d 100644 --- a/app/src/main/java/com/duckduckgo/app/notification/AndroidNotificationScheduler.kt +++ b/app/src/main/java/com/duckduckgo/app/notification/AndroidNotificationScheduler.kt @@ -20,8 +20,11 @@ import android.content.Context import androidx.annotation.WorkerThread import androidx.core.app.NotificationManagerCompat import androidx.work.* +import com.duckduckgo.app.global.plugins.worker.WorkerInjectorPlugin import com.duckduckgo.app.notification.db.NotificationDao +import com.duckduckgo.app.notification.model.ClearDataNotification import com.duckduckgo.app.notification.model.Notification +import com.duckduckgo.app.notification.model.PrivacyProtectionNotification import com.duckduckgo.app.notification.model.SchedulableNotification import com.duckduckgo.app.statistics.pixels.Pixel import com.duckduckgo.app.statistics.pixels.Pixel.PixelName.NOTIFICATION_SHOWN @@ -107,3 +110,45 @@ class NotificationScheduler( const val UNUSED_APP_WORK_REQUEST_TAG = "com.duckduckgo.notification.schedule" } } + +class ClearDataNotificationWorkerInjectorPlugin( + private val notificationManagerCompat: NotificationManagerCompat, + private val notificationDao: NotificationDao, + private val notificationFactory: NotificationFactory, + private val pixel: Pixel, + private val clearDataNotification: ClearDataNotification +) : WorkerInjectorPlugin { + + override fun inject(worker: ListenableWorker): Boolean { + if (worker is NotificationScheduler.ClearDataNotificationWorker) { + worker.manager = notificationManagerCompat + worker.notificationDao = notificationDao + worker.factory = notificationFactory + worker.pixel = pixel + worker.notification = clearDataNotification + return true + } + return false + } +} + +class PrivacyNotificationWorkerInjectorPlugin( + private val notificationManagerCompat: NotificationManagerCompat, + private val notificationDao: NotificationDao, + private val notificationFactory: NotificationFactory, + private val pixel: Pixel, + private val privacyProtectionNotification: PrivacyProtectionNotification +) : WorkerInjectorPlugin { + + override fun inject(worker: ListenableWorker): Boolean { + if (worker is NotificationScheduler.PrivacyNotificationWorker) { + worker.manager = notificationManagerCompat + worker.notificationDao = notificationDao + worker.factory = notificationFactory + worker.pixel = pixel + worker.notification = privacyProtectionNotification + return true + } + return false + } +} diff --git a/app/src/main/java/com/duckduckgo/app/statistics/api/OfflinePixelScheduler.kt b/app/src/main/java/com/duckduckgo/app/statistics/api/OfflinePixelScheduler.kt index 44e475672c2b..9c6494fdb516 100644 --- a/app/src/main/java/com/duckduckgo/app/statistics/api/OfflinePixelScheduler.kt +++ b/app/src/main/java/com/duckduckgo/app/statistics/api/OfflinePixelScheduler.kt @@ -18,6 +18,7 @@ package com.duckduckgo.app.statistics.api import android.content.Context import androidx.work.* +import com.duckduckgo.app.global.plugins.worker.WorkerInjectorPlugin import timber.log.Timber import java.util.concurrent.TimeUnit import javax.inject.Inject @@ -66,3 +67,16 @@ class OfflinePixelScheduler @Inject constructor(private val workManager: WorkMan private val BACKOFF_TIME_UNIT = TimeUnit.MINUTES } } + +class OfflinePixelWorkerInjectorPlugin( + private val offlinePixelSender: OfflinePixelSender +) : WorkerInjectorPlugin { + + override fun inject(worker: ListenableWorker): Boolean { + if (worker is OfflinePixelScheduler.OfflinePixelWorker) { + worker.offlinePixelSender = offlinePixelSender + return true + } + return false + } +}