Commit 024e3de4 authored by ekager's avatar ekager Committed by Sebastian Kaspari
Browse files

Close #2201 - Adds TwoStateButton in BrowserMenuItemToolbar

parent 01837eee
......@@ -30,27 +30,42 @@ class BrowserMenuItemToolbar(
for (item in items) {
val button = AppCompatImageButton(view.context)
button.setImageResource(item.imageResource)
if (item is TwoStateButton && !item.isInPrimaryState.invoke()) {
button.setImageResource(item.secondaryImageResource)
button.contentDescription = item.secondaryContentDescription
if (item.secondaryImageTintResource != 0) {
button.imageTintList = ContextCompat.getColorStateList(
view.context,
item.secondaryImageTintResource
)
}
button.isEnabled = !item.disableInSecondaryState
} else {
button.setImageResource(item.imageResource)
button.contentDescription = item.contentDescription
if (item.iconTintColorResource != 0) {
button.imageTintList =
ContextCompat.getColorStateList(view.context, item.iconTintColorResource)
}
}
val outValue = TypedValue()
view.context.theme.resolveAttribute(
android.R.attr.selectableItemBackgroundBorderless,
outValue,
true)
android.R.attr.selectableItemBackgroundBorderless,
outValue,
true
)
button.setBackgroundResource(outValue.resourceId)
button.contentDescription = item.contentDescription
button.setOnClickListener {
item.listener.invoke()
menu.dismiss()
}
if (item.iconTintColorResource != 0) {
button.imageTintList = ContextCompat.getColorStateList(view.context, item.iconTintColorResource)
}
layout.addView(button,
LinearLayout.LayoutParams(0, view.resources.pxToDp(ICON_HEIGHT_DP), 1f))
layout.addView(
button,
LinearLayout.LayoutParams(0, view.resources.pxToDp(ICON_HEIGHT_DP), 1f)
)
}
}
......@@ -62,13 +77,44 @@ class BrowserMenuItemToolbar(
* @param iconTintColorResource Optional ID of color resource to tint the icon.
* @param listener Callback to be invoked when the button is pressed.
*/
class Button(
open class Button(
val imageResource: Int,
val contentDescription: String,
val iconTintColorResource: Int = 0,
val listener: () -> Unit
)
/**
* A button that either shows an primary state or an secondary state based on the provided
* <code>isInPrimaryState</code> lambda.
*
* @param primaryImageResource ID of a drawable resource to be shown as primary icon.
* @param primaryContentDescription The button's primary content description, used for accessibility support.
* @param primaryImageTintResource Optional ID of color resource to tint the primary icon.
* @param secondaryImageResource Optional ID of a different drawable resource to be shown as secondary icon.
* @param secondaryContentDescription Optional secondary content description for button, for accessibility support.
* @param secondaryImageTintResource Optional ID of secondary color resource to tint the icon.
* @param isInPrimaryState Lambda to return true/false to indicate if this button should be primary or secondary.
* @param disableInSecondaryState Optional boolean to disable the button when in secondary state.
* @param listener Callback to be invoked when the button is pressed.
*/
open class TwoStateButton(
val primaryImageResource: Int,
val primaryContentDescription: String,
val primaryImageTintResource: Int = 0,
val secondaryImageResource: Int = primaryImageResource,
val secondaryContentDescription: String = primaryContentDescription,
val secondaryImageTintResource: Int = primaryImageTintResource,
val isInPrimaryState: () -> Boolean = { true },
val disableInSecondaryState: Boolean = false,
listener: () -> Unit
) : Button(
primaryImageResource,
primaryContentDescription,
primaryImageTintResource,
listener = listener
)
companion object {
private const val ICON_HEIGHT_DP = 24
}
......
......@@ -36,7 +36,7 @@ class BrowserMenuItemToolbarTest {
val toolbar = BrowserMenuItemToolbar(emptyList())
val view = LayoutInflater.from(
RuntimeEnvironment.application
RuntimeEnvironment.application
).inflate(toolbar.getLayoutResource(), null)
assertNotNull(view)
......@@ -73,12 +73,21 @@ class BrowserMenuItemToolbarTest {
@Test
fun `items are added as ImageButton to view group`() {
val buttons = listOf(
BrowserMenuItemToolbar.Button(
R.drawable.abc_ic_ab_back_material,
"Button01") {},
BrowserMenuItemToolbar.Button(
R.drawable.abc_ic_ab_back_material,
"Button02") {})
BrowserMenuItemToolbar.Button(
R.drawable.abc_ic_ab_back_material,
"Button01"
) {},
BrowserMenuItemToolbar.Button(
R.drawable.abc_ic_ab_back_material,
"Button02"
) {},
BrowserMenuItemToolbar.TwoStateButton(
primaryImageResource = R.drawable.abc_ic_go_search_api_material,
primaryContentDescription = "TwoStatePrimary",
secondaryImageResource = R.drawable.abc_ic_clear_material,
secondaryContentDescription = "TwoStateSecondary"
) {}
)
val menu = mock(BrowserMenu::class.java)
val layout = LinearLayout(RuntimeEnvironment.application)
......@@ -86,16 +95,66 @@ class BrowserMenuItemToolbarTest {
val toolbar = BrowserMenuItemToolbar(buttons)
toolbar.bind(menu, layout)
assertEquals(2, layout.childCount)
assertEquals(3, layout.childCount)
val child1 = layout.getChildAt(0)
val child2 = layout.getChildAt(1)
val child3 = layout.getChildAt(2)
assertTrue(child1 is ImageButton)
assertTrue(child2 is ImageButton)
assertTrue(child3 is ImageButton)
assertEquals("Button01", child1.contentDescription)
assertEquals("Button02", child2.contentDescription)
assertEquals("TwoStatePrimary", child3.contentDescription)
}
@Test
fun `Disabled TwoState Button in secondary state is disabled`() {
val buttons = listOf(
BrowserMenuItemToolbar.TwoStateButton(
primaryImageResource = R.drawable.abc_ic_go_search_api_material,
primaryContentDescription = "TwoStateEnabled",
secondaryImageResource = R.drawable.abc_ic_clear_material,
secondaryContentDescription = "TwoStateDisabled",
isInPrimaryState = { false },
disableInSecondaryState = true
) {}
)
val menu = mock(BrowserMenu::class.java)
val layout = LinearLayout(RuntimeEnvironment.application)
val toolbar = BrowserMenuItemToolbar(buttons)
toolbar.bind(menu, layout)
val child1 = layout.getChildAt(0)
assertEquals("TwoStateDisabled", child1.contentDescription)
assertFalse(child1.isEnabled)
}
@Test
fun `TwoStateButton has primary and secondary state invoked`() {
val primaryResource = R.drawable.abc_ic_go_search_api_material
val secondaryResource = R.drawable.abc_ic_clear_material
var reloadPageAction = BrowserMenuItemToolbar.TwoStateButton(
primaryImageResource = primaryResource,
primaryContentDescription = "primary",
secondaryImageResource = secondaryResource,
secondaryContentDescription = "secondary"
) {}
assertTrue(reloadPageAction.isInPrimaryState.invoke())
reloadPageAction = BrowserMenuItemToolbar.TwoStateButton(
primaryImageResource = primaryResource,
primaryContentDescription = "primary",
secondaryImageResource = secondaryResource,
secondaryContentDescription = "secondary",
isInPrimaryState = { false }
) {}
assertFalse(reloadPageAction.isInPrimaryState.invoke())
}
@Test
......
......@@ -15,6 +15,9 @@ permalink: /changelog/
* **browser-session**
* Added `Session.webAppManifest` to expose the [Web App Manifest](https://developer.mozilla.org/en-US/docs/Web/Manifest) of the currently visible page. This functionality will only be available in [GeckoView](https://mozilla.github.io/geckoview/)-flavored [concept-engine](https://github.com/mozilla-mobile/android-components/tree/master/components/concept/engine) implementations.
* **browser-menu**
* Added `TwoStateButton` in `BrowserMenuItemToolbar` that will change resources based on the `isInPrimaryState` lambda and added ability to disable the button with optional `disableInSecondaryState` argument.
# 0.46.0
* [Commits](https://github.com/mozilla-mobile/android-components/compare/v0.45.0...v0.46.0)
......
......@@ -13,11 +13,11 @@ import android.support.v7.widget.LinearLayoutManager
import android.view.View
import kotlinx.android.synthetic.main.activity_toolbar.*
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
import mozilla.components.browser.domains.autocomplete.CustomDomainsProvider
import mozilla.components.browser.domains.autocomplete.ShippedDomainsProvider
import mozilla.components.browser.menu.BrowserMenu
......@@ -204,10 +204,19 @@ class ToolbarActivity : AppCompatActivity() {
simulateReload()
}
val reload = BrowserMenuItemToolbar.Button(
mozilla.components.ui.icons.R.drawable.mozac_ic_refresh,
"Reload") {
simulateReload()
val reload = BrowserMenuItemToolbar.TwoStateButton(
primaryImageResource = mozilla.components.ui.icons.R.drawable.mozac_ic_refresh,
primaryContentDescription = "Reload",
secondaryImageResource = R.drawable.mozac_ic_stop,
secondaryContentDescription = "Stop",
isInPrimaryState = { !loading },
disableInSecondaryState = false
) {
if (loading) {
job?.cancel()
} else {
simulateReload()
}
}
val menuToolbar = BrowserMenuItemToolbar(listOf(forward, reload))
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment