diff --git a/app/src/main/java/com/duckduckgo/app/privacy/ui/WebsitesAdapter.kt b/app/src/main/java/com/duckduckgo/app/privacy/ui/WebsitesAdapter.kt index 30a0bc2cc770..03547d2c1569 100644 --- a/app/src/main/java/com/duckduckgo/app/privacy/ui/WebsitesAdapter.kt +++ b/app/src/main/java/com/duckduckgo/app/privacy/ui/WebsitesAdapter.kt @@ -32,7 +32,6 @@ import com.duckduckgo.app.privacy.model.UserWhitelistedDomain import com.duckduckgo.mobile.android.databinding.RowOneLineListItemBinding import com.duckduckgo.mobile.android.databinding.ViewSectionHeaderBinding import com.duckduckgo.mobile.android.ui.menu.PopupMenu -import com.duckduckgo.mobile.android.ui.view.divider.HorizontalDivider import kotlinx.coroutines.launch import timber.log.Timber @@ -46,15 +45,13 @@ class WebsitesAdapter( const val SITE_ENTRY = 0 const val DESCRIPTION_TYPE = 1 const val EMPTY_STATE_TYPE = 2 - const val DIVIDER_TYPE = 3 - const val SECTION_TITLE_TYPE = 4 + const val SECTION_TITLE_TYPE = 3 const val EMPTY_HINT_ITEM_SIZE = 1 } private val sortedHeaderElements = listOf( DESCRIPTION_TYPE, - DIVIDER_TYPE, SECTION_TITLE_TYPE, ) @@ -106,9 +103,6 @@ class WebsitesAdapter( binding.websiteDescription.setText(R.string.whitelistExplanation) WebsiteViewHolder.SimpleViewHolder(binding.root) } - DIVIDER_TYPE -> { - WebsiteViewHolder.SimpleViewHolder(HorizontalDivider(parent.context)) - } SECTION_TITLE_TYPE -> { val binding = ViewSectionHeaderBinding.inflate(inflater, parent, false) binding.sectionHeader.setText(R.string.settingsPrivacyProtectionWhitelist) diff --git a/app/src/main/java/com/duckduckgo/app/privacy/ui/WhitelistActivity.kt b/app/src/main/java/com/duckduckgo/app/privacy/ui/WhitelistActivity.kt index d7916976a083..c2ce0a38a7c1 100644 --- a/app/src/main/java/com/duckduckgo/app/privacy/ui/WhitelistActivity.kt +++ b/app/src/main/java/com/duckduckgo/app/privacy/ui/WhitelistActivity.kt @@ -22,17 +22,19 @@ import android.os.Bundle import android.view.Menu import android.view.MenuItem import android.widget.Toast -import androidx.appcompat.app.AlertDialog import com.duckduckgo.anvil.annotations.InjectWith import com.duckduckgo.app.browser.R import com.duckduckgo.app.browser.databinding.ActivityWhitelistBinding -import com.duckduckgo.app.browser.databinding.EditWhitelistBinding +import com.duckduckgo.app.browser.databinding.DialogEditWhitelistBinding import com.duckduckgo.app.browser.favicon.FaviconManager import com.duckduckgo.app.global.DuckDuckGoActivity import com.duckduckgo.app.global.extensions.html import com.duckduckgo.app.privacy.model.UserWhitelistedDomain import com.duckduckgo.app.privacy.ui.WhitelistViewModel.Command.* import com.duckduckgo.di.scopes.ActivityScope +import com.duckduckgo.mobile.android.ui.view.dialog.CustomAlertDialogBuilder +import com.duckduckgo.mobile.android.ui.view.dialog.TextAlertDialogBuilder +import com.duckduckgo.mobile.android.ui.view.dialog.TextAlertDialogBuilder.EventListener import com.duckduckgo.mobile.android.ui.viewbinding.viewBinding import javax.inject.Inject @@ -43,10 +45,7 @@ class WhitelistActivity : DuckDuckGoActivity() { lateinit var faviconManager: FaviconManager private lateinit var adapter: WebsitesAdapter - private val binding: ActivityWhitelistBinding by viewBinding() - - private var dialog: AlertDialog? = null private val viewModel: WhitelistViewModel by bindViewModel() private val toolbar @@ -107,64 +106,62 @@ class WhitelistActivity : DuckDuckGoActivity() { } private fun showAddDialog() { - val dialogBinding = EditWhitelistBinding.inflate(layoutInflater) - val addDialog = AlertDialog.Builder(this).apply { - setTitle(R.string.dialogAddTitle) - setView(dialogBinding.root) - setPositiveButton(R.string.dialogSaveAction) { _, _ -> - val newText = dialogBinding.textInput.text.toString() - viewModel.onEntryAdded(UserWhitelistedDomain(newText)) - } - setNegativeButton(android.R.string.no) { _, _ -> } - }.create() - - dialog?.dismiss() - dialog = addDialog - addDialog.show() + val inputBinding = DialogEditWhitelistBinding.inflate(layoutInflater) + CustomAlertDialogBuilder(this) + .setTitle(R.string.dialogAddTitle) + .setPositiveButton(R.string.dialogSave) + .setNegativeButton(R.string.cancel) + .setView(inputBinding) + .addEventListener( + object : CustomAlertDialogBuilder.EventListener() { + override fun onPositiveButtonClicked() { + val newText = inputBinding.customDialogTextInput.text + viewModel.onEntryAdded(UserWhitelistedDomain(newText)) + } + }, + ) + .show() } private fun showEditDialog(entry: UserWhitelistedDomain) { - val dialogBinding = EditWhitelistBinding.inflate(layoutInflater) - val editDialog = AlertDialog.Builder(this).apply { - setTitle(R.string.dialogEditTitle) - setView(dialogBinding.root) - setPositiveButton(R.string.dialogSaveAction) { _, _ -> - val newText = dialogBinding.textInput.text.toString() - viewModel.onEntryEdited(entry, UserWhitelistedDomain(newText)) - } - setNegativeButton(android.R.string.no) { _, _ -> } - }.create() - - dialog?.dismiss() - dialog = editDialog - editDialog.show() - - dialogBinding.textInput.setText(entry.domain) - dialogBinding.textInput.setSelection(entry.domain.length) + val inputBinding = DialogEditWhitelistBinding.inflate(layoutInflater) + inputBinding.customDialogTextInput.text = entry.domain + CustomAlertDialogBuilder(this) + .setTitle(R.string.dialogEditTitle) + .setPositiveButton(R.string.dialogSave) + .setNegativeButton(R.string.cancel) + .setView(inputBinding) + .addEventListener( + object : CustomAlertDialogBuilder.EventListener() { + override fun onPositiveButtonClicked() { + val newText = inputBinding.customDialogTextInput.text + viewModel.onEntryEdited(entry, UserWhitelistedDomain(newText)) + } + }, + ) + .show() } private fun showDeleteDialog(entry: UserWhitelistedDomain) { - val deleteDialog = AlertDialog.Builder(this).apply { - setTitle(R.string.dialogConfirmTitle) - setMessage(getString(R.string.whitelistEntryDeleteConfirmMessage, entry.domain).html(this.context)) - setPositiveButton(android.R.string.yes) { _, _ -> viewModel.onEntryDeleted(entry) } - setNegativeButton(android.R.string.no) { _, _ -> } - }.create() - - dialog?.dismiss() - dialog = deleteDialog - deleteDialog.show() + TextAlertDialogBuilder(this) + .setTitle(R.string.dialogConfirmTitle) + .setMessage(getString(R.string.whitelistEntryDeleteConfirmMessage, entry.domain).html(this)) + .setPositiveButton(android.R.string.yes) + .setNegativeButton(android.R.string.no) + .addEventListener( + object : EventListener() { + override fun onPositiveButtonClicked() { + viewModel.onEntryDeleted(entry) + } + }, + ) + .show() } private fun showWhitelistFormatError() { Toast.makeText(this, R.string.whitelistFormatError, Toast.LENGTH_LONG).show() } - override fun onDestroy() { - dialog?.dismiss() - super.onDestroy() - } - companion object { fun intent(context: Context): Intent { return Intent(context, WhitelistActivity::class.java) diff --git a/app/src/main/res/layout/dialog_edit_whitelist.xml b/app/src/main/res/layout/dialog_edit_whitelist.xml new file mode 100644 index 000000000000..7b3fb86fcebf --- /dev/null +++ b/app/src/main/res/layout/dialog_edit_whitelist.xml @@ -0,0 +1,24 @@ + + + \ No newline at end of file diff --git a/app/src/main/res/layout/edit_whitelist.xml b/app/src/main/res/layout/edit_whitelist.xml deleted file mode 100644 index 0b374fcfae44..000000000000 --- a/app/src/main/res/layout/edit_whitelist.xml +++ /dev/null @@ -1,39 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/view_fireproof_website_empty_hint.xml b/app/src/main/res/layout/view_fireproof_website_empty_hint.xml index adfae272373a..7eb40f4390e1 100644 --- a/app/src/main/res/layout/view_fireproof_website_empty_hint.xml +++ b/app/src/main/res/layout/view_fireproof_website_empty_hint.xml @@ -18,10 +18,7 @@ xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="wrap_content" - android:paddingStart="@dimen/keyline_4" - android:paddingTop="@dimen/keyline_5" - android:paddingEnd="@dimen/keyline_4" - android:paddingBottom="@dimen/keyline_5"> + android:padding="@dimen/keyline_4"> - \ No newline at end of file diff --git a/app/src/main/res/layout/view_list_item_empty_hint.xml b/app/src/main/res/layout/view_list_item_empty_hint.xml index f64690fea347..d94cb065c785 100644 --- a/app/src/main/res/layout/view_list_item_empty_hint.xml +++ b/app/src/main/res/layout/view_list_item_empty_hint.xml @@ -14,11 +14,19 @@ ~ limitations under the License. --> - \ No newline at end of file + android:padding="@dimen/keyline_4"> + + + + \ No newline at end of file diff --git a/app/src/main/res/menu/menu_tab_switcher_activity.xml b/app/src/main/res/menu/menu_tab_switcher_activity.xml index 57b9bfff4b14..e1ddea753a31 100644 --- a/app/src/main/res/menu/menu_tab_switcher_activity.xml +++ b/app/src/main/res/menu/menu_tab_switcher_activity.xml @@ -27,7 +27,7 @@ diff --git a/app/src/main/res/menu/whitelist_activity_menu.xml b/app/src/main/res/menu/whitelist_activity_menu.xml index 925c68b494f6..2b91500c70cc 100644 --- a/app/src/main/res/menu/whitelist_activity_menu.xml +++ b/app/src/main/res/menu/whitelist_activity_menu.xml @@ -21,7 +21,7 @@ diff --git a/common-ui/src/main/java/com/duckduckgo/mobile/android/ui/view/dialog/CustomAlertDialogBuilder.kt b/common-ui/src/main/java/com/duckduckgo/mobile/android/ui/view/dialog/CustomAlertDialogBuilder.kt new file mode 100644 index 000000000000..89aa6324474a --- /dev/null +++ b/common-ui/src/main/java/com/duckduckgo/mobile/android/ui/view/dialog/CustomAlertDialogBuilder.kt @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2022 DuckDuckGo + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.duckduckgo.mobile.android.ui.view.dialog + +import android.content.Context +import android.view.LayoutInflater +import androidx.annotation.StringRes +import androidx.appcompat.app.AlertDialog +import androidx.viewbinding.ViewBinding +import com.duckduckgo.mobile.android.R.style +import com.duckduckgo.mobile.android.databinding.DialogCustomAlertBinding +import com.duckduckgo.mobile.android.ui.view.gone +import com.google.android.material.dialog.MaterialAlertDialogBuilder + +class CustomAlertDialogBuilder(val context: Context) : DaxAlertDialog { + + abstract class EventListener { + open fun onDialogShown() {} + open fun onDialogDismissed() {} + open fun onPositiveButtonClicked() {} + open fun onNegativeButtonClicked() {} + } + + internal class DefaultEventListener : EventListener() + + private var dialog: AlertDialog? = null + private var customBinding: ViewBinding? = null + + var listener: EventListener = DefaultEventListener() + private set + var titleText: CharSequence = "" + private set + var messageText: CharSequence = "" + private set + var positiveButtonText: CharSequence = "" + private set + var negativeButtonText: CharSequence = "" + private set + + fun setTitle(@StringRes textId: Int): CustomAlertDialogBuilder { + titleText = context.getText(textId) + return this + } + + fun setMessage(@StringRes textId: Int): CustomAlertDialogBuilder { + messageText = context.getText(textId) + return this + } + + fun setTitle(text: CharSequence): CustomAlertDialogBuilder { + titleText = text + return this + } + + fun setMessage(text: CharSequence): CustomAlertDialogBuilder { + messageText = text + return this + } + + fun setPositiveButton(@StringRes textId: Int): CustomAlertDialogBuilder { + positiveButtonText = context.getText(textId) + return this + } + + fun setNegativeButton(@StringRes textId: Int): CustomAlertDialogBuilder { + negativeButtonText = context.getText(textId) + return this + } + + fun addEventListener(eventListener: EventListener): CustomAlertDialogBuilder { + listener = eventListener + return this + } + + fun setView(binding: ViewBinding): CustomAlertDialogBuilder { + customBinding = binding + return this + } + + override fun build(): DaxAlertDialog { + val binding: DialogCustomAlertBinding = DialogCustomAlertBinding.inflate(LayoutInflater.from(context)) + binding.customDialogContent.addView(customBinding?.root) + + val dialogBuilder = MaterialAlertDialogBuilder(context, style.Widget_DuckDuckGo_Dialog) + .setView(binding.root) + .apply { + setCancelable(false) + setOnDismissListener { listener.onDialogDismissed() } + } + dialog = dialogBuilder.create() + setViews(binding, dialog!!) + + return this + } + + override fun show() { + if (dialog == null) { + build() + } + dialog?.show() + listener.onDialogShown() + } + + override fun dismiss() { + dialog?.dismiss() + } + + private fun setViews( + binding: DialogCustomAlertBinding, + dialog: AlertDialog, + ) { + binding.customDialogTitle.text = titleText + + if (messageText.isEmpty()) { + binding.customDialogMessage.gone() + } else { + binding.customDialogMessage.text = messageText + } + + if (positiveButtonText.isEmpty()) { + binding.customDialogPositiveButton.gone() + } else { + binding.customDialogPositiveButton.text = positiveButtonText + binding.customDialogPositiveButton.setOnClickListener { + listener.onPositiveButtonClicked() + dialog.dismiss() + } + } + + if (negativeButtonText.isEmpty()) { + binding.customDialogNegativeButton.gone() + } else { + binding.customDialogNegativeButton.text = negativeButtonText + binding.customDialogNegativeButton.setOnClickListener { + listener.onNegativeButtonClicked() + dialog.dismiss() + } + } + } +} diff --git a/app/src/main/res/drawable/ic_add.xml b/common-ui/src/main/res/drawable/ic_add_24.xml similarity index 67% rename from app/src/main/res/drawable/ic_add.xml rename to common-ui/src/main/res/drawable/ic_add_24.xml index ece870200492..e06fe79003fa 100644 --- a/app/src/main/res/drawable/ic_add.xml +++ b/common-ui/src/main/res/drawable/ic_add_24.xml @@ -1,5 +1,5 @@ - + + + + + + + + + + + + + + \ No newline at end of file diff --git a/common-ui/src/main/res/layout/view_dax_text_input.xml b/common-ui/src/main/res/layout/view_dax_text_input.xml index ff13c838c9f7..8e70e2442a40 100644 --- a/common-ui/src/main/res/layout/view_dax_text_input.xml +++ b/common-ui/src/main/res/layout/view_dax_text_input.xml @@ -33,7 +33,8 @@ android:id="@+id/internal_edit_text" style="@style/Typography.DuckDuckGo.Body1" android:layout_width="match_parent" - android:layout_height="wrap_content"/> + android:layout_height="wrap_content" + android:textColor="?attr/daxColorPrimaryText" /> @@ -45,9 +46,9 @@ android:layout_marginEnd="@dimen/outlinedTextPasswordEndMarginWithEndIcon" android:foregroundGravity="center_vertical" android:visibility="gone" + app:backgroundTint="?attr/daxColorPrimaryIcon" app:layout_constraintBottom_toBottomOf="@+id/internal_input_layout" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toTopOf="@+id/internal_input_layout" - app:srcCompat="@drawable/ic_password_show" - app:backgroundTint="?attr/daxColorPrimaryIcon"/> + app:srcCompat="@drawable/ic_password_show" /> \ No newline at end of file diff --git a/common-ui/src/main/res/layout/view_section_header.xml b/common-ui/src/main/res/layout/view_section_header.xml index b849b54e1468..4f100171c0e9 100644 --- a/common-ui/src/main/res/layout/view_section_header.xml +++ b/common-ui/src/main/res/layout/view_section_header.xml @@ -1,5 +1,4 @@ - - - \ No newline at end of file + android:layout_height="wrap_content" /> \ No newline at end of file diff --git a/common-ui/src/main/res/values/design-system-theming.xml b/common-ui/src/main/res/values/design-system-theming.xml index e7b83d6c46b1..afd91a7426a4 100644 --- a/common-ui/src/main/res/values/design-system-theming.xml +++ b/common-ui/src/main/res/values/design-system-theming.xml @@ -76,7 +76,7 @@ @style/Widget.DuckDuckGo.AutoCompleteTextView @style/AlertDialogTheme @style/AlertDialogTheme - @style/AlertDialogTheme + @style/Widget.DuckDuckGo.Dialog @style/Widget.DuckDuckGo.DaxButton.TextButton.Primary @style/Widget.DuckDuckGo.DaxButton.Secondary @style/Widget.DuckDuckGo.DaxButton.Ghost