Bug 1917919 - Add 'Learn more' button to the addon installation dialog. r=android-reviewers,amejiamarmol,zmckenney,gmalekpour,willdurand

Differential Revision: https://phabricator.services.mozilla.com/D221826
This commit is contained in:
Tara
2024-10-23 15:45:21 +00:00
parent 3351a0f780
commit 8909938c32
8 changed files with 119 additions and 2 deletions

View File

@@ -48,6 +48,8 @@ open class AddonDialogFragment : AppCompatDialogFragment() {
@ColorRes
val confirmButtonBackgroundColor: Int? = null,
@ColorRes
val learnMoreLinkTextColor: Int? = null,
@ColorRes
val confirmButtonTextColor: Int? = null,
val confirmButtonRadius: Float? = null,
)

View File

@@ -8,6 +8,7 @@ import android.annotation.SuppressLint
import android.app.Dialog
import android.content.DialogInterface
import android.graphics.Color
import android.graphics.Paint
import android.graphics.drawable.ColorDrawable
import android.graphics.drawable.GradientDrawable
import android.os.Bundle
@@ -35,6 +36,7 @@ private const val KEY_DIALOG_WIDTH_MATCH_PARENT = "KEY_DIALOG_WIDTH_MATCH_PARENT
private const val KEY_POSITIVE_BUTTON_BACKGROUND_COLOR = "KEY_POSITIVE_BUTTON_BACKGROUND_COLOR"
private const val KEY_POSITIVE_BUTTON_TEXT_COLOR = "KEY_POSITIVE_BUTTON_TEXT_COLOR"
private const val KEY_POSITIVE_BUTTON_RADIUS = "KEY_POSITIVE_BUTTON_RADIUS"
private const val KEY_LEARN_MORE_LINK_TEXT_COLOR = "KEY_LEARN_MORE_LINK_TEXT_COLOR"
private const val KEY_FOR_OPTIONAL_PERMISSIONS = "KEY_FOR_OPTIONAL_PERMISSIONS"
internal const val KEY_PERMISSIONS = "KEY_PERMISSIONS"
private const val DEFAULT_VALUE = Int.MAX_VALUE
@@ -55,6 +57,11 @@ class PermissionsDialogFragment : AddonDialogFragment() {
*/
var onNegativeButtonClicked: (() -> Unit)? = null
/**
* A lambda called when the learn more link is clicked.
*/
var onLearnMoreClicked: (() -> Unit)? = null
internal val addon get() = requireNotNull(safeArguments.getParcelableCompat(KEY_ADDON, Addon::class.java))
internal val positiveButtonRadius
@@ -85,6 +92,13 @@ class PermissionsDialogFragment : AddonDialogFragment() {
DEFAULT_VALUE,
)
internal val learnMoreLinkTextColor
get() =
safeArguments.getInt(
KEY_LEARN_MORE_LINK_TEXT_COLOR,
DEFAULT_VALUE,
)
/**
* This flag is used to adjust the permissions prompt for optional permissions (instead of asking
* users to grant the required permissions at install time, which is the default).
@@ -160,6 +174,9 @@ class PermissionsDialogFragment : AddonDialogFragment() {
rootView.findViewById<TextView>(R.id.optional_or_required_text).text =
buildOptionalOrRequiredText(listPermissions.isNotEmpty())
val learnMoreLink = rootView.findViewById<TextView>(R.id.learn_more_link)
learnMoreLink.paintFlags = Paint.UNDERLINE_TEXT_FLAG
val permissionsRecyclerView = rootView.findViewById<RecyclerView>(R.id.permissions)
val positiveButton = rootView.findViewById<Button>(R.id.allow_button)
val negativeButton = rootView.findViewById<Button>(R.id.deny_button)
@@ -211,6 +228,13 @@ class PermissionsDialogFragment : AddonDialogFragment() {
dismiss()
}
if (learnMoreLinkTextColor != DEFAULT_VALUE) {
val color = ContextCompat.getColor(requireContext(), learnMoreLinkTextColor)
learnMoreLink.setTextColor(color)
}
learnMoreLink.setOnClickListener {
onLearnMoreClicked?.invoke()
}
return rootView
}
@@ -243,6 +267,7 @@ class PermissionsDialogFragment : AddonDialogFragment() {
* @param promptsStyling Styling properties for the dialog.
* @param onPositiveButtonClicked A lambda called when the allow button is clicked.
* @param onNegativeButtonClicked A lambda called when the deny button is clicked.
* @param onLearnMoreClicked A lambda called when the learn more button is clicked.
*/
fun newInstance(
addon: Addon,
@@ -254,6 +279,7 @@ class PermissionsDialogFragment : AddonDialogFragment() {
),
onPositiveButtonClicked: ((Addon, Boolean) -> Unit)? = null,
onNegativeButtonClicked: (() -> Unit)? = null,
onLearnMoreClicked: (() -> Unit)? = null,
): PermissionsDialogFragment {
val fragment = PermissionsDialogFragment()
val arguments = fragment.arguments ?: Bundle()
@@ -272,13 +298,16 @@ class PermissionsDialogFragment : AddonDialogFragment() {
promptsStyling?.confirmButtonBackgroundColor?.apply {
putInt(KEY_POSITIVE_BUTTON_BACKGROUND_COLOR, this)
}
promptsStyling?.confirmButtonTextColor?.apply {
putInt(KEY_POSITIVE_BUTTON_TEXT_COLOR, this)
}
promptsStyling?.learnMoreLinkTextColor?.apply {
putInt(KEY_LEARN_MORE_LINK_TEXT_COLOR, this)
}
}
fragment.onPositiveButtonClicked = onPositiveButtonClicked
fragment.onNegativeButtonClicked = onNegativeButtonClicked
fragment.onLearnMoreClicked = onLearnMoreClicked
fragment.arguments = arguments
return fragment
}

View File

@@ -56,6 +56,15 @@
android:textColor="?android:attr/textColorPrimary"
tools:text="It requires your permission to:" />
<TextView
android:id="@+id/learn_more_link"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/allow_in_private_browsing"
android:layout_alignStart="@id/title"
android:layout_marginTop="30dp"
android:text="@string/mozac_feature_addons_permissions_dialog_learn_more" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/permissions"
android:layout_width="match_parent"

View File

@@ -133,6 +133,8 @@
<string name="mozac_feature_addons_permissions_dialog_allow">Allow</string>
<!-- This is a button to deny the optional permissions requested by an add-on . -->
<string name="mozac_feature_addons_permissions_dialog_deny">Deny</string>
<!-- Text link to learn more about permission requests on the add-on permissions dialog . -->
<string name="mozac_feature_addons_permissions_dialog_learn_more">Learn more</string>
<!-- This is a button to cancel the add-on installation . -->
<string name="mozac_feature_addons_permissions_dialog_cancel">Cancel</string>
<!-- Accessibility content description to install add-on button. %1$s is the add-on name. -->

View File

@@ -79,6 +79,7 @@ class PermissionsDialogFragmentTest {
val fragment = createPermissionsDialogFragment(addon, permissions = emptyList())
var allowedWasExecuted = false
var denyWasExecuted = false
var learnMoreWasExecuted = false
fragment.onPositiveButtonClicked = { _, _ ->
allowedWasExecuted = true
@@ -88,6 +89,10 @@ class PermissionsDialogFragmentTest {
denyWasExecuted = true
}
fragment.onLearnMoreClicked = {
learnMoreWasExecuted = true
}
doReturn(testContext).`when`(fragment).requireContext()
val dialog = fragment.onCreateDialog(null)
@@ -95,12 +100,15 @@ class PermissionsDialogFragmentTest {
val positiveButton = dialog.findViewById<Button>(R.id.allow_button)
val negativeButton = dialog.findViewById<Button>(R.id.deny_button)
val learnMoreLink = dialog.findViewById<TextView>(R.id.learn_more_link)
positiveButton.performClick()
negativeButton.performClick()
learnMoreLink.performClick()
assertTrue(allowedWasExecuted)
assertTrue(denyWasExecuted)
assertTrue(learnMoreWasExecuted)
}
@Test

View File

@@ -60,6 +60,7 @@ import mozilla.components.concept.engine.EngineSession
import mozilla.components.concept.engine.EngineView
import mozilla.components.concept.storage.HistoryMetadataKey
import mozilla.components.feature.contextmenu.DefaultSelectionActionDelegate
import mozilla.components.feature.customtabs.isCustomTabIntent
import mozilla.components.feature.media.ext.findActiveMediaTab
import mozilla.components.feature.privatemode.notification.PrivateNotificationFeature
import mozilla.components.feature.search.BrowserStoreSearchAdapter
@@ -144,6 +145,7 @@ import org.mozilla.fenix.perf.StartupPathProvider
import org.mozilla.fenix.perf.StartupTimeline
import org.mozilla.fenix.perf.StartupTypeTelemetry
import org.mozilla.fenix.session.PrivateNotificationService
import org.mozilla.fenix.settings.SupportUtils
import org.mozilla.fenix.shortcut.NewTabShortcutIntentProcessor.Companion.ACTION_OPEN_PRIVATE_TAB
import org.mozilla.fenix.tabhistory.TabHistoryDialogFragment
import org.mozilla.fenix.tabstray.TabsTrayFragment
@@ -193,6 +195,13 @@ open class HomeActivity : LocaleAwareAppCompatActivity(), NavHostActivity {
newTab = true,
from = BrowserDirection.FromGlobal,
)
} else {
startActivity(
SupportUtils.createCustomTabIntent(
context = this,
url = url,
),
)
}
},
)
@@ -406,10 +415,13 @@ open class HomeActivity : LocaleAwareAppCompatActivity(), NavHostActivity {
extensionsProcessDisabledForegroundController,
extensionsProcessDisabledBackgroundController,
serviceWorkerSupport,
webExtensionPromptFeature,
crashReporterBinding,
)
if (!isCustomTabIntent(intent)) {
lifecycle.addObserver(webExtensionPromptFeature)
}
if (shouldAddToRecentsScreen(intent)) {
intent.removeExtra(START_IN_RECENTS_SCREEN)
moveTaskToBack(true)

View File

@@ -34,6 +34,7 @@ import mozilla.components.ui.widgets.withCenterAlignedButtons
import org.mozilla.fenix.BuildConfig
import org.mozilla.fenix.R
import org.mozilla.fenix.ext.components
import org.mozilla.fenix.settings.SupportUtils
import org.mozilla.fenix.theme.ThemeManager
/**
@@ -272,6 +273,10 @@ class WebExtensionPromptFeature(
),
confirmButtonRadius =
(context.resources.getDimensionPixelSize(R.dimen.tab_corner_radius)).toFloat(),
learnMoreLinkTextColor = ThemeManager.resolveAttribute(
R.attr.textAccent,
context,
),
),
onPositiveButtonClicked = { _, privateBrowsingAllowed ->
handlePermissions(
@@ -287,6 +292,16 @@ class WebExtensionPromptFeature(
privateBrowsingAllowed = false,
)
},
onLearnMoreClicked = {
onLinkClicked.invoke(
// Bug 1920564 - add finalized Learn More SUMO link for the install dialog
SupportUtils.getSumoURLForTopic(
context,
SupportUtils.SumoTopic.MANAGE_OPTIONAL_EXTENSION_PERMISSIONS,
),
false,
)
},
)
dialog.show(
fragmentManager,
@@ -312,6 +327,20 @@ class WebExtensionPromptFeature(
}
}
}
dialog.onLearnMoreClicked = {
store.state.webExtensionPromptRequest?.let { promptRequest ->
if (promptRequest is WebExtensionPromptRequest.AfterInstallation.Permissions) {
onLinkClicked.invoke(
// Bug 1920564 - add finalized Learn More SUMO link for the install dialog
SupportUtils.getSumoURLForTopic(
context,
SupportUtils.SumoTopic.MANAGE_OPTIONAL_EXTENSION_PERMISSIONS,
),
false,
)
}
}
}
}
findPreviousPostInstallationDialogFragment()?.let { dialog ->

View File

@@ -16,6 +16,7 @@ import mozilla.components.browser.state.state.extension.WebExtensionPromptReques
import mozilla.components.browser.state.store.BrowserStore
import mozilla.components.concept.engine.webextension.WebExtensionInstallException
import mozilla.components.feature.addons.Addon
import mozilla.components.feature.addons.ui.PermissionsDialogFragment
import mozilla.components.support.ktx.android.content.appVersionName
import mozilla.components.support.test.ext.joinBlocking
import mozilla.components.support.test.robolectric.testContext
@@ -29,6 +30,7 @@ import org.junit.runner.RunWith
import org.mozilla.fenix.BuildConfig
import org.mozilla.fenix.R
import org.mozilla.fenix.helpers.FenixRobolectricTestRunner
import org.mozilla.fenix.settings.SupportUtils
import org.mozilla.fenix.utils.LinkTextView
@RunWith(FenixRobolectricTestRunner::class)
@@ -390,4 +392,28 @@ class WebExtensionPromptFeatureTest {
dialog.dismiss()
}
}
@Test
fun `WHEN clicking Learn More on the Permissions Dialog THEN open the correct SUMO page in a custom tab`() {
val addon: Addon = mockk(relaxed = true)
// Bug 1920564 - add finalized Learn More SUMO link for the install dialog
val expectedUrl = SupportUtils.getSumoURLForTopic(
testContext,
SupportUtils.SumoTopic.MANAGE_OPTIONAL_EXTENSION_PERMISSIONS,
)
val dialog = PermissionsDialogFragment.newInstance(
addon = addon,
forOptionalPermissions = false,
permissions = listOf("tabs"),
onLearnMoreClicked = {
onLinkClicked(expectedUrl, false)
},
)
dialog.onLearnMoreClicked?.invoke()
verify { onLinkClicked(expectedUrl, false) }
}
}