@@ -27,6 +27,9 @@ import com.duckduckgo.autofill.impl.importing.takeout.processor.TakeoutBookmarkI
27
27
import com.duckduckgo.autofill.impl.importing.takeout.store.BookmarkImportConfigStore
28
28
import com.duckduckgo.autofill.impl.importing.takeout.webflow.ImportGoogleBookmarksWebFlowViewModel.ViewState.HideWebPage
29
29
import com.duckduckgo.autofill.impl.importing.takeout.webflow.ImportGoogleBookmarksWebFlowViewModel.ViewState.ShowWebPage
30
+ import com.duckduckgo.autofill.impl.importing.takeout.webflow.TakeoutMessageResult.TakeoutActionError
31
+ import com.duckduckgo.autofill.impl.importing.takeout.webflow.TakeoutMessageResult.TakeoutActionSuccess
32
+ import com.duckduckgo.autofill.impl.importing.takeout.webflow.TakeoutMessageResult.UnknownMessageFormat
30
33
import com.duckduckgo.autofill.impl.importing.takeout.zip.TakeoutBookmarkExtractor
31
34
import com.duckduckgo.autofill.impl.importing.takeout.zip.TakeoutBookmarkExtractor.ExtractionResult
32
35
import com.duckduckgo.autofill.impl.importing.takeout.zip.TakeoutZipDownloader
@@ -55,6 +58,7 @@ class ImportGoogleBookmarksWebFlowViewModel @Inject constructor(
55
58
private val takeoutBookmarkImporter : TakeoutBookmarkImporter ,
56
59
private val takeoutZipDownloader : TakeoutZipDownloader ,
57
60
private val bookmarkImportConfigStore : BookmarkImportConfigStore ,
61
+ private val takeoutWebMessageParser : TakeoutWebMessageParser ,
58
62
) : ViewModel() {
59
63
60
64
private val _viewState = MutableStateFlow <ViewState >(ViewState .Initializing )
@@ -63,6 +67,8 @@ class ImportGoogleBookmarksWebFlowViewModel @Inject constructor(
63
67
private val _commands = MutableSharedFlow <Command >(replay = 0 , extraBufferCapacity = 1 )
64
68
val commands: SharedFlow <Command > = _commands
65
69
70
+ private var latestStepInWebFlow: String = STEP_UNINITIALIZED
71
+
66
72
suspend fun loadInitialWebpage () {
67
73
withContext(dispatchers.io()) {
68
74
val initialUrl = bookmarkImportConfigStore.getConfig().launchUrlGoogleTakeout
@@ -164,22 +170,22 @@ class ImportGoogleBookmarksWebFlowViewModel @Inject constructor(
164
170
}
165
171
166
172
fun onCloseButtonPressed () {
167
- terminateFlowAsCancellation()
173
+ terminateFlowAsCancellation(latestStepInWebFlow )
168
174
}
169
175
170
176
fun onBackButtonPressed (canGoBack : Boolean = false) {
171
177
// if WebView can't go back, then we're at the first stage or something's gone wrong. Either way, time to cancel out of the screen.
172
178
if (! canGoBack) {
173
- terminateFlowAsCancellation()
179
+ terminateFlowAsCancellation(latestStepInWebFlow )
174
180
return
175
181
}
176
182
177
183
_viewState .value = ViewState .NavigatingBack
178
184
}
179
185
180
- private fun terminateFlowAsCancellation () {
186
+ private fun terminateFlowAsCancellation (stage : String ) {
181
187
viewModelScope.launch {
182
- _viewState .value = ViewState .UserCancelledImportFlow (stage = " unknown " )
188
+ _viewState .value = ViewState .UserCancelledImportFlow (stage)
183
189
}
184
190
}
185
191
@@ -254,12 +260,63 @@ class ImportGoogleBookmarksWebFlowViewModel @Inject constructor(
254
260
fun onPageStarted (url : String? ) {
255
261
val host = url?.toUri()?.host ? : return
256
262
_viewState .value = if (host.contains(" takeout.google.com" , ignoreCase = true )) {
263
+ updateLatestStepSpecificStage(GOOGLE_TAKEOUT_PAGE_REACHED )
257
264
HideWebPage
265
+ } else if (host.contains(" accounts.google.com" , ignoreCase = true )) {
266
+ updateLatestStepLoginPage()
267
+ ShowWebPage
258
268
} else {
259
269
ShowWebPage
260
270
}
261
271
}
262
272
273
+ private fun updateLatestStepLoginPage () {
274
+ // if we already have the login page as the current step, do nothing
275
+ if (latestStepInWebFlow == GOOGLE_ACCOUNTS_PAGE_FIRST || latestStepInWebFlow == GOOGLE_ACCOUNTS_REPEATED ) {
276
+ return
277
+ }
278
+
279
+ // if uninitialized, this is the first time seeing the login page
280
+ if (latestStepInWebFlow == STEP_UNINITIALIZED ) {
281
+ updateLatestStepSpecificStage(GOOGLE_ACCOUNTS_PAGE_FIRST )
282
+ return
283
+ }
284
+
285
+ // this must be a repeated visit to the login page
286
+ updateLatestStepSpecificStage(GOOGLE_ACCOUNTS_REPEATED )
287
+ }
288
+
289
+ private fun updateLatestStepSpecificStage (step : String ) {
290
+ latestStepInWebFlow = step
291
+ logcat { " cdr latest step is: $step " }
292
+ }
293
+
294
+ fun onWebMessageReceived (data : String ) {
295
+ viewModelScope.launch {
296
+ when (val result = takeoutWebMessageParser.parseMessage(data)) {
297
+ is TakeoutActionSuccess -> {
298
+ logcat { " cdr successfully parsed message: $result " }
299
+ updateLatestStepSpecificStage(result.actionID)
300
+ }
301
+
302
+ is TakeoutActionError -> {
303
+ logcat { " cdr experienced an error in the step: $result , raw:$data " }
304
+ }
305
+
306
+ UnknownMessageFormat -> {
307
+ logcat(WARN ) { " cdr failed to parse message, unknown format: $data " }
308
+ }
309
+ }
310
+ }
311
+ }
312
+
313
+ companion object {
314
+ private const val GOOGLE_TAKEOUT_PAGE_REACHED = " takeout"
315
+ private const val GOOGLE_ACCOUNTS_PAGE_FIRST = " login-first"
316
+ private const val GOOGLE_ACCOUNTS_REPEATED = " login-repeat"
317
+ private const val STEP_UNINITIALIZED = " uninitialized"
318
+ }
319
+
263
320
sealed interface Command {
264
321
data class InjectCredentialsFromReauth (val url : String? = null , val username : String = " " , val password : String? ) : Command
265
322
data class PromptUserToSelectFromStoredCredentials (
0 commit comments