Commit c257ffb4 authored by Tiger Oakes's avatar Tiger Oakes Committed by Tiger Oakes
Browse files

Issue #3647 - Custom HTTP(S) icons in toolbar

parent 6d61ab87
Loading
Loading
Loading
Loading
+20 −9
Original line number Diff line number Diff line
@@ -31,6 +31,7 @@ import kotlinx.coroutines.launch
import mozilla.components.browser.menu.BrowserMenuBuilder
import mozilla.components.browser.toolbar.display.DisplayToolbar
import mozilla.components.browser.toolbar.display.DisplayToolbar.Companion.BOTTOM_PROGRESS_BAR
import mozilla.components.browser.toolbar.display.SiteSecurityIcons
import mozilla.components.browser.toolbar.display.TrackingProtectionIconView
import mozilla.components.browser.toolbar.edit.EditToolbar
import mozilla.components.concept.toolbar.AutocompleteDelegate
@@ -113,12 +114,12 @@ class BrowserToolbar @JvmOverloads constructor(
        }

    /**
     *  Set/Get the site security icon colours (usually a lock or globe icon). It uses a pair of integers
     *  Set/Get the site security icons (usually a lock or globe icon). It uses a pair of drawables
     *  which represent the insecure and secure colours respectively.
     */
    var siteSecurityColor: Pair<Int, Int>
        get() = displayToolbar.securityIconColor
        set(value) { displayToolbar.securityIconColor = value }
    var siteSecurityIcons
        get() = displayToolbar.securityIcons
        set(value) { displayToolbar.securityIcons = value }

    /**
     * Gets/Sets a custom view that will be drawn as behind the URL and page actions in display mode.
@@ -300,6 +301,11 @@ class BrowserToolbar @JvmOverloads constructor(
            editToolbar.urlView.typeface = value
        }

    fun setSiteSecurityColor(colors: Pair<Int, Int>) {
        displayToolbar.securityIcons =
            displayToolbar.securityIcons.withColorFilter(colors.first, colors.second)
    }

    /**
     * Sets a listener to be invoked when focus of the URL input view (in edit mode) changed.
     */
@@ -468,15 +474,20 @@ class BrowserToolbar @JvmOverloads constructor(
                    R.styleable.BrowserToolbar_browserToolbarSuggestionBackgroundColor,
                    suggestionBackgroundColor
                )
                val inSecure = getColor(
                val inSecureIcon = getDrawable(R.styleable.BrowserToolbar_browserToolbarInsecureIcon)
                    ?: displayToolbar.securityIcons.insecure
                val secureIcon = getDrawable(R.styleable.BrowserToolbar_browserToolbarSecureIcon)
                    ?: displayToolbar.securityIcons.secure
                val inSecureColor = getColor(
                    R.styleable.BrowserToolbar_browserToolbarInsecureColor,
                    displayToolbar.securityIconColor.first
                    displayToolbar.defaultColor
                )
                val secure = getColor(
                val secureColor = getColor(
                    R.styleable.BrowserToolbar_browserToolbarSecureColor,
                    displayToolbar.securityIconColor.second
                    displayToolbar.defaultColor
                )
                siteSecurityColor = Pair(inSecure, secure)
                siteSecurityIcons = SiteSecurityIcons(inSecureIcon, secureIcon)
                    .withColorFilter(inSecureColor, secureColor)
                val fadingEdgeLength = getDimensionPixelSize(
                    R.styleable.BrowserToolbar_browserToolbarFadingEdgeSize,
                    resources.getDimensionPixelSize(R.dimen.mozac_browser_toolbar_url_fading_edge_size)
+57 −62
Original line number Diff line number Diff line
@@ -33,8 +33,6 @@ import mozilla.components.concept.toolbar.Toolbar.SiteTrackingProtection.ON_NO_T
import mozilla.components.concept.toolbar.Toolbar.SiteTrackingProtection.ON_TRACKERS_BLOCKED
import mozilla.components.concept.toolbar.Toolbar.SiteTrackingProtection.OFF_GLOBALLY
import mozilla.components.concept.toolbar.Toolbar.SiteTrackingProtection.OFF_FOR_A_SITE
import mozilla.components.ui.icons.R.drawable.mozac_ic_globe
import mozilla.components.ui.icons.R.drawable.mozac_ic_lock

/**
 * Sub-component of the browser toolbar responsible for displaying the URL and related controls.
@@ -89,6 +87,58 @@ internal class DisplayToolbar(
            setTrackingProtectionState(siteTrackingProtection)
        }

    private val browserActions: MutableList<ActionWrapper> = mutableListOf()
    private val pageActions: MutableList<ActionWrapper> = mutableListOf()
    private val navigationActions: MutableList<ActionWrapper> = mutableListOf()

    // Margin between browser actions.
    internal var browserActionMargin = 0

    // Location of progress bar
    internal var progressBarGravity = BOTTOM_PROGRESS_BAR

    // Horizontal margin of URL Box (surrounding URL and page actions).
    internal var urlBoxMargin = 0

    // An optional view to be drawn behind the URL and page actions.
    internal var urlBoxView: View? = null
        set(value) {
            // Remove previous view from ViewGroup
            if (field != null) {
                removeView(field)
            }
            // Add new view to ViewGroup (at position 0 to be drawn *before* the URL and page actions)
            if (value != null) {
                addView(value, 0)
            }
            field = value
        }

    // Callback to determine whether to open edit mode or not.
    internal var onUrlClicked: () -> Boolean = { true }

    @ColorInt
    internal val defaultColor = ContextCompat.getColor(context, R.color.photonWhite)

    @ColorInt
    internal var separatorColor = ContextCompat.getColor(context, R.color.photonGrey80)

    private var currentSiteSecurity = SiteSecurity.INSECURE

    private var siteTrackingProtection = OFF_GLOBALLY

    internal var securityIcons = SiteSecurityIcons.getDefaultSecurityIcons(context, defaultColor)
        set(value) {
            field = value
            setSiteSecurity(currentSiteSecurity)
        }

    internal var menuViewColor = defaultColor
        set(value) {
            field = value
            menuView.setColorFilter(value)
        }

    internal val trackingProtectionIconView = TrackingProtectionIconView(context).apply {
        isVisible = false
        setImageResource(R.drawable.mozac_tracking_protection_state_list)
@@ -113,7 +163,7 @@ internal class DisplayToolbar(
    internal val siteSecurityIconView = AppCompatImageView(context).apply {
        setPadding(resources.getDimensionPixelSize(R.dimen.mozac_browser_toolbar_icon_padding))

        setImageResource(mozac_ic_globe)
        setImageDrawable(securityIcons.insecure)

        // Avoiding text behind the icon being selectable. If the listener is not set
        // with a value or null text behind the icon can be selectable.
@@ -165,58 +215,6 @@ internal class DisplayToolbar(
        })
    }

    private val browserActions: MutableList<ActionWrapper> = mutableListOf()
    private val pageActions: MutableList<ActionWrapper> = mutableListOf()
    private val navigationActions: MutableList<ActionWrapper> = mutableListOf()

    // Margin between browser actions.
    internal var browserActionMargin = 0

    // Location of progress bar
    internal var progressBarGravity = BOTTOM_PROGRESS_BAR

    // Horizontal margin of URL Box (surrounding URL and page actions).
    internal var urlBoxMargin = 0

    // An optional view to be drawn behind the URL and page actions.
    internal var urlBoxView: View? = null
        set(value) {
            // Remove previous view from ViewGroup
            if (field != null) {
                removeView(field)
            }
            // Add new view to ViewGroup (at position 0 to be drawn *before* the URL and page actions)
            if (value != null) {
                addView(value, 0)
            }
            field = value
        }

    // Callback to determine whether to open edit mode or not.
    internal var onUrlClicked: () -> Boolean = { true }

    private val defaultColor = ContextCompat.getColor(context, R.color.photonWhite)

    @ColorInt
    internal var separatorColor =
        ContextCompat.getColor(context, R.color.photonGrey80)

    private var currentSiteSecurity = SiteSecurity.INSECURE

    private var siteTrackingProtection = OFF_GLOBALLY

    internal var securityIconColor = Pair(defaultColor, defaultColor)
        set(value) {
            field = value
            setSiteSecurity(currentSiteSecurity)
        }

    internal var menuViewColor = defaultColor
        set(value) {
            field = value
            menuView.setColorFilter(value)
        }

    init {
        addView(trackingProtectionIconView)
        addView(trackingProtectionAndSecurityIndicatorSeparatorView)
@@ -307,14 +305,11 @@ internal class DisplayToolbar(
     * Sets the site's security icon as secure if true, else the regular globe.
     */
    fun setSiteSecurity(secure: SiteSecurity) {
        val (image, color) = when (secure) {
            SiteSecurity.INSECURE -> Pair(mozac_ic_globe, securityIconColor.first)
            SiteSecurity.SECURE -> Pair(mozac_ic_lock, securityIconColor.second)
        }
        siteSecurityIconView.apply {
            setImageResource(image)
            setColorFilter(color)
        val drawable = when (secure) {
            SiteSecurity.INSECURE -> securityIcons.insecure
            SiteSecurity.SECURE -> securityIcons.secure
        }
        siteSecurityIconView.setImageDrawable(drawable)

        currentSiteSecurity = secure
    }
+67 −0
Original line number Diff line number Diff line
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

package mozilla.components.browser.toolbar.display

import android.content.Context
import android.graphics.BlendMode
import android.graphics.BlendModeColorFilter
import android.graphics.ColorFilter
import android.graphics.PorterDuff
import android.graphics.PorterDuffColorFilter
import android.graphics.drawable.Drawable
import android.os.Build
import android.os.Build.VERSION.SDK_INT
import androidx.annotation.ColorInt
import mozilla.components.ui.icons.R

/**
 * Specifies icons to display in the toolbar representing the security of the current website.
 *
 * @property insecure Icon to display for HTTP sites.
 * @property secure Icon to display for HTTPS sites.
 */
data class SiteSecurityIcons(
    val insecure: Drawable?,
    val secure: Drawable?
) {

    /**
     * Returns an instance of [SiteSecurityIcons] with a color filter applied to each icon.
     */
    fun withColorFilter(insecureColorFilter: ColorFilter, secureColorFilter: ColorFilter): SiteSecurityIcons {
        return copy(
            insecure = insecure?.apply { colorFilter = insecureColorFilter },
            secure = secure?.apply { colorFilter = secureColorFilter }
        )
    }

    /**
     * Returns an instance of [SiteSecurityIcons] with a color tint applied to each icon.
     */
    fun withColorFilter(@ColorInt insecureColor: Int, @ColorInt secureColor: Int): SiteSecurityIcons {
        val insecureColorFilter: ColorFilter = if (SDK_INT >= Build.VERSION_CODES.Q) {
            BlendModeColorFilter(insecureColor, BlendMode.SRC_IN)
        } else {
            PorterDuffColorFilter(insecureColor, PorterDuff.Mode.SRC_IN)
        }

        val secureColorFilter: ColorFilter = if (SDK_INT >= Build.VERSION_CODES.Q) {
            BlendModeColorFilter(secureColor, BlendMode.SRC_IN)
        } else {
            PorterDuffColorFilter(secureColor, PorterDuff.Mode.SRC_IN)
        }

        return withColorFilter(insecureColorFilter, secureColorFilter)
    }

    companion object {
        fun getDefaultSecurityIcons(context: Context, @ColorInt color: Int): SiteSecurityIcons {
            return SiteSecurityIcons(
                insecure = context.getDrawable(R.drawable.mozac_ic_globe),
                secure = context.getDrawable(R.drawable.mozac_ic_lock)
            ).withColorFilter(color, color)
        }
    }
}
+2 −0
Original line number Diff line number Diff line
@@ -8,6 +8,8 @@
    <attr name="browserToolbarHintColor" format="color"/>
    <attr name="browserToolbarTextColor" format="color"/>
    <attr name="browserToolbarTextSize" format="dimension"/>
    <attr name="browserToolbarSecureIcon" format="reference"/>
    <attr name="browserToolbarInsecureIcon" format="reference"/>
    <attr name="browserToolbarSecureColor" format="color"/>
    <attr name="browserToolbarInsecureColor" format="color"/>
    <attr name="browserToolbarMenuColor" format="color"/>
+21 −2
Original line number Diff line number Diff line
@@ -6,6 +6,8 @@ package mozilla.components.browser.toolbar

import android.content.Context
import android.graphics.Color
import android.graphics.PorterDuff
import android.graphics.PorterDuffColorFilter
import android.graphics.Typeface
import android.graphics.drawable.Drawable
import android.util.AttributeSet
@@ -22,13 +24,13 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import mozilla.components.browser.menu.BrowserMenuBuilder
import mozilla.components.browser.toolbar.BrowserToolbar.Companion.ACTION_PADDING_DP
import mozilla.components.browser.toolbar.display.DisplayToolbar
import mozilla.components.browser.toolbar.display.TrackingProtectionIconView.Companion.DEFAULT_ICON_OFF_FOR_A_SITE
import mozilla.components.browser.toolbar.display.TrackingProtectionIconView.Companion.DEFAULT_ICON_ON_NO_TRACKERS_BLOCKED
import mozilla.components.browser.toolbar.display.TrackingProtectionIconView.Companion.DEFAULT_ICON_ON_TRACKERS_BLOCKED
import mozilla.components.browser.toolbar.display.TrackingProtectionIconView.Companion.DEFAULT_ICON_OFF_FOR_A_SITE
import mozilla.components.browser.toolbar.edit.EditToolbar
import mozilla.components.concept.toolbar.Toolbar
import mozilla.components.concept.toolbar.Toolbar.SiteTrackingProtection
import mozilla.components.concept.toolbar.Toolbar.SiteSecurity
import mozilla.components.concept.toolbar.Toolbar.SiteTrackingProtection
import mozilla.components.support.base.android.Padding
import mozilla.components.support.test.mock
import mozilla.components.support.test.robolectric.testContext
@@ -906,6 +908,23 @@ class BrowserToolbarTest {
        assertEquals(View.VISIBLE, toolbar.displayToolbar.siteSecurityIconView.visibility)
    }

    @Test
    fun `siteSecurityColor setter`() {
        val toolbar = BrowserToolbar(testContext)

        toolbar.setSiteSecurityColor(Color.RED to Color.BLUE)
        assertEquals(
            PorterDuffColorFilter(Color.RED, PorterDuff.Mode.SRC_IN),
            toolbar.displayToolbar.siteSecurityIconView.drawable.colorFilter
        )

        toolbar.siteSecure = SiteSecurity.SECURE
        assertEquals(
            PorterDuffColorFilter(Color.BLUE, PorterDuff.Mode.SRC_IN),
            toolbar.displayToolbar.siteSecurityIconView.drawable.colorFilter
        )
    }

    @Test
    fun `urlBoxView getter`() {
        val toolbar = BrowserToolbar(testContext)
Loading