Bug 1940625 - Show compose-browser-toolbar as an option in samples-toolbar r=android-reviewers,petru

Differential Revision: https://phabricator.services.mozilla.com/D233603
This commit is contained in:
Gabriel Luong
2025-02-19 03:24:27 +00:00
parent a112290343
commit ada5444ba8
5 changed files with 135 additions and 0 deletions

View File

@@ -26,6 +26,11 @@ android {
buildFeatures { buildFeatures {
viewBinding true viewBinding true
compose true
}
composeOptions {
kotlinCompilerExtensionVersion = Versions.compose_compiler
} }
namespace 'org.mozilla.samples.toolbar' namespace 'org.mozilla.samples.toolbar'
@@ -39,6 +44,8 @@ dependencies {
implementation project(':browser-menu') implementation project(':browser-menu')
implementation project(':browser-menu2') implementation project(':browser-menu2')
implementation project(':browser-domains') implementation project(':browser-domains')
implementation project(':browser-state')
implementation project(':compose-browser-toolbar')
implementation project(':ui-colors') implementation project(':ui-colors')
implementation project(':ui-tabcounter') implementation project(':ui-tabcounter')
@@ -53,6 +60,7 @@ dependencies {
implementation ComponentsDependencies.kotlin_coroutines implementation ComponentsDependencies.kotlin_coroutines
implementation ComponentsDependencies.androidx_appcompat implementation ComponentsDependencies.androidx_appcompat
implementation ComponentsDependencies.androidx_compose_ui
implementation ComponentsDependencies.androidx_core_ktx implementation ComponentsDependencies.androidx_core_ktx
implementation ComponentsDependencies.androidx_recyclerview implementation ComponentsDependencies.androidx_recyclerview
} }

View File

@@ -31,6 +31,7 @@ enum class ToolbarConfiguration(val label: String) {
PRIVATE_MODE("Private Mode"), PRIVATE_MODE("Private Mode"),
FENIX("Fenix"), FENIX("Fenix"),
FENIX_CUSTOMTAB("Fenix (Custom Tab)"), FENIX_CUSTOMTAB("Fenix (Custom Tab)"),
COMPOSE_TOOLBAR("Compose Toolbar"),
} }
class ConfigurationAdapter( class ConfigurationAdapter(

View File

@@ -11,8 +11,11 @@ import android.view.ViewGroup
import android.widget.Toast import android.widget.Toast
import androidx.annotation.DrawableRes import androidx.annotation.DrawableRes
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.content.res.ResourcesCompat import androidx.core.content.res.ResourcesCompat
import androidx.core.view.isVisible
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.Observer import androidx.lifecycle.Observer
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
@@ -36,6 +39,9 @@ import mozilla.components.browser.menu.item.SimpleBrowserMenuItem
import mozilla.components.browser.menu2.BrowserMenuController import mozilla.components.browser.menu2.BrowserMenuController
import mozilla.components.browser.toolbar.BrowserToolbar import mozilla.components.browser.toolbar.BrowserToolbar
import mozilla.components.browser.toolbar.display.DisplayToolbar import mozilla.components.browser.toolbar.display.DisplayToolbar
import mozilla.components.compose.browser.toolbar.store.BrowserEditToolbarAction
import mozilla.components.compose.browser.toolbar.store.BrowserToolbarAction
import mozilla.components.compose.browser.toolbar.store.BrowserToolbarStore
import mozilla.components.concept.menu.Side import mozilla.components.concept.menu.Side
import mozilla.components.concept.menu.candidate.DividerMenuCandidate import mozilla.components.concept.menu.candidate.DividerMenuCandidate
import mozilla.components.concept.menu.candidate.DrawableMenuIcon import mozilla.components.concept.menu.candidate.DrawableMenuIcon
@@ -43,10 +49,12 @@ import mozilla.components.concept.menu.candidate.NestedMenuCandidate
import mozilla.components.concept.menu.candidate.TextMenuCandidate import mozilla.components.concept.menu.candidate.TextMenuCandidate
import mozilla.components.concept.toolbar.Toolbar import mozilla.components.concept.toolbar.Toolbar
import mozilla.components.feature.toolbar.ToolbarAutocompleteFeature 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.content.res.resolveAttribute
import mozilla.components.support.ktx.android.view.hideKeyboard import mozilla.components.support.ktx.android.view.hideKeyboard
import mozilla.components.support.ktx.util.URLStringUtils import mozilla.components.support.ktx.util.URLStringUtils
import mozilla.components.ui.tabcounter.TabCounter import mozilla.components.ui.tabcounter.TabCounter
import org.mozilla.samples.toolbar.compose.BrowserToolbar
import org.mozilla.samples.toolbar.databinding.ActivityToolbarBinding import org.mozilla.samples.toolbar.databinding.ActivityToolbarBinding
import mozilla.components.browser.menu.R as menuR import mozilla.components.browser.menu.R as menuR
import mozilla.components.browser.toolbar.R as toolbarR import mozilla.components.browser.toolbar.R as toolbarR
@@ -81,6 +89,7 @@ class ToolbarActivity : AppCompatActivity() {
ToolbarConfiguration.PRIVATE_MODE -> setupDefaultToolbar(private = true) ToolbarConfiguration.PRIVATE_MODE -> setupDefaultToolbar(private = true)
ToolbarConfiguration.FENIX -> setupFenixToolbar() ToolbarConfiguration.FENIX -> setupFenixToolbar()
ToolbarConfiguration.FENIX_CUSTOMTAB -> setupFenixCustomTabToolbar() ToolbarConfiguration.FENIX_CUSTOMTAB -> setupFenixCustomTabToolbar()
ToolbarConfiguration.COMPOSE_TOOLBAR -> setupComposeToolbar()
} }
val recyclerView: RecyclerView = findViewById(R.id.recyclerView) val recyclerView: RecyclerView = findViewById(R.id.recyclerView)
@@ -105,6 +114,8 @@ class ToolbarActivity : AppCompatActivity() {
* A very simple toolbar with mostly default values. * A very simple toolbar with mostly default values.
*/ */
private fun setupDefaultToolbar(private: Boolean = false) { private fun setupDefaultToolbar(private: Boolean = false) {
showToolbar()
binding.toolbar.setBackgroundColor( binding.toolbar.setBackgroundColor(
ContextCompat.getColor(this, colorsR.color.photonBlue80), ContextCompat.getColor(this, colorsR.color.photonBlue80),
) )
@@ -118,6 +129,8 @@ class ToolbarActivity : AppCompatActivity() {
* A toolbar that looks like Firefox Focus on tablets. * A toolbar that looks like Firefox Focus on tablets.
*/ */
private fun setupFocusTabletToolbar() { private fun setupFocusTabletToolbar() {
showToolbar()
// ////////////////////////////////////////////////////////////////////////////////////////// // //////////////////////////////////////////////////////////////////////////////////////////
// Use the iconic gradient background // Use the iconic gradient background
// ////////////////////////////////////////////////////////////////////////////////////////// // //////////////////////////////////////////////////////////////////////////////////////////
@@ -191,6 +204,8 @@ class ToolbarActivity : AppCompatActivity() {
* A custom browser menu. * A custom browser menu.
*/ */
private fun setupCustomMenu() { private fun setupCustomMenu() {
showToolbar()
binding.toolbar.setBackgroundColor( binding.toolbar.setBackgroundColor(
ContextCompat.getColor(this, colorsR.color.photonBlue80), ContextCompat.getColor(this, colorsR.color.photonBlue80),
) )
@@ -224,6 +239,8 @@ class ToolbarActivity : AppCompatActivity() {
* A toolbar that looks like Firefox Focus on phones. * A toolbar that looks like Firefox Focus on phones.
*/ */
private fun setupFocusPhoneToolbar() { private fun setupFocusPhoneToolbar() {
showToolbar()
// ////////////////////////////////////////////////////////////////////////////////////////// // //////////////////////////////////////////////////////////////////////////////////////////
// Use the iconic gradient background // Use the iconic gradient background
// ////////////////////////////////////////////////////////////////////////////////////////// // //////////////////////////////////////////////////////////////////////////////////////////
@@ -314,6 +331,8 @@ class ToolbarActivity : AppCompatActivity() {
*/ */
@Suppress("MagicNumber") @Suppress("MagicNumber")
fun setupFenixToolbar() { fun setupFenixToolbar() {
showToolbar()
binding.toolbar.setBackgroundColor(0xFFFFFFFF.toInt()) binding.toolbar.setBackgroundColor(0xFFFFFFFF.toInt())
binding.toolbar.display.indicators = listOf( binding.toolbar.display.indicators = listOf(
@@ -400,6 +419,8 @@ class ToolbarActivity : AppCompatActivity() {
@OptIn(DelicateCoroutinesApi::class) // GlobalScope usage @OptIn(DelicateCoroutinesApi::class) // GlobalScope usage
@Suppress("MagicNumber") @Suppress("MagicNumber")
fun setupFenixCustomTabToolbar() { fun setupFenixCustomTabToolbar() {
showToolbar()
binding.toolbar.setBackgroundColor(0xFFFFFFFF.toInt()) binding.toolbar.setBackgroundColor(0xFFFFFFFF.toInt())
binding.toolbar.display.indicators = listOf( binding.toolbar.display.indicators = listOf(
@@ -464,6 +485,40 @@ class ToolbarActivity : AppCompatActivity() {
} }
} }
private fun setupComposeToolbar() {
showToolbar(isCompose = true)
binding.composeToolbar.setContent {
val store = remember {
BrowserToolbarStore()
}
val uiState by store.observeAsState(initialValue = store.state) { it }
BrowserToolbar(
onDisplayMenuClicked = {},
onDisplayToolbarClick = {
store.dispatch(BrowserToolbarAction.ToggleEditMode(editMode = true))
},
onTextEdit = { text ->
store.dispatch(BrowserEditToolbarAction.UpdateEditText(text = text))
},
onTextCommit = {
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,
)
}
}
private fun showToolbar(isCompose: Boolean = false) {
binding.toolbar.isVisible = !isCompose
binding.composeToolbar.isVisible = isCompose
}
// For testing purposes // For testing purposes
private var forward = true private var forward = true
private var back = true private var back = true

View File

@@ -0,0 +1,65 @@
/* 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 org.mozilla.samples.toolbar.compose
import androidx.compose.runtime.Composable
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
/**
* A customizable toolbar for browsers.
*
* The toolbar can switch between two modes: display and edit. The display mode displays the current
* URL and controls for navigation. In edit mode the current URL can be edited. Those two modes are
* implemented by the [BrowserDisplayToolbar] and [BrowserEditToolbar] composables.
*
* @param onDisplayMenuClicked Invoked when the user clicks on the menu button in "display" mode.
* @param onTextEdit Invoked when the user edits the text in the toolbar in "edit" mode.
* @param onTextCommit Invoked when the user has finished editing the URL and wants
* 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(
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) {
null -> url
else -> editText
}
if (editMode) {
BrowserEditToolbar(
url = input,
colors = colors.editToolbarColors,
onUrlCommitted = { text -> onTextCommit(text) },
onUrlEdit = { text -> onTextEdit(text) },
)
} else {
BrowserDisplayToolbar(
url = url.takeIf { it.isNotEmpty() } ?: hint,
colors = colors.displayToolbarColors,
onUrlClicked = {
onDisplayToolbarClick()
},
onMenuClicked = { onDisplayMenuClicked() },
)
}
}

View File

@@ -14,6 +14,12 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" /> android:layout_height="wrap_content" />
<androidx.compose.ui.platform.ComposeView
android:id="@+id/composeToolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone" />
<androidx.recyclerview.widget.RecyclerView <androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView" android:id="@+id/recyclerView"
android:layout_width="match_parent" android:layout_width="match_parent"