Skip to content

Commit

Permalink
Launch import flow from Password management screen
Browse files Browse the repository at this point in the history
  • Loading branch information
CDRussell committed Oct 21, 2024
1 parent 60acf5f commit 2fbb14b
Show file tree
Hide file tree
Showing 17 changed files with 839 additions and 35 deletions.
1 change: 1 addition & 0 deletions autofill/autofill-impl/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ dependencies {
implementation AndroidX.biometric

implementation "net.zetetic:android-database-sqlcipher:_"
implementation "com.facebook.shimmer:shimmer:_"

// Testing dependencies
testImplementation "org.mockito.kotlin:mockito-kotlin:_"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,11 @@ interface AutofillAuthorizationGracePeriod {
*/
fun recordSuccessfulAuthorization()

/**
* Requests an extended grace period. This may extend the grace period to a longer duration.
*/
fun requestExtendedGracePeriod()

/**
* Invalidates the grace period, so that the next call to [isAuthRequired] will return true
*/
Expand All @@ -53,12 +58,17 @@ class AutofillTimeBasedAuthorizationGracePeriod @Inject constructor(
) : AutofillAuthorizationGracePeriod {

private var lastSuccessfulAuthTime: Long? = null
private var extendedGraceTimeRequested: Long? = null

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

override fun requestExtendedGracePeriod() {
extendedGraceTimeRequested = timeProvider.currentTimeMillis()
}

override fun isAuthRequired(): Boolean {
lastSuccessfulAuthTime?.let { lastAuthTime ->
val timeSinceLastAuth = timeProvider.currentTimeMillis() - lastAuthTime
Expand All @@ -67,17 +77,36 @@ class AutofillTimeBasedAuthorizationGracePeriod @Inject constructor(
Timber.v("Within grace period; auth not required")
return false
}

if (inExtendedGracePeriod()) {
Timber.v("Within extended grace period; auth not required")
return false
}
}

extendedGraceTimeRequested = null
Timber.v("No last auth time recorded or outside grace period; auth required")

return true
}

private fun inExtendedGracePeriod(): Boolean {
val extendedRequest = extendedGraceTimeRequested
if (extendedRequest == null) {
return false
} else {
val timeSinceExtendedGrace = timeProvider.currentTimeMillis() - extendedRequest
return timeSinceExtendedGrace <= AUTH_GRACE_EXTENDED_PERIOD_MS
}
}

override fun invalidate() {
lastSuccessfulAuthTime = null
extendedGraceTimeRequested = null
}

companion object {
private const val AUTH_GRACE_PERIOD_MS = 15_000
private const val AUTH_GRACE_EXTENDED_PERIOD_MS = 60_000
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -308,20 +308,15 @@ class ImportGooglePasswordsWebFlowFragment :

override suspend fun onCsvAvailable(csv: String) {
Timber.i("cdr CSV available %s", csv)
val parseResult = csvPasswordImporter.readCsv(csv)
when (parseResult) {
when (val parseResult = csvPasswordImporter.readCsv(csv)) {
is Success -> {
passwordImporter.importPasswords(parseResult.loginCredentialsToImport)
onCsvParsed(parseResult)
}

Error -> {
Timber.e("cdr Error parsing CSV")
is Error -> {
onCsvError()
}
}

val resultBundle = Bundle().also { it.putParcelable(RESULT_KEY_DETAILS, parseResult) }
setFragmentResult(RESULT_KEY, resultBundle)

/**
* val result = csvPasswordImporter.importCsv(csv)
* val resultDetails = when (result) {
Expand All @@ -337,6 +332,12 @@ class ImportGooglePasswordsWebFlowFragment :
*/
}

private suspend fun onCsvParsed(parseResult: Success) {
passwordImporter.importPasswords(parseResult.loginCredentialsToImport)
val resultBundle = Bundle()
setFragmentResult(RESULT_KEY, resultBundle)
}

override suspend fun onCsvError() {
Timber.e("cdr Error decoding CSV")
val resultBundle = Bundle().also {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,7 @@ import com.duckduckgo.autofill.impl.pixel.AutofillPixelNames.AUTOFILL_ENGAGEMENT
import com.duckduckgo.autofill.impl.pixel.AutofillPixelNames.AUTOFILL_ENGAGEMENT_ONBOARDED_USER
import com.duckduckgo.autofill.impl.pixel.AutofillPixelNames.AUTOFILL_ENGAGEMENT_STACKED_LOGINS
import com.duckduckgo.autofill.impl.pixel.AutofillPixelNames.AUTOFILL_IMPORT_PASSWORDS_COPIED_DESKTOP_LINK
import com.duckduckgo.autofill.impl.pixel.AutofillPixelNames.AUTOFILL_IMPORT_PASSWORDS_CTA_BUTTON
import com.duckduckgo.autofill.impl.pixel.AutofillPixelNames.AUTOFILL_IMPORT_PASSWORDS_GET_DESKTOP_BROWSER
import com.duckduckgo.autofill.impl.pixel.AutofillPixelNames.AUTOFILL_IMPORT_PASSWORDS_OVERFLOW_MENU
import com.duckduckgo.autofill.impl.pixel.AutofillPixelNames.AUTOFILL_IMPORT_PASSWORDS_SHARED_DESKTOP_LINK
import com.duckduckgo.autofill.impl.pixel.AutofillPixelNames.AUTOFILL_IMPORT_PASSWORDS_SYNC_WITH_DESKTOP
import com.duckduckgo.autofill.impl.pixel.AutofillPixelNames.AUTOFILL_IMPORT_PASSWORDS_USER_JOURNEY_RESTARTED
Expand All @@ -43,6 +41,8 @@ import com.duckduckgo.autofill.impl.pixel.AutofillPixelNames.AUTOFILL_SITE_BREAK
import com.duckduckgo.autofill.impl.pixel.AutofillPixelNames.AUTOFILL_SITE_BREAKAGE_REPORT_CONFIRMATION_CONFIRMED
import com.duckduckgo.autofill.impl.pixel.AutofillPixelNames.AUTOFILL_SITE_BREAKAGE_REPORT_CONFIRMATION_DISMISSED
import com.duckduckgo.autofill.impl.pixel.AutofillPixelNames.AUTOFILL_SITE_BREAKAGE_REPORT_CONFIRMATION_DISPLAYED
import com.duckduckgo.autofill.impl.pixel.AutofillPixelNames.AUTOFILL_SYNC_DESKTOP_PASSWORDS_CTA_BUTTON
import com.duckduckgo.autofill.impl.pixel.AutofillPixelNames.AUTOFILL_SYNC_DESKTOP_PASSWORDS_OVERFLOW_MENU
import com.duckduckgo.autofill.impl.pixel.AutofillPixelNames.EMAIL_TOOLTIP_DISMISSED
import com.duckduckgo.autofill.impl.pixel.AutofillPixelNames.EMAIL_USE_ADDRESS
import com.duckduckgo.autofill.impl.pixel.AutofillPixelNames.EMAIL_USE_ALIAS
Expand Down Expand Up @@ -142,8 +142,8 @@ enum class AutofillPixelNames(override val pixelName: String) : Pixel.PixelName
AUTOFILL_TOGGLED_ON_SEARCH("m_autofill_toggled_on"),
AUTOFILL_TOGGLED_OFF_SEARCH("m_autofill_toggled_off"),

AUTOFILL_IMPORT_PASSWORDS_CTA_BUTTON("m_autofill_logins_import_no_passwords"),
AUTOFILL_IMPORT_PASSWORDS_OVERFLOW_MENU("m_autofill_logins_import"),
AUTOFILL_SYNC_DESKTOP_PASSWORDS_CTA_BUTTON("m_autofill_logins_import_no_passwords"),
AUTOFILL_SYNC_DESKTOP_PASSWORDS_OVERFLOW_MENU("m_autofill_logins_import"),
AUTOFILL_IMPORT_PASSWORDS_GET_DESKTOP_BROWSER("m_autofill_logins_import_get_desktop"),
AUTOFILL_IMPORT_PASSWORDS_SYNC_WITH_DESKTOP("m_autofill_logins_import_sync"),
AUTOFILL_IMPORT_PASSWORDS_USER_TOOK_NO_ACTION("m_autofill_logins_import_no-action"),
Expand Down Expand Up @@ -177,8 +177,8 @@ object AutofillPixelsRequiringDataCleaning : PixelParamRemovalPlugin {
AUTOFILL_ENGAGEMENT_ONBOARDED_USER.pixelName to PixelParameter.removeAtb(),
AUTOFILL_ENGAGEMENT_STACKED_LOGINS.pixelName to PixelParameter.removeAtb(),

AUTOFILL_IMPORT_PASSWORDS_CTA_BUTTON.pixelName to PixelParameter.removeAtb(),
AUTOFILL_IMPORT_PASSWORDS_OVERFLOW_MENU.pixelName to PixelParameter.removeAtb(),
AUTOFILL_SYNC_DESKTOP_PASSWORDS_CTA_BUTTON.pixelName to PixelParameter.removeAtb(),
AUTOFILL_SYNC_DESKTOP_PASSWORDS_OVERFLOW_MENU.pixelName to PixelParameter.removeAtb(),
AUTOFILL_IMPORT_PASSWORDS_GET_DESKTOP_BROWSER.pixelName to PixelParameter.removeAtb(),
AUTOFILL_IMPORT_PASSWORDS_SYNC_WITH_DESKTOP.pixelName to PixelParameter.removeAtb(),
AUTOFILL_IMPORT_PASSWORDS_USER_TOOK_NO_ACTION.pixelName to PixelParameter.removeAtb(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

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

import android.os.Parcelable
import android.util.Patterns
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
Expand All @@ -36,6 +37,7 @@ import com.duckduckgo.autofill.api.email.EmailManager
import com.duckduckgo.autofill.impl.R
import com.duckduckgo.autofill.impl.deviceauth.DeviceAuthenticator
import com.duckduckgo.autofill.impl.deviceauth.DeviceAuthenticator.AuthConfiguration
import com.duckduckgo.autofill.impl.importing.gpm.feature.AutofillImportPasswordsFeature
import com.duckduckgo.autofill.impl.pixel.AutofillPixelNames
import com.duckduckgo.autofill.impl.pixel.AutofillPixelNames.AUTOFILL_DELETE_LOGIN
import com.duckduckgo.autofill.impl.pixel.AutofillPixelNames.AUTOFILL_ENABLE_AUTOFILL_TOGGLE_MANUALLY_DISABLED
Expand Down Expand Up @@ -112,6 +114,7 @@ import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import kotlinx.parcelize.Parcelize
import timber.log.Timber

@ContributesViewModel(ActivityScope::class)
Expand All @@ -133,6 +136,7 @@ class AutofillSettingsViewModel @Inject constructor(
private val autofillBreakageReportSender: AutofillBreakageReportSender,
private val autofillBreakageReportDataStore: AutofillSiteBreakageReportingDataStore,
private val autofillBreakageReportCanShowRules: AutofillBreakageReportCanShowRules,
private val importPasswordsFeature: AutofillImportPasswordsFeature,
) : ViewModel() {

private val _viewState = MutableStateFlow(ViewState())
Expand Down Expand Up @@ -690,7 +694,18 @@ class AutofillSettingsViewModel @Inject constructor(
}

fun onImportPasswords() {
addCommand(LaunchImportPasswords)
viewModelScope.launch(dispatchers.io()) {
with(importPasswordsFeature) {
val csvImport = self().isEnabled() && canImportFromCsvFile().isEnabled()
val gpmImport = self().isEnabled() && canImportFromGooglePasswordManager().isEnabled()
val importConfig = ImportPasswordConfig(canImportFromCsv = csvImport, canImportFromGooglePasswordManager = gpmImport)
addCommand(LaunchImportPasswords(importConfig))

// addCommand(LaunchImportPasswords(ImportPasswordConfig(canImportFromCsv = false, canImportFromGooglePasswordManager = false)))
// addCommand(LaunchImportPasswords(ImportPasswordConfig(canImportFromCsv = false, canImportFromGooglePasswordManager = true)))
// addCommand(LaunchImportPasswords(ImportPasswordConfig(canImportFromCsv = true, canImportFromGooglePasswordManager = false)))
}
}
}

fun onReportBreakageClicked() {
Expand All @@ -702,7 +717,10 @@ class AutofillSettingsViewModel @Inject constructor(
}
}

fun updateCurrentSite(currentUrl: String?, privacyProtectionEnabled: Boolean?) {
fun updateCurrentSite(
currentUrl: String?,
privacyProtectionEnabled: Boolean?,
) {
val updatedReportBreakageState = _viewState.value.reportBreakageState.copy(
currentUrl = currentUrl,
privacyProtectionEnabled = privacyProtectionEnabled,
Expand Down Expand Up @@ -854,12 +872,19 @@ class AutofillSettingsViewModel @Inject constructor(
data object LaunchResetNeverSaveListConfirmation : ListModeCommand()
data class LaunchDeleteAllPasswordsConfirmation(val numberToDelete: Int) : ListModeCommand()
data class PromptUserToAuthenticateMassDeletion(val authConfiguration: AuthConfiguration) : ListModeCommand()
data object LaunchImportPasswords : ListModeCommand()
data class LaunchImportPasswords(val config: ImportPasswordConfig) : ListModeCommand()

data class LaunchReportAutofillBreakageConfirmation(val eTldPlusOne: String) : ListModeCommand()
data object ShowUserReportSentMessage : ListModeCommand()
data object ReevalutePromotions : ListModeCommand()
}

@Parcelize
data class ImportPasswordConfig(
val canImportFromGooglePasswordManager: Boolean,
val canImportFromCsv: Boolean,
) : Parcelable

sealed class DuckAddressStatus {
object NotADuckAddress : DuckAddressStatus()
data class FetchingActivationStatus(val address: String) : DuckAddressStatus()
Expand Down
Loading

0 comments on commit 2fbb14b

Please sign in to comment.