Commit a7a0d2e2 authored by Simon Chae's avatar Simon Chae
Browse files

Closes #5433: Add UX flow for unsupported add-ons

Also allow AddonManager.getAddons() to return unsupported add-ons
parent e8e19a6b
Loading
Loading
Loading
Loading
+15 −7
Original line number Diff line number Diff line
@@ -34,10 +34,10 @@ import kotlinx.android.parcel.Parcelize
@Parcelize
data class Addon(
    val id: String,
    val authors: List<Author>,
    val categories: List<String>,
    val downloadUrl: String,
    val version: String,
    val authors: List<Author> = emptyList(),
    val categories: List<String> = emptyList(),
    val downloadUrl: String = "",
    val version: String = "",
    val permissions: List<String> = emptyList(),
    val translatableName: Map<String, String> = emptyMap(),
    val translatableDescription: Map<String, String> = emptyMap(),
@@ -45,8 +45,8 @@ data class Addon(
    val iconUrl: String = "",
    val siteUrl: String = "",
    val rating: Rating? = null,
    val createdAt: String,
    val updatedAt: String,
    val createdAt: String = "",
    val updatedAt: String = "",
    val installedState: InstalledState? = null
) : Parcelable {
    /**
@@ -85,6 +85,8 @@ data class Addon(
     * @property version The installed version.
     * @property enabled Indicates if this [Addon] is enabled to interact with web content or not,
     * defaults to false.
     * @property supported Indicates if this [Addon] is supported by the browser or not, defaults
     * to true.
     * @property optionsPageUrl the URL of the page displaying the
     * options page (options_ui in the extension's manifest).
     */
@@ -93,7 +95,8 @@ data class Addon(
        val id: String,
        val version: String,
        val optionsPageUrl: String,
        val enabled: Boolean = false
        val enabled: Boolean = false,
        val supported: Boolean = true
    ) : Parcelable

    /**
@@ -113,6 +116,11 @@ data class Addon(
     */
    fun isEnabled() = installedState?.enabled == true

    /**
     * Returns whether or not this [Addon] is currently supported by the browser.
     */
    fun isSupported() = installedState?.supported == true

    companion object {
        /**
         * A map of permissions to translation string ids.
+19 −2
Original line number Diff line number Diff line
@@ -43,9 +43,26 @@ class AddonManager(
            // Make sure extension support is initialized, i.e. the state of all installed extensions is known.
            WebExtensionSupport.awaitInitialization()

            return addonsProvider.getAvailableAddons().map { addon ->
                installedExtensions[addon.id]?.let { addon.copy(installedState = it.toInstalledState()) } ?: addon
            val supportedAddons = addonsProvider.getAvailableAddons().map { addon ->
                installedExtensions[addon.id]?.let {
                    addon.copy(installedState = it.toInstalledState())
                } ?: addon
            }

            val supportedAddonIds = supportedAddons.map { it.id }

            // Get all installed addons that are not yet supported.
            val unsupportedAddons = installedExtensions
                    .filterKeys { !supportedAddonIds.contains(it) }
                    .filterValues { !it.isBuiltIn() }
                    .map { extensionEntry ->
                val extension = extensionEntry.value

                val installedState = extension.toInstalledState().copy(enabled = false, supported = false)
                Addon(extension.id, siteUrl = extension.url, installedState = installedState)
            }

            return supportedAddons + unsupportedAddons
        } catch (throwable: Throwable) {
            throw AddonManagerException(throwable)
        }
+4 −0
Original line number Diff line number Diff line
@@ -73,6 +73,8 @@
    <string name="mozac_feature_addons_installed_section">Installed</string>
    <!-- This is displayed in a page where the user can see the installed and recommended(not installed yet) addons, this string indicates the recommended section. -->
    <string name="mozac_feature_addons_recommended_section">Recommended</string>
    <!-- This is displayed in a page where the user can see the installed and recommended(not installed yet) addons, this string indicates the not yet supported section. -->
    <string name="mozac_feature_addons_unsupported_section">Not yet supported</string>
    <!-- Displays a page with all the details of an addon. -->
    <string name="mozac_feature_addons_details">Details</string>
    <!-- Displays a page with all the permissions of an addon. -->
@@ -105,6 +107,8 @@
    <string name="mozac_feature_addons_updater_notification_content" tools:ignore="PluralsCandidate">%1$d new permissions are required</string>
    <!-- Name of the "notification channel" used for displaying a notification for updating an addon. See https://developer.android.com/training/notify-user/channels -->
    <string name="mozac_feature_addons_updater_notification_channel">Addon updates</string>
    <!-- This is the caption for not yet supported screen caption -->
    <string name="mozac_feature_addons_not_yet_supported_caption">Firefox add-on technology is modernizing. These add-ons use frameworks that are not compatible with Firefox 75 &amp; beyond.</string>
    <!-- This is the caption for the addon installation progress overlay -->
    <string name="mozac_add_on_install_progress_caption">Downloading and verifying add-on&#8230;</string>

+15 −2
Original line number Diff line number Diff line
@@ -32,6 +32,7 @@ import mozilla.components.support.test.whenever
import mozilla.components.support.webextensions.WebExtensionSupport
import org.junit.After
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertNotEquals
import org.junit.Assert.assertNotNull
import org.junit.Assert.assertNull
@@ -95,7 +96,10 @@ class AddonManagerTest {
        }
        val store = BrowserStore(
            BrowserState(
                extensions = mapOf("ext1" to WebExtensionState("ext1", "url"))
                extensions = mapOf(
                    "ext1" to WebExtensionState("ext1", "url"),
                    "unsupported_ext" to WebExtensionState("unsupported_ext", "url", enabled = false)
                )
            )
        )

@@ -104,15 +108,24 @@ class AddonManagerTest {
        whenever(extension.id).thenReturn("ext1")
        WebExtensionSupport.installedExtensions["ext1"] = extension

        val unsupportedExtension: WebExtension = mock()
        whenever(unsupportedExtension.id).thenReturn("unsupported_ext")
        whenever(unsupportedExtension.url).thenReturn("site_url")
        WebExtensionSupport.installedExtensions["unsupported_ext"] = unsupportedExtension

        // Verify add-ons were updated with state provided by the engine/store
        val addons = AddonManager(store, mock(), addonsProvider, mock()).getAddons()
        assertEquals(2, addons.size)
        assertEquals(3, addons.size)
        assertEquals("ext1", addons[0].id)
        assertNotNull(addons[0].installedState)
        assertEquals("ext1", addons[0].installedState!!.id)

        assertEquals("ext2", addons[1].id)
        assertNull(addons[1].installedState)

        // Verify the unsupported add-on was included in addons
        assertEquals("unsupported_ext", addons[2].id)
        assertFalse(addons[2].installedState!!.supported)
    }

    @Test(expected = AddonManagerException::class)
+5 −0
Original line number Diff line number Diff line
@@ -68,6 +68,11 @@
            android:label="@string/mozac_feature_addons_addons"
            android:theme="@style/Theme.AppCompat.Light" />

        <activity
            android:name=".addons.NotYetSupportedAddonActivity"
            android:label="@string/mozac_feature_addons_addons"
            android:theme="@style/Theme.AppCompat.Light" />

        <activity android:name=".IntentReceiverActivity">
            <intent-filter>
                <action android:name="android.intent.action.VIEW" />
Loading