@@ -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
@@ -162,22 +168,22 @@ class ImportGoogleBookmarksWebFlowViewModel @Inject constructor(
162
168
}
163
169
164
170
fun onCloseButtonPressed () {
165
- terminateFlowAsCancellation()
171
+ terminateFlowAsCancellation(latestStepInWebFlow )
166
172
}
167
173
168
174
fun onBackButtonPressed (canGoBack : Boolean = false) {
169
175
// 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.
170
176
if (! canGoBack) {
171
- terminateFlowAsCancellation()
177
+ terminateFlowAsCancellation(latestStepInWebFlow )
172
178
return
173
179
}
174
180
175
181
_viewState .value = ViewState .NavigatingBack
176
182
}
177
183
178
- private fun terminateFlowAsCancellation () {
184
+ private fun terminateFlowAsCancellation (stage : String ) {
179
185
viewModelScope.launch {
180
- _viewState .value = ViewState .UserCancelledImportFlow (stage = " unknown " )
186
+ _viewState .value = ViewState .UserCancelledImportFlow (stage)
181
187
}
182
188
}
183
189
@@ -252,12 +258,63 @@ class ImportGoogleBookmarksWebFlowViewModel @Inject constructor(
252
258
fun onPageStarted (url : String? ) {
253
259
val host = url?.toUri()?.host ? : return
254
260
_viewState .value = if (host.contains(" takeout.google.com" , ignoreCase = true )) {
261
+ updateLatestStepSpecificStage(GOOGLE_TAKEOUT_PAGE_REACHED )
255
262
HideWebPage
263
+ } else if (host.contains(" accounts.google.com" , ignoreCase = true )) {
264
+ updateLatestStepLoginPage()
265
+ ShowWebPage
256
266
} else {
257
267
ShowWebPage
258
268
}
259
269
}
260
270
271
+ private fun updateLatestStepLoginPage () {
272
+ // if we already have the login page as the current step, do nothing
273
+ if (latestStepInWebFlow == GOOGLE_ACCOUNTS_PAGE_FIRST || latestStepInWebFlow == GOOGLE_ACCOUNTS_REPEATED ) {
274
+ return
275
+ }
276
+
277
+ // if uninitialized, this is the first time seeing the login page
278
+ if (latestStepInWebFlow == STEP_UNINITIALIZED ) {
279
+ updateLatestStepSpecificStage(GOOGLE_ACCOUNTS_PAGE_FIRST )
280
+ return
281
+ }
282
+
283
+ // this must be a repeated visit to the login page
284
+ updateLatestStepSpecificStage(GOOGLE_ACCOUNTS_REPEATED )
285
+ }
286
+
287
+ private fun updateLatestStepSpecificStage (step : String ) {
288
+ latestStepInWebFlow = step
289
+ logcat { " cdr latest step is: $step " }
290
+ }
291
+
292
+ fun onWebMessageReceived (data : String ) {
293
+ viewModelScope.launch {
294
+ when (val result = takeoutWebMessageParser.parseMessage(data)) {
295
+ is TakeoutActionSuccess -> {
296
+ logcat { " cdr successfully parsed message: $result " }
297
+ updateLatestStepSpecificStage(result.actionID)
298
+ }
299
+
300
+ is TakeoutActionError -> {
301
+ logcat { " cdr experienced an error in the step: $result , raw:$data " }
302
+ }
303
+
304
+ UnknownMessageFormat -> {
305
+ logcat(WARN ) { " cdr failed to parse message, unknown format: $data " }
306
+ }
307
+ }
308
+ }
309
+ }
310
+
311
+ companion object {
312
+ private const val GOOGLE_TAKEOUT_PAGE_REACHED = " takeout"
313
+ private const val GOOGLE_ACCOUNTS_PAGE_FIRST = " login-first"
314
+ private const val GOOGLE_ACCOUNTS_REPEATED = " login-repeat"
315
+ private const val STEP_UNINITIALIZED = " uninitialized"
316
+ }
317
+
261
318
sealed interface Command {
262
319
data class InjectCredentialsFromReauth (val url : String? = null , val username : String = " " , val password : String? ) : Command
263
320
data class PromptUserToSelectFromStoredCredentials (
0 commit comments