From be1e37999402fa1753ac58c765272f1d4ba5c48e Mon Sep 17 00:00:00 2001 From: Aitor Viana Date: Fri, 4 Apr 2025 13:05:35 +0100 Subject: [PATCH 1/9] Remove AutoconsentExceptionsStore --- .../annotations/ContributesRemoteFeature.kt | 1 + .../autoconsent/impl/di/AutoconsentModule.kt | 13 ---- .../AutoconsentExceptionsRepository.kt | 53 ++++++++------ .../AutoconsentExceptionsStore.kt | 34 --------- .../impl/remoteconfig/AutoconsentFeature.kt | 1 - .../autoconsent/impl/store/AutoconsentDao.kt | 23 ------ .../impl/store/AutoconsentDatabase.kt | 3 +- .../impl/store/AutoconsentDatabaseModels.kt | 11 --- ...RealAutoconsentExceptionsRepositoryTest.kt | 72 +++++++------------ 9 files changed, 62 insertions(+), 149 deletions(-) delete mode 100644 autoconsent/autoconsent-impl/src/main/java/com/duckduckgo/autoconsent/impl/remoteconfig/AutoconsentExceptionsStore.kt diff --git a/anvil/anvil-annotations/src/main/java/com/duckduckgo/anvil/annotations/ContributesRemoteFeature.kt b/anvil/anvil-annotations/src/main/java/com/duckduckgo/anvil/annotations/ContributesRemoteFeature.kt index bd5205973c98..9c9cbe375594 100644 --- a/anvil/anvil-annotations/src/main/java/com/duckduckgo/anvil/annotations/ContributesRemoteFeature.kt +++ b/anvil/anvil-annotations/src/main/java/com/duckduckgo/anvil/annotations/ContributesRemoteFeature.kt @@ -59,6 +59,7 @@ annotation class ContributesRemoteFeature( val settingsStore: KClass<*> = Unit::class, /** The class that implements the [FeatureExceptions.Store] interface */ + @Deprecated("Not needed anymore. Exceptions is now supported in top-level and sub-features and Toggle#getExceptions returns it") val exceptionsStore: KClass<*> = Unit::class, /** The class that implements the [Toggle.Store] interface */ diff --git a/autoconsent/autoconsent-impl/src/main/java/com/duckduckgo/autoconsent/impl/di/AutoconsentModule.kt b/autoconsent/autoconsent-impl/src/main/java/com/duckduckgo/autoconsent/impl/di/AutoconsentModule.kt index d51c56ad376a..7b7dbde21ed2 100644 --- a/autoconsent/autoconsent-impl/src/main/java/com/duckduckgo/autoconsent/impl/di/AutoconsentModule.kt +++ b/autoconsent/autoconsent-impl/src/main/java/com/duckduckgo/autoconsent/impl/di/AutoconsentModule.kt @@ -20,10 +20,8 @@ import android.content.Context import androidx.room.Room import com.duckduckgo.app.di.AppCoroutineScope import com.duckduckgo.app.di.IsMainProcess -import com.duckduckgo.autoconsent.impl.remoteconfig.AutoconsentExceptionsRepository import com.duckduckgo.autoconsent.impl.remoteconfig.AutoconsentFeature import com.duckduckgo.autoconsent.impl.remoteconfig.AutoconsentFeatureSettingsRepository -import com.duckduckgo.autoconsent.impl.remoteconfig.RealAutoconsentExceptionsRepository import com.duckduckgo.autoconsent.impl.remoteconfig.RealAutoconsentFeatureSettingsRepository import com.duckduckgo.autoconsent.impl.store.AutoconsentDatabase import com.duckduckgo.autoconsent.impl.store.AutoconsentSettingsRepository @@ -58,17 +56,6 @@ object AutoconsentModule { .build() } - @SingleInstanceIn(AppScope::class) - @Provides - fun provideAutoconsentExceptionsRepository( - database: AutoconsentDatabase, - @AppCoroutineScope appCoroutineScope: CoroutineScope, - dispatcherProvider: DispatcherProvider, - @IsMainProcess isMainProcess: Boolean, - ): AutoconsentExceptionsRepository { - return RealAutoconsentExceptionsRepository(appCoroutineScope, dispatcherProvider, database, isMainProcess) - } - @SingleInstanceIn(AppScope::class) @Provides fun provideAutoconsentFeatureSettingsRepository( diff --git a/autoconsent/autoconsent-impl/src/main/java/com/duckduckgo/autoconsent/impl/remoteconfig/AutoconsentExceptionsRepository.kt b/autoconsent/autoconsent-impl/src/main/java/com/duckduckgo/autoconsent/impl/remoteconfig/AutoconsentExceptionsRepository.kt index 414db2c3bed6..de270bd81152 100644 --- a/autoconsent/autoconsent-impl/src/main/java/com/duckduckgo/autoconsent/impl/remoteconfig/AutoconsentExceptionsRepository.kt +++ b/autoconsent/autoconsent-impl/src/main/java/com/duckduckgo/autoconsent/impl/remoteconfig/AutoconsentExceptionsRepository.kt @@ -16,45 +16,58 @@ package com.duckduckgo.autoconsent.impl.remoteconfig -import com.duckduckgo.autoconsent.impl.store.AutoconsentDatabase -import com.duckduckgo.autoconsent.impl.store.AutoconsentExceptionEntity +import com.duckduckgo.app.di.AppCoroutineScope +import com.duckduckgo.app.di.IsMainProcess import com.duckduckgo.common.utils.DispatcherProvider +import com.duckduckgo.di.scopes.AppScope import com.duckduckgo.feature.toggles.api.FeatureExceptions.FeatureException +import com.duckduckgo.privacy.config.api.PrivacyConfigCallbackPlugin +import com.squareup.anvil.annotations.ContributesBinding +import com.squareup.anvil.annotations.ContributesMultibinding +import dagger.SingleInstanceIn import java.util.concurrent.CopyOnWriteArrayList +import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch interface AutoconsentExceptionsRepository { - fun insertAllExceptions(exceptions: List) val exceptions: CopyOnWriteArrayList } -class RealAutoconsentExceptionsRepository( - coroutineScope: CoroutineScope, - dispatcherProvider: DispatcherProvider, - val database: AutoconsentDatabase, - isMainProcess: Boolean, -) : AutoconsentExceptionsRepository { +@SingleInstanceIn(AppScope::class) +@ContributesBinding( + scope = AppScope::class, + boundType = AutoconsentExceptionsRepository::class, +) +@ContributesMultibinding( + scope = AppScope::class, + boundType = PrivacyConfigCallbackPlugin::class, +) +class RealAutoconsentExceptionsRepository @Inject constructor( + @AppCoroutineScope private val coroutineScope: CoroutineScope, + private val dispatcherProvider: DispatcherProvider, + private val autoconsentFeature: AutoconsentFeature, + @IsMainProcess private val isMainProcess: Boolean, +) : AutoconsentExceptionsRepository, PrivacyConfigCallbackPlugin { - private val dao = database.autoconsentDao() override val exceptions = CopyOnWriteArrayList() init { coroutineScope.launch(dispatcherProvider.io()) { - if (isMainProcess) { - loadToMemory() - } + loadToMemory() } } - override fun insertAllExceptions(exceptions: List) { - dao.updateAllExceptions(exceptions.map { AutoconsentExceptionEntity(domain = it.domain, reason = it.reason ?: "") }) - loadToMemory() + private fun loadToMemory() { + if (isMainProcess) { + exceptions.clear() + exceptions.addAll(autoconsentFeature.self().getExceptions()) + } } - private fun loadToMemory() { - exceptions.clear() - val exceptionsEntityList = dao.getExceptions() - exceptions.addAll(exceptionsEntityList.map { FeatureException(domain = it.domain, reason = it.reason) }) + override fun onPrivacyConfigDownloaded() { + coroutineScope.launch(dispatcherProvider.io()) { + loadToMemory() + } } } diff --git a/autoconsent/autoconsent-impl/src/main/java/com/duckduckgo/autoconsent/impl/remoteconfig/AutoconsentExceptionsStore.kt b/autoconsent/autoconsent-impl/src/main/java/com/duckduckgo/autoconsent/impl/remoteconfig/AutoconsentExceptionsStore.kt deleted file mode 100644 index 818c25d44048..000000000000 --- a/autoconsent/autoconsent-impl/src/main/java/com/duckduckgo/autoconsent/impl/remoteconfig/AutoconsentExceptionsStore.kt +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (c) 2024 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.autoconsent.impl.remoteconfig - -import com.duckduckgo.di.scopes.AppScope -import com.duckduckgo.feature.toggles.api.FeatureExceptions -import com.duckduckgo.feature.toggles.api.FeatureExceptions.FeatureException -import com.duckduckgo.feature.toggles.api.RemoteFeatureStoreNamed -import com.squareup.anvil.annotations.ContributesBinding -import javax.inject.Inject - -@ContributesBinding(AppScope::class) -@RemoteFeatureStoreNamed(AutoconsentFeature::class) -class AutoconsentExceptionsStore @Inject constructor( - val autoconsentExceptionsRepository: AutoconsentExceptionsRepository, -) : FeatureExceptions.Store { - override fun insertAll(exception: List) { - autoconsentExceptionsRepository.insertAllExceptions(exception) - } -} diff --git a/autoconsent/autoconsent-impl/src/main/java/com/duckduckgo/autoconsent/impl/remoteconfig/AutoconsentFeature.kt b/autoconsent/autoconsent-impl/src/main/java/com/duckduckgo/autoconsent/impl/remoteconfig/AutoconsentFeature.kt index e673a66bc2b2..329acc87a723 100644 --- a/autoconsent/autoconsent-impl/src/main/java/com/duckduckgo/autoconsent/impl/remoteconfig/AutoconsentFeature.kt +++ b/autoconsent/autoconsent-impl/src/main/java/com/duckduckgo/autoconsent/impl/remoteconfig/AutoconsentFeature.kt @@ -25,7 +25,6 @@ import com.duckduckgo.feature.toggles.api.Toggle.DefaultFeatureValue scope = AppScope::class, featureName = "autoconsent", settingsStore = AutoconsentFeatureSettingsStore::class, - exceptionsStore = AutoconsentExceptionsStore::class, ) /** * This is the class that represents the autoconsent feature flags diff --git a/autoconsent/autoconsent-impl/src/main/java/com/duckduckgo/autoconsent/impl/store/AutoconsentDao.kt b/autoconsent/autoconsent-impl/src/main/java/com/duckduckgo/autoconsent/impl/store/AutoconsentDao.kt index a02f49ae9187..6359bc80f41d 100644 --- a/autoconsent/autoconsent-impl/src/main/java/com/duckduckgo/autoconsent/impl/store/AutoconsentDao.kt +++ b/autoconsent/autoconsent-impl/src/main/java/com/duckduckgo/autoconsent/impl/store/AutoconsentDao.kt @@ -28,38 +28,15 @@ abstract class AutoconsentDao { @Insert(onConflict = OnConflictStrategy.REPLACE) abstract fun insertDisabledCmps(disabledCmps: List) - @Insert(onConflict = OnConflictStrategy.REPLACE) - abstract fun insertExceptions(exceptions: List) - - @Transaction - open fun updateAll(exceptions: List, disabledCmps: List) { - deleteDisabledCmps() - deleteExceptions() - insertDisabledCmps(disabledCmps) - insertExceptions(exceptions) - } - @Transaction open fun updateAllDisabledCMPs(disabledCMPs: List) { deleteDisabledCmps() insertDisabledCmps(disabledCMPs) } - @Transaction - open fun updateAllExceptions(exceptions: List) { - deleteExceptions() - insertExceptions(exceptions) - } - - @Query("select * from autoconsent_exceptions") - abstract fun getExceptions(): List - @Query("select * from autoconsent_disabled_cmps") abstract fun getDisabledCmps(): List @Query("delete from autoconsent_disabled_cmps") abstract fun deleteDisabledCmps() - - @Query("delete from autoconsent_exceptions") - abstract fun deleteExceptions() } diff --git a/autoconsent/autoconsent-impl/src/main/java/com/duckduckgo/autoconsent/impl/store/AutoconsentDatabase.kt b/autoconsent/autoconsent-impl/src/main/java/com/duckduckgo/autoconsent/impl/store/AutoconsentDatabase.kt index 8777fc2310e2..8970405f9a8d 100644 --- a/autoconsent/autoconsent-impl/src/main/java/com/duckduckgo/autoconsent/impl/store/AutoconsentDatabase.kt +++ b/autoconsent/autoconsent-impl/src/main/java/com/duckduckgo/autoconsent/impl/store/AutoconsentDatabase.kt @@ -21,9 +21,8 @@ import androidx.room.RoomDatabase @Database( exportSchema = true, - version = 1, + version = 2, entities = [ - AutoconsentExceptionEntity::class, DisabledCmpsEntity::class, ], ) diff --git a/autoconsent/autoconsent-impl/src/main/java/com/duckduckgo/autoconsent/impl/store/AutoconsentDatabaseModels.kt b/autoconsent/autoconsent-impl/src/main/java/com/duckduckgo/autoconsent/impl/store/AutoconsentDatabaseModels.kt index 7818fdc8d47f..a4724d9ad914 100644 --- a/autoconsent/autoconsent-impl/src/main/java/com/duckduckgo/autoconsent/impl/store/AutoconsentDatabaseModels.kt +++ b/autoconsent/autoconsent-impl/src/main/java/com/duckduckgo/autoconsent/impl/store/AutoconsentDatabaseModels.kt @@ -18,17 +18,6 @@ package com.duckduckgo.autoconsent.impl.store import androidx.room.Entity import androidx.room.PrimaryKey -import com.duckduckgo.feature.toggles.api.FeatureExceptions.FeatureException - -@Entity(tableName = "autoconsent_exceptions") -data class AutoconsentExceptionEntity( - @PrimaryKey val domain: String, - val reason: String, -) - -fun AutoconsentExceptionEntity.toFeatureException(): FeatureException { - return FeatureException(domain = this.domain, reason = this.reason) -} @Entity(tableName = "autoconsent_disabled_cmps") data class DisabledCmpsEntity( diff --git a/autoconsent/autoconsent-impl/src/test/java/com/duckduckgo/autoconsent/impl/remoteconfig/RealAutoconsentExceptionsRepositoryTest.kt b/autoconsent/autoconsent-impl/src/test/java/com/duckduckgo/autoconsent/impl/remoteconfig/RealAutoconsentExceptionsRepositoryTest.kt index b158c37963b1..5158b2f0e116 100644 --- a/autoconsent/autoconsent-impl/src/test/java/com/duckduckgo/autoconsent/impl/remoteconfig/RealAutoconsentExceptionsRepositoryTest.kt +++ b/autoconsent/autoconsent-impl/src/test/java/com/duckduckgo/autoconsent/impl/remoteconfig/RealAutoconsentExceptionsRepositoryTest.kt @@ -16,74 +16,56 @@ package com.duckduckgo.autoconsent.impl.remoteconfig -import com.duckduckgo.autoconsent.impl.store.AutoconsentDao -import com.duckduckgo.autoconsent.impl.store.AutoconsentDatabase -import com.duckduckgo.autoconsent.impl.store.AutoconsentExceptionEntity -import com.duckduckgo.autoconsent.impl.store.toFeatureException import com.duckduckgo.common.test.CoroutineTestRule +import com.duckduckgo.feature.toggles.api.FakeFeatureToggleFactory +import com.duckduckgo.feature.toggles.api.FeatureExceptions.FeatureException +import com.duckduckgo.feature.toggles.api.Toggle import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.runTest import org.junit.Assert.* import org.junit.Before import org.junit.Rule import org.junit.Test -import org.mockito.ArgumentMatchers.anyList -import org.mockito.kotlin.mock -import org.mockito.kotlin.reset -import org.mockito.kotlin.verify -import org.mockito.kotlin.whenever class RealAutoconsentExceptionsRepositoryTest { @get:Rule var coroutineRule = CoroutineTestRule() - private val mockDatabase: AutoconsentDatabase = mock() - private val mockDao: AutoconsentDao = mock() - - lateinit var repository: AutoconsentExceptionsRepository + private val autoconsentFeature = FakeFeatureToggleFactory.create(AutoconsentFeature::class.java) @Before - fun before() { - whenever(mockDatabase.autoconsentDao()).thenReturn(mockDao) + fun setup() { + autoconsentFeature.self().setRawStoredState(Toggle.State(exceptions = listOf(exception))) } @Test - fun whenRepositoryIsCreatedThenExceptionsLoadedIntoMemory() { - givenDaoContainsExceptions() - - repository = RealAutoconsentExceptionsRepository(TestScope(), coroutineRule.testDispatcherProvider, mockDatabase, isMainProcess = true) - - assertEquals(exception.toFeatureException(), repository.exceptions.first()) + fun whenRepositoryIsCreatedThenExceptionsLoadedIntoMemory() = runTest { + val repository = RealAutoconsentExceptionsRepository( + TestScope(), + coroutineRule.testDispatcherProvider, + autoconsentFeature, + isMainProcess = true, + ) + assertEquals(exception, repository.exceptions.first()) } @Test - fun whenUpdateAllThenUpdateAllCalled() = runTest { - repository = RealAutoconsentExceptionsRepository(TestScope(), coroutineRule.testDispatcherProvider, mockDatabase, isMainProcess = true) - - repository.insertAllExceptions(listOf()) - - verify(mockDao).updateAllExceptions(anyList()) - } - - @Test - fun whenUpdateAllThenPreviousExceptionsAreCleared() = runTest { - givenDaoContainsExceptions() - repository = RealAutoconsentExceptionsRepository(TestScope(), coroutineRule.testDispatcherProvider, mockDatabase, isMainProcess = true) - - assertEquals(1, repository.exceptions.size) - reset(mockDao) - - repository.insertAllExceptions(listOf()) - - assertEquals(0, repository.exceptions.size) - } - - private fun givenDaoContainsExceptions() { - whenever(mockDao.getExceptions()).thenReturn(listOf(exception)) + fun whenRemoteConfigUpdateThenExceptionsUpdated() = runTest { + val repository = RealAutoconsentExceptionsRepository( + TestScope(), + coroutineRule.testDispatcherProvider, + autoconsentFeature, + isMainProcess = true, + ) + + assertEquals(listOf(exception), repository.exceptions) + autoconsentFeature.self().setRawStoredState(Toggle.State(exceptions = emptyList())) + repository.onPrivacyConfigDownloaded() + assertEquals(emptyList(), repository.exceptions) } companion object { - val exception = AutoconsentExceptionEntity("example.com", "reason") + val exception = FeatureException("example.com", "reason") } } From 685d9127635aad2bad93e293266051fdff9bf1f7 Mon Sep 17 00:00:00 2001 From: Aitor Viana Date: Fri, 4 Apr 2025 13:27:21 +0100 Subject: [PATCH 2/9] remove AutofillServiceExceptionsStore --- .../impl/service/AutofillServiceExceptions.kt | 4 +- .../impl/service/AutofillServiceFeature.kt | 2 - .../service/store/AutofillServiceDatabase.kt | 42 ----------- .../store/AutofillServiceExceptionsStore.kt | 37 ---------- ...kt => AutofillServiceFeatureRepository.kt} | 42 ++++++----- .../service/store/AutofillServiceModule.kt | 10 --- .../impl/service/store/ExceptionsDao.kt | 45 ----------- ...ealAutofillServiceFeatureRepositoryTest.kt | 74 +++++++++++++++++++ 8 files changed, 101 insertions(+), 155 deletions(-) delete mode 100644 autofill/autofill-impl/src/main/java/com/duckduckgo/autofill/impl/service/store/AutofillServiceDatabase.kt delete mode 100644 autofill/autofill-impl/src/main/java/com/duckduckgo/autofill/impl/service/store/AutofillServiceExceptionsStore.kt rename autofill/autofill-impl/src/main/java/com/duckduckgo/autofill/impl/service/store/{AutofillFeatureRepository.kt => AutofillServiceFeatureRepository.kt} (61%) delete mode 100644 autofill/autofill-impl/src/main/java/com/duckduckgo/autofill/impl/service/store/ExceptionsDao.kt create mode 100644 autofill/autofill-impl/src/test/java/com/duckduckgo/autofill/impl/store/RealAutofillServiceFeatureRepositoryTest.kt diff --git a/autofill/autofill-impl/src/main/java/com/duckduckgo/autofill/impl/service/AutofillServiceExceptions.kt b/autofill/autofill-impl/src/main/java/com/duckduckgo/autofill/impl/service/AutofillServiceExceptions.kt index d1a1fdec2819..1d13a3842875 100644 --- a/autofill/autofill-impl/src/main/java/com/duckduckgo/autofill/impl/service/AutofillServiceExceptions.kt +++ b/autofill/autofill-impl/src/main/java/com/duckduckgo/autofill/impl/service/AutofillServiceExceptions.kt @@ -17,7 +17,7 @@ package com.duckduckgo.autofill.impl.service import com.duckduckgo.app.browser.UriString.Companion.sameOrSubdomain -import com.duckduckgo.autofill.impl.service.store.AutofillFeatureRepository +import com.duckduckgo.autofill.impl.service.store.AutofillServiceFeatureRepository import com.duckduckgo.di.scopes.AppScope import com.squareup.anvil.annotations.ContributesBinding import dagger.SingleInstanceIn @@ -30,7 +30,7 @@ interface AutofillServiceExceptions { @ContributesBinding(AppScope::class) @SingleInstanceIn(AppScope::class) class RealAutofillServiceExceptions @Inject constructor( - private val repository: AutofillFeatureRepository, + private val repository: AutofillServiceFeatureRepository, ) : AutofillServiceExceptions { override fun isAnException(domain: String): Boolean { diff --git a/autofill/autofill-impl/src/main/java/com/duckduckgo/autofill/impl/service/AutofillServiceFeature.kt b/autofill/autofill-impl/src/main/java/com/duckduckgo/autofill/impl/service/AutofillServiceFeature.kt index 46023e3110e0..c839245bce01 100644 --- a/autofill/autofill-impl/src/main/java/com/duckduckgo/autofill/impl/service/AutofillServiceFeature.kt +++ b/autofill/autofill-impl/src/main/java/com/duckduckgo/autofill/impl/service/AutofillServiceFeature.kt @@ -17,7 +17,6 @@ package com.duckduckgo.autofill.impl.service import com.duckduckgo.anvil.annotations.ContributesRemoteFeature -import com.duckduckgo.autofill.impl.service.store.AutofillServiceExceptionsStore import com.duckduckgo.di.scopes.AppScope import com.duckduckgo.feature.toggles.api.Toggle import com.duckduckgo.feature.toggles.api.Toggle.DefaultFeatureValue @@ -26,7 +25,6 @@ import com.duckduckgo.feature.toggles.api.Toggle.InternalAlwaysEnabled @ContributesRemoteFeature( scope = AppScope::class, featureName = "autofillService", - exceptionsStore = AutofillServiceExceptionsStore::class, ) interface AutofillServiceFeature { diff --git a/autofill/autofill-impl/src/main/java/com/duckduckgo/autofill/impl/service/store/AutofillServiceDatabase.kt b/autofill/autofill-impl/src/main/java/com/duckduckgo/autofill/impl/service/store/AutofillServiceDatabase.kt deleted file mode 100644 index 3302e67942f9..000000000000 --- a/autofill/autofill-impl/src/main/java/com/duckduckgo/autofill/impl/service/store/AutofillServiceDatabase.kt +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (c) 2025 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.autofill.impl.service.store - -import androidx.room.Database -import androidx.room.Entity -import androidx.room.PrimaryKey -import androidx.room.RoomDatabase -import androidx.room.migration.Migration - -@Database( - exportSchema = true, - version = 1, - entities = [ - AutofillServiceException::class, - ], -) -abstract class AutofillServiceDatabase : RoomDatabase() { - abstract fun exceptionsDao(): ExceptionsDao -} - -@Entity(tableName = "autofill_service_exceptions") -data class AutofillServiceException( - @PrimaryKey val domain: String, - val reason: String, -) - -val ALL_MIGRATIONS = emptyArray() diff --git a/autofill/autofill-impl/src/main/java/com/duckduckgo/autofill/impl/service/store/AutofillServiceExceptionsStore.kt b/autofill/autofill-impl/src/main/java/com/duckduckgo/autofill/impl/service/store/AutofillServiceExceptionsStore.kt deleted file mode 100644 index 3884d05358b6..000000000000 --- a/autofill/autofill-impl/src/main/java/com/duckduckgo/autofill/impl/service/store/AutofillServiceExceptionsStore.kt +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (c) 2025 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.autofill.impl.service.store - -import com.duckduckgo.autofill.impl.service.AutofillServiceFeature -import com.duckduckgo.di.scopes.AppScope -import com.duckduckgo.feature.toggles.api.FeatureExceptions -import com.duckduckgo.feature.toggles.api.FeatureExceptions.FeatureException -import com.duckduckgo.feature.toggles.api.RemoteFeatureStoreNamed -import com.squareup.anvil.annotations.ContributesBinding -import javax.inject.Inject - -@ContributesBinding(AppScope::class) -@RemoteFeatureStoreNamed(AutofillServiceFeature::class) -class AutofillServiceExceptionsStore @Inject constructor( - private val autofillFeatureRepository: AutofillFeatureRepository, -) : FeatureExceptions.Store { - override fun insertAll(exception: List) { - autofillFeatureRepository.insertAll( - exception.map { AutofillServiceException(domain = it.domain, reason = it.reason.orEmpty()) }, - ) - } -} diff --git a/autofill/autofill-impl/src/main/java/com/duckduckgo/autofill/impl/service/store/AutofillFeatureRepository.kt b/autofill/autofill-impl/src/main/java/com/duckduckgo/autofill/impl/service/store/AutofillServiceFeatureRepository.kt similarity index 61% rename from autofill/autofill-impl/src/main/java/com/duckduckgo/autofill/impl/service/store/AutofillFeatureRepository.kt rename to autofill/autofill-impl/src/main/java/com/duckduckgo/autofill/impl/service/store/AutofillServiceFeatureRepository.kt index e1e0562d3d56..ff4757622c03 100644 --- a/autofill/autofill-impl/src/main/java/com/duckduckgo/autofill/impl/service/store/AutofillFeatureRepository.kt +++ b/autofill/autofill-impl/src/main/java/com/duckduckgo/autofill/impl/service/store/AutofillServiceFeatureRepository.kt @@ -19,9 +19,12 @@ package com.duckduckgo.autofill.impl.service.store import com.duckduckgo.app.di.AppCoroutineScope import com.duckduckgo.app.di.IsMainProcess import com.duckduckgo.app.di.ProcessName +import com.duckduckgo.autofill.impl.service.AutofillServiceFeature import com.duckduckgo.common.utils.DispatcherProvider import com.duckduckgo.di.scopes.AppScope +import com.duckduckgo.privacy.config.api.PrivacyConfigCallbackPlugin import com.squareup.anvil.annotations.ContributesBinding +import com.squareup.anvil.annotations.ContributesMultibinding import dagger.SingleInstanceIn import java.util.concurrent.CopyOnWriteArrayList import javax.inject.Inject @@ -29,41 +32,46 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch import timber.log.Timber -interface AutofillFeatureRepository { - fun insertAll(newList: List) +interface AutofillServiceFeatureRepository { val exceptions: CopyOnWriteArrayList } @SingleInstanceIn(AppScope::class) -@ContributesBinding(AppScope::class) -class RealAutofillFeatureRepository @Inject constructor( - private val autofillServiceDatabase: AutofillServiceDatabase, +@ContributesBinding( + scope = AppScope::class, + boundType = AutofillServiceFeatureRepository::class, +) +@ContributesMultibinding( + scope = AppScope::class, + boundType = AutofillServiceFeatureRepository::class, +) +class RealAutofillServiceFeatureRepository @Inject constructor( @IsMainProcess private val isMainProcess: Boolean, @ProcessName private val processName: String, - @AppCoroutineScope appCoroutineScope: CoroutineScope, - dispatcherProvider: DispatcherProvider, -) : AutofillFeatureRepository { - - private val dao = autofillServiceDatabase.exceptionsDao() + @AppCoroutineScope private val appCoroutineScope: CoroutineScope, + private val dispatcherProvider: DispatcherProvider, + private val autofillServiceFeature: AutofillServiceFeature, +) : AutofillServiceFeatureRepository, PrivacyConfigCallbackPlugin { override val exceptions = CopyOnWriteArrayList() init { appCoroutineScope.launch(dispatcherProvider.io()) { Timber.i("DDGAutofillService: Init AutofillFeatureRepository from $processName") - if (isMainProcess || processName == ":autofill") { - loadToMemory() - } + loadToMemory() } } - override fun insertAll(newList: List) { - dao.updateAll(newList) + override fun onPrivacyConfigDownloaded() { loadToMemory() } private fun loadToMemory() { - exceptions.clear() - dao.getAll().map { exceptions.add(it.domain) } + appCoroutineScope.launch(dispatcherProvider.io()) { + if (isMainProcess || processName == ":autofill") { + exceptions.clear() + exceptions.addAll(autofillServiceFeature.self().getExceptions().map { it.domain }) + } + } } } diff --git a/autofill/autofill-impl/src/main/java/com/duckduckgo/autofill/impl/service/store/AutofillServiceModule.kt b/autofill/autofill-impl/src/main/java/com/duckduckgo/autofill/impl/service/store/AutofillServiceModule.kt index 9309d0546696..53095faf24a4 100644 --- a/autofill/autofill-impl/src/main/java/com/duckduckgo/autofill/impl/service/store/AutofillServiceModule.kt +++ b/autofill/autofill-impl/src/main/java/com/duckduckgo/autofill/impl/service/store/AutofillServiceModule.kt @@ -21,7 +21,6 @@ import android.view.autofill.AutofillManager import androidx.datastore.core.DataStore import androidx.datastore.preferences.core.Preferences import androidx.datastore.preferences.preferencesDataStore -import androidx.room.Room import com.duckduckgo.di.scopes.AppScope import com.squareup.anvil.annotations.ContributesTo import dagger.Module @@ -44,15 +43,6 @@ class AutofillServiceModule { return context.autofillServiceStore } - @SingleInstanceIn(AppScope::class) - @Provides - fun provideAutofillServiceDatabase(context: Context): AutofillServiceDatabase { - return Room.databaseBuilder(context, AutofillServiceDatabase::class.java, "autofillService.db") - .fallbackToDestructiveMigration() - .addMigrations(*ALL_MIGRATIONS) - .build() - } - @Provides fun providesAutofillManager(context: Context): AutofillManager? { return kotlin.runCatching { context.getSystemService(AutofillManager::class.java) as AutofillManager }.getOrNull() diff --git a/autofill/autofill-impl/src/main/java/com/duckduckgo/autofill/impl/service/store/ExceptionsDao.kt b/autofill/autofill-impl/src/main/java/com/duckduckgo/autofill/impl/service/store/ExceptionsDao.kt deleted file mode 100644 index 6cbe33966c81..000000000000 --- a/autofill/autofill-impl/src/main/java/com/duckduckgo/autofill/impl/service/store/ExceptionsDao.kt +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (c) 2025 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.autofill.impl.service.store - -import androidx.room.Dao -import androidx.room.Insert -import androidx.room.OnConflictStrategy -import androidx.room.Query -import androidx.room.Transaction - -@Dao -abstract class ExceptionsDao { - - @Insert(onConflict = OnConflictStrategy.REPLACE) - abstract fun insertAll(exceptions: List) - - @Transaction - open fun updateAll(exceptions: List) { - deleteAll() - insertAll(exceptions) - } - - @Query("select * from autofill_service_exceptions where domain = :domain") - abstract fun get(domain: String): AutofillServiceException - - @Query("select * from autofill_service_exceptions") - abstract fun getAll(): List - - @Query("delete from autofill_service_exceptions") - abstract fun deleteAll() -} diff --git a/autofill/autofill-impl/src/test/java/com/duckduckgo/autofill/impl/store/RealAutofillServiceFeatureRepositoryTest.kt b/autofill/autofill-impl/src/test/java/com/duckduckgo/autofill/impl/store/RealAutofillServiceFeatureRepositoryTest.kt new file mode 100644 index 000000000000..29c2919b1b75 --- /dev/null +++ b/autofill/autofill-impl/src/test/java/com/duckduckgo/autofill/impl/store/RealAutofillServiceFeatureRepositoryTest.kt @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2025 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.autofill.impl.store + +import com.duckduckgo.autofill.impl.service.AutofillServiceFeature +import com.duckduckgo.autofill.impl.service.store.RealAutofillServiceFeatureRepository +import com.duckduckgo.common.test.CoroutineTestRule +import com.duckduckgo.feature.toggles.api.FakeFeatureToggleFactory +import com.duckduckgo.feature.toggles.api.FeatureExceptions.FeatureException +import com.duckduckgo.feature.toggles.api.Toggle +import kotlinx.coroutines.test.runTest +import org.junit.Assert.assertEquals +import org.junit.Before +import org.junit.Rule +import org.junit.Test + +class RealAutofillServiceFeatureRepositoryTest { + @get:Rule + var coroutineRule = CoroutineTestRule() + + private val autofillServiceFeature = FakeFeatureToggleFactory.create(AutofillServiceFeature::class.java) + + @Before + fun setup() { + autofillServiceFeature.self().setRawStoredState(Toggle.State(exceptions = listOf(exception))) + } + + @Test + fun whenRepositoryIsCreatedThenExceptionsLoadIntoMemory() = runTest { + val repository = RealAutofillServiceFeatureRepository( + true, + "processName", + coroutineRule.testScope, + coroutineRule.testDispatcherProvider, + autofillServiceFeature, + ) + + assertEquals(listOf(exception.domain), repository.exceptions) + } + + @Test + fun whenRemoteConfigUpdateThenExceptionsUpdated() = runTest { + val repository = RealAutofillServiceFeatureRepository( + true, + "processName", + coroutineRule.testScope, + coroutineRule.testDispatcherProvider, + autofillServiceFeature, + ) + + assertEquals(listOf(exception.domain), repository.exceptions) + autofillServiceFeature.self().setRawStoredState(Toggle.State(exceptions = emptyList())) + repository.onPrivacyConfigDownloaded() + assertEquals(emptyList(), repository.exceptions) + } + + companion object { + val exception = FeatureException("example.com", "reason") + } +} From ec7af8e2953af301b3b45589760eb80111942001 Mon Sep 17 00:00:00 2001 From: Aitor Viana Date: Fri, 4 Apr 2025 14:00:51 +0100 Subject: [PATCH 3/9] remove MediaPlaybackStore --- .../app/browser/di/BrowserModule.kt | 20 ------ ...diaPlaybackFeatureFeatureCodegenTrigger.kt | 2 - .../mediaplayback/store/MediaPlaybackDao.kt | 41 ------------ .../store/MediaPlaybackDatabase.kt | 34 ---------- .../store/MediaPlaybackExceptionEntity.kt | 28 -------- .../store/MediaPlaybackRepository.kt | 40 ++++++----- .../mediaplayback/store/MediaPlaybackStore.kt | 36 ---------- .../store/RealMediaPlaybackRepositoryTest.kt | 67 ++++++------------- 8 files changed, 44 insertions(+), 224 deletions(-) delete mode 100644 app/src/main/java/com/duckduckgo/app/browser/mediaplayback/store/MediaPlaybackDao.kt delete mode 100644 app/src/main/java/com/duckduckgo/app/browser/mediaplayback/store/MediaPlaybackDatabase.kt delete mode 100644 app/src/main/java/com/duckduckgo/app/browser/mediaplayback/store/MediaPlaybackExceptionEntity.kt delete mode 100644 app/src/main/java/com/duckduckgo/app/browser/mediaplayback/store/MediaPlaybackStore.kt diff --git a/app/src/main/java/com/duckduckgo/app/browser/di/BrowserModule.kt b/app/src/main/java/com/duckduckgo/app/browser/di/BrowserModule.kt index fb24bfea97bf..ea842b8c2068 100644 --- a/app/src/main/java/com/duckduckgo/app/browser/di/BrowserModule.kt +++ b/app/src/main/java/com/duckduckgo/app/browser/di/BrowserModule.kt @@ -22,7 +22,6 @@ import android.content.pm.PackageManager import androidx.datastore.core.DataStore import androidx.datastore.preferences.core.Preferences import androidx.datastore.preferences.preferencesDataStore -import androidx.room.Room import androidx.work.WorkManager import com.duckduckgo.adclick.api.AdClickManager import com.duckduckgo.app.browser.* @@ -45,9 +44,6 @@ import com.duckduckgo.app.browser.httperrors.HttpCodeSiteErrorHandlerImpl import com.duckduckgo.app.browser.httperrors.StringSiteErrorHandler import com.duckduckgo.app.browser.httperrors.StringSiteErrorHandlerImpl import com.duckduckgo.app.browser.logindetection.* -import com.duckduckgo.app.browser.mediaplayback.store.ALL_MIGRATIONS -import com.duckduckgo.app.browser.mediaplayback.store.MediaPlaybackDao -import com.duckduckgo.app.browser.mediaplayback.store.MediaPlaybackDatabase import com.duckduckgo.app.browser.pageloadpixel.PageLoadedPixelDao import com.duckduckgo.app.browser.pageloadpixel.firstpaint.PagePaintedPixelDao import com.duckduckgo.app.browser.session.WebViewSessionInMemoryStorage @@ -340,22 +336,6 @@ class BrowserModule { return appDatabase.pagePaintedPixelDao() } - @Provides - @SingleInstanceIn(AppScope::class) - fun provideMediaPlaybackDatabase(context: Context): MediaPlaybackDatabase { - return Room.databaseBuilder(context, MediaPlaybackDatabase::class.java, "media_playback.db") - .enableMultiInstanceInvalidation() - .fallbackToDestructiveMigration() - .addMigrations(*ALL_MIGRATIONS) - .build() - } - - @Provides - @SingleInstanceIn(AppScope::class) - fun providesMediaPlaybackDao(mediaPlaybackDatabase: MediaPlaybackDatabase): MediaPlaybackDao { - return mediaPlaybackDatabase.mediaPlaybackDao() - } - private val Context.indonesiaNewTabSectionDataStore: DataStore by preferencesDataStore( name = "indonesia_new_tab_section_store", ) diff --git a/app/src/main/java/com/duckduckgo/app/browser/mediaplayback/UnusedMediaPlaybackFeatureFeatureCodegenTrigger.kt b/app/src/main/java/com/duckduckgo/app/browser/mediaplayback/UnusedMediaPlaybackFeatureFeatureCodegenTrigger.kt index e6d576c5cb1b..785a67efcf07 100644 --- a/app/src/main/java/com/duckduckgo/app/browser/mediaplayback/UnusedMediaPlaybackFeatureFeatureCodegenTrigger.kt +++ b/app/src/main/java/com/duckduckgo/app/browser/mediaplayback/UnusedMediaPlaybackFeatureFeatureCodegenTrigger.kt @@ -17,13 +17,11 @@ package com.duckduckgo.app.browser.mediaplayback import com.duckduckgo.anvil.annotations.ContributesRemoteFeature -import com.duckduckgo.app.browser.mediaplayback.store.MediaPlaybackStore import com.duckduckgo.di.scopes.AppScope @ContributesRemoteFeature( scope = AppScope::class, featureName = "mediaPlaybackRequiresUserGesture", - exceptionsStore = MediaPlaybackStore::class, boundType = MediaPlaybackFeature::class, ) @Suppress("unused") diff --git a/app/src/main/java/com/duckduckgo/app/browser/mediaplayback/store/MediaPlaybackDao.kt b/app/src/main/java/com/duckduckgo/app/browser/mediaplayback/store/MediaPlaybackDao.kt deleted file mode 100644 index d509ab94b65f..000000000000 --- a/app/src/main/java/com/duckduckgo/app/browser/mediaplayback/store/MediaPlaybackDao.kt +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (c) 2024 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.browser.mediaplayback.store - -import androidx.room.Dao -import androidx.room.Insert -import androidx.room.OnConflictStrategy -import androidx.room.Query -import androidx.room.Transaction - -@Dao -abstract class MediaPlaybackDao { - @Insert(onConflict = OnConflictStrategy.REPLACE) - abstract fun insertAll(domains: List) - - @Transaction - open fun updateAll(domains: List) { - deleteAll() - insertAll(domains) - } - - @Query("select * from media_playback_user_gesture_exceptions") - abstract fun getAll(): List - - @Query("delete from media_playback_user_gesture_exceptions") - abstract fun deleteAll() -} diff --git a/app/src/main/java/com/duckduckgo/app/browser/mediaplayback/store/MediaPlaybackDatabase.kt b/app/src/main/java/com/duckduckgo/app/browser/mediaplayback/store/MediaPlaybackDatabase.kt deleted file mode 100644 index 275cfdfa5e59..000000000000 --- a/app/src/main/java/com/duckduckgo/app/browser/mediaplayback/store/MediaPlaybackDatabase.kt +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (c) 2024 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.browser.mediaplayback.store - -import androidx.room.Database -import androidx.room.RoomDatabase -import androidx.room.migration.Migration - -@Database( - exportSchema = true, - version = 1, - entities = [ - MediaPlaybackExceptionEntity::class, - ], -) -abstract class MediaPlaybackDatabase : RoomDatabase() { - abstract fun mediaPlaybackDao(): MediaPlaybackDao -} - -val ALL_MIGRATIONS = emptyArray() diff --git a/app/src/main/java/com/duckduckgo/app/browser/mediaplayback/store/MediaPlaybackExceptionEntity.kt b/app/src/main/java/com/duckduckgo/app/browser/mediaplayback/store/MediaPlaybackExceptionEntity.kt deleted file mode 100644 index 66a6e9aff3f9..000000000000 --- a/app/src/main/java/com/duckduckgo/app/browser/mediaplayback/store/MediaPlaybackExceptionEntity.kt +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (c) 2024 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.browser.mediaplayback.store - -import androidx.room.Entity -import androidx.room.PrimaryKey -import com.duckduckgo.feature.toggles.api.FeatureExceptions - -@Entity(tableName = "media_playback_user_gesture_exceptions") -data class MediaPlaybackExceptionEntity(@PrimaryKey val domain: String) - -fun MediaPlaybackExceptionEntity.toFeatureException(): FeatureExceptions.FeatureException { - return FeatureExceptions.FeatureException(domain = this.domain, reason = null) -} diff --git a/app/src/main/java/com/duckduckgo/app/browser/mediaplayback/store/MediaPlaybackRepository.kt b/app/src/main/java/com/duckduckgo/app/browser/mediaplayback/store/MediaPlaybackRepository.kt index 829a799b1e17..052576082986 100644 --- a/app/src/main/java/com/duckduckgo/app/browser/mediaplayback/store/MediaPlaybackRepository.kt +++ b/app/src/main/java/com/duckduckgo/app/browser/mediaplayback/store/MediaPlaybackRepository.kt @@ -16,12 +16,15 @@ package com.duckduckgo.app.browser.mediaplayback.store +import com.duckduckgo.app.browser.mediaplayback.MediaPlaybackFeature import com.duckduckgo.app.di.AppCoroutineScope import com.duckduckgo.app.di.IsMainProcess import com.duckduckgo.common.utils.DispatcherProvider import com.duckduckgo.di.scopes.AppScope import com.duckduckgo.feature.toggles.api.FeatureExceptions +import com.duckduckgo.privacy.config.api.PrivacyConfigCallbackPlugin import com.squareup.anvil.annotations.ContributesBinding +import com.squareup.anvil.annotations.ContributesMultibinding import dagger.SingleInstanceIn import java.util.concurrent.CopyOnWriteArrayList import javax.inject.Inject @@ -29,38 +32,41 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch interface MediaPlaybackRepository { - fun updateAll(exceptions: List) val exceptions: CopyOnWriteArrayList } -@ContributesBinding(AppScope::class) +@ContributesBinding( + scope = AppScope::class, + boundType = MediaPlaybackRepository::class, +) +@ContributesMultibinding( + scope = AppScope::class, + boundType = PrivacyConfigCallbackPlugin::class, +) @SingleInstanceIn(AppScope::class) class RealMediaPlaybackRepository @Inject constructor( - private val mediaPlaybackDao: MediaPlaybackDao, - @AppCoroutineScope appCoroutineScope: CoroutineScope, - dispatcherProvider: DispatcherProvider, - @IsMainProcess isMainProcess: Boolean, -) : MediaPlaybackRepository { + private val mediaPlaybackFeature: MediaPlaybackFeature, + @AppCoroutineScope private val appCoroutineScope: CoroutineScope, + private val dispatcherProvider: DispatcherProvider, + @IsMainProcess private val isMainProcess: Boolean, +) : MediaPlaybackRepository, PrivacyConfigCallbackPlugin { override val exceptions = CopyOnWriteArrayList() init { - appCoroutineScope.launch(dispatcherProvider.io()) { - if (isMainProcess) { - loadToMemory() - } - } + loadToMemory() } - override fun updateAll(exceptions: List) { - mediaPlaybackDao.updateAll(exceptions) + override fun onPrivacyConfigDownloaded() { loadToMemory() } private fun loadToMemory() { - exceptions.clear() - mediaPlaybackDao.getAll().map { - exceptions.add(it.toFeatureException()) + appCoroutineScope.launch(dispatcherProvider.io()) { + if (isMainProcess) { + exceptions.clear() + exceptions.addAll(mediaPlaybackFeature.self().getExceptions()) + } } } } diff --git a/app/src/main/java/com/duckduckgo/app/browser/mediaplayback/store/MediaPlaybackStore.kt b/app/src/main/java/com/duckduckgo/app/browser/mediaplayback/store/MediaPlaybackStore.kt deleted file mode 100644 index bef97d1fd1d8..000000000000 --- a/app/src/main/java/com/duckduckgo/app/browser/mediaplayback/store/MediaPlaybackStore.kt +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (c) 2024 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.browser.mediaplayback.store - -import com.duckduckgo.app.browser.mediaplayback.MediaPlaybackFeature -import com.duckduckgo.di.scopes.AppScope -import com.duckduckgo.feature.toggles.api.FeatureExceptions -import com.duckduckgo.feature.toggles.api.RemoteFeatureStoreNamed -import com.squareup.anvil.annotations.ContributesBinding -import javax.inject.Inject - -@ContributesBinding(AppScope::class) -@RemoteFeatureStoreNamed(MediaPlaybackFeature::class) -class MediaPlaybackStore @Inject constructor( - private val mediaPlaybackRepository: MediaPlaybackRepository, -) : FeatureExceptions.Store { - override fun insertAll(exception: List) { - mediaPlaybackRepository.updateAll( - exception.map { MediaPlaybackExceptionEntity(domain = it.domain) }, - ) - } -} diff --git a/app/src/test/java/com/duckduckgo/app/browser/mediaplayback/store/RealMediaPlaybackRepositoryTest.kt b/app/src/test/java/com/duckduckgo/app/browser/mediaplayback/store/RealMediaPlaybackRepositoryTest.kt index ef75ef8172bc..492d75899ee9 100644 --- a/app/src/test/java/com/duckduckgo/app/browser/mediaplayback/store/RealMediaPlaybackRepositoryTest.kt +++ b/app/src/test/java/com/duckduckgo/app/browser/mediaplayback/store/RealMediaPlaybackRepositoryTest.kt @@ -1,81 +1,56 @@ package com.duckduckgo.app.browser.mediaplayback.store +import com.duckduckgo.app.browser.mediaplayback.MediaPlaybackFeature import com.duckduckgo.common.test.CoroutineTestRule +import com.duckduckgo.feature.toggles.api.FakeFeatureToggleFactory +import com.duckduckgo.feature.toggles.api.FeatureExceptions.FeatureException +import com.duckduckgo.feature.toggles.api.Toggle import junit.framework.TestCase.assertEquals import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Rule import org.junit.Test -import org.mockito.ArgumentMatchers.anyList -import org.mockito.kotlin.mock -import org.mockito.kotlin.reset -import org.mockito.kotlin.verify -import org.mockito.kotlin.whenever class RealMediaPlaybackRepositoryTest { @get:Rule var coroutineRule = CoroutineTestRule() - lateinit var testee: RealMediaPlaybackRepository - - private val mockMediaPlaybackDatabase: MediaPlaybackDatabase = mock() - private val mockMediaPlaybackDao: MediaPlaybackDao = mock() + private val mediaPlaybackFeature = FakeFeatureToggleFactory.create(MediaPlaybackFeature::class.java) @Before fun before() { - whenever(mockMediaPlaybackDatabase.mediaPlaybackDao()).thenReturn(mockMediaPlaybackDao) - initRepository() + mediaPlaybackFeature.self().setRawStoredState(Toggle.State(exceptions = exceptions)) } @Test fun whenRepositoryIsCreatedThenValuesLoadedIntoMemory() { - givenMediaPlaybackDaoContainsEntities() - - initRepository() - - assertEquals(mediaPlaybackExceptionEntity.toFeatureException(), testee.exceptions.first()) - } - - @Test - fun whenUpdateAllThenUpdateAllCalled() = runTest { - initRepository() - - testee.updateAll(listOf()) + val repository = RealMediaPlaybackRepository( + mediaPlaybackFeature, + TestScope(), + coroutineRule.testDispatcherProvider, + isMainProcess = true, + ) - verify(mockMediaPlaybackDao).updateAll(anyList()) + assertEquals(exceptions, repository.exceptions) } @Test - fun whenUpdateAllThenPreviousValuesAreClearedAndNewValuesUpdated() = runTest { - givenMediaPlaybackDaoContainsEntities() - - initRepository() - assertEquals(1, testee.exceptions.size) - - reset(mockMediaPlaybackDao) - - testee.updateAll(listOf()) - - assertEquals(0, testee.exceptions.size) - } - - private fun initRepository() { - testee = RealMediaPlaybackRepository( - mockMediaPlaybackDao, + fun whenRemoteConfigUpdateThenExceptionsUpdated() = runTest { + val repository = RealMediaPlaybackRepository( + mediaPlaybackFeature, TestScope(), coroutineRule.testDispatcherProvider, isMainProcess = true, ) - } - private fun givenMediaPlaybackDaoContainsEntities() { - whenever(mockMediaPlaybackDao.getAll()).thenReturn(listOf(mediaPlaybackExceptionEntity)) + assertEquals(exceptions, repository.exceptions) + mediaPlaybackFeature.self().setRawStoredState(Toggle.State(exceptions = emptyList())) + repository.onPrivacyConfigDownloaded() + assertEquals(emptyList(), repository.exceptions) } companion object { - val mediaPlaybackExceptionEntity = MediaPlaybackExceptionEntity( - domain = "example.com", - ) + val exceptions = listOf(FeatureException("example.com", "reason")) } } From 0a37a6d2c2178e4844e7403a2d7e74ae283f8cc8 Mon Sep 17 00:00:00 2001 From: Aitor Viana Date: Fri, 4 Apr 2025 14:15:23 +0100 Subject: [PATCH 4/9] remove DrmBlockFeatureExceptionStore --- .../site-permissions-impl/build.gradle | 1 + .../impl/di/SitePermissionsModule.kt | 7 --- .../impl/drmblock/DrmBlockFeature.kt | 1 - .../drmblock/DrmBlockFeatureExceptionStore.kt | 36 ------------- .../impl/drmblock/DrmBlockRepository.kt | 42 ++++++++------- .../drmblock/RealDrmBlockRepositoryTest.kt | 53 +++++++++++++++++++ .../store/SitePermissionsDatabase.kt | 14 ++--- .../permissions/store/drmblock/DrmBlockDao.kt | 42 --------------- .../store/drmblock/DrmBlockExceptionEntity.kt | 28 ---------- 9 files changed, 84 insertions(+), 140 deletions(-) delete mode 100644 site-permissions/site-permissions-impl/src/main/java/com/duckduckgo/site/permissions/impl/drmblock/DrmBlockFeatureExceptionStore.kt create mode 100644 site-permissions/site-permissions-impl/src/test/java/com/duckduckgo/site/permissions/impl/drmblock/RealDrmBlockRepositoryTest.kt delete mode 100644 site-permissions/site-permissions-store/src/main/java/com/duckduckgo/site/permissions/store/drmblock/DrmBlockDao.kt delete mode 100644 site-permissions/site-permissions-store/src/main/java/com/duckduckgo/site/permissions/store/drmblock/DrmBlockExceptionEntity.kt diff --git a/site-permissions/site-permissions-impl/build.gradle b/site-permissions/site-permissions-impl/build.gradle index 8166b55b59d6..f4893db80504 100644 --- a/site-permissions/site-permissions-impl/build.gradle +++ b/site-permissions/site-permissions-impl/build.gradle @@ -48,6 +48,7 @@ dependencies { implementation Square.retrofit2.converter.moshi testImplementation project(path: ':common-test') + testImplementation project(path: ':feature-toggles-test') testImplementation CashApp.turbine testImplementation Testing.junit4 testImplementation AndroidX.test.ext.junit diff --git a/site-permissions/site-permissions-impl/src/main/java/com/duckduckgo/site/permissions/impl/di/SitePermissionsModule.kt b/site-permissions/site-permissions-impl/src/main/java/com/duckduckgo/site/permissions/impl/di/SitePermissionsModule.kt index 29fb4c9e9daf..3176bb0820f2 100644 --- a/site-permissions/site-permissions-impl/src/main/java/com/duckduckgo/site/permissions/impl/di/SitePermissionsModule.kt +++ b/site-permissions/site-permissions-impl/src/main/java/com/duckduckgo/site/permissions/impl/di/SitePermissionsModule.kt @@ -23,7 +23,6 @@ import com.duckduckgo.site.permissions.store.ALL_MIGRATIONS import com.duckduckgo.site.permissions.store.SitePermissionsDatabase import com.duckduckgo.site.permissions.store.SitePermissionsPreferences import com.duckduckgo.site.permissions.store.SitePermissionsPreferencesImp -import com.duckduckgo.site.permissions.store.drmblock.DrmBlockDao import com.duckduckgo.site.permissions.store.sitepermissions.SitePermissionsDao import com.duckduckgo.site.permissions.store.sitepermissionsallowed.SitePermissionsAllowedDao import com.squareup.anvil.annotations.ContributesTo @@ -56,12 +55,6 @@ object SitePermissionsModule { return sitePermissionsDatabase.sitePermissionsAllowedDao() } - @Provides - @SingleInstanceIn(AppScope::class) - fun providesDrmBlockDao(sitePermissionsDatabase: SitePermissionsDatabase): DrmBlockDao { - return sitePermissionsDatabase.drmBlockDao() - } - @Provides fun providesSitePermissionsPreferences(context: Context): SitePermissionsPreferences { return SitePermissionsPreferencesImp(context) diff --git a/site-permissions/site-permissions-impl/src/main/java/com/duckduckgo/site/permissions/impl/drmblock/DrmBlockFeature.kt b/site-permissions/site-permissions-impl/src/main/java/com/duckduckgo/site/permissions/impl/drmblock/DrmBlockFeature.kt index 9f11501844ae..c2d21e8796f3 100644 --- a/site-permissions/site-permissions-impl/src/main/java/com/duckduckgo/site/permissions/impl/drmblock/DrmBlockFeature.kt +++ b/site-permissions/site-permissions-impl/src/main/java/com/duckduckgo/site/permissions/impl/drmblock/DrmBlockFeature.kt @@ -24,7 +24,6 @@ import com.duckduckgo.feature.toggles.api.Toggle.DefaultFeatureValue @ContributesRemoteFeature( scope = AppScope::class, featureName = "emeBlock", - exceptionsStore = DrmBlockFeatureExceptionStore::class, ) interface DrmBlockFeature { @Toggle.DefaultValue(DefaultFeatureValue.FALSE) diff --git a/site-permissions/site-permissions-impl/src/main/java/com/duckduckgo/site/permissions/impl/drmblock/DrmBlockFeatureExceptionStore.kt b/site-permissions/site-permissions-impl/src/main/java/com/duckduckgo/site/permissions/impl/drmblock/DrmBlockFeatureExceptionStore.kt deleted file mode 100644 index 15dbfae67e35..000000000000 --- a/site-permissions/site-permissions-impl/src/main/java/com/duckduckgo/site/permissions/impl/drmblock/DrmBlockFeatureExceptionStore.kt +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (c) 2023 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.site.permissions.impl.drmblock - -import com.duckduckgo.di.scopes.AppScope -import com.duckduckgo.feature.toggles.api.FeatureExceptions -import com.duckduckgo.feature.toggles.api.RemoteFeatureStoreNamed -import com.duckduckgo.site.permissions.store.drmblock.DrmBlockExceptionEntity -import com.squareup.anvil.annotations.ContributesBinding -import javax.inject.Inject - -@ContributesBinding(AppScope::class) -@RemoteFeatureStoreNamed(DrmBlockFeature::class) -class DrmBlockFeatureExceptionStore @Inject constructor( - private val drmBlockRepository: DrmBlockRepository, -) : FeatureExceptions.Store { - override fun insertAll(exception: List) { - drmBlockRepository.updateAll( - exception.map { DrmBlockExceptionEntity(domain = it.domain) }, - ) - } -} diff --git a/site-permissions/site-permissions-impl/src/main/java/com/duckduckgo/site/permissions/impl/drmblock/DrmBlockRepository.kt b/site-permissions/site-permissions-impl/src/main/java/com/duckduckgo/site/permissions/impl/drmblock/DrmBlockRepository.kt index c2853731e540..81b778690b43 100644 --- a/site-permissions/site-permissions-impl/src/main/java/com/duckduckgo/site/permissions/impl/drmblock/DrmBlockRepository.kt +++ b/site-permissions/site-permissions-impl/src/main/java/com/duckduckgo/site/permissions/impl/drmblock/DrmBlockRepository.kt @@ -21,10 +21,9 @@ import com.duckduckgo.app.di.IsMainProcess import com.duckduckgo.common.utils.DispatcherProvider import com.duckduckgo.di.scopes.AppScope import com.duckduckgo.feature.toggles.api.FeatureExceptions.FeatureException -import com.duckduckgo.site.permissions.store.drmblock.DrmBlockDao -import com.duckduckgo.site.permissions.store.drmblock.DrmBlockExceptionEntity -import com.duckduckgo.site.permissions.store.drmblock.toFeatureException +import com.duckduckgo.privacy.config.api.PrivacyConfigCallbackPlugin import com.squareup.anvil.annotations.ContributesBinding +import com.squareup.anvil.annotations.ContributesMultibinding import dagger.SingleInstanceIn import java.util.concurrent.CopyOnWriteArrayList import javax.inject.Inject @@ -32,38 +31,41 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch interface DrmBlockRepository { - fun updateAll(exceptions: List) val exceptions: CopyOnWriteArrayList } -@ContributesBinding(AppScope::class) +@ContributesBinding( + scope = AppScope::class, + boundType = DrmBlockRepository::class, +) +@ContributesMultibinding( + scope = AppScope::class, + boundType = PrivacyConfigCallbackPlugin::class, +) @SingleInstanceIn(AppScope::class) class RealDrmBlockRepository @Inject constructor( - val drmBlockDao: DrmBlockDao, - @AppCoroutineScope appCoroutineScope: CoroutineScope, - dispatcherProvider: DispatcherProvider, - @IsMainProcess isMainProcess: Boolean, -) : DrmBlockRepository { + private val drmBlockFeature: DrmBlockFeature, + @AppCoroutineScope private val appCoroutineScope: CoroutineScope, + private val dispatcherProvider: DispatcherProvider, + @IsMainProcess private val isMainProcess: Boolean, +) : DrmBlockRepository, PrivacyConfigCallbackPlugin { override val exceptions = CopyOnWriteArrayList() init { - appCoroutineScope.launch(dispatcherProvider.io()) { - if (isMainProcess) { - loadToMemory() - } - } + loadToMemory() } - override fun updateAll(exceptions: List) { - drmBlockDao.updateAll(exceptions) + override fun onPrivacyConfigDownloaded() { loadToMemory() } private fun loadToMemory() { - exceptions.clear() - drmBlockDao.getAll().map { - exceptions.add(it.toFeatureException()) + appCoroutineScope.launch(dispatcherProvider.io()) { + if (isMainProcess) { + exceptions.clear() + exceptions.addAll(drmBlockFeature.self().getExceptions()) + } } } } diff --git a/site-permissions/site-permissions-impl/src/test/java/com/duckduckgo/site/permissions/impl/drmblock/RealDrmBlockRepositoryTest.kt b/site-permissions/site-permissions-impl/src/test/java/com/duckduckgo/site/permissions/impl/drmblock/RealDrmBlockRepositoryTest.kt new file mode 100644 index 000000000000..b71f3c8af483 --- /dev/null +++ b/site-permissions/site-permissions-impl/src/test/java/com/duckduckgo/site/permissions/impl/drmblock/RealDrmBlockRepositoryTest.kt @@ -0,0 +1,53 @@ +package com.duckduckgo.site.permissions.impl.drmblock + +import com.duckduckgo.common.test.CoroutineTestRule +import com.duckduckgo.feature.toggles.api.FakeFeatureToggleFactory +import com.duckduckgo.feature.toggles.api.FeatureExceptions.FeatureException +import com.duckduckgo.feature.toggles.api.Toggle +import kotlinx.coroutines.test.runTest +import org.junit.Assert.assertEquals +import org.junit.Before +import org.junit.Rule +import org.junit.Test + +class RealDrmBlockRepositoryTest { + @get:Rule + var coroutineRule = CoroutineTestRule() + + private val drmBlockFeature = FakeFeatureToggleFactory.create(DrmBlockFeature::class.java) + + @Before + fun setup() { + drmBlockFeature.self().setRawStoredState(Toggle.State(exceptions = exceptions)) + } + + @Test + fun whenRepositoryIsCreatedThenExceptionsLoadIntoMemory() = runTest { + val repository = RealDrmBlockRepository( + drmBlockFeature, + coroutineRule.testScope, + coroutineRule.testDispatcherProvider, + true, + ) + assertEquals(exceptions, repository.exceptions) + } + + @Test + fun whenRemoteConfigUpdateThenExceptionsUpdated() = runTest { + val repository = RealDrmBlockRepository( + drmBlockFeature, + coroutineRule.testScope, + coroutineRule.testDispatcherProvider, + true, + ) + + assertEquals(exceptions, repository.exceptions) + drmBlockFeature.self().setRawStoredState(Toggle.State(exceptions = emptyList())) + repository.onPrivacyConfigDownloaded() + assertEquals(emptyList(), repository.exceptions) + } + + companion object { + val exceptions = listOf(FeatureException("example.com", "reason")) + } +} diff --git a/site-permissions/site-permissions-store/src/main/java/com/duckduckgo/site/permissions/store/SitePermissionsDatabase.kt b/site-permissions/site-permissions-store/src/main/java/com/duckduckgo/site/permissions/store/SitePermissionsDatabase.kt index 76075db32c23..6477decffa96 100644 --- a/site-permissions/site-permissions-store/src/main/java/com/duckduckgo/site/permissions/store/SitePermissionsDatabase.kt +++ b/site-permissions/site-permissions-store/src/main/java/com/duckduckgo/site/permissions/store/SitePermissionsDatabase.kt @@ -20,8 +20,6 @@ import androidx.room.Database import androidx.room.RoomDatabase import androidx.room.migration.Migration import androidx.sqlite.db.SupportSQLiteDatabase -import com.duckduckgo.site.permissions.store.drmblock.DrmBlockDao -import com.duckduckgo.site.permissions.store.drmblock.DrmBlockExceptionEntity import com.duckduckgo.site.permissions.store.sitepermissions.SitePermissionsDao import com.duckduckgo.site.permissions.store.sitepermissions.SitePermissionsEntity import com.duckduckgo.site.permissions.store.sitepermissionsallowed.SitePermissionAllowedEntity @@ -29,18 +27,16 @@ import com.duckduckgo.site.permissions.store.sitepermissionsallowed.SitePermissi @Database( exportSchema = true, - version = 4, + version = 5, entities = [ SitePermissionsEntity::class, SitePermissionAllowedEntity::class, - DrmBlockExceptionEntity::class, ], ) abstract class SitePermissionsDatabase : RoomDatabase() { abstract fun sitePermissionsDao(): SitePermissionsDao abstract fun sitePermissionsAllowedDao(): SitePermissionsAllowedDao - abstract fun drmBlockDao(): DrmBlockDao } val MIGRATION_1_2 = object : Migration(1, 2) { @@ -55,4 +51,10 @@ val MIGRATION_3_4 = object : Migration(3, 4) { } } -val ALL_MIGRATIONS = arrayOf(MIGRATION_1_2, MIGRATION_3_4) +val MIGRATION_4_5 = object : Migration(4, 5) { + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL("DROP TABLE IF EXISTS `drm_block_exceptions`") + } +} + +val ALL_MIGRATIONS = arrayOf(MIGRATION_1_2, MIGRATION_3_4, MIGRATION_4_5) diff --git a/site-permissions/site-permissions-store/src/main/java/com/duckduckgo/site/permissions/store/drmblock/DrmBlockDao.kt b/site-permissions/site-permissions-store/src/main/java/com/duckduckgo/site/permissions/store/drmblock/DrmBlockDao.kt deleted file mode 100644 index ca25d1533a57..000000000000 --- a/site-permissions/site-permissions-store/src/main/java/com/duckduckgo/site/permissions/store/drmblock/DrmBlockDao.kt +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (c) 2023 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.site.permissions.store.drmblock - -import androidx.room.Dao -import androidx.room.Insert -import androidx.room.OnConflictStrategy -import androidx.room.Query -import androidx.room.Transaction - -@Dao -abstract class DrmBlockDao { - - @Insert(onConflict = OnConflictStrategy.REPLACE) - abstract fun insertAll(domains: List) - - @Transaction - open fun updateAll(domains: List) { - deleteAll() - insertAll(domains) - } - - @Query("select * from drm_block_exceptions") - abstract fun getAll(): List - - @Query("delete from drm_block_exceptions") - abstract fun deleteAll() -} diff --git a/site-permissions/site-permissions-store/src/main/java/com/duckduckgo/site/permissions/store/drmblock/DrmBlockExceptionEntity.kt b/site-permissions/site-permissions-store/src/main/java/com/duckduckgo/site/permissions/store/drmblock/DrmBlockExceptionEntity.kt deleted file mode 100644 index 58165b69b367..000000000000 --- a/site-permissions/site-permissions-store/src/main/java/com/duckduckgo/site/permissions/store/drmblock/DrmBlockExceptionEntity.kt +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (c) 2023 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.site.permissions.store.drmblock - -import androidx.room.Entity -import androidx.room.PrimaryKey -import com.duckduckgo.feature.toggles.api.FeatureExceptions.FeatureException - -@Entity(tableName = "drm_block_exceptions") -data class DrmBlockExceptionEntity(@PrimaryKey val domain: String) - -fun DrmBlockExceptionEntity.toFeatureException(): FeatureException { - return FeatureException(domain = this.domain, reason = null) -} From f42623c9a28d8207ec955b5661855da710362f76 Mon Sep 17 00:00:00 2001 From: Aitor Viana Date: Fri, 4 Apr 2025 14:28:17 +0100 Subject: [PATCH 5/9] remove MaliciousSiteProtectionExceptionsStore --- .../impl/MaliciousSiteProtectionFeature.kt | 2 - .../impl/data/db/MaliciousSiteDao.kt | 15 ----- .../data/db/MaliciousSitesDataEntities.kt | 7 --- .../impl/data/db/MaliciousSitesDatabase.kt | 12 +++- .../MaliciousSiteProtectionExceptionsStore.kt | 35 ------------ .../MaliciousSiteProtectionRCRepository.kt | 45 ++++++++------- ...MaliciousSiteProtectionRCRepositoryTest.kt | 55 +++++++++++++++++++ 7 files changed, 90 insertions(+), 81 deletions(-) delete mode 100644 malicious-site-protection/malicious-site-protection-impl/src/main/kotlin/com/duckduckgo/malicioussiteprotection/impl/remoteconfig/MaliciousSiteProtectionExceptionsStore.kt create mode 100644 malicious-site-protection/malicious-site-protection-impl/src/test/kotlin/com/duckduckgo/malicioussiteprotection/impl/remoteconfig/RealMaliciousSiteProtectionRCRepositoryTest.kt diff --git a/malicious-site-protection/malicious-site-protection-impl/src/main/kotlin/com/duckduckgo/malicioussiteprotection/impl/MaliciousSiteProtectionFeature.kt b/malicious-site-protection/malicious-site-protection-impl/src/main/kotlin/com/duckduckgo/malicioussiteprotection/impl/MaliciousSiteProtectionFeature.kt index 2a9a88d0bdf9..6f5e706b0e1b 100644 --- a/malicious-site-protection/malicious-site-protection-impl/src/main/kotlin/com/duckduckgo/malicioussiteprotection/impl/MaliciousSiteProtectionFeature.kt +++ b/malicious-site-protection/malicious-site-protection-impl/src/main/kotlin/com/duckduckgo/malicioussiteprotection/impl/MaliciousSiteProtectionFeature.kt @@ -20,12 +20,10 @@ import com.duckduckgo.anvil.annotations.ContributesRemoteFeature import com.duckduckgo.di.scopes.AppScope import com.duckduckgo.feature.toggles.api.Toggle import com.duckduckgo.feature.toggles.api.Toggle.DefaultFeatureValue -import com.duckduckgo.malicioussiteprotection.impl.remoteconfig.MaliciousSiteProtectionExceptionsStore @ContributesRemoteFeature( scope = AppScope::class, featureName = "maliciousSiteProtection", - exceptionsStore = MaliciousSiteProtectionExceptionsStore::class, ) /** * This is the class that represents the maliciousSiteProtection feature flags diff --git a/malicious-site-protection/malicious-site-protection-impl/src/main/kotlin/com/duckduckgo/malicioussiteprotection/impl/data/db/MaliciousSiteDao.kt b/malicious-site-protection/malicious-site-protection-impl/src/main/kotlin/com/duckduckgo/malicioussiteprotection/impl/data/db/MaliciousSiteDao.kt index de45177ad60b..a94981232f41 100644 --- a/malicious-site-protection/malicious-site-protection-impl/src/main/kotlin/com/duckduckgo/malicioussiteprotection/impl/data/db/MaliciousSiteDao.kt +++ b/malicious-site-protection/malicious-site-protection-impl/src/main/kotlin/com/duckduckgo/malicioussiteprotection/impl/data/db/MaliciousSiteDao.kt @@ -161,19 +161,4 @@ interface MaliciousSiteDao { private suspend fun getLatestRevision(feed: Feed, type: Type): Int { return getLatestRevision(feed = feed.name, type = type.name)?.revision ?: 0 } - - @Transaction - fun updateAllExceptions(exceptions: List) { - deleteAllExceptions() - insertAllExceptions(exceptions) - } - - @Query("DELETE FROM featureExceptions") - fun deleteAllExceptions() {} - - @Insert(onConflict = OnConflictStrategy.REPLACE) - fun insertAllExceptions(exceptions: List) - - @Query("SELECT * FROM featureExceptions") - fun getExceptions(): List } diff --git a/malicious-site-protection/malicious-site-protection-impl/src/main/kotlin/com/duckduckgo/malicioussiteprotection/impl/data/db/MaliciousSitesDataEntities.kt b/malicious-site-protection/malicious-site-protection-impl/src/main/kotlin/com/duckduckgo/malicioussiteprotection/impl/data/db/MaliciousSitesDataEntities.kt index a5f34df99dff..5cb1d1fb79c6 100644 --- a/malicious-site-protection/malicious-site-protection-impl/src/main/kotlin/com/duckduckgo/malicioussiteprotection/impl/data/db/MaliciousSitesDataEntities.kt +++ b/malicious-site-protection/malicious-site-protection-impl/src/main/kotlin/com/duckduckgo/malicioussiteprotection/impl/data/db/MaliciousSitesDataEntities.kt @@ -44,10 +44,3 @@ data class FilterEntity( val regex: String, val type: String, ) - -@Entity(tableName = "featureExceptions") -data class FeatureExceptionEntity( - @PrimaryKey - val domain: String, - val reason: String?, -) diff --git a/malicious-site-protection/malicious-site-protection-impl/src/main/kotlin/com/duckduckgo/malicioussiteprotection/impl/data/db/MaliciousSitesDatabase.kt b/malicious-site-protection/malicious-site-protection-impl/src/main/kotlin/com/duckduckgo/malicioussiteprotection/impl/data/db/MaliciousSitesDatabase.kt index 3140c270c61b..8cb9e6e9d481 100644 --- a/malicious-site-protection/malicious-site-protection-impl/src/main/kotlin/com/duckduckgo/malicioussiteprotection/impl/data/db/MaliciousSitesDatabase.kt +++ b/malicious-site-protection/malicious-site-protection-impl/src/main/kotlin/com/duckduckgo/malicioussiteprotection/impl/data/db/MaliciousSitesDatabase.kt @@ -19,16 +19,22 @@ package com.duckduckgo.malicioussiteprotection.impl.data.db import androidx.room.Database import androidx.room.RoomDatabase import androidx.room.migration.Migration +import androidx.sqlite.db.SupportSQLiteDatabase @Database( exportSchema = true, - entities = [RevisionEntity::class, HashPrefixEntity::class, FilterEntity::class, FeatureExceptionEntity::class], - version = 2, + entities = [RevisionEntity::class, HashPrefixEntity::class, FilterEntity::class], + version = 3, ) abstract class MaliciousSitesDatabase : RoomDatabase() { abstract fun maliciousSiteDao(): MaliciousSiteDao companion object { - val ALL_MIGRATIONS = arrayOf() + internal val MIGRATION_2_3 = object : Migration(2, 3) { + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL("DROP TABLE IF EXISTS `featureExceptions`") + } + } + internal val ALL_MIGRATIONS = arrayOf(MIGRATION_2_3) } } diff --git a/malicious-site-protection/malicious-site-protection-impl/src/main/kotlin/com/duckduckgo/malicioussiteprotection/impl/remoteconfig/MaliciousSiteProtectionExceptionsStore.kt b/malicious-site-protection/malicious-site-protection-impl/src/main/kotlin/com/duckduckgo/malicioussiteprotection/impl/remoteconfig/MaliciousSiteProtectionExceptionsStore.kt deleted file mode 100644 index 97214e5f3bf2..000000000000 --- a/malicious-site-protection/malicious-site-protection-impl/src/main/kotlin/com/duckduckgo/malicioussiteprotection/impl/remoteconfig/MaliciousSiteProtectionExceptionsStore.kt +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (c) 2024 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.malicioussiteprotection.impl.remoteconfig - -import com.duckduckgo.di.scopes.AppScope -import com.duckduckgo.feature.toggles.api.FeatureExceptions -import com.duckduckgo.feature.toggles.api.FeatureExceptions.FeatureException -import com.duckduckgo.feature.toggles.api.RemoteFeatureStoreNamed -import com.duckduckgo.malicioussiteprotection.impl.MaliciousSiteProtectionFeature -import com.squareup.anvil.annotations.ContributesBinding -import javax.inject.Inject - -@ContributesBinding(AppScope::class) -@RemoteFeatureStoreNamed(MaliciousSiteProtectionFeature::class) -class MaliciousSiteProtectionExceptionsStore @Inject constructor( - private val maliciousSiteProtectionRCRepository: MaliciousSiteProtectionRCRepository, -) : FeatureExceptions.Store { - override fun insertAll(exception: List) { - maliciousSiteProtectionRCRepository.insertAllExceptions(exception) - } -} diff --git a/malicious-site-protection/malicious-site-protection-impl/src/main/kotlin/com/duckduckgo/malicioussiteprotection/impl/remoteconfig/MaliciousSiteProtectionRCRepository.kt b/malicious-site-protection/malicious-site-protection-impl/src/main/kotlin/com/duckduckgo/malicioussiteprotection/impl/remoteconfig/MaliciousSiteProtectionRCRepository.kt index dc2820026f4f..1ee651b43741 100644 --- a/malicious-site-protection/malicious-site-protection-impl/src/main/kotlin/com/duckduckgo/malicioussiteprotection/impl/remoteconfig/MaliciousSiteProtectionRCRepository.kt +++ b/malicious-site-protection/malicious-site-protection-impl/src/main/kotlin/com/duckduckgo/malicioussiteprotection/impl/remoteconfig/MaliciousSiteProtectionRCRepository.kt @@ -21,40 +21,44 @@ import com.duckduckgo.app.di.IsMainProcess import com.duckduckgo.common.utils.DispatcherProvider import com.duckduckgo.di.scopes.AppScope import com.duckduckgo.feature.toggles.api.FeatureExceptions.FeatureException -import com.duckduckgo.malicioussiteprotection.impl.data.db.FeatureExceptionEntity -import com.duckduckgo.malicioussiteprotection.impl.data.db.MaliciousSiteDao +import com.duckduckgo.malicioussiteprotection.impl.MaliciousSiteProtectionFeature +import com.duckduckgo.privacy.config.api.PrivacyConfigCallbackPlugin import com.squareup.anvil.annotations.ContributesBinding +import com.squareup.anvil.annotations.ContributesMultibinding +import dagger.SingleInstanceIn import java.util.concurrent.CopyOnWriteArrayList import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch interface MaliciousSiteProtectionRCRepository { - fun insertAllExceptions(exceptions: List) fun isExempted(hostName: String): Boolean val exceptions: CopyOnWriteArrayList } -@ContributesBinding(AppScope::class) +@ContributesBinding( + scope = AppScope::class, + boundType = MaliciousSiteProtectionRCRepository::class, +) +@ContributesMultibinding( + scope = AppScope::class, + boundType = PrivacyConfigCallbackPlugin::class, +) +@SingleInstanceIn(AppScope::class) class RealMaliciousSiteProtectionRCRepository @Inject constructor( - @AppCoroutineScope coroutineScope: CoroutineScope, - dispatcherProvider: DispatcherProvider, - val dao: MaliciousSiteDao, - @IsMainProcess isMainProcess: Boolean, -) : MaliciousSiteProtectionRCRepository { + private val feature: MaliciousSiteProtectionFeature, + @AppCoroutineScope private val coroutineScope: CoroutineScope, + private val dispatcherProvider: DispatcherProvider, + @IsMainProcess private val isMainProcess: Boolean, +) : MaliciousSiteProtectionRCRepository, PrivacyConfigCallbackPlugin { override val exceptions = CopyOnWriteArrayList() init { - coroutineScope.launch(dispatcherProvider.io()) { - if (isMainProcess) { - loadToMemory() - } - } + loadToMemory() } - override fun insertAllExceptions(exceptions: List) { - dao.updateAllExceptions(exceptions.map { FeatureExceptionEntity(domain = it.domain, reason = it.reason) }) + override fun onPrivacyConfigDownloaded() { loadToMemory() } @@ -63,8 +67,11 @@ class RealMaliciousSiteProtectionRCRepository @Inject constructor( } private fun loadToMemory() { - exceptions.clear() - val exceptionsEntityList = dao.getExceptions() - exceptions.addAll(exceptionsEntityList.map { FeatureException(domain = it.domain, reason = it.reason) }) + coroutineScope.launch(dispatcherProvider.io()) { + if (isMainProcess) { + exceptions.clear() + exceptions.addAll(feature.self().getExceptions()) + } + } } } diff --git a/malicious-site-protection/malicious-site-protection-impl/src/test/kotlin/com/duckduckgo/malicioussiteprotection/impl/remoteconfig/RealMaliciousSiteProtectionRCRepositoryTest.kt b/malicious-site-protection/malicious-site-protection-impl/src/test/kotlin/com/duckduckgo/malicioussiteprotection/impl/remoteconfig/RealMaliciousSiteProtectionRCRepositoryTest.kt new file mode 100644 index 000000000000..e5f24127061b --- /dev/null +++ b/malicious-site-protection/malicious-site-protection-impl/src/test/kotlin/com/duckduckgo/malicioussiteprotection/impl/remoteconfig/RealMaliciousSiteProtectionRCRepositoryTest.kt @@ -0,0 +1,55 @@ +package com.duckduckgo.malicioussiteprotection.impl.remoteconfig + +import com.duckduckgo.common.test.CoroutineTestRule +import com.duckduckgo.feature.toggles.api.FakeFeatureToggleFactory +import com.duckduckgo.feature.toggles.api.FeatureExceptions.FeatureException +import com.duckduckgo.feature.toggles.api.Toggle +import com.duckduckgo.malicioussiteprotection.impl.MaliciousSiteProtectionFeature +import kotlinx.coroutines.test.runTest +import org.junit.Assert.assertEquals +import org.junit.Before +import org.junit.Rule +import org.junit.Test + +class RealMaliciousSiteProtectionRCRepositoryTest { + @get:Rule + var coroutineRule = CoroutineTestRule() + + private val feature = FakeFeatureToggleFactory.create(MaliciousSiteProtectionFeature::class.java) + + @Before + fun setup() { + feature.self().setRawStoredState(Toggle.State(exceptions = exceptions)) + } + + @Test + fun whenRepositoryIsCreatedThenExceptionsLoadIntoMemory() = runTest { + val repository = RealMaliciousSiteProtectionRCRepository( + feature, + coroutineRule.testScope, + coroutineRule.testDispatcherProvider, + true, + ) + + assertEquals(exceptions, repository.exceptions) + } + + @Test + fun whenRemoteConfigUpdateThenExceptionsUpdated() = runTest { + val repository = RealMaliciousSiteProtectionRCRepository( + feature, + coroutineRule.testScope, + coroutineRule.testDispatcherProvider, + true, + ) + + assertEquals(exceptions, repository.exceptions) + feature.self().setRawStoredState(Toggle.State(exceptions = emptyList())) + repository.onPrivacyConfigDownloaded() + assertEquals(emptyList(), repository.exceptions) + } + + companion object { + val exceptions = listOf(FeatureException("example.com", "reason")) + } +} From a155843f5c63eb3c6de7749fcdcbc5267747dd9f Mon Sep 17 00:00:00 2001 From: Aitor Viana Date: Fri, 4 Apr 2025 15:27:12 +0100 Subject: [PATCH 6/9] remove AutofillSiteBreakageReportingExceptionsPersister --- .../AutofillBreakageReportCanShowRules.kt | 2 +- ...iteBreakageReportingExceptionsPersister.kt | 37 ---------- .../AutofillSiteBreakageReportingFeature.kt | 1 - ...lSiteBreakageReportingFeatureRepository.kt | 70 +++++++++++++++++++ .../AutofillSiteBreakageReportingModule.kt | 29 -------- ...ofillBreakageReportCanShowRulesImplTest.kt | 2 +- ...akageReportingFeatureRepositoryImplTest.kt | 54 ++++++++++++++ .../AutofillSiteBreakageExceptionDatabase.kt | 66 ----------------- ...lSiteBreakageReportingFeatureRepository.kt | 56 --------------- 9 files changed, 126 insertions(+), 191 deletions(-) delete mode 100644 autofill/autofill-impl/src/main/java/com/duckduckgo/autofill/impl/reporting/remoteconfig/AutofillSiteBreakageReportingExceptionsPersister.kt create mode 100644 autofill/autofill-impl/src/main/java/com/duckduckgo/autofill/impl/reporting/remoteconfig/AutofillSiteBreakageReportingFeatureRepository.kt create mode 100644 autofill/autofill-impl/src/test/java/com/duckduckgo/autofill/impl/reporting/remoteconfig/AutofillSiteBreakageReportingFeatureRepositoryImplTest.kt delete mode 100644 autofill/autofill-store/src/main/java/com/duckduckgo/autofill/store/reporting/AutofillSiteBreakageExceptionDatabase.kt delete mode 100644 autofill/autofill-store/src/main/java/com/duckduckgo/autofill/store/reporting/AutofillSiteBreakageReportingFeatureRepository.kt diff --git a/autofill/autofill-impl/src/main/java/com/duckduckgo/autofill/impl/reporting/AutofillBreakageReportCanShowRules.kt b/autofill/autofill-impl/src/main/java/com/duckduckgo/autofill/impl/reporting/AutofillBreakageReportCanShowRules.kt index 6224ef28b229..8f93acfbfb88 100644 --- a/autofill/autofill-impl/src/main/java/com/duckduckgo/autofill/impl/reporting/AutofillBreakageReportCanShowRules.kt +++ b/autofill/autofill-impl/src/main/java/com/duckduckgo/autofill/impl/reporting/AutofillBreakageReportCanShowRules.kt @@ -17,9 +17,9 @@ package com.duckduckgo.autofill.impl.reporting import com.duckduckgo.autofill.impl.reporting.remoteconfig.AutofillSiteBreakageReportingFeature +import com.duckduckgo.autofill.impl.reporting.remoteconfig.AutofillSiteBreakageReportingFeatureRepository import com.duckduckgo.autofill.impl.time.TimeProvider import com.duckduckgo.autofill.impl.urlmatcher.AutofillUrlMatcher -import com.duckduckgo.autofill.store.reporting.AutofillSiteBreakageReportingFeatureRepository import com.duckduckgo.common.utils.DispatcherProvider import com.duckduckgo.di.scopes.AppScope import com.squareup.anvil.annotations.ContributesBinding diff --git a/autofill/autofill-impl/src/main/java/com/duckduckgo/autofill/impl/reporting/remoteconfig/AutofillSiteBreakageReportingExceptionsPersister.kt b/autofill/autofill-impl/src/main/java/com/duckduckgo/autofill/impl/reporting/remoteconfig/AutofillSiteBreakageReportingExceptionsPersister.kt deleted file mode 100644 index 86b4ca318729..000000000000 --- a/autofill/autofill-impl/src/main/java/com/duckduckgo/autofill/impl/reporting/remoteconfig/AutofillSiteBreakageReportingExceptionsPersister.kt +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (c) 2024 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.autofill.impl.reporting.remoteconfig - -import com.duckduckgo.autofill.store.reporting.AutofillSiteBreakageReportingEntity -import com.duckduckgo.autofill.store.reporting.AutofillSiteBreakageReportingFeatureRepository -import com.duckduckgo.di.scopes.AppScope -import com.duckduckgo.feature.toggles.api.FeatureExceptions -import com.duckduckgo.feature.toggles.api.RemoteFeatureStoreNamed -import com.squareup.anvil.annotations.ContributesBinding -import javax.inject.Inject - -@ContributesBinding(AppScope::class) -@RemoteFeatureStoreNamed(AutofillSiteBreakageReportingFeature::class) -class AutofillSiteBreakageReportingExceptionsPersister @Inject constructor( - private val repository: AutofillSiteBreakageReportingFeatureRepository, -) : FeatureExceptions.Store { - override fun insertAll(exception: List) { - repository.updateAllExceptions( - exception.map { AutofillSiteBreakageReportingEntity(domain = it.domain, reason = it.reason.orEmpty()) }, - ) - } -} diff --git a/autofill/autofill-impl/src/main/java/com/duckduckgo/autofill/impl/reporting/remoteconfig/AutofillSiteBreakageReportingFeature.kt b/autofill/autofill-impl/src/main/java/com/duckduckgo/autofill/impl/reporting/remoteconfig/AutofillSiteBreakageReportingFeature.kt index 9c21bf022539..64d51aa792a3 100644 --- a/autofill/autofill-impl/src/main/java/com/duckduckgo/autofill/impl/reporting/remoteconfig/AutofillSiteBreakageReportingFeature.kt +++ b/autofill/autofill-impl/src/main/java/com/duckduckgo/autofill/impl/reporting/remoteconfig/AutofillSiteBreakageReportingFeature.kt @@ -26,7 +26,6 @@ import com.duckduckgo.feature.toggles.api.Toggle.DefaultFeatureValue boundType = AutofillSiteBreakageReportingFeature::class, featureName = "autofillBreakageReporter", settingsStore = AutofillSiteBreakageReportingRemoteSettingsPersister::class, - exceptionsStore = AutofillSiteBreakageReportingExceptionsPersister::class, ) /** * This is the class that represents the feature flag for offering to report Autofill breakages diff --git a/autofill/autofill-impl/src/main/java/com/duckduckgo/autofill/impl/reporting/remoteconfig/AutofillSiteBreakageReportingFeatureRepository.kt b/autofill/autofill-impl/src/main/java/com/duckduckgo/autofill/impl/reporting/remoteconfig/AutofillSiteBreakageReportingFeatureRepository.kt new file mode 100644 index 000000000000..08a6d4f6a89a --- /dev/null +++ b/autofill/autofill-impl/src/main/java/com/duckduckgo/autofill/impl/reporting/remoteconfig/AutofillSiteBreakageReportingFeatureRepository.kt @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2025 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.autofill.impl.reporting.remoteconfig + +import com.duckduckgo.app.di.AppCoroutineScope +import com.duckduckgo.app.di.IsMainProcess +import com.duckduckgo.common.utils.DispatcherProvider +import com.duckduckgo.di.scopes.AppScope +import com.duckduckgo.privacy.config.api.PrivacyConfigCallbackPlugin +import com.squareup.anvil.annotations.ContributesBinding +import com.squareup.anvil.annotations.ContributesMultibinding +import dagger.SingleInstanceIn +import java.util.concurrent.CopyOnWriteArrayList +import javax.inject.Inject +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.launch + +interface AutofillSiteBreakageReportingFeatureRepository { + val exceptions: List +} + +@ContributesBinding( + scope = AppScope::class, + boundType = AutofillSiteBreakageReportingFeatureRepository::class, +) +@ContributesMultibinding( + scope = AppScope::class, + boundType = PrivacyConfigCallbackPlugin::class, +) +@SingleInstanceIn(AppScope::class) +class AutofillSiteBreakageReportingFeatureRepositoryImpl @Inject constructor( + private val feature: AutofillSiteBreakageReportingFeature, + @AppCoroutineScope private val coroutineScope: CoroutineScope, + private val dispatcherProvider: DispatcherProvider, + @IsMainProcess private val isMainProcess: Boolean, +) : AutofillSiteBreakageReportingFeatureRepository, PrivacyConfigCallbackPlugin { + + override val exceptions = CopyOnWriteArrayList() + + init { + loadToMemory() + } + + override fun onPrivacyConfigDownloaded() { + loadToMemory() + } + + private fun loadToMemory() { + coroutineScope.launch(dispatcherProvider.io()) { + if (isMainProcess) { + exceptions.clear() + exceptions.addAll(feature.self().getExceptions().map { it.domain }) + } + } + } +} diff --git a/autofill/autofill-impl/src/main/java/com/duckduckgo/autofill/impl/reporting/remoteconfig/AutofillSiteBreakageReportingModule.kt b/autofill/autofill-impl/src/main/java/com/duckduckgo/autofill/impl/reporting/remoteconfig/AutofillSiteBreakageReportingModule.kt index 350bf7c667ef..f5171c9bd06a 100644 --- a/autofill/autofill-impl/src/main/java/com/duckduckgo/autofill/impl/reporting/remoteconfig/AutofillSiteBreakageReportingModule.kt +++ b/autofill/autofill-impl/src/main/java/com/duckduckgo/autofill/impl/reporting/remoteconfig/AutofillSiteBreakageReportingModule.kt @@ -20,46 +20,17 @@ import android.content.Context import androidx.datastore.core.DataStore import androidx.datastore.preferences.core.Preferences import androidx.datastore.preferences.preferencesDataStore -import androidx.room.Room -import com.duckduckgo.app.di.AppCoroutineScope -import com.duckduckgo.app.di.IsMainProcess -import com.duckduckgo.autofill.store.reporting.ALL_MIGRATIONS -import com.duckduckgo.autofill.store.reporting.AutofillSiteBreakageReportingDatabase -import com.duckduckgo.autofill.store.reporting.AutofillSiteBreakageReportingFeatureRepository -import com.duckduckgo.autofill.store.reporting.AutofillSiteBreakageReportingFeatureRepositoryImpl -import com.duckduckgo.common.utils.DispatcherProvider import com.duckduckgo.di.scopes.AppScope import com.squareup.anvil.annotations.ContributesTo import dagger.Module import dagger.Provides import dagger.SingleInstanceIn import javax.inject.Qualifier -import kotlinx.coroutines.CoroutineScope @Module @ContributesTo(AppScope::class) class AutofillSiteBreakageReportingModule { - @SingleInstanceIn(AppScope::class) - @Provides - fun repository( - database: AutofillSiteBreakageReportingDatabase, - @AppCoroutineScope appCoroutineScope: CoroutineScope, - dispatcherProvider: DispatcherProvider, - @IsMainProcess isMainProcess: Boolean, - ): AutofillSiteBreakageReportingFeatureRepository { - return AutofillSiteBreakageReportingFeatureRepositoryImpl(database, appCoroutineScope, dispatcherProvider, isMainProcess) - } - - @Provides - @SingleInstanceIn(AppScope::class) - fun database(context: Context): AutofillSiteBreakageReportingDatabase { - return Room.databaseBuilder(context, AutofillSiteBreakageReportingDatabase::class.java, "autofillSiteBreakageReporting.db") - .fallbackToDestructiveMigration() - .addMigrations(*ALL_MIGRATIONS) - .build() - } - private val Context.autofillSiteBreakageReportingDataStore: DataStore by preferencesDataStore( name = "autofill_site_breakage_reporting", ) diff --git a/autofill/autofill-impl/src/test/java/com/duckduckgo/autofill/impl/reporting/AutofillBreakageReportCanShowRulesImplTest.kt b/autofill/autofill-impl/src/test/java/com/duckduckgo/autofill/impl/reporting/AutofillBreakageReportCanShowRulesImplTest.kt index 767d6f36aa1a..bf5a04155f8d 100644 --- a/autofill/autofill-impl/src/test/java/com/duckduckgo/autofill/impl/reporting/AutofillBreakageReportCanShowRulesImplTest.kt +++ b/autofill/autofill-impl/src/test/java/com/duckduckgo/autofill/impl/reporting/AutofillBreakageReportCanShowRulesImplTest.kt @@ -3,9 +3,9 @@ package com.duckduckgo.autofill.impl.reporting import androidx.test.ext.junit.runners.AndroidJUnit4 import com.duckduckgo.autofill.impl.encoding.UrlUnicodeNormalizerImpl import com.duckduckgo.autofill.impl.reporting.remoteconfig.AutofillSiteBreakageReportingFeature +import com.duckduckgo.autofill.impl.reporting.remoteconfig.AutofillSiteBreakageReportingFeatureRepository import com.duckduckgo.autofill.impl.time.TimeProvider import com.duckduckgo.autofill.impl.urlmatcher.AutofillDomainNameUrlMatcher -import com.duckduckgo.autofill.store.reporting.AutofillSiteBreakageReportingFeatureRepository import com.duckduckgo.common.test.CoroutineTestRule import com.duckduckgo.feature.toggles.api.FakeFeatureToggleFactory import com.duckduckgo.feature.toggles.api.Toggle.State diff --git a/autofill/autofill-impl/src/test/java/com/duckduckgo/autofill/impl/reporting/remoteconfig/AutofillSiteBreakageReportingFeatureRepositoryImplTest.kt b/autofill/autofill-impl/src/test/java/com/duckduckgo/autofill/impl/reporting/remoteconfig/AutofillSiteBreakageReportingFeatureRepositoryImplTest.kt new file mode 100644 index 000000000000..b89edcb6abc4 --- /dev/null +++ b/autofill/autofill-impl/src/test/java/com/duckduckgo/autofill/impl/reporting/remoteconfig/AutofillSiteBreakageReportingFeatureRepositoryImplTest.kt @@ -0,0 +1,54 @@ +package com.duckduckgo.autofill.impl.reporting.remoteconfig + +import com.duckduckgo.common.test.CoroutineTestRule +import com.duckduckgo.feature.toggles.api.FakeFeatureToggleFactory +import com.duckduckgo.feature.toggles.api.FeatureExceptions.FeatureException +import com.duckduckgo.feature.toggles.api.Toggle +import kotlinx.coroutines.test.runTest +import org.junit.Assert.assertEquals +import org.junit.Before +import org.junit.Rule +import org.junit.Test + +class AutofillSiteBreakageReportingFeatureRepositoryImplTest { + @get:Rule + var coroutineRule = CoroutineTestRule() + + private val feature = FakeFeatureToggleFactory.create(AutofillSiteBreakageReportingFeature::class.java) + + @Before + fun setup() { + feature.self().setRawStoredState(Toggle.State(exceptions = exceptions)) + } + + @Test + fun whenRepositoryIsCreatedThenExceptionsLoadIntoMemory() = runTest { + val repository = AutofillSiteBreakageReportingFeatureRepositoryImpl( + feature, + coroutineRule.testScope, + coroutineRule.testDispatcherProvider, + true, + ) + + assertEquals(exceptions.map { it.domain }, repository.exceptions) + } + + @Test + fun whenRemoteConfigUpdateThenExceptionsUpdated() = runTest { + val repository = AutofillSiteBreakageReportingFeatureRepositoryImpl( + feature, + coroutineRule.testScope, + coroutineRule.testDispatcherProvider, + true, + ) + + assertEquals(exceptions.map { it.domain }, repository.exceptions) + feature.self().setRawStoredState(Toggle.State(exceptions = emptyList())) + repository.onPrivacyConfigDownloaded() + assertEquals(emptyList(), repository.exceptions) + } + + companion object { + val exceptions = listOf(FeatureException("example.com", "reason")) + } +} diff --git a/autofill/autofill-store/src/main/java/com/duckduckgo/autofill/store/reporting/AutofillSiteBreakageExceptionDatabase.kt b/autofill/autofill-store/src/main/java/com/duckduckgo/autofill/store/reporting/AutofillSiteBreakageExceptionDatabase.kt deleted file mode 100644 index 40c915fb03e4..000000000000 --- a/autofill/autofill-store/src/main/java/com/duckduckgo/autofill/store/reporting/AutofillSiteBreakageExceptionDatabase.kt +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (c) 2024 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.autofill.store.reporting - -import androidx.room.Dao -import androidx.room.Database -import androidx.room.Entity -import androidx.room.Insert -import androidx.room.OnConflictStrategy -import androidx.room.PrimaryKey -import androidx.room.Query -import androidx.room.RoomDatabase -import androidx.room.Transaction -import androidx.room.migration.Migration - -@Database( - exportSchema = true, - version = 1, - entities = [ - AutofillSiteBreakageReportingEntity::class, - ], -) -abstract class AutofillSiteBreakageReportingDatabase : RoomDatabase() { - abstract fun dao(): AutofillSiteBreakageReportingDao -} - -@Entity(tableName = "autofill_site_breakage_reporting") -data class AutofillSiteBreakageReportingEntity( - @PrimaryKey val domain: String, - val reason: String, -) - -val ALL_MIGRATIONS = emptyArray() - -@Dao -abstract class AutofillSiteBreakageReportingDao { - - @Insert(onConflict = OnConflictStrategy.REPLACE) - abstract fun insertAll(domains: List) - - @Transaction - open fun updateAll(domains: List) { - deleteAll() - insertAll(domains) - } - - @Query("select * from autofill_site_breakage_reporting") - abstract fun getAll(): List - - @Query("delete from autofill_site_breakage_reporting") - abstract fun deleteAll() -} diff --git a/autofill/autofill-store/src/main/java/com/duckduckgo/autofill/store/reporting/AutofillSiteBreakageReportingFeatureRepository.kt b/autofill/autofill-store/src/main/java/com/duckduckgo/autofill/store/reporting/AutofillSiteBreakageReportingFeatureRepository.kt deleted file mode 100644 index c00f71d8ec50..000000000000 --- a/autofill/autofill-store/src/main/java/com/duckduckgo/autofill/store/reporting/AutofillSiteBreakageReportingFeatureRepository.kt +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (c) 2024 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.autofill.store.reporting - -import com.duckduckgo.common.utils.DispatcherProvider -import java.util.concurrent.CopyOnWriteArrayList -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.launch - -interface AutofillSiteBreakageReportingFeatureRepository { - fun updateAllExceptions(exceptions: List) - val exceptions: List -} - -class AutofillSiteBreakageReportingFeatureRepositoryImpl( - val database: AutofillSiteBreakageReportingDatabase, - coroutineScope: CoroutineScope, - dispatcherProvider: DispatcherProvider, - isMainProcess: Boolean, -) : AutofillSiteBreakageReportingFeatureRepository { - - private val dao = database.dao() - override val exceptions = CopyOnWriteArrayList() - - init { - coroutineScope.launch(dispatcherProvider.io()) { - if (isMainProcess) { - loadToMemory() - } - } - } - - override fun updateAllExceptions(exceptions: List) { - dao.updateAll(exceptions) - loadToMemory() - } - - private fun loadToMemory() { - exceptions.clear() - dao.getAll().map { exceptions.add(it.domain) } - } -} From 06a846508320259486491e5fc49f71819a57b669 Mon Sep 17 00:00:00 2001 From: Aitor Viana Date: Fri, 4 Apr 2025 16:11:49 +0100 Subject: [PATCH 7/9] remove EmailProtectionInContextRemoteExceptionsPersister --- .../autofill/impl/di/AutofillModule.kt | 24 ------- .../EmailProtectionInContextExceptionsImpl.kt | 1 - ...ailProtectionInContextFeatureRepository.kt | 71 +++++++++++++++++++ ...ctionInContextRemoteExceptionsPersister.kt | 38 ---------- ...ontextSignupRemoteFeatureCodegenTrigger.kt | 1 - ...rotectionInContextFeatureRepositoryTest.kt | 55 ++++++++++++++ .../incontext/EmailProtectionInContextDao.kt | 45 ------------ .../EmailProtectionInContextDatabase.kt | 42 ----------- ...ailProtectionInContextFeatureRepository.kt | 56 --------------- 9 files changed, 126 insertions(+), 207 deletions(-) create mode 100644 autofill/autofill-impl/src/main/java/com/duckduckgo/autofill/impl/email/remoteconfig/EmailProtectionInContextFeatureRepository.kt delete mode 100644 autofill/autofill-impl/src/main/java/com/duckduckgo/autofill/impl/email/remoteconfig/EmailProtectionInContextRemoteExceptionsPersister.kt create mode 100644 autofill/autofill-impl/src/test/java/com/duckduckgo/autofill/impl/email/remoteconfig/RealEmailProtectionInContextFeatureRepositoryTest.kt delete mode 100644 autofill/autofill-store/src/main/java/com/duckduckgo/autofill/store/feature/email/incontext/EmailProtectionInContextDao.kt delete mode 100644 autofill/autofill-store/src/main/java/com/duckduckgo/autofill/store/feature/email/incontext/EmailProtectionInContextDatabase.kt delete mode 100644 autofill/autofill-store/src/main/java/com/duckduckgo/autofill/store/feature/email/incontext/EmailProtectionInContextFeatureRepository.kt diff --git a/autofill/autofill-impl/src/main/java/com/duckduckgo/autofill/impl/di/AutofillModule.kt b/autofill/autofill-impl/src/main/java/com/duckduckgo/autofill/impl/di/AutofillModule.kt index 7259323e5520..038e39036ba3 100644 --- a/autofill/autofill-impl/src/main/java/com/duckduckgo/autofill/impl/di/AutofillModule.kt +++ b/autofill/autofill-impl/src/main/java/com/duckduckgo/autofill/impl/di/AutofillModule.kt @@ -42,10 +42,6 @@ import com.duckduckgo.autofill.store.feature.AutofillDefaultStateDecider import com.duckduckgo.autofill.store.feature.AutofillFeatureRepository import com.duckduckgo.autofill.store.feature.RealAutofillDefaultStateDecider import com.duckduckgo.autofill.store.feature.RealAutofillFeatureRepository -import com.duckduckgo.autofill.store.feature.email.incontext.ALL_MIGRATIONS as EmailInContextMigrations -import com.duckduckgo.autofill.store.feature.email.incontext.EmailProtectionInContextDatabase -import com.duckduckgo.autofill.store.feature.email.incontext.EmailProtectionInContextFeatureRepository -import com.duckduckgo.autofill.store.feature.email.incontext.RealEmailProtectionInContextFeatureRepository import com.duckduckgo.autofill.store.targets.DomainTargetAppDao import com.duckduckgo.autofill.store.targets.DomainTargetAppsDatabase import com.duckduckgo.browser.api.UserBrowserProperties @@ -103,15 +99,6 @@ class AutofillModule { .build() } - @SingleInstanceIn(AppScope::class) - @Provides - fun provideEmailInContextDatabase(context: Context): EmailProtectionInContextDatabase { - return Room.databaseBuilder(context, EmailProtectionInContextDatabase::class.java, "emailInContext.db") - .fallbackToDestructiveMigration() - .addMigrations(*EmailInContextMigrations) - .build() - } - @SingleInstanceIn(AppScope::class) @Provides fun provideAutofillRepository( @@ -123,17 +110,6 @@ class AutofillModule { return RealAutofillFeatureRepository(database, appCoroutineScope, dispatcherProvider, isMainProcess) } - @SingleInstanceIn(AppScope::class) - @Provides - fun provideEmailInContextRepository( - database: EmailProtectionInContextDatabase, - @AppCoroutineScope appCoroutineScope: CoroutineScope, - dispatcherProvider: DispatcherProvider, - @IsMainProcess isMainProcess: Boolean, - ): EmailProtectionInContextFeatureRepository { - return RealEmailProtectionInContextFeatureRepository(database, appCoroutineScope, dispatcherProvider, isMainProcess) - } - @Provides @SingleInstanceIn(AppScope::class) fun providesCredentialsSyncDao( diff --git a/autofill/autofill-impl/src/main/java/com/duckduckgo/autofill/impl/email/remoteconfig/EmailProtectionInContextExceptionsImpl.kt b/autofill/autofill-impl/src/main/java/com/duckduckgo/autofill/impl/email/remoteconfig/EmailProtectionInContextExceptionsImpl.kt index 8b3048c6194d..90306c8d122b 100644 --- a/autofill/autofill-impl/src/main/java/com/duckduckgo/autofill/impl/email/remoteconfig/EmailProtectionInContextExceptionsImpl.kt +++ b/autofill/autofill-impl/src/main/java/com/duckduckgo/autofill/impl/email/remoteconfig/EmailProtectionInContextExceptionsImpl.kt @@ -17,7 +17,6 @@ package com.duckduckgo.autofill.impl.email.remoteconfig import com.duckduckgo.app.browser.UriString.Companion.sameOrSubdomain -import com.duckduckgo.autofill.store.feature.email.incontext.EmailProtectionInContextFeatureRepository import com.duckduckgo.di.scopes.AppScope import com.squareup.anvil.annotations.ContributesBinding import dagger.SingleInstanceIn diff --git a/autofill/autofill-impl/src/main/java/com/duckduckgo/autofill/impl/email/remoteconfig/EmailProtectionInContextFeatureRepository.kt b/autofill/autofill-impl/src/main/java/com/duckduckgo/autofill/impl/email/remoteconfig/EmailProtectionInContextFeatureRepository.kt new file mode 100644 index 000000000000..f8afda0071b8 --- /dev/null +++ b/autofill/autofill-impl/src/main/java/com/duckduckgo/autofill/impl/email/remoteconfig/EmailProtectionInContextFeatureRepository.kt @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2025 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.autofill.impl.email.remoteconfig + +import com.duckduckgo.app.di.AppCoroutineScope +import com.duckduckgo.app.di.IsMainProcess +import com.duckduckgo.autofill.impl.email.incontext.EmailProtectionInContextSignupFeature +import com.duckduckgo.common.utils.DispatcherProvider +import com.duckduckgo.di.scopes.AppScope +import com.duckduckgo.privacy.config.api.PrivacyConfigCallbackPlugin +import com.squareup.anvil.annotations.ContributesBinding +import com.squareup.anvil.annotations.ContributesMultibinding +import dagger.SingleInstanceIn +import java.util.concurrent.CopyOnWriteArrayList +import javax.inject.Inject +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.launch + +interface EmailProtectionInContextFeatureRepository { + val exceptions: CopyOnWriteArrayList +} + +@ContributesBinding( + scope = AppScope::class, + boundType = EmailProtectionInContextFeatureRepository::class, +) +@ContributesMultibinding( + scope = AppScope::class, + boundType = PrivacyConfigCallbackPlugin::class, +) +@SingleInstanceIn(AppScope::class) +class RealEmailProtectionInContextFeatureRepository @Inject constructor( + private val feature: EmailProtectionInContextSignupFeature, + @AppCoroutineScope private val coroutineScope: CoroutineScope, + private val dispatcherProvider: DispatcherProvider, + @IsMainProcess private val isMainProcess: Boolean, +) : EmailProtectionInContextFeatureRepository, PrivacyConfigCallbackPlugin { + + override val exceptions = CopyOnWriteArrayList() + + init { + loadToMemory() + } + + override fun onPrivacyConfigDownloaded() { + loadToMemory() + } + + private fun loadToMemory() { + coroutineScope.launch(dispatcherProvider.io()) { + if (isMainProcess) { + exceptions.clear() + exceptions.addAll(feature.self().getExceptions().map { it.domain }) + } + } + } +} diff --git a/autofill/autofill-impl/src/main/java/com/duckduckgo/autofill/impl/email/remoteconfig/EmailProtectionInContextRemoteExceptionsPersister.kt b/autofill/autofill-impl/src/main/java/com/duckduckgo/autofill/impl/email/remoteconfig/EmailProtectionInContextRemoteExceptionsPersister.kt deleted file mode 100644 index f9eec8266f97..000000000000 --- a/autofill/autofill-impl/src/main/java/com/duckduckgo/autofill/impl/email/remoteconfig/EmailProtectionInContextRemoteExceptionsPersister.kt +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (c) 2023 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.autofill.impl.email.remoteconfig - -import com.duckduckgo.autofill.impl.email.incontext.EmailProtectionInContextSignupFeature -import com.duckduckgo.autofill.store.feature.email.incontext.EmailInContextExceptionEntity -import com.duckduckgo.autofill.store.feature.email.incontext.EmailProtectionInContextFeatureRepository -import com.duckduckgo.di.scopes.AppScope -import com.duckduckgo.feature.toggles.api.FeatureExceptions -import com.duckduckgo.feature.toggles.api.RemoteFeatureStoreNamed -import com.squareup.anvil.annotations.ContributesBinding -import javax.inject.Inject - -@ContributesBinding(AppScope::class) -@RemoteFeatureStoreNamed(EmailProtectionInContextSignupFeature::class) -class EmailProtectionInContextRemoteExceptionsPersister @Inject constructor( - private val emailProtectionFeatureRepository: EmailProtectionInContextFeatureRepository, -) : FeatureExceptions.Store { - override fun insertAll(exception: List) { - emailProtectionFeatureRepository.updateAllExceptions( - exception.map { EmailInContextExceptionEntity(domain = it.domain, reason = it.reason.orEmpty()) }, - ) - } -} diff --git a/autofill/autofill-impl/src/main/java/com/duckduckgo/autofill/impl/email/remoteconfig/UnusedEmailProtectionInContextSignupRemoteFeatureCodegenTrigger.kt b/autofill/autofill-impl/src/main/java/com/duckduckgo/autofill/impl/email/remoteconfig/UnusedEmailProtectionInContextSignupRemoteFeatureCodegenTrigger.kt index 4b2a72111079..890f8d91b87b 100644 --- a/autofill/autofill-impl/src/main/java/com/duckduckgo/autofill/impl/email/remoteconfig/UnusedEmailProtectionInContextSignupRemoteFeatureCodegenTrigger.kt +++ b/autofill/autofill-impl/src/main/java/com/duckduckgo/autofill/impl/email/remoteconfig/UnusedEmailProtectionInContextSignupRemoteFeatureCodegenTrigger.kt @@ -25,7 +25,6 @@ import com.duckduckgo.di.scopes.AppScope boundType = EmailProtectionInContextSignupFeature::class, featureName = "incontextSignup", settingsStore = EmailProtectionInContextRemoteSettingsPersister::class, - exceptionsStore = EmailProtectionInContextRemoteExceptionsPersister::class, ) @Suppress("unused") private interface UnusedEmailProtectionInContextSignupRemoteFeatureCodegenTrigger diff --git a/autofill/autofill-impl/src/test/java/com/duckduckgo/autofill/impl/email/remoteconfig/RealEmailProtectionInContextFeatureRepositoryTest.kt b/autofill/autofill-impl/src/test/java/com/duckduckgo/autofill/impl/email/remoteconfig/RealEmailProtectionInContextFeatureRepositoryTest.kt new file mode 100644 index 000000000000..eb8e90180a0a --- /dev/null +++ b/autofill/autofill-impl/src/test/java/com/duckduckgo/autofill/impl/email/remoteconfig/RealEmailProtectionInContextFeatureRepositoryTest.kt @@ -0,0 +1,55 @@ +package com.duckduckgo.autofill.impl.email.remoteconfig + +import com.duckduckgo.autofill.impl.email.incontext.EmailProtectionInContextSignupFeature +import com.duckduckgo.common.test.CoroutineTestRule +import com.duckduckgo.feature.toggles.api.FakeFeatureToggleFactory +import com.duckduckgo.feature.toggles.api.FeatureExceptions.FeatureException +import com.duckduckgo.feature.toggles.api.Toggle +import kotlinx.coroutines.test.runTest +import org.junit.Assert.assertEquals +import org.junit.Before +import org.junit.Rule +import org.junit.Test + +class RealEmailProtectionInContextFeatureRepositoryTest { + @get:Rule + var coroutineRule = CoroutineTestRule() + + private val feature = FakeFeatureToggleFactory.create(EmailProtectionInContextSignupFeature::class.java) + + @Before + fun setup() { + feature.self().setRawStoredState(Toggle.State(exceptions = exceptions)) + } + + @Test + fun whenRepositoryIsCreatedThenExceptionsLoadIntoMemory() = runTest { + val repository = RealEmailProtectionInContextFeatureRepository( + feature, + coroutineRule.testScope, + coroutineRule.testDispatcherProvider, + true, + ) + + assertEquals(exceptions.map { it.domain }, repository.exceptions) + } + + @Test + fun whenRemoteConfigUpdateThenExceptionsUpdated() = runTest { + val repository = RealEmailProtectionInContextFeatureRepository( + feature, + coroutineRule.testScope, + coroutineRule.testDispatcherProvider, + true, + ) + + assertEquals(exceptions.map { it.domain }, repository.exceptions) + feature.self().setRawStoredState(Toggle.State(exceptions = emptyList())) + repository.onPrivacyConfigDownloaded() + assertEquals(emptyList(), repository.exceptions) + } + + companion object { + val exceptions = listOf(FeatureException("example.com", "reason")) + } +} diff --git a/autofill/autofill-store/src/main/java/com/duckduckgo/autofill/store/feature/email/incontext/EmailProtectionInContextDao.kt b/autofill/autofill-store/src/main/java/com/duckduckgo/autofill/store/feature/email/incontext/EmailProtectionInContextDao.kt deleted file mode 100644 index 9f11b1bc4561..000000000000 --- a/autofill/autofill-store/src/main/java/com/duckduckgo/autofill/store/feature/email/incontext/EmailProtectionInContextDao.kt +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (c) 2023 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.autofill.store.feature.email.incontext - -import androidx.room.Dao -import androidx.room.Insert -import androidx.room.OnConflictStrategy -import androidx.room.Query -import androidx.room.Transaction - -@Dao -abstract class EmailProtectionInContextDao { - - @Insert(onConflict = OnConflictStrategy.REPLACE) - abstract fun insertAll(domains: List) - - @Transaction - open fun updateAll(domains: List) { - deleteAll() - insertAll(domains) - } - - @Query("select * from email_incontext_exceptions where domain = :domain") - abstract fun get(domain: String): EmailInContextExceptionEntity - - @Query("select * from email_incontext_exceptions") - abstract fun getAll(): List - - @Query("delete from email_incontext_exceptions") - abstract fun deleteAll() -} diff --git a/autofill/autofill-store/src/main/java/com/duckduckgo/autofill/store/feature/email/incontext/EmailProtectionInContextDatabase.kt b/autofill/autofill-store/src/main/java/com/duckduckgo/autofill/store/feature/email/incontext/EmailProtectionInContextDatabase.kt deleted file mode 100644 index f7a131370cbc..000000000000 --- a/autofill/autofill-store/src/main/java/com/duckduckgo/autofill/store/feature/email/incontext/EmailProtectionInContextDatabase.kt +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (c) 2023 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.autofill.store.feature.email.incontext - -import androidx.room.Database -import androidx.room.Entity -import androidx.room.PrimaryKey -import androidx.room.RoomDatabase -import androidx.room.migration.Migration - -@Database( - exportSchema = true, - version = 1, - entities = [ - EmailInContextExceptionEntity::class, - ], -) -abstract class EmailProtectionInContextDatabase : RoomDatabase() { - abstract fun emailInContextDao(): EmailProtectionInContextDao -} - -@Entity(tableName = "email_incontext_exceptions") -data class EmailInContextExceptionEntity( - @PrimaryKey val domain: String, - val reason: String, -) - -val ALL_MIGRATIONS = emptyArray() diff --git a/autofill/autofill-store/src/main/java/com/duckduckgo/autofill/store/feature/email/incontext/EmailProtectionInContextFeatureRepository.kt b/autofill/autofill-store/src/main/java/com/duckduckgo/autofill/store/feature/email/incontext/EmailProtectionInContextFeatureRepository.kt deleted file mode 100644 index 38c80aca19bf..000000000000 --- a/autofill/autofill-store/src/main/java/com/duckduckgo/autofill/store/feature/email/incontext/EmailProtectionInContextFeatureRepository.kt +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (c) 2023 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.autofill.store.feature.email.incontext - -import com.duckduckgo.common.utils.DispatcherProvider -import java.util.concurrent.CopyOnWriteArrayList -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.launch - -interface EmailProtectionInContextFeatureRepository { - fun updateAllExceptions(exceptions: List) - val exceptions: CopyOnWriteArrayList -} - -class RealEmailProtectionInContextFeatureRepository( - val database: EmailProtectionInContextDatabase, - coroutineScope: CoroutineScope, - dispatcherProvider: DispatcherProvider, - isMainProcess: Boolean, -) : EmailProtectionInContextFeatureRepository { - - private val dao = database.emailInContextDao() - override val exceptions = CopyOnWriteArrayList() - - init { - coroutineScope.launch(dispatcherProvider.io()) { - if (isMainProcess) { - loadToMemory() - } - } - } - - override fun updateAllExceptions(exceptions: List) { - dao.updateAll(exceptions) - loadToMemory() - } - - private fun loadToMemory() { - exceptions.clear() - dao.getAll().map { exceptions.add(it.domain) } - } -} From 48cc5fa16409682809cc9261135c8cb3cb643633 Mon Sep 17 00:00:00 2001 From: Aitor Viana Date: Fri, 4 Apr 2025 16:55:19 +0100 Subject: [PATCH 8/9] remove AutofillFeatureExceptionStore --- .../duckduckgo/autofill/impl/RealAutofill.kt | 2 +- .../autofill/impl/di/AutofillModule.kt | 17 ----- .../plugin/AutofillFeatureExceptionStore.kt | 38 ---------- .../plugin/AutofillFeatureRepository.kt | 72 +++++++++++++++++++ ...usedAutofillRemoteFeatureCodegenTrigger.kt | 1 - .../autofill/impl/RealAutofillTest.kt | 2 +- .../RealAutofillFeatureRepositoryTest.kt | 55 ++++++++++++++ .../duckduckgo/autofill/store/AutofillDao.kt | 45 ------------ .../autofill/store/AutofillDatabase.kt | 12 ++-- .../autofill/store/AutofillDatabaseModels.kt | 31 -------- .../feature/AutofillFeatureRepository.kt | 61 ---------------- 11 files changed, 137 insertions(+), 199 deletions(-) delete mode 100644 autofill/autofill-impl/src/main/java/com/duckduckgo/autofill/impl/feature/plugin/AutofillFeatureExceptionStore.kt create mode 100644 autofill/autofill-impl/src/main/java/com/duckduckgo/autofill/impl/feature/plugin/AutofillFeatureRepository.kt create mode 100644 autofill/autofill-impl/src/test/java/com/duckduckgo/autofill/impl/feature/plugin/RealAutofillFeatureRepositoryTest.kt delete mode 100644 autofill/autofill-store/src/main/java/com/duckduckgo/autofill/store/AutofillDao.kt delete mode 100644 autofill/autofill-store/src/main/java/com/duckduckgo/autofill/store/AutofillDatabaseModels.kt delete mode 100644 autofill/autofill-store/src/main/java/com/duckduckgo/autofill/store/feature/AutofillFeatureRepository.kt diff --git a/autofill/autofill-impl/src/main/java/com/duckduckgo/autofill/impl/RealAutofill.kt b/autofill/autofill-impl/src/main/java/com/duckduckgo/autofill/impl/RealAutofill.kt index 6e814725a4a4..dffa44dbcac9 100644 --- a/autofill/autofill-impl/src/main/java/com/duckduckgo/autofill/impl/RealAutofill.kt +++ b/autofill/autofill-impl/src/main/java/com/duckduckgo/autofill/impl/RealAutofill.kt @@ -18,7 +18,7 @@ package com.duckduckgo.autofill.impl import com.duckduckgo.app.browser.UriString.Companion.sameOrSubdomain import com.duckduckgo.autofill.api.Autofill -import com.duckduckgo.autofill.store.feature.AutofillFeatureRepository +import com.duckduckgo.autofill.impl.feature.plugin.AutofillFeatureRepository import com.duckduckgo.di.scopes.AppScope import com.duckduckgo.privacy.config.api.UnprotectedTemporary import com.squareup.anvil.annotations.ContributesBinding diff --git a/autofill/autofill-impl/src/main/java/com/duckduckgo/autofill/impl/di/AutofillModule.kt b/autofill/autofill-impl/src/main/java/com/duckduckgo/autofill/impl/di/AutofillModule.kt index 038e39036ba3..42fb473b2437 100644 --- a/autofill/autofill-impl/src/main/java/com/duckduckgo/autofill/impl/di/AutofillModule.kt +++ b/autofill/autofill-impl/src/main/java/com/duckduckgo/autofill/impl/di/AutofillModule.kt @@ -19,8 +19,6 @@ package com.duckduckgo.autofill.impl.di import android.content.Context import androidx.room.Room import com.duckduckgo.anvil.annotations.ContributesPluginPoint -import com.duckduckgo.app.di.AppCoroutineScope -import com.duckduckgo.app.di.IsMainProcess import com.duckduckgo.autofill.api.AutofillFeature import com.duckduckgo.autofill.api.AutofillFragmentResultsPlugin import com.duckduckgo.autofill.api.InternalTestUserChecker @@ -39,20 +37,16 @@ import com.duckduckgo.autofill.store.RealInternalTestUserStore import com.duckduckgo.autofill.store.RealLastUpdatedTimeProvider import com.duckduckgo.autofill.store.engagement.AutofillEngagementDatabase import com.duckduckgo.autofill.store.feature.AutofillDefaultStateDecider -import com.duckduckgo.autofill.store.feature.AutofillFeatureRepository import com.duckduckgo.autofill.store.feature.RealAutofillDefaultStateDecider -import com.duckduckgo.autofill.store.feature.RealAutofillFeatureRepository import com.duckduckgo.autofill.store.targets.DomainTargetAppDao import com.duckduckgo.autofill.store.targets.DomainTargetAppsDatabase import com.duckduckgo.browser.api.UserBrowserProperties -import com.duckduckgo.common.utils.DispatcherProvider import com.duckduckgo.di.scopes.ActivityScope import com.duckduckgo.di.scopes.AppScope import com.squareup.anvil.annotations.ContributesTo import dagger.Module import dagger.Provides import dagger.SingleInstanceIn -import kotlinx.coroutines.CoroutineScope @Module @ContributesTo(AppScope::class) @@ -99,17 +93,6 @@ class AutofillModule { .build() } - @SingleInstanceIn(AppScope::class) - @Provides - fun provideAutofillRepository( - database: AutofillDatabase, - @AppCoroutineScope appCoroutineScope: CoroutineScope, - dispatcherProvider: DispatcherProvider, - @IsMainProcess isMainProcess: Boolean, - ): AutofillFeatureRepository { - return RealAutofillFeatureRepository(database, appCoroutineScope, dispatcherProvider, isMainProcess) - } - @Provides @SingleInstanceIn(AppScope::class) fun providesCredentialsSyncDao( diff --git a/autofill/autofill-impl/src/main/java/com/duckduckgo/autofill/impl/feature/plugin/AutofillFeatureExceptionStore.kt b/autofill/autofill-impl/src/main/java/com/duckduckgo/autofill/impl/feature/plugin/AutofillFeatureExceptionStore.kt deleted file mode 100644 index 057877f93fbb..000000000000 --- a/autofill/autofill-impl/src/main/java/com/duckduckgo/autofill/impl/feature/plugin/AutofillFeatureExceptionStore.kt +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (c) 2023 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.autofill.impl.feature.plugin - -import com.duckduckgo.autofill.api.AutofillFeature -import com.duckduckgo.autofill.store.AutofillExceptionEntity -import com.duckduckgo.autofill.store.feature.AutofillFeatureRepository -import com.duckduckgo.di.scopes.AppScope -import com.duckduckgo.feature.toggles.api.FeatureExceptions -import com.duckduckgo.feature.toggles.api.RemoteFeatureStoreNamed -import com.squareup.anvil.annotations.ContributesBinding -import javax.inject.Inject - -@ContributesBinding(AppScope::class) -@RemoteFeatureStoreNamed(AutofillFeature::class) -class AutofillFeatureExceptionStore @Inject constructor( - private val autofillFeatureRepository: AutofillFeatureRepository, -) : FeatureExceptions.Store { - override fun insertAll(exception: List) { - autofillFeatureRepository.updateAllExceptions( - exception.map { AutofillExceptionEntity(domain = it.domain, reason = it.reason.orEmpty()) }, - ) - } -} diff --git a/autofill/autofill-impl/src/main/java/com/duckduckgo/autofill/impl/feature/plugin/AutofillFeatureRepository.kt b/autofill/autofill-impl/src/main/java/com/duckduckgo/autofill/impl/feature/plugin/AutofillFeatureRepository.kt new file mode 100644 index 000000000000..2bef7bae0fab --- /dev/null +++ b/autofill/autofill-impl/src/main/java/com/duckduckgo/autofill/impl/feature/plugin/AutofillFeatureRepository.kt @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2025 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.autofill.impl.feature.plugin + +import com.duckduckgo.app.di.AppCoroutineScope +import com.duckduckgo.app.di.IsMainProcess +import com.duckduckgo.autofill.api.AutofillFeature +import com.duckduckgo.common.utils.DispatcherProvider +import com.duckduckgo.di.scopes.AppScope +import com.duckduckgo.feature.toggles.api.FeatureExceptions.FeatureException +import com.duckduckgo.privacy.config.api.PrivacyConfigCallbackPlugin +import com.squareup.anvil.annotations.ContributesBinding +import com.squareup.anvil.annotations.ContributesMultibinding +import dagger.SingleInstanceIn +import java.util.concurrent.CopyOnWriteArrayList +import javax.inject.Inject +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.launch + +interface AutofillFeatureRepository { + val exceptions: CopyOnWriteArrayList +} + +@ContributesMultibinding( + scope = AppScope::class, + boundType = PrivacyConfigCallbackPlugin::class, +) +@ContributesBinding( + scope = AppScope::class, + boundType = AutofillFeatureRepository::class, +) +@SingleInstanceIn(AppScope::class) +class RealAutofillFeatureRepository @Inject constructor( + private val feature: AutofillFeature, + @AppCoroutineScope private val coroutineScope: CoroutineScope, + private val dispatcherProvider: DispatcherProvider, + @IsMainProcess private val isMainProcess: Boolean, +) : AutofillFeatureRepository, PrivacyConfigCallbackPlugin { + + override val exceptions = CopyOnWriteArrayList() + + init { + loadToMemory() + } + + override fun onPrivacyConfigDownloaded() { + loadToMemory() + } + + private fun loadToMemory() { + coroutineScope.launch(dispatcherProvider.io()) { + if (isMainProcess) { + exceptions.clear() + exceptions.addAll(feature.self().getExceptions()) + } + } + } +} diff --git a/autofill/autofill-impl/src/main/java/com/duckduckgo/autofill/impl/feature/plugin/UnusedAutofillRemoteFeatureCodegenTrigger.kt b/autofill/autofill-impl/src/main/java/com/duckduckgo/autofill/impl/feature/plugin/UnusedAutofillRemoteFeatureCodegenTrigger.kt index c09dca48523e..e1b5d71d14b5 100644 --- a/autofill/autofill-impl/src/main/java/com/duckduckgo/autofill/impl/feature/plugin/UnusedAutofillRemoteFeatureCodegenTrigger.kt +++ b/autofill/autofill-impl/src/main/java/com/duckduckgo/autofill/impl/feature/plugin/UnusedAutofillRemoteFeatureCodegenTrigger.kt @@ -24,7 +24,6 @@ import com.duckduckgo.di.scopes.AppScope scope = AppScope::class, boundType = AutofillFeature::class, featureName = "autofill", - exceptionsStore = AutofillFeatureExceptionStore::class, ) @Suppress("unused") private interface UnusedAutofillRemoteFeatureCodegenTrigger diff --git a/autofill/autofill-impl/src/test/java/com/duckduckgo/autofill/impl/RealAutofillTest.kt b/autofill/autofill-impl/src/test/java/com/duckduckgo/autofill/impl/RealAutofillTest.kt index 1fffbc36cb3b..1448c2a02a06 100644 --- a/autofill/autofill-impl/src/test/java/com/duckduckgo/autofill/impl/RealAutofillTest.kt +++ b/autofill/autofill-impl/src/test/java/com/duckduckgo/autofill/impl/RealAutofillTest.kt @@ -17,7 +17,7 @@ package com.duckduckgo.autofill.impl import androidx.test.ext.junit.runners.AndroidJUnit4 -import com.duckduckgo.autofill.store.feature.AutofillFeatureRepository +import com.duckduckgo.autofill.impl.feature.plugin.AutofillFeatureRepository import com.duckduckgo.feature.toggles.api.FeatureExceptions.FeatureException import com.duckduckgo.privacy.config.api.UnprotectedTemporary import java.util.concurrent.CopyOnWriteArrayList diff --git a/autofill/autofill-impl/src/test/java/com/duckduckgo/autofill/impl/feature/plugin/RealAutofillFeatureRepositoryTest.kt b/autofill/autofill-impl/src/test/java/com/duckduckgo/autofill/impl/feature/plugin/RealAutofillFeatureRepositoryTest.kt new file mode 100644 index 000000000000..d2d9e1378801 --- /dev/null +++ b/autofill/autofill-impl/src/test/java/com/duckduckgo/autofill/impl/feature/plugin/RealAutofillFeatureRepositoryTest.kt @@ -0,0 +1,55 @@ +package com.duckduckgo.autofill.impl.feature.plugin + +import com.duckduckgo.autofill.api.AutofillFeature +import com.duckduckgo.common.test.CoroutineTestRule +import com.duckduckgo.feature.toggles.api.FakeFeatureToggleFactory +import com.duckduckgo.feature.toggles.api.FeatureExceptions.FeatureException +import com.duckduckgo.feature.toggles.api.Toggle +import kotlinx.coroutines.test.runTest +import org.junit.Assert.assertEquals +import org.junit.Before +import org.junit.Rule +import org.junit.Test + +class RealAutofillFeatureRepositoryTest { + @get:Rule + var coroutineRule = CoroutineTestRule() + + private val feature = FakeFeatureToggleFactory.create(AutofillFeature::class.java) + + @Before + fun setup() { + feature.self().setRawStoredState(Toggle.State(exceptions = exceptions)) + } + + @Test + fun whenRepositoryIsCreatedThenExceptionsLoadIntoMemory() = runTest { + val repository = RealAutofillFeatureRepository( + feature, + coroutineRule.testScope, + coroutineRule.testDispatcherProvider, + true, + ) + + assertEquals(exceptions, repository.exceptions) + } + + @Test + fun whenRemoteConfigUpdateThenExceptionsUpdated() = runTest { + val repository = RealAutofillFeatureRepository( + feature, + coroutineRule.testScope, + coroutineRule.testDispatcherProvider, + true, + ) + + assertEquals(exceptions, repository.exceptions) + feature.self().setRawStoredState(Toggle.State(exceptions = emptyList())) + repository.onPrivacyConfigDownloaded() + assertEquals(emptyList(), repository.exceptions) + } + + companion object { + val exceptions = listOf(FeatureException("example.com", "reason")) + } +} diff --git a/autofill/autofill-store/src/main/java/com/duckduckgo/autofill/store/AutofillDao.kt b/autofill/autofill-store/src/main/java/com/duckduckgo/autofill/store/AutofillDao.kt deleted file mode 100644 index 1a9df43749ec..000000000000 --- a/autofill/autofill-store/src/main/java/com/duckduckgo/autofill/store/AutofillDao.kt +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (c) 2021 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.autofill.store - -import androidx.room.Dao -import androidx.room.Insert -import androidx.room.OnConflictStrategy -import androidx.room.Query -import androidx.room.Transaction - -@Dao -abstract class AutofillDao { - - @Insert(onConflict = OnConflictStrategy.REPLACE) - abstract fun insertAll(domains: List) - - @Transaction - open fun updateAll(domains: List) { - deleteAll() - insertAll(domains) - } - - @Query("select * from autofill_exceptions where domain = :domain") - abstract fun get(domain: String): AutofillExceptionEntity - - @Query("select * from autofill_exceptions") - abstract fun getAll(): List - - @Query("delete from autofill_exceptions") - abstract fun deleteAll() -} diff --git a/autofill/autofill-store/src/main/java/com/duckduckgo/autofill/store/AutofillDatabase.kt b/autofill/autofill-store/src/main/java/com/duckduckgo/autofill/store/AutofillDatabase.kt index 505418933fe2..31f1e309b6eb 100644 --- a/autofill/autofill-store/src/main/java/com/duckduckgo/autofill/store/AutofillDatabase.kt +++ b/autofill/autofill-store/src/main/java/com/duckduckgo/autofill/store/AutofillDatabase.kt @@ -23,14 +23,12 @@ import androidx.sqlite.db.SupportSQLiteDatabase @Database( exportSchema = true, - version = 2, + version = 3, entities = [ - AutofillExceptionEntity::class, CredentialsSyncMetadataEntity::class, ], ) abstract class AutofillDatabase : RoomDatabase() { - abstract fun autofillDao(): AutofillDao abstract fun credentialsSyncDao(): CredentialsSyncMetadataDao } @@ -46,4 +44,10 @@ val MIGRATION_1_2 = object : Migration(1, 2) { } } -val ALL_MIGRATIONS = arrayOf(MIGRATION_1_2) +val MIGRATION_2_3 = object : Migration(2, 3) { + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL("DROP TABLE `autofill_exceptions`") + } +} + +val ALL_MIGRATIONS = arrayOf(MIGRATION_1_2, MIGRATION_2_3) diff --git a/autofill/autofill-store/src/main/java/com/duckduckgo/autofill/store/AutofillDatabaseModels.kt b/autofill/autofill-store/src/main/java/com/duckduckgo/autofill/store/AutofillDatabaseModels.kt deleted file mode 100644 index 594ed1b2e3e7..000000000000 --- a/autofill/autofill-store/src/main/java/com/duckduckgo/autofill/store/AutofillDatabaseModels.kt +++ /dev/null @@ -1,31 +0,0 @@ -// ktlint-disable filename -/* - * Copyright (c) 2023 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.autofill.store - -import androidx.room.Entity -import androidx.room.PrimaryKey -import com.duckduckgo.feature.toggles.api.FeatureExceptions.FeatureException - -@Entity(tableName = "autofill_exceptions") -data class AutofillExceptionEntity( - @PrimaryKey val domain: String, - val reason: String, -) - -fun AutofillExceptionEntity.toFeatureException(): FeatureException { - return FeatureException(domain = this.domain, reason = this.reason) -} diff --git a/autofill/autofill-store/src/main/java/com/duckduckgo/autofill/store/feature/AutofillFeatureRepository.kt b/autofill/autofill-store/src/main/java/com/duckduckgo/autofill/store/feature/AutofillFeatureRepository.kt deleted file mode 100644 index 18c61845cfbd..000000000000 --- a/autofill/autofill-store/src/main/java/com/duckduckgo/autofill/store/feature/AutofillFeatureRepository.kt +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (c) 2023 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.autofill.store.feature - -import com.duckduckgo.autofill.store.AutofillDao -import com.duckduckgo.autofill.store.AutofillDatabase -import com.duckduckgo.autofill.store.AutofillExceptionEntity -import com.duckduckgo.autofill.store.toFeatureException -import com.duckduckgo.common.utils.DispatcherProvider -import com.duckduckgo.feature.toggles.api.FeatureExceptions.FeatureException -import java.util.concurrent.CopyOnWriteArrayList -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.launch - -interface AutofillFeatureRepository { - fun updateAllExceptions(exceptions: List) - val exceptions: CopyOnWriteArrayList -} - -class RealAutofillFeatureRepository( - val database: AutofillDatabase, - coroutineScope: CoroutineScope, - dispatcherProvider: DispatcherProvider, - isMainProcess: Boolean, -) : AutofillFeatureRepository { - - private val autofillDao: AutofillDao = database.autofillDao() - override val exceptions = CopyOnWriteArrayList() - - init { - coroutineScope.launch(dispatcherProvider.io()) { - if (isMainProcess) { - loadToMemory() - } - } - } - - override fun updateAllExceptions(exceptions: List) { - autofillDao.updateAll(exceptions) - loadToMemory() - } - - private fun loadToMemory() { - exceptions.clear() - autofillDao.getAll().map { exceptions.add(it.toFeatureException()) } - } -} From d8d930762edb148111eb61d811a38730a913cc8b Mon Sep 17 00:00:00 2001 From: Aitor Viana Date: Wed, 9 Apr 2025 13:30:17 +0100 Subject: [PATCH 9/9] Fix test --- .../AutoconsentExceptionsRepository.kt | 16 +++++++--------- ...RealAutoconsentExceptionsRepositoryTest.kt | 19 +++++++++---------- ...rotectionInContextFeatureRepositoryTest.kt | 2 ++ .../RealAutofillFeatureRepositoryTest.kt | 2 ++ ...akageReportingFeatureRepositoryImplTest.kt | 2 ++ ...ealAutofillServiceFeatureRepositoryTest.kt | 2 ++ ...MaliciousSiteProtectionRCRepositoryTest.kt | 2 ++ .../drmblock/RealDrmBlockRepositoryTest.kt | 2 ++ 8 files changed, 28 insertions(+), 19 deletions(-) diff --git a/autoconsent/autoconsent-impl/src/main/java/com/duckduckgo/autoconsent/impl/remoteconfig/AutoconsentExceptionsRepository.kt b/autoconsent/autoconsent-impl/src/main/java/com/duckduckgo/autoconsent/impl/remoteconfig/AutoconsentExceptionsRepository.kt index de270bd81152..2ccd55206db4 100644 --- a/autoconsent/autoconsent-impl/src/main/java/com/duckduckgo/autoconsent/impl/remoteconfig/AutoconsentExceptionsRepository.kt +++ b/autoconsent/autoconsent-impl/src/main/java/com/duckduckgo/autoconsent/impl/remoteconfig/AutoconsentExceptionsRepository.kt @@ -53,21 +53,19 @@ class RealAutoconsentExceptionsRepository @Inject constructor( override val exceptions = CopyOnWriteArrayList() init { - coroutineScope.launch(dispatcherProvider.io()) { - loadToMemory() - } + loadToMemory() } private fun loadToMemory() { - if (isMainProcess) { - exceptions.clear() - exceptions.addAll(autoconsentFeature.self().getExceptions()) + coroutineScope.launch(dispatcherProvider.io()) { + if (isMainProcess) { + exceptions.clear() + exceptions.addAll(autoconsentFeature.self().getExceptions()) + } } } override fun onPrivacyConfigDownloaded() { - coroutineScope.launch(dispatcherProvider.io()) { - loadToMemory() - } + loadToMemory() } } diff --git a/autoconsent/autoconsent-impl/src/test/java/com/duckduckgo/autoconsent/impl/remoteconfig/RealAutoconsentExceptionsRepositoryTest.kt b/autoconsent/autoconsent-impl/src/test/java/com/duckduckgo/autoconsent/impl/remoteconfig/RealAutoconsentExceptionsRepositoryTest.kt index 5158b2f0e116..284188049b19 100644 --- a/autoconsent/autoconsent-impl/src/test/java/com/duckduckgo/autoconsent/impl/remoteconfig/RealAutoconsentExceptionsRepositoryTest.kt +++ b/autoconsent/autoconsent-impl/src/test/java/com/duckduckgo/autoconsent/impl/remoteconfig/RealAutoconsentExceptionsRepositoryTest.kt @@ -16,27 +16,25 @@ package com.duckduckgo.autoconsent.impl.remoteconfig +import android.annotation.SuppressLint import com.duckduckgo.common.test.CoroutineTestRule import com.duckduckgo.feature.toggles.api.FakeFeatureToggleFactory import com.duckduckgo.feature.toggles.api.FeatureExceptions.FeatureException import com.duckduckgo.feature.toggles.api.Toggle import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.runTest -import org.junit.Assert.* -import org.junit.Before +import org.junit.Assert.assertEquals import org.junit.Rule import org.junit.Test +@SuppressLint("DenyListedApi") // setRawStoredState class RealAutoconsentExceptionsRepositoryTest { @get:Rule var coroutineRule = CoroutineTestRule() - private val autoconsentFeature = FakeFeatureToggleFactory.create(AutoconsentFeature::class.java) - - @Before - fun setup() { - autoconsentFeature.self().setRawStoredState(Toggle.State(exceptions = listOf(exception))) + private val autoconsentFeature: AutoconsentFeature = FakeFeatureToggleFactory.create(AutoconsentFeature::class.java).apply { + self().setRawStoredState(Toggle.State(exceptions = exceptions)) } @Test @@ -47,7 +45,8 @@ class RealAutoconsentExceptionsRepositoryTest { autoconsentFeature, isMainProcess = true, ) - assertEquals(exception, repository.exceptions.first()) + + assertEquals(exceptions, repository.exceptions) } @Test @@ -59,13 +58,13 @@ class RealAutoconsentExceptionsRepositoryTest { isMainProcess = true, ) - assertEquals(listOf(exception), repository.exceptions) + assertEquals(exceptions, repository.exceptions) autoconsentFeature.self().setRawStoredState(Toggle.State(exceptions = emptyList())) repository.onPrivacyConfigDownloaded() assertEquals(emptyList(), repository.exceptions) } companion object { - val exception = FeatureException("example.com", "reason") + val exceptions = listOf(FeatureException("example.com", "reason")) } } diff --git a/autofill/autofill-impl/src/test/java/com/duckduckgo/autofill/impl/email/remoteconfig/RealEmailProtectionInContextFeatureRepositoryTest.kt b/autofill/autofill-impl/src/test/java/com/duckduckgo/autofill/impl/email/remoteconfig/RealEmailProtectionInContextFeatureRepositoryTest.kt index eb8e90180a0a..0fe87b222780 100644 --- a/autofill/autofill-impl/src/test/java/com/duckduckgo/autofill/impl/email/remoteconfig/RealEmailProtectionInContextFeatureRepositoryTest.kt +++ b/autofill/autofill-impl/src/test/java/com/duckduckgo/autofill/impl/email/remoteconfig/RealEmailProtectionInContextFeatureRepositoryTest.kt @@ -1,5 +1,6 @@ package com.duckduckgo.autofill.impl.email.remoteconfig +import android.annotation.SuppressLint import com.duckduckgo.autofill.impl.email.incontext.EmailProtectionInContextSignupFeature import com.duckduckgo.common.test.CoroutineTestRule import com.duckduckgo.feature.toggles.api.FakeFeatureToggleFactory @@ -11,6 +12,7 @@ import org.junit.Before import org.junit.Rule import org.junit.Test +@SuppressLint("DenyListedApi") // setRawStoredState class RealEmailProtectionInContextFeatureRepositoryTest { @get:Rule var coroutineRule = CoroutineTestRule() diff --git a/autofill/autofill-impl/src/test/java/com/duckduckgo/autofill/impl/feature/plugin/RealAutofillFeatureRepositoryTest.kt b/autofill/autofill-impl/src/test/java/com/duckduckgo/autofill/impl/feature/plugin/RealAutofillFeatureRepositoryTest.kt index d2d9e1378801..1f96bc66d374 100644 --- a/autofill/autofill-impl/src/test/java/com/duckduckgo/autofill/impl/feature/plugin/RealAutofillFeatureRepositoryTest.kt +++ b/autofill/autofill-impl/src/test/java/com/duckduckgo/autofill/impl/feature/plugin/RealAutofillFeatureRepositoryTest.kt @@ -1,5 +1,6 @@ package com.duckduckgo.autofill.impl.feature.plugin +import android.annotation.SuppressLint import com.duckduckgo.autofill.api.AutofillFeature import com.duckduckgo.common.test.CoroutineTestRule import com.duckduckgo.feature.toggles.api.FakeFeatureToggleFactory @@ -11,6 +12,7 @@ import org.junit.Before import org.junit.Rule import org.junit.Test +@SuppressLint("DenyListedApi") // setRawStoredState class RealAutofillFeatureRepositoryTest { @get:Rule var coroutineRule = CoroutineTestRule() diff --git a/autofill/autofill-impl/src/test/java/com/duckduckgo/autofill/impl/reporting/remoteconfig/AutofillSiteBreakageReportingFeatureRepositoryImplTest.kt b/autofill/autofill-impl/src/test/java/com/duckduckgo/autofill/impl/reporting/remoteconfig/AutofillSiteBreakageReportingFeatureRepositoryImplTest.kt index b89edcb6abc4..a8e6495c5007 100644 --- a/autofill/autofill-impl/src/test/java/com/duckduckgo/autofill/impl/reporting/remoteconfig/AutofillSiteBreakageReportingFeatureRepositoryImplTest.kt +++ b/autofill/autofill-impl/src/test/java/com/duckduckgo/autofill/impl/reporting/remoteconfig/AutofillSiteBreakageReportingFeatureRepositoryImplTest.kt @@ -1,5 +1,6 @@ package com.duckduckgo.autofill.impl.reporting.remoteconfig +import android.annotation.SuppressLint import com.duckduckgo.common.test.CoroutineTestRule import com.duckduckgo.feature.toggles.api.FakeFeatureToggleFactory import com.duckduckgo.feature.toggles.api.FeatureExceptions.FeatureException @@ -10,6 +11,7 @@ import org.junit.Before import org.junit.Rule import org.junit.Test +@SuppressLint("DenyListedApi") // setRawStoredState class AutofillSiteBreakageReportingFeatureRepositoryImplTest { @get:Rule var coroutineRule = CoroutineTestRule() diff --git a/autofill/autofill-impl/src/test/java/com/duckduckgo/autofill/impl/store/RealAutofillServiceFeatureRepositoryTest.kt b/autofill/autofill-impl/src/test/java/com/duckduckgo/autofill/impl/store/RealAutofillServiceFeatureRepositoryTest.kt index 29c2919b1b75..499bea77fa6e 100644 --- a/autofill/autofill-impl/src/test/java/com/duckduckgo/autofill/impl/store/RealAutofillServiceFeatureRepositoryTest.kt +++ b/autofill/autofill-impl/src/test/java/com/duckduckgo/autofill/impl/store/RealAutofillServiceFeatureRepositoryTest.kt @@ -16,6 +16,7 @@ package com.duckduckgo.autofill.impl.store +import android.annotation.SuppressLint import com.duckduckgo.autofill.impl.service.AutofillServiceFeature import com.duckduckgo.autofill.impl.service.store.RealAutofillServiceFeatureRepository import com.duckduckgo.common.test.CoroutineTestRule @@ -28,6 +29,7 @@ import org.junit.Before import org.junit.Rule import org.junit.Test +@SuppressLint("DenyListedApi") // setRawStoredState class RealAutofillServiceFeatureRepositoryTest { @get:Rule var coroutineRule = CoroutineTestRule() diff --git a/malicious-site-protection/malicious-site-protection-impl/src/test/kotlin/com/duckduckgo/malicioussiteprotection/impl/remoteconfig/RealMaliciousSiteProtectionRCRepositoryTest.kt b/malicious-site-protection/malicious-site-protection-impl/src/test/kotlin/com/duckduckgo/malicioussiteprotection/impl/remoteconfig/RealMaliciousSiteProtectionRCRepositoryTest.kt index e5f24127061b..310b1ded6489 100644 --- a/malicious-site-protection/malicious-site-protection-impl/src/test/kotlin/com/duckduckgo/malicioussiteprotection/impl/remoteconfig/RealMaliciousSiteProtectionRCRepositoryTest.kt +++ b/malicious-site-protection/malicious-site-protection-impl/src/test/kotlin/com/duckduckgo/malicioussiteprotection/impl/remoteconfig/RealMaliciousSiteProtectionRCRepositoryTest.kt @@ -1,5 +1,6 @@ package com.duckduckgo.malicioussiteprotection.impl.remoteconfig +import android.annotation.SuppressLint import com.duckduckgo.common.test.CoroutineTestRule import com.duckduckgo.feature.toggles.api.FakeFeatureToggleFactory import com.duckduckgo.feature.toggles.api.FeatureExceptions.FeatureException @@ -11,6 +12,7 @@ import org.junit.Before import org.junit.Rule import org.junit.Test +@SuppressLint("DenyListedApi") // setRawStoredState class RealMaliciousSiteProtectionRCRepositoryTest { @get:Rule var coroutineRule = CoroutineTestRule() diff --git a/site-permissions/site-permissions-impl/src/test/java/com/duckduckgo/site/permissions/impl/drmblock/RealDrmBlockRepositoryTest.kt b/site-permissions/site-permissions-impl/src/test/java/com/duckduckgo/site/permissions/impl/drmblock/RealDrmBlockRepositoryTest.kt index b71f3c8af483..fe9eeef20169 100644 --- a/site-permissions/site-permissions-impl/src/test/java/com/duckduckgo/site/permissions/impl/drmblock/RealDrmBlockRepositoryTest.kt +++ b/site-permissions/site-permissions-impl/src/test/java/com/duckduckgo/site/permissions/impl/drmblock/RealDrmBlockRepositoryTest.kt @@ -1,5 +1,6 @@ package com.duckduckgo.site.permissions.impl.drmblock +import android.annotation.SuppressLint import com.duckduckgo.common.test.CoroutineTestRule import com.duckduckgo.feature.toggles.api.FakeFeatureToggleFactory import com.duckduckgo.feature.toggles.api.FeatureExceptions.FeatureException @@ -10,6 +11,7 @@ import org.junit.Before import org.junit.Rule import org.junit.Test +@SuppressLint("DenyListedApi") // setRawStoredState class RealDrmBlockRepositoryTest { @get:Rule var coroutineRule = CoroutineTestRule()