diff --git a/mobile/android/android-components/samples/toolbar/build.gradle b/mobile/android/android-components/samples/toolbar/build.gradle
index 80c58683dc2e..7c3992ce8169 100644
--- a/mobile/android/android-components/samples/toolbar/build.gradle
+++ b/mobile/android/android-components/samples/toolbar/build.gradle
@@ -26,6 +26,11 @@ android {
buildFeatures {
viewBinding true
+ compose true
+ }
+
+ composeOptions {
+ kotlinCompilerExtensionVersion = Versions.compose_compiler
}
namespace 'org.mozilla.samples.toolbar'
@@ -39,6 +44,8 @@ dependencies {
implementation project(':browser-menu')
implementation project(':browser-menu2')
implementation project(':browser-domains')
+ implementation project(':browser-state')
+ implementation project(':compose-browser-toolbar')
implementation project(':ui-colors')
implementation project(':ui-tabcounter')
@@ -53,6 +60,7 @@ dependencies {
implementation ComponentsDependencies.kotlin_coroutines
implementation ComponentsDependencies.androidx_appcompat
+ implementation ComponentsDependencies.androidx_compose_ui
implementation ComponentsDependencies.androidx_core_ktx
implementation ComponentsDependencies.androidx_recyclerview
}
diff --git a/mobile/android/android-components/samples/toolbar/src/main/java/org/mozilla/samples/toolbar/SampleToolbarHelpers.kt b/mobile/android/android-components/samples/toolbar/src/main/java/org/mozilla/samples/toolbar/SampleToolbarHelpers.kt
index 81f06ded22a7..ea975519a216 100644
--- a/mobile/android/android-components/samples/toolbar/src/main/java/org/mozilla/samples/toolbar/SampleToolbarHelpers.kt
+++ b/mobile/android/android-components/samples/toolbar/src/main/java/org/mozilla/samples/toolbar/SampleToolbarHelpers.kt
@@ -31,6 +31,7 @@ enum class ToolbarConfiguration(val label: String) {
PRIVATE_MODE("Private Mode"),
FENIX("Fenix"),
FENIX_CUSTOMTAB("Fenix (Custom Tab)"),
+ COMPOSE_TOOLBAR("Compose Toolbar"),
}
class ConfigurationAdapter(
diff --git a/mobile/android/android-components/samples/toolbar/src/main/java/org/mozilla/samples/toolbar/ToolbarActivity.kt b/mobile/android/android-components/samples/toolbar/src/main/java/org/mozilla/samples/toolbar/ToolbarActivity.kt
index 58dbb8a3120d..63e3a54a832e 100644
--- a/mobile/android/android-components/samples/toolbar/src/main/java/org/mozilla/samples/toolbar/ToolbarActivity.kt
+++ b/mobile/android/android-components/samples/toolbar/src/main/java/org/mozilla/samples/toolbar/ToolbarActivity.kt
@@ -11,8 +11,11 @@ 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.core.content.ContextCompat
import androidx.core.content.res.ResourcesCompat
+import androidx.core.view.isVisible
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.Observer
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.toolbar.BrowserToolbar
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.candidate.DividerMenuCandidate
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.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
import mozilla.components.ui.tabcounter.TabCounter
+import org.mozilla.samples.toolbar.compose.BrowserToolbar
import org.mozilla.samples.toolbar.databinding.ActivityToolbarBinding
import mozilla.components.browser.menu.R as menuR
import mozilla.components.browser.toolbar.R as toolbarR
@@ -81,6 +89,7 @@ class ToolbarActivity : AppCompatActivity() {
ToolbarConfiguration.PRIVATE_MODE -> setupDefaultToolbar(private = true)
ToolbarConfiguration.FENIX -> setupFenixToolbar()
ToolbarConfiguration.FENIX_CUSTOMTAB -> setupFenixCustomTabToolbar()
+ ToolbarConfiguration.COMPOSE_TOOLBAR -> setupComposeToolbar()
}
val recyclerView: RecyclerView = findViewById(R.id.recyclerView)
@@ -105,6 +114,8 @@ class ToolbarActivity : AppCompatActivity() {
* A very simple toolbar with mostly default values.
*/
private fun setupDefaultToolbar(private: Boolean = false) {
+ showToolbar()
+
binding.toolbar.setBackgroundColor(
ContextCompat.getColor(this, colorsR.color.photonBlue80),
)
@@ -118,6 +129,8 @@ class ToolbarActivity : AppCompatActivity() {
* A toolbar that looks like Firefox Focus on tablets.
*/
private fun setupFocusTabletToolbar() {
+ showToolbar()
+
// //////////////////////////////////////////////////////////////////////////////////////////
// Use the iconic gradient background
// //////////////////////////////////////////////////////////////////////////////////////////
@@ -191,6 +204,8 @@ class ToolbarActivity : AppCompatActivity() {
* A custom browser menu.
*/
private fun setupCustomMenu() {
+ showToolbar()
+
binding.toolbar.setBackgroundColor(
ContextCompat.getColor(this, colorsR.color.photonBlue80),
)
@@ -224,6 +239,8 @@ class ToolbarActivity : AppCompatActivity() {
* A toolbar that looks like Firefox Focus on phones.
*/
private fun setupFocusPhoneToolbar() {
+ showToolbar()
+
// //////////////////////////////////////////////////////////////////////////////////////////
// Use the iconic gradient background
// //////////////////////////////////////////////////////////////////////////////////////////
@@ -314,6 +331,8 @@ class ToolbarActivity : AppCompatActivity() {
*/
@Suppress("MagicNumber")
fun setupFenixToolbar() {
+ showToolbar()
+
binding.toolbar.setBackgroundColor(0xFFFFFFFF.toInt())
binding.toolbar.display.indicators = listOf(
@@ -400,6 +419,8 @@ class ToolbarActivity : AppCompatActivity() {
@OptIn(DelicateCoroutinesApi::class) // GlobalScope usage
@Suppress("MagicNumber")
fun setupFenixCustomTabToolbar() {
+ showToolbar()
+
binding.toolbar.setBackgroundColor(0xFFFFFFFF.toInt())
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
private var forward = true
private var back = true
diff --git a/mobile/android/android-components/samples/toolbar/src/main/java/org/mozilla/samples/toolbar/compose/BrowserToolbar.kt b/mobile/android/android-components/samples/toolbar/src/main/java/org/mozilla/samples/toolbar/compose/BrowserToolbar.kt
new file mode 100644
index 000000000000..813956e67fb5
--- /dev/null
+++ b/mobile/android/android-components/samples/toolbar/src/main/java/org/mozilla/samples/toolbar/compose/BrowserToolbar.kt
@@ -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() },
+ )
+ }
+}
diff --git a/mobile/android/android-components/samples/toolbar/src/main/res/layout/activity_toolbar.xml b/mobile/android/android-components/samples/toolbar/src/main/res/layout/activity_toolbar.xml
index 48bda7881ce1..3c731b9791cc 100644
--- a/mobile/android/android-components/samples/toolbar/src/main/res/layout/activity_toolbar.xml
+++ b/mobile/android/android-components/samples/toolbar/src/main/res/layout/activity_toolbar.xml
@@ -14,6 +14,12 @@
android:layout_width="match_parent"
android:layout_height="wrap_content" />
+
+