Skip to content

Commit dc1ceb8

Browse files
committed
Launch import flow from Password management screen
1 parent fd4d5b7 commit dc1ceb8

17 files changed

+839
-35
lines changed

autofill/autofill-impl/build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ dependencies {
7272
implementation AndroidX.biometric
7373

7474
implementation "net.zetetic:android-database-sqlcipher:_"
75+
implementation "com.facebook.shimmer:shimmer:_"
7576

7677
// Testing dependencies
7778
testImplementation "org.mockito.kotlin:mockito-kotlin:_"

autofill/autofill-impl/src/main/java/com/duckduckgo/autofill/impl/deviceauth/AutofillAuthorizationGracePeriod.kt

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,11 @@ interface AutofillAuthorizationGracePeriod {
4040
*/
4141
fun recordSuccessfulAuthorization()
4242

43+
/**
44+
* Requests an extended grace period. This may extend the grace period to a longer duration.
45+
*/
46+
fun requestExtendedGracePeriod()
47+
4348
/**
4449
* Invalidates the grace period, so that the next call to [isAuthRequired] will return true
4550
*/
@@ -53,12 +58,17 @@ class AutofillTimeBasedAuthorizationGracePeriod @Inject constructor(
5358
) : AutofillAuthorizationGracePeriod {
5459

5560
private var lastSuccessfulAuthTime: Long? = null
61+
private var extendedGraceTimeRequested: Long? = null
5662

5763
override fun recordSuccessfulAuthorization() {
5864
lastSuccessfulAuthTime = timeProvider.currentTimeMillis()
5965
Timber.v("Recording timestamp of successful auth")
6066
}
6167

68+
override fun requestExtendedGracePeriod() {
69+
extendedGraceTimeRequested = timeProvider.currentTimeMillis()
70+
}
71+
6272
override fun isAuthRequired(): Boolean {
6373
lastSuccessfulAuthTime?.let { lastAuthTime ->
6474
val timeSinceLastAuth = timeProvider.currentTimeMillis() - lastAuthTime
@@ -67,17 +77,36 @@ class AutofillTimeBasedAuthorizationGracePeriod @Inject constructor(
6777
Timber.v("Within grace period; auth not required")
6878
return false
6979
}
80+
81+
if (inExtendedGracePeriod()) {
82+
Timber.v("Within extended grace period; auth not required")
83+
return false
84+
}
7085
}
86+
87+
extendedGraceTimeRequested = null
7188
Timber.v("No last auth time recorded or outside grace period; auth required")
7289

7390
return true
7491
}
7592

93+
private fun inExtendedGracePeriod(): Boolean {
94+
val extendedRequest = extendedGraceTimeRequested
95+
if (extendedRequest == null) {
96+
return false
97+
} else {
98+
val timeSinceExtendedGrace = timeProvider.currentTimeMillis() - extendedRequest
99+
return timeSinceExtendedGrace <= AUTH_GRACE_EXTENDED_PERIOD_MS
100+
}
101+
}
102+
76103
override fun invalidate() {
77104
lastSuccessfulAuthTime = null
105+
extendedGraceTimeRequested = null
78106
}
79107

80108
companion object {
81109
private const val AUTH_GRACE_PERIOD_MS = 15_000
110+
private const val AUTH_GRACE_EXTENDED_PERIOD_MS = 60_000
82111
}
83112
}

autofill/autofill-impl/src/main/java/com/duckduckgo/autofill/impl/importing/gpm/webflow/ImportGooglePasswordsWebFlowFragment.kt

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -308,20 +308,15 @@ class ImportGooglePasswordsWebFlowFragment :
308308

309309
override suspend fun onCsvAvailable(csv: String) {
310310
Timber.i("cdr CSV available %s", csv)
311-
val parseResult = csvPasswordImporter.readCsv(csv)
312-
when (parseResult) {
311+
when (val parseResult = csvPasswordImporter.readCsv(csv)) {
313312
is Success -> {
314-
passwordImporter.importPasswords(parseResult.loginCredentialsToImport)
313+
onCsvParsed(parseResult)
315314
}
316-
317-
Error -> {
318-
Timber.e("cdr Error parsing CSV")
315+
is Error -> {
316+
onCsvError()
319317
}
320318
}
321319

322-
val resultBundle = Bundle().also { it.putParcelable(RESULT_KEY_DETAILS, parseResult) }
323-
setFragmentResult(RESULT_KEY, resultBundle)
324-
325320
/**
326321
* val result = csvPasswordImporter.importCsv(csv)
327322
* val resultDetails = when (result) {
@@ -337,6 +332,12 @@ class ImportGooglePasswordsWebFlowFragment :
337332
*/
338333
}
339334

335+
private suspend fun onCsvParsed(parseResult: Success) {
336+
passwordImporter.importPasswords(parseResult.loginCredentialsToImport)
337+
val resultBundle = Bundle()
338+
setFragmentResult(RESULT_KEY, resultBundle)
339+
}
340+
340341
override suspend fun onCsvError() {
341342
Timber.e("cdr Error decoding CSV")
342343
val resultBundle = Bundle().also {

autofill/autofill-impl/src/main/java/com/duckduckgo/autofill/impl/pixel/AutofillPixelNames.kt

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,7 @@ import com.duckduckgo.autofill.impl.pixel.AutofillPixelNames.AUTOFILL_ENGAGEMENT
2424
import com.duckduckgo.autofill.impl.pixel.AutofillPixelNames.AUTOFILL_ENGAGEMENT_ONBOARDED_USER
2525
import com.duckduckgo.autofill.impl.pixel.AutofillPixelNames.AUTOFILL_ENGAGEMENT_STACKED_LOGINS
2626
import com.duckduckgo.autofill.impl.pixel.AutofillPixelNames.AUTOFILL_IMPORT_PASSWORDS_COPIED_DESKTOP_LINK
27-
import com.duckduckgo.autofill.impl.pixel.AutofillPixelNames.AUTOFILL_IMPORT_PASSWORDS_CTA_BUTTON
2827
import com.duckduckgo.autofill.impl.pixel.AutofillPixelNames.AUTOFILL_IMPORT_PASSWORDS_GET_DESKTOP_BROWSER
29-
import com.duckduckgo.autofill.impl.pixel.AutofillPixelNames.AUTOFILL_IMPORT_PASSWORDS_OVERFLOW_MENU
3028
import com.duckduckgo.autofill.impl.pixel.AutofillPixelNames.AUTOFILL_IMPORT_PASSWORDS_SHARED_DESKTOP_LINK
3129
import com.duckduckgo.autofill.impl.pixel.AutofillPixelNames.AUTOFILL_IMPORT_PASSWORDS_SYNC_WITH_DESKTOP
3230
import com.duckduckgo.autofill.impl.pixel.AutofillPixelNames.AUTOFILL_IMPORT_PASSWORDS_USER_JOURNEY_RESTARTED
@@ -43,6 +41,8 @@ import com.duckduckgo.autofill.impl.pixel.AutofillPixelNames.AUTOFILL_SITE_BREAK
4341
import com.duckduckgo.autofill.impl.pixel.AutofillPixelNames.AUTOFILL_SITE_BREAKAGE_REPORT_CONFIRMATION_CONFIRMED
4442
import com.duckduckgo.autofill.impl.pixel.AutofillPixelNames.AUTOFILL_SITE_BREAKAGE_REPORT_CONFIRMATION_DISMISSED
4543
import com.duckduckgo.autofill.impl.pixel.AutofillPixelNames.AUTOFILL_SITE_BREAKAGE_REPORT_CONFIRMATION_DISPLAYED
44+
import com.duckduckgo.autofill.impl.pixel.AutofillPixelNames.AUTOFILL_SYNC_DESKTOP_PASSWORDS_CTA_BUTTON
45+
import com.duckduckgo.autofill.impl.pixel.AutofillPixelNames.AUTOFILL_SYNC_DESKTOP_PASSWORDS_OVERFLOW_MENU
4646
import com.duckduckgo.autofill.impl.pixel.AutofillPixelNames.EMAIL_TOOLTIP_DISMISSED
4747
import com.duckduckgo.autofill.impl.pixel.AutofillPixelNames.EMAIL_USE_ADDRESS
4848
import com.duckduckgo.autofill.impl.pixel.AutofillPixelNames.EMAIL_USE_ALIAS
@@ -142,8 +142,8 @@ enum class AutofillPixelNames(override val pixelName: String) : Pixel.PixelName
142142
AUTOFILL_TOGGLED_ON_SEARCH("m_autofill_toggled_on"),
143143
AUTOFILL_TOGGLED_OFF_SEARCH("m_autofill_toggled_off"),
144144

145-
AUTOFILL_IMPORT_PASSWORDS_CTA_BUTTON("m_autofill_logins_import_no_passwords"),
146-
AUTOFILL_IMPORT_PASSWORDS_OVERFLOW_MENU("m_autofill_logins_import"),
145+
AUTOFILL_SYNC_DESKTOP_PASSWORDS_CTA_BUTTON("m_autofill_logins_import_no_passwords"),
146+
AUTOFILL_SYNC_DESKTOP_PASSWORDS_OVERFLOW_MENU("m_autofill_logins_import"),
147147
AUTOFILL_IMPORT_PASSWORDS_GET_DESKTOP_BROWSER("m_autofill_logins_import_get_desktop"),
148148
AUTOFILL_IMPORT_PASSWORDS_SYNC_WITH_DESKTOP("m_autofill_logins_import_sync"),
149149
AUTOFILL_IMPORT_PASSWORDS_USER_TOOK_NO_ACTION("m_autofill_logins_import_no-action"),
@@ -177,8 +177,8 @@ object AutofillPixelsRequiringDataCleaning : PixelParamRemovalPlugin {
177177
AUTOFILL_ENGAGEMENT_ONBOARDED_USER.pixelName to PixelParameter.removeAtb(),
178178
AUTOFILL_ENGAGEMENT_STACKED_LOGINS.pixelName to PixelParameter.removeAtb(),
179179

180-
AUTOFILL_IMPORT_PASSWORDS_CTA_BUTTON.pixelName to PixelParameter.removeAtb(),
181-
AUTOFILL_IMPORT_PASSWORDS_OVERFLOW_MENU.pixelName to PixelParameter.removeAtb(),
180+
AUTOFILL_SYNC_DESKTOP_PASSWORDS_CTA_BUTTON.pixelName to PixelParameter.removeAtb(),
181+
AUTOFILL_SYNC_DESKTOP_PASSWORDS_OVERFLOW_MENU.pixelName to PixelParameter.removeAtb(),
182182
AUTOFILL_IMPORT_PASSWORDS_GET_DESKTOP_BROWSER.pixelName to PixelParameter.removeAtb(),
183183
AUTOFILL_IMPORT_PASSWORDS_SYNC_WITH_DESKTOP.pixelName to PixelParameter.removeAtb(),
184184
AUTOFILL_IMPORT_PASSWORDS_USER_TOOK_NO_ACTION.pixelName to PixelParameter.removeAtb(),

autofill/autofill-impl/src/main/java/com/duckduckgo/autofill/impl/ui/credential/management/AutofillSettingsViewModel.kt

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
package com.duckduckgo.autofill.impl.ui.credential.management
1818

19+
import android.os.Parcelable
1920
import android.util.Patterns
2021
import androidx.lifecycle.ViewModel
2122
import androidx.lifecycle.viewModelScope
@@ -36,6 +37,7 @@ import com.duckduckgo.autofill.api.email.EmailManager
3637
import com.duckduckgo.autofill.impl.R
3738
import com.duckduckgo.autofill.impl.deviceauth.DeviceAuthenticator
3839
import com.duckduckgo.autofill.impl.deviceauth.DeviceAuthenticator.AuthConfiguration
40+
import com.duckduckgo.autofill.impl.importing.gpm.feature.AutofillImportPasswordsFeature
3941
import com.duckduckgo.autofill.impl.pixel.AutofillPixelNames
4042
import com.duckduckgo.autofill.impl.pixel.AutofillPixelNames.AUTOFILL_DELETE_LOGIN
4143
import com.duckduckgo.autofill.impl.pixel.AutofillPixelNames.AUTOFILL_ENABLE_AUTOFILL_TOGGLE_MANUALLY_DISABLED
@@ -112,6 +114,7 @@ import kotlinx.coroutines.flow.distinctUntilChanged
112114
import kotlinx.coroutines.flow.first
113115
import kotlinx.coroutines.launch
114116
import kotlinx.coroutines.withContext
117+
import kotlinx.parcelize.Parcelize
115118
import timber.log.Timber
116119

117120
@ContributesViewModel(ActivityScope::class)
@@ -133,6 +136,7 @@ class AutofillSettingsViewModel @Inject constructor(
133136
private val autofillBreakageReportSender: AutofillBreakageReportSender,
134137
private val autofillBreakageReportDataStore: AutofillSiteBreakageReportingDataStore,
135138
private val autofillBreakageReportCanShowRules: AutofillBreakageReportCanShowRules,
139+
private val importPasswordsFeature: AutofillImportPasswordsFeature,
136140
) : ViewModel() {
137141

138142
private val _viewState = MutableStateFlow(ViewState())
@@ -690,7 +694,18 @@ class AutofillSettingsViewModel @Inject constructor(
690694
}
691695

692696
fun onImportPasswords() {
693-
addCommand(LaunchImportPasswords)
697+
viewModelScope.launch(dispatchers.io()) {
698+
with(importPasswordsFeature) {
699+
val csvImport = self().isEnabled() && canImportFromCsvFile().isEnabled()
700+
val gpmImport = self().isEnabled() && canImportFromGooglePasswordManager().isEnabled()
701+
val importConfig = ImportPasswordConfig(canImportFromCsv = csvImport, canImportFromGooglePasswordManager = gpmImport)
702+
addCommand(LaunchImportPasswords(importConfig))
703+
704+
// addCommand(LaunchImportPasswords(ImportPasswordConfig(canImportFromCsv = false, canImportFromGooglePasswordManager = false)))
705+
// addCommand(LaunchImportPasswords(ImportPasswordConfig(canImportFromCsv = false, canImportFromGooglePasswordManager = true)))
706+
// addCommand(LaunchImportPasswords(ImportPasswordConfig(canImportFromCsv = true, canImportFromGooglePasswordManager = false)))
707+
}
708+
}
694709
}
695710

696711
fun onReportBreakageClicked() {
@@ -702,7 +717,10 @@ class AutofillSettingsViewModel @Inject constructor(
702717
}
703718
}
704719

705-
fun updateCurrentSite(currentUrl: String?, privacyProtectionEnabled: Boolean?) {
720+
fun updateCurrentSite(
721+
currentUrl: String?,
722+
privacyProtectionEnabled: Boolean?,
723+
) {
706724
val updatedReportBreakageState = _viewState.value.reportBreakageState.copy(
707725
currentUrl = currentUrl,
708726
privacyProtectionEnabled = privacyProtectionEnabled,
@@ -854,12 +872,19 @@ class AutofillSettingsViewModel @Inject constructor(
854872
data object LaunchResetNeverSaveListConfirmation : ListModeCommand()
855873
data class LaunchDeleteAllPasswordsConfirmation(val numberToDelete: Int) : ListModeCommand()
856874
data class PromptUserToAuthenticateMassDeletion(val authConfiguration: AuthConfiguration) : ListModeCommand()
857-
data object LaunchImportPasswords : ListModeCommand()
875+
data class LaunchImportPasswords(val config: ImportPasswordConfig) : ListModeCommand()
876+
858877
data class LaunchReportAutofillBreakageConfirmation(val eTldPlusOne: String) : ListModeCommand()
859878
data object ShowUserReportSentMessage : ListModeCommand()
860879
data object ReevalutePromotions : ListModeCommand()
861880
}
862881

882+
@Parcelize
883+
data class ImportPasswordConfig(
884+
val canImportFromGooglePasswordManager: Boolean,
885+
val canImportFromCsv: Boolean,
886+
) : Parcelable
887+
863888
sealed class DuckAddressStatus {
864889
object NotADuckAddress : DuckAddressStatus()
865890
data class FetchingActivationStatus(val address: String) : DuckAddressStatus()

0 commit comments

Comments
 (0)