Bug 1940630 - Add search selector toolbar action as a custom toolbar action r=android-reviewers,petru

- Add a CustomAction to the toolbar action concept to allow for displaying a provided composable content.
- Add a SearchSelector toolbar action that can display a menu.
- Provide an usage example in sample-toolbar

Differential Revision: https://phabricator.services.mozilla.com/D233608
This commit is contained in:
Gabriel Luong
2025-02-19 03:24:29 +00:00
parent 01af35daba
commit dadaab8aa0
11 changed files with 347 additions and 24 deletions

View File

@@ -11,8 +11,8 @@ import android.view.ViewGroup
import android.widget.Toast
import androidx.annotation.DrawableRes
import androidx.appcompat.app.AppCompatActivity
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.platform.LocalContext
import androidx.core.content.ContextCompat
import androidx.core.content.res.ResourcesCompat
import androidx.core.view.isVisible
@@ -40,17 +40,22 @@ import mozilla.components.browser.menu2.BrowserMenuController
import mozilla.components.browser.toolbar.BrowserToolbar
import mozilla.components.browser.toolbar.display.DisplayToolbar
import mozilla.components.compose.base.theme.AcornTheme
import mozilla.components.compose.browser.toolbar.concept.Action.CustomAction
import mozilla.components.compose.browser.toolbar.store.BrowserEditToolbarAction
import mozilla.components.compose.browser.toolbar.store.BrowserToolbarAction
import mozilla.components.compose.browser.toolbar.store.BrowserToolbarState
import mozilla.components.compose.browser.toolbar.store.BrowserToolbarStore
import mozilla.components.compose.browser.toolbar.store.DisplayState
import mozilla.components.compose.browser.toolbar.store.EditState
import mozilla.components.compose.browser.toolbar.ui.SearchSelector
import mozilla.components.concept.menu.Side
import mozilla.components.concept.menu.candidate.DecorativeTextMenuCandidate
import mozilla.components.concept.menu.candidate.DividerMenuCandidate
import mozilla.components.concept.menu.candidate.DrawableMenuIcon
import mozilla.components.concept.menu.candidate.NestedMenuCandidate
import mozilla.components.concept.menu.candidate.TextMenuCandidate
import mozilla.components.concept.toolbar.Toolbar
import mozilla.components.feature.toolbar.ToolbarAutocompleteFeature
import mozilla.components.lib.state.ext.observeAsState
import mozilla.components.support.ktx.android.content.res.resolveAttribute
import mozilla.components.support.ktx.android.view.hideKeyboard
import mozilla.components.support.ktx.util.URLStringUtils
@@ -486,18 +491,63 @@ class ToolbarActivity : AppCompatActivity() {
}
}
@Suppress("LongMethod")
private fun setupComposeToolbar() {
showToolbar(isCompose = true)
val header = DecorativeTextMenuCandidate(
text = "This time search in:",
)
val bookmarks = TextMenuCandidate(
"Bookmarks",
start = DrawableMenuIcon(this, iconsR.drawable.mozac_ic_bookmark_tray_24),
) { /* Do nothing */ }
val tabs = TextMenuCandidate(
"Tabs",
start = DrawableMenuIcon(this, iconsR.drawable.mozac_ic_tab_tray_24),
) { /* Do nothing */ }
val history = TextMenuCandidate(
"History",
start = DrawableMenuIcon(this, iconsR.drawable.mozac_ic_history_24),
) { /* Do nothing */ }
val settings = TextMenuCandidate(
"Search settings",
start = DrawableMenuIcon(this, iconsR.drawable.mozac_ic_settings_24),
) { /* Do nothing */ }
val items = listOf(header, bookmarks, tabs, history, settings)
val menuController = BrowserMenuController().apply {
submitList(items)
}
binding.composeToolbar.setContent {
AcornTheme {
val store = remember {
BrowserToolbarStore()
BrowserToolbarStore(
initialState = BrowserToolbarState(
displayState = DisplayState(hint = "Search or enter address"),
editState = EditState(
editActionsStart = listOf(
CustomAction(
content = {
SearchSelector(
onClick = {},
menu = menuController,
icon = ContextCompat.getDrawable(
LocalContext.current,
iconsR.drawable.mozac_ic_search_24,
),
)
},
),
),
),
),
)
}
val uiState by store.observeAsState(initialValue = store.state) { it }
BrowserToolbar(
store = store,
onDisplayMenuClicked = {},
onDisplayToolbarClick = {
store.dispatch(BrowserToolbarAction.ToggleEditMode(editMode = true))
@@ -509,9 +559,6 @@ class ToolbarActivity : AppCompatActivity() {
store.dispatch(BrowserToolbarAction.ToggleEditMode(editMode = false))
},
url = "https://www.mozilla.org/en-US/firefox/mobile/",
hint = "Search or enter address",
editMode = uiState.editMode,
editText = uiState.editState.editText,
)
}
}

View File

@@ -5,10 +5,13 @@
package org.mozilla.samples.toolbar.compose
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import mozilla.components.compose.browser.toolbar.BrowserDisplayToolbar
import mozilla.components.compose.browser.toolbar.BrowserEditToolbar
import mozilla.components.compose.browser.toolbar.BrowserToolbarColors
import mozilla.components.compose.browser.toolbar.BrowserToolbarDefaults
import mozilla.components.compose.browser.toolbar.store.BrowserToolbarStore
import mozilla.components.lib.state.ext.observeAsState
/**
* A customizable toolbar for browsers.
@@ -23,38 +26,37 @@ import mozilla.components.compose.browser.toolbar.BrowserToolbarDefaults
* to commit the entered text.
* @param onDisplayToolbarClick Invoked when the user clicks on the URL in "display" mode.
* @param colors The color scheme the browser toolbar will use for the UI.
* @param hint Text displayed in the toolbar when there's no URL to display (no tab or empty URL)
* @param editMode Whether the toolbar is in "edit" or "display" mode.
* @param editText The text the user is editing in "edit" mode.
*/
@Suppress("MagicNumber")
@Composable
fun BrowserToolbar(
store: BrowserToolbarStore,
onDisplayMenuClicked: () -> Unit,
onDisplayToolbarClick: () -> Unit,
onTextEdit: (String) -> Unit,
onTextCommit: (String) -> Unit,
colors: BrowserToolbarColors = BrowserToolbarDefaults.colors(),
url: String = "",
hint: String = "",
editMode: Boolean = false,
editText: String? = null,
) {
val input = when (editText) {
val uiState by store.observeAsState(initialValue = store.state) { it }
val input = when (val editText = uiState.editState.editText) {
null -> url
else -> editText
}
if (editMode) {
if (uiState.editMode) {
BrowserEditToolbar(
url = input,
colors = colors.editToolbarColors,
editActionsStart = uiState.editState.editActionsStart,
editActionsEnd = uiState.editState.editActionsEnd,
onUrlCommitted = { text -> onTextCommit(text) },
onUrlEdit = { text -> onTextEdit(text) },
)
} else {
BrowserDisplayToolbar(
url = url.takeIf { it.isNotEmpty() } ?: hint,
url = url.takeIf { it.isNotEmpty() } ?: uiState.displayState.hint,
colors = colors.displayToolbarColors,
onUrlClicked = {
onDisplayToolbarClick()