Skip to content

Commit 4ab4dbf

Browse files
committed
Make "bookmark added" dialog specific to that use case
1 parent fb2396f commit 4ab4dbf

File tree

4 files changed

+160
-275
lines changed

4 files changed

+160
-275
lines changed
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
/*
2+
* Copyright (c) 2024 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.app.bookmarks.dialog
18+
19+
import android.annotation.SuppressLint
20+
import android.content.Context
21+
import android.graphics.Typeface
22+
import android.text.Spannable
23+
import android.text.SpannableString
24+
import android.text.style.StyleSpan
25+
import android.view.LayoutInflater
26+
import android.view.WindowManager
27+
import android.widget.FrameLayout
28+
import androidx.lifecycle.lifecycleScope
29+
import com.duckduckgo.app.browser.databinding.BottomSheetAddBookmarkBinding
30+
import com.duckduckgo.common.utils.ConflatedJob
31+
import com.duckduckgo.savedsites.api.models.BookmarkFolder
32+
import com.google.android.material.R
33+
import com.google.android.material.bottomsheet.BottomSheetBehavior
34+
import com.google.android.material.bottomsheet.BottomSheetDialog
35+
import com.google.android.material.shape.CornerFamily
36+
import com.google.android.material.shape.MaterialShapeDrawable
37+
import com.google.android.material.shape.ShapeAppearanceModel
38+
import kotlinx.coroutines.delay
39+
import kotlinx.coroutines.launch
40+
import com.duckduckgo.mobile.android.R as CommonR
41+
42+
@SuppressLint("NoBottomSheetDialog")
43+
class BookmarkAddedConfirmationDialog(
44+
context: Context,
45+
private val bookmarkFolder: BookmarkFolder?,
46+
) : BottomSheetDialog(context) {
47+
48+
abstract class EventListener {
49+
/** Sets a listener to be invoked when favorite state is changed */
50+
open fun onFavoriteStateChangeClicked(isFavorited: Boolean) {}
51+
52+
/** Sets a listener to be invoked when edit bookmarks is clicked */
53+
open fun onEditBookmarkClicked() {}
54+
}
55+
56+
private var listener: EventListener? = null
57+
58+
private val binding = BottomSheetAddBookmarkBinding.inflate(LayoutInflater.from(context))
59+
60+
private val autoDismissDialogJob = ConflatedJob()
61+
62+
override fun show() {
63+
setContentView(binding.root)
64+
65+
window?.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND)
66+
behavior.state = BottomSheetBehavior.STATE_EXPANDED
67+
behavior.isDraggable = false
68+
roundCornersAlways(this)
69+
binding.bookmarksBottomSheetDialogTitle.text = getBookmarksBottomSheetTitle(context, bookmarkFolder)
70+
71+
binding.setAsFavorite.setOnClickListener {
72+
cancelDialogAutoDismiss()
73+
binding.setAsFavoriteSwitch.isChecked = !binding.setAsFavoriteSwitch.isChecked
74+
listener?.onFavoriteStateChangeClicked(binding.setAsFavoriteSwitch.isChecked)
75+
}
76+
binding.setAsFavoriteSwitch.setOnClickListener {
77+
cancelDialogAutoDismiss()
78+
listener?.onFavoriteStateChangeClicked(binding.setAsFavoriteSwitch.isChecked)
79+
}
80+
binding.editBookmark.setOnClickListener {
81+
cancelDialogAutoDismiss()
82+
listener?.onEditBookmarkClicked()
83+
dismiss()
84+
}
85+
86+
autoDismissDialogJob += lifecycleScope.launch {
87+
delay(BOOKMARKS_BOTTOM_SHEET_DURATION)
88+
dismiss()
89+
}
90+
super.show()
91+
}
92+
93+
private fun cancelDialogAutoDismiss() {
94+
autoDismissDialogJob.cancel()
95+
}
96+
97+
private fun getBookmarksBottomSheetTitle(context: Context, bookmarkFolder: BookmarkFolder?): SpannableString {
98+
val folderName = bookmarkFolder?.name ?: ""
99+
val fullText = context.getString(com.duckduckgo.saved.sites.impl.R.string.bookmarkAddedInBookmarks, folderName)
100+
val spannableString = SpannableString(fullText)
101+
102+
val boldStart = fullText.indexOf(folderName)
103+
val boldEnd = boldStart + folderName.length
104+
spannableString.setSpan(StyleSpan(Typeface.BOLD), boldStart, boldEnd, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
105+
return spannableString
106+
}
107+
108+
/** Sets event listener for the bottom sheet dialog */
109+
fun addEventListener(eventListener: EventListener) {
110+
listener = eventListener
111+
}
112+
113+
// TODO: Use a style when bookmarks is moved to its own module
114+
private fun roundCornersAlways(dialog: BottomSheetDialog) {
115+
dialog.setOnShowListener { dialogInterface ->
116+
val bottomSheetDialog = dialogInterface as BottomSheetDialog
117+
val bottomSheet = bottomSheetDialog.findViewById<FrameLayout>(R.id.design_bottom_sheet)
118+
bottomSheet?.background = MaterialShapeDrawable(
119+
ShapeAppearanceModel.builder().apply {
120+
setTopLeftCorner(CornerFamily.ROUNDED, context.resources.getDimension(CommonR.dimen.dialogBorderRadius))
121+
setTopRightCorner(CornerFamily.ROUNDED, context.resources.getDimension(CommonR.dimen.dialogBorderRadius))
122+
}.build(),
123+
)
124+
}
125+
}
126+
127+
private companion object {
128+
private const val BOOKMARKS_BOTTOM_SHEET_DURATION = 3_500L
129+
}
130+
}

app/src/main/java/com/duckduckgo/app/browser/BrowserTabFragment.kt

Lines changed: 21 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -31,21 +31,16 @@ import android.content.pm.ActivityInfo
3131
import android.content.pm.PackageManager
3232
import android.content.pm.ResolveInfo
3333
import android.content.res.Configuration
34-
import android.graphics.Typeface
3534
import android.net.Uri
3635
import android.os.Build
3736
import android.os.Bundle
3837
import android.os.Environment
3938
import android.os.Handler
40-
import android.os.Looper
4139
import android.os.Message
4240
import android.print.PrintAttributes
4341
import android.print.PrintManager
4442
import android.provider.MediaStore
45-
import android.text.Spannable
46-
import android.text.SpannableString
4743
import android.text.Spanned
48-
import android.text.style.StyleSpan
4944
import android.view.ContextMenu
5045
import android.view.MenuItem
5146
import android.view.MotionEvent
@@ -102,6 +97,7 @@ import androidx.webkit.WebViewCompat
10297
import androidx.webkit.WebViewFeature
10398
import com.duckduckgo.anvil.annotations.InjectWith
10499
import com.duckduckgo.app.accessibility.data.AccessibilitySettingsDataStore
100+
import com.duckduckgo.app.bookmarks.dialog.BookmarkAddedConfirmationDialog
105101
import com.duckduckgo.app.browser.BrowserTabViewModel.FileChooserRequestedParams
106102
import com.duckduckgo.app.browser.R.string
107103
import com.duckduckgo.app.browser.SSLErrorType.NONE
@@ -322,11 +318,9 @@ import com.duckduckgo.privacy.dashboard.api.ui.PrivacyDashboardHybridScreenResul
322318
import com.duckduckgo.privacyprotectionspopup.api.PrivacyProtectionsPopup
323319
import com.duckduckgo.privacyprotectionspopup.api.PrivacyProtectionsPopupFactory
324320
import com.duckduckgo.privacyprotectionspopup.api.PrivacyProtectionsPopupViewState
325-
import com.duckduckgo.savedsites.api.models.BookmarkFolder
326321
import com.duckduckgo.savedsites.api.models.SavedSite
327322
import com.duckduckgo.savedsites.api.models.SavedSite.Bookmark
328323
import com.duckduckgo.savedsites.api.models.SavedSitesNames
329-
import com.duckduckgo.savedsites.impl.bookmarks.BookmarksBottomSheetDialog
330324
import com.duckduckgo.savedsites.impl.bookmarks.FaviconPromptSheet
331325
import com.duckduckgo.savedsites.impl.dialogs.EditSavedSiteDialogFragment
332326
import com.duckduckgo.serp.logos.api.SerpLogoScreens.EasterEggLogoScreen
@@ -651,8 +645,6 @@ class BrowserTabFragment :
651645

652646
private lateinit var webViewContainer: FrameLayout
653647

654-
private var bookmarksBottomSheetDialog: BookmarksBottomSheetDialog.Builder? = null
655-
656648
private var autocompleteItemOffsetTop: Int = 0
657649
private var autocompleteFirstVisibleItemPosition: Int = 0
658650

@@ -3770,70 +3762,30 @@ class BrowserTabFragment :
37703762
}
37713763

37723764
private fun savedSiteAdded(savedSiteChangedViewState: SavedSiteChangedViewState) {
3773-
val dismissHandler = Handler(Looper.getMainLooper())
3774-
val dismissRunnable =
3775-
Runnable {
3776-
if (isAdded) {
3777-
bookmarksBottomSheetDialog?.dialog?.let { dialog ->
3778-
if (dialog.isShowing) {
3779-
dialog.dismiss()
3780-
}
3765+
context?.let { ctx ->
3766+
val dialog = BookmarkAddedConfirmationDialog(ctx, savedSiteChangedViewState.bookmarkFolder)
3767+
dialog.addEventListener(
3768+
object : BookmarkAddedConfirmationDialog.EventListener() {
3769+
override fun onFavoriteStateChangeClicked(isFavorited: Boolean) {
3770+
viewModel.onFavoriteMenuClicked()
37813771
}
3782-
}
3783-
}
3784-
val title = getBookmarksBottomSheetTitle(savedSiteChangedViewState.bookmarkFolder)
37853772

3786-
bookmarksBottomSheetDialog =
3787-
BookmarksBottomSheetDialog
3788-
.Builder(requireContext())
3789-
.setTitle(title)
3790-
.setPrimaryItem(
3791-
getString(com.duckduckgo.saved.sites.impl.R.string.addToFavorites),
3792-
icon = com.duckduckgo.mobile.android.R.drawable.ic_favorite_24,
3793-
).setSecondaryItem(
3794-
getString(com.duckduckgo.saved.sites.impl.R.string.editBookmark),
3795-
icon = com.duckduckgo.mobile.android.R.drawable.ic_edit_24,
3796-
).addEventListener(
3797-
object : BookmarksBottomSheetDialog.EventListener() {
3798-
override fun onPrimaryItemClicked() {
3799-
viewModel.onFavoriteMenuClicked()
3800-
dismissHandler.removeCallbacks(dismissRunnable)
3801-
}
3802-
3803-
override fun onSecondaryItemClicked() {
3804-
if (savedSiteChangedViewState.savedSite is Bookmark) {
3805-
pixel.fire(AppPixelName.ADD_BOOKMARK_CONFIRM_EDITED)
3806-
editSavedSite(
3807-
savedSiteChangedViewState.copy(
3808-
savedSite = savedSiteChangedViewState.savedSite.copy(
3809-
isFavorite = viewModel.browserViewState.value?.favorite != null,
3810-
),
3773+
override fun onEditBookmarkClicked() {
3774+
if (savedSiteChangedViewState.savedSite is Bookmark) {
3775+
pixel.fire(AppPixelName.ADD_BOOKMARK_CONFIRM_EDITED)
3776+
editSavedSite(
3777+
savedSiteChangedViewState.copy(
3778+
savedSite = savedSiteChangedViewState.savedSite.copy(
3779+
isFavorite = viewModel.browserViewState.value?.favorite != null,
38113780
),
3812-
)
3813-
dismissHandler.removeCallbacks(dismissRunnable)
3814-
}
3815-
}
3816-
3817-
override fun onBottomSheetDismissed() {
3818-
super.onBottomSheetDismissed()
3819-
dismissHandler.removeCallbacks(dismissRunnable)
3781+
),
3782+
)
38203783
}
3821-
},
3822-
)
3823-
bookmarksBottomSheetDialog?.show()
3824-
3825-
dismissHandler.postDelayed(dismissRunnable, BOOKMARKS_BOTTOM_SHEET_DURATION)
3826-
}
3827-
3828-
private fun getBookmarksBottomSheetTitle(bookmarkFolder: BookmarkFolder?): SpannableString {
3829-
val folderName = bookmarkFolder?.name ?: ""
3830-
val fullText = getString(com.duckduckgo.saved.sites.impl.R.string.bookmarkAddedInBookmarks, folderName)
3831-
val spannableString = SpannableString(fullText)
3832-
3833-
val boldStart = fullText.indexOf(folderName)
3834-
val boldEnd = boldStart + folderName.length
3835-
spannableString.setSpan(StyleSpan(Typeface.BOLD), boldStart, boldEnd, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
3836-
return spannableString
3784+
}
3785+
},
3786+
)
3787+
dialog.show()
3788+
}
38373789
}
38383790

38393791
private fun editSavedSite(savedSiteChangedViewState: SavedSiteChangedViewState) {
@@ -4371,8 +4323,6 @@ class BrowserTabFragment :
43714323

43724324
private const val COOKIES_ANIMATION_DELAY = 400L
43734325

4374-
private const val BOOKMARKS_BOTTOM_SHEET_DURATION = 3500L
4375-
43764326
private const val AUTOCOMPLETE_PADDING_DP = 6
43774327

43784328
private const val SITE_SECURITY_WARNING = "Warning: Security Risk"
@@ -4618,7 +4568,6 @@ class BrowserTabFragment :
46184568
renderFullscreenMode(viewState)
46194569
privacyProtectionsPopup.setViewState(viewState.privacyProtectionsPopupViewState)
46204570

4621-
bookmarksBottomSheetDialog?.dialog?.toggleSwitch(viewState.favorite != null)
46224571
val bookmark =
46234572
viewModel.browserViewState.value
46244573
?.bookmark
Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,10 @@
1919
xmlns:tools="http://schemas.android.com/tools"
2020
android:layout_width="match_parent"
2121
android:layout_height="match_parent"
22+
android:background="@drawable/rounded_top_corners_bottom_sheet_drawable"
2223
android:orientation="vertical"
2324
android:paddingTop="@dimen/actionBottomSheetVerticalPadding"
24-
android:paddingBottom="@dimen/actionBottomSheetVerticalPadding"
25-
android:background="@drawable/rounded_top_corners_bottom_sheet_drawable">
25+
android:paddingBottom="@dimen/actionBottomSheetVerticalPadding">
2626

2727
<com.duckduckgo.common.ui.view.text.DaxTextView
2828
android:id="@+id/bookmarksBottomSheetDialogTitle"
@@ -31,7 +31,6 @@
3131
android:layout_marginStart="@dimen/keyline_4"
3232
android:paddingTop="@dimen/bottomSheetTitleVerticalPadding"
3333
android:paddingBottom="@dimen/bottomSheetTitleVerticalPadding"
34-
android:visibility="gone"
3534
app:textType="secondary"
3635
app:typography="body1"
3736
tools:text="Actions" />
@@ -41,14 +40,15 @@
4140
android:layout_height="wrap_content">
4241

4342
<com.duckduckgo.common.ui.view.listitem.OneLineListItem
44-
android:id="@+id/bookmarksBottomSheetDialogPrimaryItem"
43+
android:id="@+id/setAsFavorite"
4544
android:layout_width="match_parent"
4645
android:layout_height="wrap_content"
4746
app:leadingIconBackground="circular"
48-
app:primaryText="Primary Item" />
47+
app:primaryText="@string/addToFavorites"
48+
app:leadingIcon="@drawable/ic_favorite_24" />
4949

5050
<com.duckduckgo.common.ui.view.DaxSwitch
51-
android:id="@+id/bookmarksBottomSheetSwitch"
51+
android:id="@+id/setAsFavoriteSwitch"
5252
android:layout_width="wrap_content"
5353
android:layout_height="wrap_content"
5454
android:layout_gravity="end|center_vertical"
@@ -57,10 +57,11 @@
5757

5858

5959
<com.duckduckgo.common.ui.view.listitem.OneLineListItem
60-
android:id="@+id/bookmarksBottomSheetDialogSecondaryItem"
60+
android:id="@+id/editBookmark"
6161
android:layout_width="match_parent"
6262
android:layout_height="wrap_content"
63+
app:leadingIcon="@drawable/ic_edit_24"
6364
app:leadingIconBackground="circular"
64-
app:primaryText="Secondary Item" />
65+
app:primaryText="@string/editBookmark" />
6566

6667
</LinearLayout>

0 commit comments

Comments
 (0)