Skip to content

Commit

Permalink
Issue mozilla-mobile#3647 - Custom HTTP(S) icons in toolbar
Browse files Browse the repository at this point in the history
  • Loading branch information
NotWoods committed Aug 7, 2019
1 parent 3e45c68 commit d2545f5
Show file tree
Hide file tree
Showing 10 changed files with 258 additions and 94 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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.
*/
Expand Down Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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)
Expand All @@ -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.
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/* 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)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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"/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,14 @@ import org.robolectric.Shadows.shadowOf
@RunWith(AndroidJUnit4::class)
class DisplayToolbarTest {

@Test
fun `initialized with security icon`() {
val toolbar = mock(BrowserToolbar::class.java)
val displayToolbar = DisplayToolbar(testContext, toolbar)

assertNotNull(displayToolbar.siteSecurityIconView.drawable)
}

@Test
fun `clicking on the URL switches the toolbar to editing mode`() {
val toolbar = mock(BrowserToolbar::class.java)
Expand Down Expand Up @@ -970,26 +978,27 @@ class DisplayToolbarTest {
}

@Test
fun `securityIconColor is set when securityIconColor changes`() {
fun `securityIcons is set when securityIcons changes`() {
val toolbar = mock(BrowserToolbar::class.java)
val displayToolbar = DisplayToolbar(testContext, toolbar)

displayToolbar.securityIconColor = Pair(R.color.photonBlue40, R.color.photonBlue40)
val insecure = testContext.getDrawable(R.drawable.mozac_ic_globe)
val secure = testContext.getDrawable(R.drawable.mozac_ic_lock)
displayToolbar.securityIcons = SiteSecurityIcons(insecure, secure)

assertEquals(R.color.photonBlue40, displayToolbar.securityIconColor.first)
assertEquals(R.color.photonBlue40, displayToolbar.securityIconColor.second)
assertEquals(insecure, displayToolbar.securityIcons.insecure)
assertEquals(secure, displayToolbar.securityIcons.secure)
}

@Test
fun `setSiteSecurity is called when securityIconColor changes`() {
fun `setSiteSecurity is called when securityIcons changes`() {
val toolbar = BrowserToolbar(testContext)
toolbar.displayToolbar

assertNull(toolbar.displayToolbar.siteSecurityIconView.colorFilter)

toolbar.siteSecurityColor = Pair(R.color.photonBlue40, R.color.photonBlue40)
val icons = SiteSecurityIcons(mock(), mock())
assertNotEquals(icons, toolbar.displayToolbar.securityIcons)

assertNotNull(toolbar.displayToolbar.siteSecurityIconView.colorFilter)
toolbar.siteSecurityIcons = icons
assertEquals(icons, toolbar.displayToolbar.securityIcons)
}

@Test
Expand Down
Loading

0 comments on commit d2545f5

Please sign in to comment.