summaryrefslogtreecommitdiffstats
path: root/mobile/android/android-components/samples/compose-browser
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-15 03:35:49 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-15 03:35:49 +0000
commitd8bbc7858622b6d9c278469aab701ca0b609cddf (patch)
treeeff41dc61d9f714852212739e6b3738b82a2af87 /mobile/android/android-components/samples/compose-browser
parentReleasing progress-linux version 125.0.3-1~progress7.99u1. (diff)
downloadfirefox-d8bbc7858622b6d9c278469aab701ca0b609cddf.tar.xz
firefox-d8bbc7858622b6d9c278469aab701ca0b609cddf.zip
Merging upstream version 126.0.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'mobile/android/android-components/samples/compose-browser')
-rw-r--r--mobile/android/android-components/samples/compose-browser/.gitignore2
-rw-r--r--mobile/android/android-components/samples/compose-browser/README.md35
-rw-r--r--mobile/android/android-components/samples/compose-browser/build.gradle86
-rw-r--r--mobile/android/android-components/samples/compose-browser/proguard-rules.pro21
-rw-r--r--mobile/android/android-components/samples/compose-browser/src/main/AndroidManifest.xml54
-rw-r--r--mobile/android/android-components/samples/compose-browser/src/main/java/org/mozilla/samples/compose/browser/BrowserApplication.kt26
-rw-r--r--mobile/android/android-components/samples/compose-browser/src/main/java/org/mozilla/samples/compose/browser/BrowserComposeActivity.kt43
-rw-r--r--mobile/android/android-components/samples/compose-browser/src/main/java/org/mozilla/samples/compose/browser/Components.kt70
-rw-r--r--mobile/android/android-components/samples/compose-browser/src/main/java/org/mozilla/samples/compose/browser/app/AppAction.kt17
-rw-r--r--mobile/android/android-components/samples/compose-browser/src/main/java/org/mozilla/samples/compose/browser/app/AppState.kt14
-rw-r--r--mobile/android/android-components/samples/compose-browser/src/main/java/org/mozilla/samples/compose/browser/app/AppStore.kt22
-rw-r--r--mobile/android/android-components/samples/compose-browser/src/main/java/org/mozilla/samples/compose/browser/browser/BrowserScreen.kt237
-rw-r--r--mobile/android/android-components/samples/compose-browser/src/main/java/org/mozilla/samples/compose/browser/browser/BrowserScreenAction.kt32
-rw-r--r--mobile/android/android-components/samples/compose-browser/src/main/java/org/mozilla/samples/compose/browser/browser/BrowserScreenState.kt22
-rw-r--r--mobile/android/android-components/samples/compose-browser/src/main/java/org/mozilla/samples/compose/browser/browser/BrowserScreenStore.kt29
-rw-r--r--mobile/android/android-components/samples/compose-browser/src/main/java/org/mozilla/samples/compose/browser/ext/Context.kt15
-rw-r--r--mobile/android/android-components/samples/compose-browser/src/main/java/org/mozilla/samples/compose/browser/settings/SettingsScreen.kt19
-rw-r--r--mobile/android/android-components/samples/compose-browser/src/main/res/drawable/ic_launcher_background.xml78
-rw-r--r--mobile/android/android-components/samples/compose-browser/src/main/res/drawable/ic_launcher_foreground.xml19
-rw-r--r--mobile/android/android-components/samples/compose-browser/src/main/res/mipmap-anydpi-v26/ic_launcher.xml9
-rw-r--r--mobile/android/android-components/samples/compose-browser/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml9
-rw-r--r--mobile/android/android-components/samples/compose-browser/src/main/res/mipmap-hdpi/ic_launcher.pngbin0 -> 2747 bytes
-rw-r--r--mobile/android/android-components/samples/compose-browser/src/main/res/mipmap-hdpi/ic_launcher_round.pngbin0 -> 4823 bytes
-rw-r--r--mobile/android/android-components/samples/compose-browser/src/main/res/mipmap-mdpi/ic_launcher.pngbin0 -> 2125 bytes
-rw-r--r--mobile/android/android-components/samples/compose-browser/src/main/res/mipmap-mdpi/ic_launcher_round.pngbin0 -> 2978 bytes
-rw-r--r--mobile/android/android-components/samples/compose-browser/src/main/res/mipmap-xhdpi/ic_launcher.pngbin0 -> 3767 bytes
-rw-r--r--mobile/android/android-components/samples/compose-browser/src/main/res/mipmap-xhdpi/ic_launcher_round.pngbin0 -> 6609 bytes
-rw-r--r--mobile/android/android-components/samples/compose-browser/src/main/res/mipmap-xxhdpi/ic_launcher.pngbin0 -> 6700 bytes
-rw-r--r--mobile/android/android-components/samples/compose-browser/src/main/res/mipmap-xxhdpi/ic_launcher_round.pngbin0 -> 11343 bytes
-rw-r--r--mobile/android/android-components/samples/compose-browser/src/main/res/mipmap-xxxhdpi/ic_launcher.pngbin0 -> 9075 bytes
-rw-r--r--mobile/android/android-components/samples/compose-browser/src/main/res/mipmap-xxxhdpi/ic_launcher_round.pngbin0 -> 16086 bytes
-rw-r--r--mobile/android/android-components/samples/compose-browser/src/main/res/values/strings.xml7
-rw-r--r--mobile/android/android-components/samples/compose-browser/src/main/res/xml/data_extraction_rules.xml9
33 files changed, 875 insertions, 0 deletions
diff --git a/mobile/android/android-components/samples/compose-browser/.gitignore b/mobile/android/android-components/samples/compose-browser/.gitignore
new file mode 100644
index 0000000000..af6eaebcd7
--- /dev/null
+++ b/mobile/android/android-components/samples/compose-browser/.gitignore
@@ -0,0 +1,2 @@
+/build
+manifest.json
diff --git a/mobile/android/android-components/samples/compose-browser/README.md b/mobile/android/android-components/samples/compose-browser/README.md
new file mode 100644
index 0000000000..a8cd90148e
--- /dev/null
+++ b/mobile/android/android-components/samples/compose-browser/README.md
@@ -0,0 +1,35 @@
+# [Android Components](../../README.md) > Samples > Browser
+
+![](src/main/res/mipmap-xhdpi/ic_launcher.png)
+
+A simple browser app that is composed from the browser components in this repository.
+
+⚠️ **Note**: This sample application is only a very basic browser. For a full-featured reference browser implementation see the **[reference-browser repository](https://github.com/mozilla-mobile/reference-browser)**.
+
+## Build variants
+
+The browser app uses a product flavor:
+
+* **channel**: Using different release channels of GeckoView: _nightly_, _beta_, _production_. In most cases you want to use the _nightly_ flavor as this will support all of the latest functionality.
+
+## Glean SDK support
+
+This sample application comes with Glean SDK telemetry initialized by default, but with upload disabled (no data is being sent).
+This is for creating a simpler metric testing workflow for Gecko engineers that need to add their metrics to Gecko and expose them to Mozilla mobile products.
+See [this bug](https://bugzilla.mozilla.org/show_bug.cgi?id=1592935) for more context.
+
+In order to enable data upload for testing purposes, change the `Glean.setUploadEnabled(false)` to `Glean.setUploadEnabled(true)` in [`SampleApplication.kt`](src/main/java/org/mozilla/samples/browser/SampleApplication.kt).
+
+Glean will send metrics from any Glean-enabled component used in this sample application:
+
+- [engine-gecko-nightly](https://github.com/mozilla-mobile/android-components/blob/master/components/browser/engine-gecko-nightly/docs/metrics.md);
+- [engine-gecko-beta](https://github.com/mozilla-mobile/android-components/blob/master/components/browser/engine-gecko-beta/docs/metrics.md);
+- [engine-gecko](https://github.com/mozilla-mobile/android-components/blob/master/components/browser/engine-gecko/docs/metrics.md);
+
+Data review for enabling the Glean SDK for this application can be found [here](https://bugzilla.mozilla.org/show_bug.cgi?id=1592935#c6).
+
+## License
+
+ 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/
diff --git a/mobile/android/android-components/samples/compose-browser/build.gradle b/mobile/android/android-components/samples/compose-browser/build.gradle
new file mode 100644
index 0000000000..1dd2422ec2
--- /dev/null
+++ b/mobile/android/android-components/samples/compose-browser/build.gradle
@@ -0,0 +1,86 @@
+/* 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/. */
+
+apply plugin: 'com.android.application'
+apply plugin: 'kotlin-android'
+apply plugin: 'kotlin-parcelize'
+
+android {
+ defaultConfig {
+ applicationId "org.mozilla.samples.compose.browser"
+ minSdkVersion config.minSdkVersion
+ compileSdk config.compileSdkVersion
+ targetSdkVersion config.targetSdkVersion
+ versionCode 1
+ versionName "1.0"
+
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+ testInstrumentationRunnerArgument "clearPackageData", "true"
+ testInstrumentationRunnerArgument "listener", "leakcanary.FailTestOnLeakRunListener"
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ }
+ }
+
+ buildFeatures {
+ compose true
+ }
+
+ composeOptions {
+ kotlinCompilerExtensionVersion = Versions.compose_compiler
+ }
+
+ namespace 'org.mozilla.samples.compose.browser'
+}
+
+tasks.register("updateBorderifyExtensionVersion", Copy) { task ->
+ updateExtensionVersion(task, 'src/main/assets/extensions/borderify')
+}
+
+tasks.register("updateTestExtensionVersion", Copy) { task ->
+ updateExtensionVersion(task, 'src/main/assets/extensions/test')
+}
+
+dependencies {
+ implementation platform(ComponentsDependencies.androidx_compose_bom)
+ implementation project(':concept-engine')
+ implementation project(':concept-awesomebar')
+ implementation project(':concept-tabstray')
+
+ implementation project(':browser-engine-gecko')
+ implementation project(':browser-state')
+ implementation project(':browser-icons')
+
+ implementation project(':compose-awesomebar')
+ implementation project(':compose-browser-toolbar')
+ implementation project(':compose-engine')
+ implementation project(':compose-tabstray')
+
+ implementation project(':feature-awesomebar')
+ implementation project(':feature-fxsuggest')
+ implementation project(':feature-search')
+ implementation project(':feature-session')
+ implementation project(':feature-tabs')
+
+ implementation project(':service-location')
+ implementation project(':support-rusthttp')
+
+ implementation project(':ui-icons')
+
+ implementation ComponentsDependencies.androidx_activity_compose
+ implementation ComponentsDependencies.androidx_appcompat
+ implementation ComponentsDependencies.androidx_core_ktx
+ implementation ComponentsDependencies.androidx_compose_ui
+ implementation ComponentsDependencies.androidx_compose_ui_tooling
+ implementation ComponentsDependencies.androidx_compose_foundation
+ implementation ComponentsDependencies.androidx_compose_material
+ implementation ComponentsDependencies.androidx_compose_navigation
+}
+
+preBuild.dependsOn updateBorderifyExtensionVersion
+preBuild.dependsOn updateTestExtensionVersion
diff --git a/mobile/android/android-components/samples/compose-browser/proguard-rules.pro b/mobile/android/android-components/samples/compose-browser/proguard-rules.pro
new file mode 100644
index 0000000000..f1b424510d
--- /dev/null
+++ b/mobile/android/android-components/samples/compose-browser/proguard-rules.pro
@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
diff --git a/mobile/android/android-components/samples/compose-browser/src/main/AndroidManifest.xml b/mobile/android/android-components/samples/compose-browser/src/main/AndroidManifest.xml
new file mode 100644
index 0000000000..cb77ad9280
--- /dev/null
+++ b/mobile/android/android-components/samples/compose-browser/src/main/AndroidManifest.xml
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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/. -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools">
+
+ <uses-permission android:name="android.permission.CAMERA" />
+
+ <!-- This is needed because the android.permission.CAMERA above automatically
+ adds a requirements for camera hardware and we don't want add those restrictions -->
+ <uses-feature
+ android:name="android.hardware.camera"
+ android:required="false" />
+ <uses-feature
+ android:name="android.hardware.camera.autofocus"
+ android:required="false" />
+
+ <uses-permission android:name="android.permission.RECORD_AUDIO" />
+ <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
+ <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
+ <uses-permission android:name="android.permission.INTERNET" />
+ <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
+ tools:ignore="ScopedStorage" />
+ <uses-permission android:name="android.permission.BLUETOOTH" />
+ <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
+ <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
+ <uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>
+ <uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
+
+ <application
+ android:allowBackup="false"
+ android:icon="@mipmap/ic_launcher"
+ android:roundIcon="@mipmap/ic_launcher_round"
+ android:label="@string/app_name"
+ android:supportsRtl="true"
+ android:theme="@style/Theme.AppCompat.DayNight.NoActionBar"
+ android:name=".BrowserApplication"
+ android:usesCleartextTraffic="true"
+ tools:ignore="DataExtractionRules,UnusedAttribute"
+ android:dataExtractionRules="@xml/data_extraction_rules">
+ <activity android:name=".BrowserComposeActivity"
+ android:launchMode="singleTask"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ </application>
+
+</manifest>
diff --git a/mobile/android/android-components/samples/compose-browser/src/main/java/org/mozilla/samples/compose/browser/BrowserApplication.kt b/mobile/android/android-components/samples/compose-browser/src/main/java/org/mozilla/samples/compose/browser/BrowserApplication.kt
new file mode 100644
index 0000000000..c4b13b1e3e
--- /dev/null
+++ b/mobile/android/android-components/samples/compose-browser/src/main/java/org/mozilla/samples/compose/browser/BrowserApplication.kt
@@ -0,0 +1,26 @@
+/* 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.compose.browser
+
+import android.app.Application
+import mozilla.appservices.Megazord
+import mozilla.components.feature.fxsuggest.GlobalFxSuggestDependencyProvider
+import mozilla.components.support.rusthttp.RustHttpConfig
+
+/**
+ * The global [Application] class of this browser application.
+ */
+class BrowserApplication : Application() {
+ val components by lazy { Components(this) }
+
+ override fun onCreate() {
+ super.onCreate()
+
+ Megazord.init()
+ RustHttpConfig.setClient(lazy { components.client })
+
+ GlobalFxSuggestDependencyProvider.initialize(components.fxSuggestStorage)
+ }
+}
diff --git a/mobile/android/android-components/samples/compose-browser/src/main/java/org/mozilla/samples/compose/browser/BrowserComposeActivity.kt b/mobile/android/android-components/samples/compose-browser/src/main/java/org/mozilla/samples/compose/browser/BrowserComposeActivity.kt
new file mode 100644
index 0000000000..1b46e4f2af
--- /dev/null
+++ b/mobile/android/android-components/samples/compose-browser/src/main/java/org/mozilla/samples/compose/browser/BrowserComposeActivity.kt
@@ -0,0 +1,43 @@
+/* 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.compose.browser
+
+import android.os.Bundle
+import androidx.activity.compose.setContent
+import androidx.appcompat.app.AppCompatActivity
+import androidx.compose.material.MaterialTheme
+import androidx.navigation.compose.NavHost
+import androidx.navigation.compose.composable
+import androidx.navigation.compose.rememberNavController
+import org.mozilla.samples.compose.browser.browser.BrowserScreen
+import org.mozilla.samples.compose.browser.ext.components
+import org.mozilla.samples.compose.browser.settings.SettingsScreen
+
+/**
+ * Ladies and gentleman, the browser. ¯\_(ツ)_/¯
+ */
+class BrowserComposeActivity : AppCompatActivity() {
+ companion object {
+ const val ROUTE_BROWSER = "browser"
+ const val ROUTE_SETTINGS = "settings"
+ }
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ setContent {
+ val navController = rememberNavController()
+
+ MaterialTheme {
+ NavHost(navController, startDestination = ROUTE_BROWSER) {
+ composable(ROUTE_BROWSER) { BrowserScreen(navController) }
+ composable(ROUTE_SETTINGS) { SettingsScreen() }
+ }
+ }
+ }
+
+ components.fxSuggestIngestionScheduler.startPeriodicIngestion()
+ }
+}
diff --git a/mobile/android/android-components/samples/compose-browser/src/main/java/org/mozilla/samples/compose/browser/Components.kt b/mobile/android/android-components/samples/compose-browser/src/main/java/org/mozilla/samples/compose/browser/Components.kt
new file mode 100644
index 0000000000..874b4081de
--- /dev/null
+++ b/mobile/android/android-components/samples/compose-browser/src/main/java/org/mozilla/samples/compose/browser/Components.kt
@@ -0,0 +1,70 @@
+/* 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.compose.browser
+
+import android.content.Context
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.platform.LocalContext
+import mozilla.components.browser.engine.gecko.GeckoEngine
+import mozilla.components.browser.engine.gecko.fetch.GeckoViewFetchClient
+import mozilla.components.browser.state.engine.EngineMiddleware
+import mozilla.components.browser.state.store.BrowserStore
+import mozilla.components.concept.engine.Engine
+import mozilla.components.concept.fetch.Client
+import mozilla.components.feature.fxsuggest.FxSuggestIngestionScheduler
+import mozilla.components.feature.fxsuggest.FxSuggestStorage
+import mozilla.components.feature.search.SearchUseCases
+import mozilla.components.feature.search.middleware.SearchMiddleware
+import mozilla.components.feature.search.region.RegionMiddleware
+import mozilla.components.feature.session.SessionUseCases
+import mozilla.components.feature.tabs.TabsUseCases
+import mozilla.components.service.location.LocationService
+import org.mozilla.geckoview.GeckoRuntime
+import org.mozilla.samples.compose.browser.app.AppStore
+
+/**
+ * Global components of the sample browser.
+ */
+class Components(
+ context: Context,
+) {
+ private val runtime by lazy { GeckoRuntime.create(context) }
+
+ val engine: Engine by lazy { GeckoEngine(context, runtime = runtime) }
+ val client: Client by lazy { GeckoViewFetchClient(context, runtime = runtime) }
+
+ val store: BrowserStore by lazy {
+ BrowserStore(
+ middleware = listOf(
+ RegionMiddleware(context, locationService),
+ SearchMiddleware(context),
+ ) + EngineMiddleware.create(engine),
+ )
+ }
+
+ val appStore: AppStore by lazy { AppStore() }
+
+ val sessionUseCases by lazy { SessionUseCases(store) }
+ val tabsUseCases by lazy { TabsUseCases(store) }
+ val searchUseCases by lazy { SearchUseCases(store, tabsUseCases, sessionUseCases) }
+
+ val locationService by lazy { LocationService.default() }
+
+ val fxSuggestStorage: FxSuggestStorage by lazy {
+ FxSuggestStorage(context)
+ }
+
+ val fxSuggestIngestionScheduler: FxSuggestIngestionScheduler by lazy {
+ FxSuggestIngestionScheduler(context)
+ }
+}
+
+/**
+ * Returns the global [Components] object from within a `@Composable` context.
+ */
+@Composable
+fun components(): Components {
+ return (LocalContext.current.applicationContext as BrowserApplication).components
+}
diff --git a/mobile/android/android-components/samples/compose-browser/src/main/java/org/mozilla/samples/compose/browser/app/AppAction.kt b/mobile/android/android-components/samples/compose-browser/src/main/java/org/mozilla/samples/compose/browser/app/AppAction.kt
new file mode 100644
index 0000000000..cf36ac991b
--- /dev/null
+++ b/mobile/android/android-components/samples/compose-browser/src/main/java/org/mozilla/samples/compose/browser/app/AppAction.kt
@@ -0,0 +1,17 @@
+/* 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.compose.browser.app
+
+import mozilla.components.lib.state.Action
+
+/**
+ * Actions for updating the global [AppState] via [AppStore].
+ */
+sealed class AppAction : Action {
+ /**
+ * Toggles the theme of the app (only for testing purposes).
+ */
+ object ToggleTheme : AppAction()
+}
diff --git a/mobile/android/android-components/samples/compose-browser/src/main/java/org/mozilla/samples/compose/browser/app/AppState.kt b/mobile/android/android-components/samples/compose-browser/src/main/java/org/mozilla/samples/compose/browser/app/AppState.kt
new file mode 100644
index 0000000000..163dddb8ec
--- /dev/null
+++ b/mobile/android/android-components/samples/compose-browser/src/main/java/org/mozilla/samples/compose/browser/app/AppState.kt
@@ -0,0 +1,14 @@
+/* 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.compose.browser.app
+
+import mozilla.components.lib.state.State
+
+/**
+ * Global state the browser is in (regardless of the currently displayed screen).
+ */
+data class AppState(
+ val theme: Int = 1,
+) : State
diff --git a/mobile/android/android-components/samples/compose-browser/src/main/java/org/mozilla/samples/compose/browser/app/AppStore.kt b/mobile/android/android-components/samples/compose-browser/src/main/java/org/mozilla/samples/compose/browser/app/AppStore.kt
new file mode 100644
index 0000000000..d75e5f3787
--- /dev/null
+++ b/mobile/android/android-components/samples/compose-browser/src/main/java/org/mozilla/samples/compose/browser/app/AppStore.kt
@@ -0,0 +1,22 @@
+/* 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.compose.browser.app
+
+import mozilla.components.lib.state.Store
+
+/**
+ * [Store] for the global [AppState].
+ */
+class AppStore : Store<AppState, AppAction>(
+ initialState = AppState(),
+ reducer = ::reduce,
+)
+
+private fun reduce(appState: AppState, appAction: AppAction): AppState {
+ if (appAction is AppAction.ToggleTheme) {
+ return appState.copy(theme = (appState.theme + 1) % 2)
+ }
+ return appState
+}
diff --git a/mobile/android/android-components/samples/compose-browser/src/main/java/org/mozilla/samples/compose/browser/browser/BrowserScreen.kt b/mobile/android/android-components/samples/compose-browser/src/main/java/org/mozilla/samples/compose/browser/browser/BrowserScreen.kt
new file mode 100644
index 0000000000..2e5a66312d
--- /dev/null
+++ b/mobile/android/android-components/samples/compose-browser/src/main/java/org/mozilla/samples/compose/browser/browser/BrowserScreen.kt
@@ -0,0 +1,237 @@
+/* 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.compose.browser.browser
+
+import androidx.activity.compose.BackHandler
+import androidx.compose.foundation.background
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.fillMaxHeight
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.material.Button
+import androidx.compose.material.ContentAlpha
+import androidx.compose.material.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.ExperimentalComposeUiApi
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.platform.LocalSoftwareKeyboardController
+import androidx.navigation.NavController
+import mozilla.components.browser.state.helper.Target
+import mozilla.components.compose.browser.awesomebar.AwesomeBar
+import mozilla.components.compose.browser.toolbar.BrowserToolbar
+import mozilla.components.compose.engine.WebContent
+import mozilla.components.compose.tabstray.TabCounterButton
+import mozilla.components.compose.tabstray.TabList
+import mozilla.components.concept.awesomebar.AwesomeBar
+import mozilla.components.feature.awesomebar.provider.ClipboardSuggestionProvider
+import mozilla.components.feature.awesomebar.provider.SearchActionProvider
+import mozilla.components.feature.awesomebar.provider.SearchSuggestionProvider
+import mozilla.components.feature.awesomebar.provider.SessionSuggestionProvider
+import mozilla.components.feature.fxsuggest.FxSuggestSuggestionProvider
+import mozilla.components.lib.state.Store
+import mozilla.components.lib.state.ext.composableStore
+import mozilla.components.lib.state.ext.observeAsComposableState
+import org.mozilla.samples.compose.browser.BrowserComposeActivity.Companion.ROUTE_SETTINGS
+import org.mozilla.samples.compose.browser.components
+
+/**
+ * The main browser screen.
+ */
+@Composable
+fun BrowserScreen(navController: NavController) {
+ val target = Target.SelectedTab
+
+ val store = composableStore<BrowserScreenState, BrowserScreenAction> { restoredState ->
+ BrowserScreenStore(restoredState ?: BrowserScreenState())
+ }
+
+ val editState = store.observeAsComposableState { state -> state.editMode }
+ val editUrl = store.observeAsComposableState { state -> state.editText }
+ val loadUrl = components().sessionUseCases.loadUrl
+ val showTabs = store.observeAsComposableState { state -> state.showTabs }
+
+ BackHandler(enabled = editState.value == true) {
+ store.dispatch(BrowserScreenAction.ToggleEditMode(false))
+ }
+
+ Box {
+ Column {
+ BrowserToolbar(
+ components().store,
+ target,
+ editMode = editState.value!!,
+ onDisplayMenuClicked = {
+ navController.navigate(ROUTE_SETTINGS)
+ },
+ onTextCommit = { text ->
+ store.dispatch(BrowserScreenAction.ToggleEditMode(false))
+ loadUrl(text)
+ },
+ onTextEdit = { text -> store.dispatch(BrowserScreenAction.UpdateEditText(text)) },
+ onDisplayToolbarClick = {
+ store.dispatch(BrowserScreenAction.ToggleEditMode(true))
+ },
+ editText = editUrl.value,
+ hint = "Search or enter address",
+ browserActions = {
+ TabCounterButton(
+ components().store,
+ onClicked = { store.dispatch(BrowserScreenAction.ShowTabs) },
+ )
+ },
+ )
+
+ Box {
+ WebContent(
+ components().engine,
+ components().store,
+ Target.SelectedTab,
+ )
+
+ val url = editUrl.value
+ if (editState.value == true && url != null) {
+ Suggestions(
+ url,
+ onSuggestionClicked = { suggestion ->
+ store.dispatch(BrowserScreenAction.ToggleEditMode(false))
+ suggestion.onSuggestionClicked?.invoke()
+ },
+ onAutoComplete = { suggestion ->
+ store.dispatch(BrowserScreenAction.UpdateEditText(suggestion.editSuggestion!!))
+ },
+ )
+ }
+ }
+ }
+
+ if (showTabs.value == true) {
+ TabsTray(store)
+ }
+ }
+}
+
+/**
+ * Shows the lit of tabs.
+ */
+@Composable
+fun TabsTray(
+ store: Store<BrowserScreenState, BrowserScreenAction>,
+) {
+ val components = components()
+
+ BackHandler(onBack = { store.dispatch(BrowserScreenAction.HideTabs) })
+
+ Box(
+ modifier = Modifier
+ .fillMaxWidth()
+ .fillMaxHeight()
+ .background(Color.Black.copy(alpha = ContentAlpha.medium))
+ .clickable {
+ store.dispatch(BrowserScreenAction.HideTabs)
+ },
+ ) {
+ Column(
+ modifier = Modifier
+ .fillMaxHeight(fraction = 0.8f)
+ .align(Alignment.BottomStart),
+ ) {
+ TabList(
+ store = components().store,
+ onTabSelected = { tab ->
+ components.tabsUseCases.selectTab(tab.id)
+ store.dispatch(BrowserScreenAction.HideTabs)
+ },
+ onTabClosed = { tab ->
+ components.tabsUseCases.removeTab(tab.id)
+ },
+ modifier = Modifier.weight(1f),
+ )
+ Button(
+ onClick = {
+ components.tabsUseCases.addTab(
+ url = "about:blank",
+ selectTab = true,
+ )
+ store.dispatch(BrowserScreenAction.HideTabs)
+ store.dispatch(BrowserScreenAction.ToggleEditMode(true))
+ },
+ ) {
+ Text("+")
+ }
+ }
+ }
+}
+
+@OptIn(ExperimentalComposeUiApi::class)
+@Composable
+private fun Suggestions(
+ url: String,
+ onSuggestionClicked: (AwesomeBar.Suggestion) -> Unit,
+ onAutoComplete: (AwesomeBar.Suggestion) -> Unit,
+) {
+ val context = LocalContext.current
+ val components = components()
+
+ val sessionSuggestionProvider = remember(context) {
+ SessionSuggestionProvider(
+ context.resources,
+ components.store,
+ components.tabsUseCases.selectTab,
+ )
+ }
+
+ val searchActionProvider = remember {
+ SearchActionProvider(components.store, components.searchUseCases.defaultSearch)
+ }
+
+ val fxSuggestSuggestionProvider = remember(context) {
+ FxSuggestSuggestionProvider(
+ context.resources,
+ loadUrlUseCase = components.sessionUseCases.loadUrl,
+ includeSponsoredSuggestions = false,
+ includeNonSponsoredSuggestions = true,
+ )
+ }
+
+ val searchSuggestionProvider = remember(context) {
+ SearchSuggestionProvider(
+ context,
+ components.store,
+ components.searchUseCases.defaultSearch,
+ components.client,
+ mode = SearchSuggestionProvider.Mode.MULTIPLE_SUGGESTIONS,
+ engine = components.engine,
+ filterExactMatch = true,
+ )
+ }
+
+ val clipboardSuggestionProvider = remember(context) {
+ ClipboardSuggestionProvider(
+ context,
+ components.sessionUseCases.loadUrl,
+ )
+ }
+
+ val keyboardController = LocalSoftwareKeyboardController.current
+
+ AwesomeBar(
+ url,
+ providers = listOf(
+ sessionSuggestionProvider,
+ searchActionProvider,
+ fxSuggestSuggestionProvider,
+ searchSuggestionProvider,
+ clipboardSuggestionProvider,
+ ),
+ onSuggestionClicked = { suggestion -> onSuggestionClicked(suggestion) },
+ onAutoComplete = { suggestion -> onAutoComplete(suggestion) },
+ onScroll = { keyboardController?.hide() },
+ )
+}
diff --git a/mobile/android/android-components/samples/compose-browser/src/main/java/org/mozilla/samples/compose/browser/browser/BrowserScreenAction.kt b/mobile/android/android-components/samples/compose-browser/src/main/java/org/mozilla/samples/compose/browser/browser/BrowserScreenAction.kt
new file mode 100644
index 0000000000..c7da237d5a
--- /dev/null
+++ b/mobile/android/android-components/samples/compose-browser/src/main/java/org/mozilla/samples/compose/browser/browser/BrowserScreenAction.kt
@@ -0,0 +1,32 @@
+/* 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.compose.browser.browser
+
+import mozilla.components.lib.state.Action
+
+/**
+ * Actions for updating the [BrowserScreenState] via [BrowserScreenStore].
+ */
+sealed class BrowserScreenAction : Action {
+ /**
+ * Updates whether the toolbar is in "display" or "edit" mode.
+ */
+ data class ToggleEditMode(val editMode: Boolean) : BrowserScreenAction()
+
+ /**
+ * Updates the text of the toolbar that is currently being edited (in "edit" mode).
+ */
+ data class UpdateEditText(val text: String) : BrowserScreenAction()
+
+ /**
+ * Shows the list of tabs on top of the web content.
+ */
+ object ShowTabs : BrowserScreenAction()
+
+ /**
+ * Hides the list of tabs.
+ */
+ object HideTabs : BrowserScreenAction()
+}
diff --git a/mobile/android/android-components/samples/compose-browser/src/main/java/org/mozilla/samples/compose/browser/browser/BrowserScreenState.kt b/mobile/android/android-components/samples/compose-browser/src/main/java/org/mozilla/samples/compose/browser/browser/BrowserScreenState.kt
new file mode 100644
index 0000000000..d3bae7578d
--- /dev/null
+++ b/mobile/android/android-components/samples/compose-browser/src/main/java/org/mozilla/samples/compose/browser/browser/BrowserScreenState.kt
@@ -0,0 +1,22 @@
+/* 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.compose.browser.browser
+
+import android.os.Parcelable
+import kotlinx.parcelize.Parcelize
+import mozilla.components.lib.state.State
+
+/**
+ * The state the browser screen is in.
+ *
+ * @param editMode Whether the toolbar is in "edit" or "display" mode.
+ * @param editText The text in the toolbar that is being edited by the user.
+ */
+@Parcelize
+data class BrowserScreenState(
+ val editMode: Boolean = false,
+ val editText: String? = null,
+ val showTabs: Boolean = false,
+) : State, Parcelable
diff --git a/mobile/android/android-components/samples/compose-browser/src/main/java/org/mozilla/samples/compose/browser/browser/BrowserScreenStore.kt b/mobile/android/android-components/samples/compose-browser/src/main/java/org/mozilla/samples/compose/browser/browser/BrowserScreenStore.kt
new file mode 100644
index 0000000000..6d7dcbb3ac
--- /dev/null
+++ b/mobile/android/android-components/samples/compose-browser/src/main/java/org/mozilla/samples/compose/browser/browser/BrowserScreenStore.kt
@@ -0,0 +1,29 @@
+/* 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.compose.browser.browser
+
+import mozilla.components.lib.state.Store
+
+/**
+ * [Store] for maintaining the state of the browser screen.
+ */
+class BrowserScreenStore(
+ initialState: BrowserScreenState = BrowserScreenState(),
+) : Store<BrowserScreenState, BrowserScreenAction>(
+ initialState = initialState,
+ reducer = ::reduce,
+)
+
+private fun reduce(state: BrowserScreenState, action: BrowserScreenAction): BrowserScreenState {
+ return when (action) {
+ is BrowserScreenAction.ToggleEditMode -> state.copy(
+ editMode = action.editMode,
+ editText = if (action.editMode) null else state.editText,
+ )
+ is BrowserScreenAction.UpdateEditText -> state.copy(editText = action.text)
+ is BrowserScreenAction.ShowTabs -> state.copy(showTabs = true)
+ is BrowserScreenAction.HideTabs -> state.copy(showTabs = false)
+ }
+}
diff --git a/mobile/android/android-components/samples/compose-browser/src/main/java/org/mozilla/samples/compose/browser/ext/Context.kt b/mobile/android/android-components/samples/compose-browser/src/main/java/org/mozilla/samples/compose/browser/ext/Context.kt
new file mode 100644
index 0000000000..a847394795
--- /dev/null
+++ b/mobile/android/android-components/samples/compose-browser/src/main/java/org/mozilla/samples/compose/browser/ext/Context.kt
@@ -0,0 +1,15 @@
+/* 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.compose.browser.ext
+
+import android.content.Context
+import org.mozilla.samples.compose.browser.BrowserApplication
+import org.mozilla.samples.compose.browser.Components
+
+val Context.application: BrowserApplication
+ get() = applicationContext as BrowserApplication
+
+val Context.components: Components
+ get() = application.components
diff --git a/mobile/android/android-components/samples/compose-browser/src/main/java/org/mozilla/samples/compose/browser/settings/SettingsScreen.kt b/mobile/android/android-components/samples/compose-browser/src/main/java/org/mozilla/samples/compose/browser/settings/SettingsScreen.kt
new file mode 100644
index 0000000000..0e83658e7a
--- /dev/null
+++ b/mobile/android/android-components/samples/compose-browser/src/main/java/org/mozilla/samples/compose/browser/settings/SettingsScreen.kt
@@ -0,0 +1,19 @@
+/* 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.compose.browser.settings
+
+import androidx.compose.foundation.layout.Column
+import androidx.compose.material.Text
+import androidx.compose.runtime.Composable
+
+/**
+ * Screen displaying the settings of the browser.
+ */
+@Composable
+fun SettingsScreen() {
+ Column {
+ Text("Settings")
+ }
+}
diff --git a/mobile/android/android-components/samples/compose-browser/src/main/res/drawable/ic_launcher_background.xml b/mobile/android/android-components/samples/compose-browser/src/main/res/drawable/ic_launcher_background.xml
new file mode 100644
index 0000000000..61f5b8183f
--- /dev/null
+++ b/mobile/android/android-components/samples/compose-browser/src/main/res/drawable/ic_launcher_background.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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/. -->
+
+<vector
+ android:height="108dp"
+ android:width="108dp"
+ android:viewportHeight="108"
+ android:viewportWidth="108"
+ xmlns:android="http://schemas.android.com/apk/res/android">
+ <path android:fillColor="#3DDC84"
+ android:pathData="M0,0h108v108h-108z"/>
+ <path android:fillColor="#00000000" android:pathData="M9,0L9,108"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M19,0L19,108"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M29,0L29,108"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M39,0L39,108"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M49,0L49,108"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M59,0L59,108"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M69,0L69,108"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M79,0L79,108"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M89,0L89,108"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M99,0L99,108"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M0,9L108,9"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M0,19L108,19"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M0,29L108,29"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M0,39L108,39"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M0,49L108,49"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M0,59L108,59"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M0,69L108,69"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M0,79L108,79"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M0,89L108,89"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M0,99L108,99"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M19,29L89,29"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M19,39L89,39"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M19,49L89,49"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M19,59L89,59"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M19,69L89,69"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M19,79L89,79"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M29,19L29,89"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M39,19L39,89"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M49,19L49,89"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M59,19L59,89"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M69,19L69,89"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M79,19L79,89"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+</vector>
diff --git a/mobile/android/android-components/samples/compose-browser/src/main/res/drawable/ic_launcher_foreground.xml b/mobile/android/android-components/samples/compose-browser/src/main/res/drawable/ic_launcher_foreground.xml
new file mode 100644
index 0000000000..8a7cd0aa55
--- /dev/null
+++ b/mobile/android/android-components/samples/compose-browser/src/main/res/drawable/ic_launcher_foreground.xml
@@ -0,0 +1,19 @@
+<!-- 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/. -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="108dp"
+ android:height="108dp"
+ android:viewportWidth="108"
+ android:viewportHeight="108"
+ android:tint="#FFFFFF">
+ <group android:scaleX="2.9232"
+ android:scaleY="2.9232"
+ android:translateX="18.9216"
+ android:translateY="18.9216">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM11,19.93c-3.95,-0.49 -7,-3.85 -7,-7.93 0,-0.62 0.08,-1.21 0.21,-1.79L9,15v1c0,1.1 0.9,2 2,2v1.93zM17.9,17.39c-0.26,-0.81 -1,-1.39 -1.9,-1.39h-1v-3c0,-0.55 -0.45,-1 -1,-1L8,12v-2h2c0.55,0 1,-0.45 1,-1L11,7h2c1.1,0 2,-0.9 2,-2v-0.41c2.93,1.19 5,4.06 5,7.41 0,2.08 -0.8,3.97 -2.1,5.39z"/>
+ </group>
+</vector>
diff --git a/mobile/android/android-components/samples/compose-browser/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/mobile/android/android-components/samples/compose-browser/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
new file mode 100644
index 0000000000..c7743a9582
--- /dev/null
+++ b/mobile/android/android-components/samples/compose-browser/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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/. -->
+
+<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
+ <background android:drawable="@drawable/ic_launcher_background"/>
+ <foreground android:drawable="@drawable/ic_launcher_foreground"/>
+</adaptive-icon> \ No newline at end of file
diff --git a/mobile/android/android-components/samples/compose-browser/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/mobile/android/android-components/samples/compose-browser/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
new file mode 100644
index 0000000000..c7743a9582
--- /dev/null
+++ b/mobile/android/android-components/samples/compose-browser/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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/. -->
+
+<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
+ <background android:drawable="@drawable/ic_launcher_background"/>
+ <foreground android:drawable="@drawable/ic_launcher_foreground"/>
+</adaptive-icon> \ No newline at end of file
diff --git a/mobile/android/android-components/samples/compose-browser/src/main/res/mipmap-hdpi/ic_launcher.png b/mobile/android/android-components/samples/compose-browser/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 0000000000..3782c0799b
--- /dev/null
+++ b/mobile/android/android-components/samples/compose-browser/src/main/res/mipmap-hdpi/ic_launcher.png
Binary files differ
diff --git a/mobile/android/android-components/samples/compose-browser/src/main/res/mipmap-hdpi/ic_launcher_round.png b/mobile/android/android-components/samples/compose-browser/src/main/res/mipmap-hdpi/ic_launcher_round.png
new file mode 100644
index 0000000000..8b8c8e4041
--- /dev/null
+++ b/mobile/android/android-components/samples/compose-browser/src/main/res/mipmap-hdpi/ic_launcher_round.png
Binary files differ
diff --git a/mobile/android/android-components/samples/compose-browser/src/main/res/mipmap-mdpi/ic_launcher.png b/mobile/android/android-components/samples/compose-browser/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 0000000000..7a42b483fd
--- /dev/null
+++ b/mobile/android/android-components/samples/compose-browser/src/main/res/mipmap-mdpi/ic_launcher.png
Binary files differ
diff --git a/mobile/android/android-components/samples/compose-browser/src/main/res/mipmap-mdpi/ic_launcher_round.png b/mobile/android/android-components/samples/compose-browser/src/main/res/mipmap-mdpi/ic_launcher_round.png
new file mode 100644
index 0000000000..3ff74c1d9d
--- /dev/null
+++ b/mobile/android/android-components/samples/compose-browser/src/main/res/mipmap-mdpi/ic_launcher_round.png
Binary files differ
diff --git a/mobile/android/android-components/samples/compose-browser/src/main/res/mipmap-xhdpi/ic_launcher.png b/mobile/android/android-components/samples/compose-browser/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 0000000000..497337793e
--- /dev/null
+++ b/mobile/android/android-components/samples/compose-browser/src/main/res/mipmap-xhdpi/ic_launcher.png
Binary files differ
diff --git a/mobile/android/android-components/samples/compose-browser/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/mobile/android/android-components/samples/compose-browser/src/main/res/mipmap-xhdpi/ic_launcher_round.png
new file mode 100644
index 0000000000..f1c35726b8
--- /dev/null
+++ b/mobile/android/android-components/samples/compose-browser/src/main/res/mipmap-xhdpi/ic_launcher_round.png
Binary files differ
diff --git a/mobile/android/android-components/samples/compose-browser/src/main/res/mipmap-xxhdpi/ic_launcher.png b/mobile/android/android-components/samples/compose-browser/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000000..cd006d2f57
--- /dev/null
+++ b/mobile/android/android-components/samples/compose-browser/src/main/res/mipmap-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/mobile/android/android-components/samples/compose-browser/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/mobile/android/android-components/samples/compose-browser/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
new file mode 100644
index 0000000000..9db209fcbc
--- /dev/null
+++ b/mobile/android/android-components/samples/compose-browser/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
Binary files differ
diff --git a/mobile/android/android-components/samples/compose-browser/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/mobile/android/android-components/samples/compose-browser/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 0000000000..3ca4e817f7
--- /dev/null
+++ b/mobile/android/android-components/samples/compose-browser/src/main/res/mipmap-xxxhdpi/ic_launcher.png
Binary files differ
diff --git a/mobile/android/android-components/samples/compose-browser/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/mobile/android/android-components/samples/compose-browser/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
new file mode 100644
index 0000000000..0d5a5a266a
--- /dev/null
+++ b/mobile/android/android-components/samples/compose-browser/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
Binary files differ
diff --git a/mobile/android/android-components/samples/compose-browser/src/main/res/values/strings.xml b/mobile/android/android-components/samples/compose-browser/src/main/res/values/strings.xml
new file mode 100644
index 0000000000..2441d14625
--- /dev/null
+++ b/mobile/android/android-components/samples/compose-browser/src/main/res/values/strings.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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/. -->
+<resources>
+ <string name="app_name">Compose Browser</string>
+</resources> \ No newline at end of file
diff --git a/mobile/android/android-components/samples/compose-browser/src/main/res/xml/data_extraction_rules.xml b/mobile/android/android-components/samples/compose-browser/src/main/res/xml/data_extraction_rules.xml
new file mode 100644
index 0000000000..55da967560
--- /dev/null
+++ b/mobile/android/android-components/samples/compose-browser/src/main/res/xml/data_extraction_rules.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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/. -->
+<data-extraction-rules>
+ <cloud-backup>
+ <include domain="sharedpref" path="."/>
+ </cloud-backup>
+</data-extraction-rules> \ No newline at end of file