Skip to content

Commit

Permalink
Add feature flag for importing passwords via Google Password Manager
Browse files Browse the repository at this point in the history
  • Loading branch information
CDRussell committed Oct 22, 2024
1 parent 2255576 commit b8de40d
Show file tree
Hide file tree
Showing 3 changed files with 159 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
package com.duckduckgo.autofill.impl.di

import android.content.Context
import androidx.datastore.core.DataStore
import androidx.datastore.preferences.core.Preferences
import androidx.room.Room
import com.duckduckgo.anvil.annotations.ContributesPluginPoint
import com.duckduckgo.app.di.AppCoroutineScope
Expand All @@ -26,6 +28,7 @@ import com.duckduckgo.autofill.api.AutofillFragmentResultsPlugin
import com.duckduckgo.autofill.api.InternalTestUserChecker
import com.duckduckgo.autofill.api.promotion.PasswordsScreenPromotionPlugin
import com.duckduckgo.autofill.impl.encoding.UrlUnicodeNormalizer
import com.duckduckgo.autofill.impl.importing.gpm.feature.AutofillImportPasswordsFeatureStore.Companion.autofillImportPasswordsFeatureStore
import com.duckduckgo.autofill.impl.urlmatcher.AutofillDomainNameUrlMatcher
import com.duckduckgo.autofill.impl.urlmatcher.AutofillUrlMatcher
import com.duckduckgo.autofill.store.ALL_MIGRATIONS as AutofillMigrations
Expand Down Expand Up @@ -54,6 +57,7 @@ import com.squareup.anvil.annotations.ContributesTo
import dagger.Module
import dagger.Provides
import dagger.SingleInstanceIn
import javax.inject.Qualifier
import kotlinx.coroutines.CoroutineScope

@Module
Expand Down Expand Up @@ -149,6 +153,16 @@ class AutofillModule {
.addMigrations(*AutofillEngagementDatabase.ALL_MIGRATIONS)
.build()
}

@Provides
@SingleInstanceIn(AppScope::class)
@AutofillImportPasswordsFeatureDataStore
fun provideInstallSourceFullPackageDataStore(context: Context): DataStore<Preferences> {
return context.autofillImportPasswordsFeatureStore
}

@Qualifier
annotation class AutofillImportPasswordsFeatureDataStore
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/*
* 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.importing.gpm.feature

import android.content.Context
import androidx.datastore.core.DataStore
import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.core.edit
import androidx.datastore.preferences.core.stringPreferencesKey
import androidx.datastore.preferences.preferencesDataStore
import com.duckduckgo.autofill.impl.di.AutofillModule.AutofillImportPasswordsFeatureDataStore
import com.duckduckgo.common.utils.DispatcherProvider
import com.duckduckgo.di.scopes.AppScope
import com.squareup.anvil.annotations.ContributesBinding
import dagger.SingleInstanceIn
import javax.inject.Inject
import kotlinx.coroutines.flow.firstOrNull
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.withContext
import timber.log.Timber

interface AutofillImportPasswordsFeatureStore {
suspend fun updateAutofillImportPasswordsSettings(json: String)
suspend fun getAutofillImportPasswordsSettings(): String

companion object {
val Context.autofillImportPasswordsFeatureStore: DataStore<Preferences> by preferencesDataStore(
name = "com.duckduckgo.autofill.impl.importing.gpm.feature.settings",
)
}
}

@ContributesBinding(AppScope::class, boundType = AutofillImportPasswordsFeatureStore::class)
@SingleInstanceIn(AppScope::class)
class InstallSourceFullPackageStoreImpl @Inject constructor(
private val dispatchers: DispatcherProvider,
@AutofillImportPasswordsFeatureDataStore private val dataStore: DataStore<Preferences>,
) : AutofillImportPasswordsFeatureStore {

override suspend fun updateAutofillImportPasswordsSettings(json: String) {
Timber.w("cdr received update for settings: $json")

withContext(dispatchers.io()) {
dataStore.edit {
it[settingsKey] = json
}
}
}
override suspend fun getAutofillImportPasswordsSettings(): String {
return withContext(dispatchers.io()) {
dataStore.data.map {
it[settingsKey] ?: DEFAULT_SETTINGS
}.firstOrNull() ?: DEFAULT_SETTINGS
}
}

companion object {
val settingsKey = stringPreferencesKey("settings")
private const val DEFAULT_SETTINGS = "{}"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/*
* 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.importing.gpm.feature

import com.duckduckgo.anvil.annotations.ContributesRemoteFeature
import com.duckduckgo.app.di.AppCoroutineScope
import com.duckduckgo.common.utils.DispatcherProvider
import com.duckduckgo.di.scopes.AppScope
import com.duckduckgo.feature.toggles.api.FeatureSettings
import com.duckduckgo.feature.toggles.api.RemoteFeatureStoreNamed
import com.duckduckgo.feature.toggles.api.Toggle
import com.duckduckgo.feature.toggles.api.Toggle.InternalAlwaysEnabled
import com.squareup.anvil.annotations.ContributesBinding
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch

@ContributesRemoteFeature(
scope = AppScope::class,
boundType = AutofillImportPasswordsFeature::class,
featureName = "autofillImportPasswords",
settingsStore = SettingsStore::class,
)
/**
* This is the class that represents the feature flag for allowing users to import passwords.
*/
interface AutofillImportPasswordsFeature {
/**
* @return `true` when the remote config has the global "autofillImportPasswords" feature flag enabled
*
* If the remote feature is not present defaults to `false`
*/

@InternalAlwaysEnabled
@Toggle.DefaultValue(false)
fun self(): Toggle

@InternalAlwaysEnabled
@Toggle.DefaultValue(false)
fun canImportFromGooglePasswordManager(): Toggle
}

@ContributesBinding(AppScope::class)
@RemoteFeatureStoreNamed(AutofillImportPasswordsFeature::class)
class SettingsStore @Inject constructor(
@AppCoroutineScope private val appCoroutineScope: CoroutineScope,
private val dispatchers: DispatcherProvider,
private val store: AutofillImportPasswordsFeatureStore,
) : FeatureSettings.Store {

override fun store(jsonString: String) {
appCoroutineScope.launch(dispatchers.io()) {
store.updateAutofillImportPasswordsSettings(jsonString)
}
}
}

0 comments on commit b8de40d

Please sign in to comment.