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" /> + +