Skip to content

Commit 700a40b

Browse files
committed
Add UI for importing bookmarks from Google
1 parent 7b058c0 commit 700a40b

21 files changed

+1489
-78
lines changed

autofill/autofill-api/src/main/java/com/duckduckgo/autofill/api/AutofillCredentialDialogs.kt

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -68,8 +68,8 @@ interface CredentialSavePickerDialog {
6868

6969
companion object {
7070
fun resultKeyUserChoseToSaveCredentials(tabId: String) = "${prefix(tabId, TAG)}/UserChoseToSave"
71-
fun resultKeyShouldPromptToDisableAutofill(tabId: String) =
72-
"${prefix(tabId, TAG)}/ShouldPromptToDisableAutofill"
71+
72+
fun resultKeyShouldPromptToDisableAutofill(tabId: String) = "${prefix(tabId, TAG)}/ShouldPromptToDisableAutofill"
7373

7474
const val TAG = "CredentialSavePickerDialog"
7575
const val KEY_URL = "url"
@@ -253,15 +253,17 @@ interface CredentialAutofillDialogFactory {
253253
/**
254254
* Creates a dialog which prompts the user to import passwords from Google Passwords
255255
*/
256-
fun autofillImportPasswordsPromoDialog(importSource: AutofillImportLaunchSource, tabId: String, url: String): DialogFragment
256+
fun autofillImportPasswordsPromoDialog(
257+
importSource: AutofillImportLaunchSource,
258+
tabId: String,
259+
url: String,
260+
): DialogFragment
257261
}
258262

259263
private fun prefix(
260264
tabId: String,
261265
tag: String,
262-
): String {
263-
return "$tabId/$tag"
264-
}
266+
): String = "$tabId/$tag"
265267

266268
@Parcelize
267269
enum class AutofillImportLaunchSource(val value: String) : Parcelable {
@@ -273,3 +275,9 @@ enum class AutofillImportLaunchSource(val value: String) : Parcelable {
273275
Unknown("unknown"),
274276
MainAppSettings("settings"),
275277
}
278+
279+
@Parcelize
280+
enum class AutofillImportBookmarksLaunchSource(val value: String) : Parcelable {
281+
Unknown("unknown"),
282+
AutofillDevSettings("autofill_dev_settings"),
283+
}
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
/*
2+
* Copyright (c) 2025 DuckDuckGo
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.duckduckgo.autofill.impl.importing.takeout.webflow
18+
19+
import android.os.Bundle
20+
import android.view.LayoutInflater
21+
import android.view.View
22+
import android.view.View.GONE
23+
import android.view.View.VISIBLE
24+
import android.view.ViewGroup
25+
import androidx.appcompat.widget.Toolbar
26+
import androidx.fragment.app.Fragment
27+
import com.duckduckgo.anvil.annotations.InjectWith
28+
import com.duckduckgo.autofill.impl.R
29+
import com.duckduckgo.autofill.impl.databinding.FragmentImportBookmarksResultBinding
30+
import com.duckduckgo.di.scopes.FragmentScope
31+
32+
@InjectWith(FragmentScope::class)
33+
class ImportFinishedFragment : Fragment() {
34+
private var binding: FragmentImportBookmarksResultBinding? = null
35+
private var onDoneCallback: (() -> Unit)? = null
36+
37+
override fun onCreateView(
38+
inflater: LayoutInflater,
39+
container: ViewGroup?,
40+
savedInstanceState: Bundle?,
41+
): View {
42+
binding = FragmentImportBookmarksResultBinding.inflate(inflater, container, false)
43+
return binding!!.root
44+
}
45+
46+
override fun onViewCreated(
47+
view: View,
48+
savedInstanceState: Bundle?,
49+
) {
50+
super.onViewCreated(view, savedInstanceState)
51+
setupUi()
52+
setupToolbar()
53+
}
54+
55+
override fun onDestroyView() {
56+
super.onDestroyView()
57+
binding = null
58+
}
59+
60+
private fun setupUi() {
61+
val success = arguments?.getBoolean(ARG_BOOKMARK_IMPORT_SUCCESS, false) ?: false
62+
63+
if (success) {
64+
setupUiForSuccess()
65+
} else {
66+
setupUiForFailure()
67+
}
68+
69+
binding?.doneButton?.setOnClickListener {
70+
onDoneCallback?.invoke()
71+
}
72+
}
73+
74+
private fun setupUiForSuccess() {
75+
val bookmarkCount = arguments?.getInt(ARG_BOOKMARK_COUNT_SUCCESS, 0) ?: 0
76+
77+
binding?.run {
78+
bookmarksCountText.setPrimaryText(getString(R.string.importBookmarksFromGoogleSuccessBookmarksCount, bookmarkCount))
79+
80+
importResultTitle.text = getString(R.string.importBookmarksSuccessTitle)
81+
importResultSubtitle.text = getString(R.string.importBookmarksSuccessSubtitle)
82+
importResultSubtitle.visibility = VISIBLE
83+
84+
bookmarksCountText.visibility = VISIBLE
85+
bookmarksFailedText.visibility = GONE
86+
}
87+
}
88+
89+
private fun setupUiForFailure() {
90+
val error = arguments?.getString(ARG_BOOKMARK_FAILURE_MESSAGE) ?: getString(R.string.importBookmarksErrorGenericMessage)
91+
92+
binding?.run {
93+
bookmarksFailedText.setPrimaryText(error)
94+
95+
importResultTitle.text = getString(R.string.importBookmarksErrorTitle)
96+
importResultSubtitle.visibility = GONE
97+
98+
bookmarksCountText.visibility = GONE
99+
bookmarksFailedText.visibility = VISIBLE
100+
}
101+
}
102+
103+
fun setOnDoneCallback(callback: () -> Unit) {
104+
onDoneCallback = callback
105+
}
106+
107+
private fun setupToolbar() {
108+
val toolbar = activity?.findViewById<Toolbar>(com.duckduckgo.mobile.android.R.id.toolbar)
109+
toolbar?.setNavigationOnClickListener {
110+
onDoneCallback?.invoke()
111+
}
112+
}
113+
114+
companion object {
115+
private const val ARG_BOOKMARK_COUNT_SUCCESS = "bookmark_import_count_success"
116+
private const val ARG_BOOKMARK_IMPORT_SUCCESS = "bookmark_import_success"
117+
private const val ARG_BOOKMARK_FAILURE_MESSAGE = "bookmark_import_failure_message"
118+
119+
fun newInstanceSuccess(bookmarksImported: Int): ImportFinishedFragment = ImportFinishedFragment().apply {
120+
arguments =
121+
Bundle().apply {
122+
putInt(ARG_BOOKMARK_COUNT_SUCCESS, bookmarksImported)
123+
putBoolean(ARG_BOOKMARK_IMPORT_SUCCESS, true)
124+
}
125+
}
126+
127+
fun newInstanceFailure(message: String): ImportFinishedFragment = ImportFinishedFragment().apply {
128+
arguments =
129+
Bundle().apply {
130+
putString(ARG_BOOKMARK_FAILURE_MESSAGE, message)
131+
putBoolean(ARG_BOOKMARK_IMPORT_SUCCESS, false)
132+
}
133+
}
134+
}
135+
}

autofill/autofill-impl/src/main/java/com/duckduckgo/autofill/impl/importing/takeout/webflow/ImportGoogleBookmarkResult.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,4 +47,7 @@ sealed interface UserCannotImportReason : Parcelable {
4747

4848
@Parcelize
4949
data object DownloadError : UserCannotImportReason
50+
51+
@Parcelize
52+
data object Unknown : UserCannotImportReason
5053
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/*
2+
* Copyright (c) 2025 DuckDuckGo
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.duckduckgo.autofill.impl.importing.takeout.webflow
18+
19+
import android.os.Bundle
20+
import android.view.View
21+
import com.duckduckgo.anvil.annotations.InjectWith
22+
import com.duckduckgo.autofill.impl.R
23+
import com.duckduckgo.autofill.impl.databinding.FragmentImportBookmarksProgressBinding
24+
import com.duckduckgo.common.ui.DuckDuckGoFragment
25+
import com.duckduckgo.di.scopes.FragmentScope
26+
27+
@InjectWith(FragmentScope::class)
28+
class ImportGoogleBookmarksAutomationInProgressViewFragment : DuckDuckGoFragment(R.layout.fragment_import_bookmarks_progress) {
29+
private var binding: FragmentImportBookmarksProgressBinding? = null
30+
31+
override fun onViewCreated(
32+
view: View,
33+
savedInstanceState: Bundle?,
34+
) {
35+
super.onViewCreated(view, savedInstanceState)
36+
binding = FragmentImportBookmarksProgressBinding.bind(view)
37+
}
38+
39+
override fun onDestroyView() {
40+
super.onDestroyView()
41+
binding = null
42+
}
43+
44+
companion object {
45+
fun newInstance(): ImportGoogleBookmarksAutomationInProgressViewFragment = ImportGoogleBookmarksAutomationInProgressViewFragment()
46+
}
47+
}

0 commit comments

Comments
 (0)