diff --git a/app/build.gradle b/app/build.gradle
index f91b4a8e6..4ab05f1fa 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -70,6 +70,7 @@ dependencies {
implementation Deps.mozilla_concept_engine
implementation Deps.mozilla_concept_storage
+ implementation Deps.mozilla_concept_toolbar
implementation Deps.mozilla_browser_awesomebar
implementation Deps.mozilla_browser_domains
@@ -81,7 +82,7 @@ dependencies {
implementation Deps.mozilla_feature_intent
implementation Deps.mozilla_feature_session
implementation Deps.mozilla_feature_toolbar
-
+ implementation Deps.mozilla_feature_tabs
implementation Deps.mozilla_support_ktx
testImplementation Deps.junit
diff --git a/app/src/main/java/org/mozilla/fenix/browser/BrowserFragment.kt b/app/src/main/java/org/mozilla/fenix/browser/BrowserFragment.kt
index 4970a584c..76db0bebf 100644
--- a/app/src/main/java/org/mozilla/fenix/browser/BrowserFragment.kt
+++ b/app/src/main/java/org/mozilla/fenix/browser/BrowserFragment.kt
@@ -14,6 +14,7 @@ import mozilla.components.feature.session.SessionFeature
import mozilla.components.feature.session.SessionUseCases
import mozilla.components.support.ktx.android.arch.lifecycle.addObservers
import org.mozilla.fenix.R
+import org.mozilla.fenix.components.toolbar.ToolbarIntegration
import org.mozilla.fenix.ext.requireComponents
class BrowserFragment : Fragment() {
@@ -41,7 +42,16 @@ class BrowserFragment : Fragment() {
sessionFeature = SessionFeature(
sessionManager,
SessionUseCases(sessionManager),
- engineView)
+ engineView
+ )
+
+ lifecycle.addObserver(
+ ToolbarIntegration(
+ requireContext(),
+ toolbar,
+ requireComponents.toolbar.shippedDomainsProvider
+ )
+ )
lifecycle.addObservers(sessionFeature)
}
diff --git a/app/src/main/java/org/mozilla/fenix/components/Components.kt b/app/src/main/java/org/mozilla/fenix/components/Components.kt
index c8c52576e..7e94fa402 100644
--- a/app/src/main/java/org/mozilla/fenix/components/Components.kt
+++ b/app/src/main/java/org/mozilla/fenix/components/Components.kt
@@ -5,6 +5,7 @@
package org.mozilla.fenix.components
import android.content.Context
+import org.mozilla.fenix.components.toolbar.Toolbar
/**
* Provides access to all components.
@@ -13,4 +14,11 @@ class Components(private val context: Context) {
val core by lazy { Core(context) }
val search by lazy { Search(context) }
val useCases by lazy { UseCases(context, core.sessionManager, search.searchEngineManager) }
+ val toolbar by lazy {
+ Toolbar(
+ context,
+ useCases.sessionUseCases,
+ core.sessionManager
+ )
+ }
}
\ No newline at end of file
diff --git a/app/src/main/java/org/mozilla/fenix/components/Search.kt b/app/src/main/java/org/mozilla/fenix/components/Search.kt
index f4d3b7889..8e85ba147 100644
--- a/app/src/main/java/org/mozilla/fenix/components/Search.kt
+++ b/app/src/main/java/org/mozilla/fenix/components/Search.kt
@@ -1,6 +1,6 @@
/* 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/. */
+ * 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.fenix.components
diff --git a/app/src/main/java/org/mozilla/fenix/components/UseCases.kt b/app/src/main/java/org/mozilla/fenix/components/UseCases.kt
index b2267ab50..f25831aa4 100644
--- a/app/src/main/java/org/mozilla/fenix/components/UseCases.kt
+++ b/app/src/main/java/org/mozilla/fenix/components/UseCases.kt
@@ -1,6 +1,6 @@
/* 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/. */
+ * 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.fenix.components
@@ -20,8 +20,18 @@ class UseCases(
private val sessionManager: SessionManager,
private val searchEngineManager: SearchEngineManager
) {
+ /**
+ * Use cases that provide engine interactions for a given browser session.
+ */
+ val sessionUseCases by lazy { SessionUseCases(sessionManager) }
+ /**
+ * Use cases that provide tab management.
+ */
+ val tabsUseCases: TabsUseCases by lazy { TabsUseCases(sessionManager) }
+
+ /**
+ * Use cases that provide search engine integration.
+ */
val searchUseCases by lazy { SearchUseCases(context, searchEngineManager, sessionManager) }
- val sessionUseCases by lazy { SessionUseCases(sessionManager) };
- val tabsUseCases by lazy { TabsUseCases(sessionManager) }
}
diff --git a/app/src/main/java/org/mozilla/fenix/components/toolbar/Toolbar.kt b/app/src/main/java/org/mozilla/fenix/components/toolbar/Toolbar.kt
new file mode 100644
index 000000000..a34c7c9e5
--- /dev/null
+++ b/app/src/main/java/org/mozilla/fenix/components/toolbar/Toolbar.kt
@@ -0,0 +1,106 @@
+/* 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.fenix.components.toolbar
+
+import android.content.Context
+import mozilla.components.browser.domains.autocomplete.ShippedDomainsProvider
+import mozilla.components.browser.menu.BrowserMenuBuilder
+import mozilla.components.browser.menu.item.BrowserMenuItemToolbar
+import mozilla.components.browser.menu.item.BrowserMenuSwitch
+import mozilla.components.browser.menu.item.SimpleBrowserMenuItem
+import mozilla.components.browser.session.SessionManager
+import mozilla.components.feature.session.SessionUseCases
+import org.mozilla.fenix.R
+
+/**
+ * Component group for all functionality related to the browser toolbar.
+ */
+class Toolbar(
+ private val context: Context,
+ private val sessionUseCases: SessionUseCases,
+ private val sessionManager: SessionManager
+) {
+
+ /**
+ * Helper class for building browser menus.app
+ */
+ val menuBuilder by lazy { BrowserMenuBuilder(menuItems) }
+
+ /**
+ * Provides autocomplete functionality for shipped / provided domain lists.
+ */
+ val shippedDomainsProvider by lazy {
+ ShippedDomainsProvider().also { it.initialize(context) }
+ }
+
+ private val menuToolbar by lazy {
+ val back = BrowserMenuItemToolbar.Button(
+ mozilla.components.ui.icons.R.drawable.mozac_ic_back,
+ iconTintColorResource = R.color.icons,
+ contentDescription = "Back"
+ ) {
+ sessionUseCases.goBack.invoke()
+ }
+
+ val forward = BrowserMenuItemToolbar.Button(
+ mozilla.components.ui.icons.R.drawable.mozac_ic_forward,
+ iconTintColorResource = R.color.icons,
+ contentDescription = "Forward"
+ ) {
+ sessionUseCases.goForward.invoke()
+ }
+
+ val refresh = BrowserMenuItemToolbar.Button(
+ mozilla.components.ui.icons.R.drawable.mozac_ic_refresh,
+ iconTintColorResource = R.color.icons,
+ contentDescription = "Refresh"
+ ) {
+ sessionUseCases.reload.invoke()
+ }
+
+ BrowserMenuItemToolbar(listOf(back, forward, refresh))
+ }
+
+ private val menuItems by lazy {
+ listOf(
+ menuToolbar,
+ SimpleBrowserMenuItem(context.getString(R.string.browser_menu_help)) {
+ // TODO Help
+ },
+
+ SimpleBrowserMenuItem(context.getString(R.string.browser_menu_settings)) {
+ openSettingsActivity()
+ },
+
+ SimpleBrowserMenuItem(context.getString(R.string.browser_menu_library)) {
+ // TODO Your Library
+ },
+
+ BrowserMenuSwitch(context.getString(R.string.browser_menu_desktop_site), {
+ sessionManager.selectedSessionOrThrow.desktopMode
+ }) { checked ->
+ sessionUseCases.requestDesktopSite.invoke(checked)
+ }.apply {
+ visible = { sessionManager.selectedSession != null }
+ },
+
+ SimpleBrowserMenuItem(context.getString(R.string.browser_menu_find_in_page)) {
+ // TODO Find in Page
+ },
+
+ SimpleBrowserMenuItem(context.getString(R.string.browser_menu_private_tab)) {
+ // TODO Private Tab
+ },
+
+ SimpleBrowserMenuItem(context.getString(R.string.browser_menu_new_tab)) {
+ // TODO New Tab
+ }
+ )
+ }
+
+ private fun openSettingsActivity() {
+ // TODO Open Settings
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/org/mozilla/fenix/components/toolbar/ToolbarIntegration.kt b/app/src/main/java/org/mozilla/fenix/components/toolbar/ToolbarIntegration.kt
index a7445610f..85faf18f4 100644
--- a/app/src/main/java/org/mozilla/fenix/components/toolbar/ToolbarIntegration.kt
+++ b/app/src/main/java/org/mozilla/fenix/components/toolbar/ToolbarIntegration.kt
@@ -5,13 +5,18 @@
package org.mozilla.fenix.components.toolbar
import android.content.Context
+import androidx.core.view.ViewCompat
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleObserver
import androidx.lifecycle.OnLifecycleEvent
+import androidx.navigation.Navigation
+import androidx.navigation.fragment.FragmentNavigator
import mozilla.components.browser.domains.autocomplete.DomainAutocompleteProvider
import mozilla.components.browser.toolbar.BrowserToolbar
import mozilla.components.feature.toolbar.ToolbarAutocompleteFeature
import mozilla.components.feature.toolbar.ToolbarFeature
+import org.mozilla.fenix.R
+import org.mozilla.fenix.ext.application
import org.mozilla.fenix.ext.components
class ToolbarIntegration(
@@ -21,6 +26,29 @@ class ToolbarIntegration(
sessionId: String? = null
) : LifecycleObserver {
init {
+ toolbar.setMenuBuilder(context.components.toolbar.menuBuilder)
+
+ val home = BrowserToolbar.Button(
+ context.resources.getDrawable(
+ R.drawable.ic_home,
+ context.application.theme
+ ),
+ "Home"
+ ) {
+ Navigation.findNavController(toolbar).navigate(R.id.action_browserFragment_to_homeFragment)
+ }
+
+ toolbar.addBrowserAction(home)
+
+ toolbar.onUrlClicked = {
+ val extras = FragmentNavigator.Extras.Builder().addSharedElement(
+ toolbar, ViewCompat.getTransitionName(toolbar)!!
+ ).build()
+ Navigation.findNavController(toolbar)
+ .navigate(R.id.action_browserFragment_to_searchFragment, null, null, extras)
+ false
+ }
+
ToolbarAutocompleteFeature(toolbar).apply {
addDomainProvider(domainAutocompleteProvider)
}
diff --git a/app/src/main/java/org/mozilla/fenix/ext/Context.kt b/app/src/main/java/org/mozilla/fenix/ext/Context.kt
index 54effd9f2..d2718aa28 100644
--- a/app/src/main/java/org/mozilla/fenix/ext/Context.kt
+++ b/app/src/main/java/org/mozilla/fenix/ext/Context.kt
@@ -1,15 +1,59 @@
/* 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/. */
+ * 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.fenix.ext
+import android.content.ActivityNotFoundException
import android.content.Context
+import android.content.Intent
+import android.content.Intent.*
+import androidx.annotation.StringRes
+import mozilla.components.support.base.log.Log
+import mozilla.components.support.base.log.Log.Priority.WARN
import org.mozilla.fenix.FenixApplication
+import org.mozilla.fenix.R
import org.mozilla.fenix.components.Components
+/**
+ * Get the BrowserApplication object from a context.
+ */
val Context.application: FenixApplication
get() = applicationContext as FenixApplication
+/**
+ * Get the requireComponents of this application.
+ */
val Context.components: Components
- get() = application.components
\ No newline at end of file
+ get() = application.components
+
+fun Context.getPreferenceKey(@StringRes resourceId: Int): String =
+ resources.getString(resourceId)
+
+/**
+ * Shares content via [ACTION_SEND] intent.
+ *
+ * @param text the data to be shared [EXTRA_TEXT]
+ * @param subject of the intent [EXTRA_TEXT]
+ * @return true it is able to share false otherwise.
+ */
+fun Context.share(text: String, subject: String = ""): Boolean {
+ return try {
+ val intent = Intent(ACTION_SEND).apply {
+ type = "text/plain"
+ putExtra(EXTRA_SUBJECT, subject)
+ putExtra(EXTRA_TEXT, text)
+ flags = FLAG_ACTIVITY_NEW_TASK
+ }
+
+ val shareIntent = Intent.createChooser(intent, getString(R.string.menu_share_with)).apply {
+ flags = FLAG_ACTIVITY_NEW_TASK
+ }
+
+ startActivity(shareIntent)
+ true
+ } catch (e: ActivityNotFoundException) {
+ Log.log(WARN, message = "No activity to share to found", throwable = e, tag = "Reference-Browser")
+ false
+ }
+}
diff --git a/app/src/main/java/org/mozilla/fenix/ext/Fragment.kt b/app/src/main/java/org/mozilla/fenix/ext/Fragment.kt
index 10c70358a..53516a60a 100644
--- a/app/src/main/java/org/mozilla/fenix/ext/Fragment.kt
+++ b/app/src/main/java/org/mozilla/fenix/ext/Fragment.kt
@@ -1,11 +1,13 @@
/* 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/. */
+ * 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.fenix.ext
-import androidx.fragment.app.Fragment
import org.mozilla.fenix.components.Components
-val Fragment.requireComponents: Components
+/**
+ * Get the requireComponents of this application.
+ */
+val androidx.fragment.app.Fragment.requireComponents: Components
get() = requireContext().components
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_home.xml b/app/src/main/res/drawable/ic_home.xml
new file mode 100644
index 000000000..9aca81966
--- /dev/null
+++ b/app/src/main/res/drawable/ic_home.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
diff --git a/app/src/main/res/layout/fragment_browser.xml b/app/src/main/res/layout/fragment_browser.xml
index ea37801d3..a88c68206 100644
--- a/app/src/main/res/layout/fragment_browser.xml
+++ b/app/src/main/res/layout/fragment_browser.xml
@@ -36,7 +36,7 @@
android:layout_width="match_parent"
android:layout_height="56dp"
android:layout_gravity="bottom"
- android:background="@android:color/white"
+ android:background="@color/colorPrimary"
android:clickable="true"
android:focusable="true"
android:focusableInTouchMode="true"
diff --git a/app/src/main/res/navigation/nav_graph.xml b/app/src/main/res/navigation/nav_graph.xml
index 1277d8f5e..010e80635 100644
--- a/app/src/main/res/navigation/nav_graph.xml
+++ b/app/src/main/res/navigation/nav_graph.xml
@@ -17,5 +17,12 @@
+ android:label="fragment_browser" tools:layout="@layout/fragment_browser">
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml
index e388ee961..d149abde3 100644
--- a/app/src/main/res/values/colors.xml
+++ b/app/src/main/res/values/colors.xml
@@ -9,4 +9,6 @@
#331b215e
#0C0C0D
+ #f9f9fa
+ #0C0C0D
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index b2ed155d7..9e73d62c3 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -6,4 +6,13 @@
Fenix
Search the Web or enter address
+ Share with…
+ Help
+ Settings
+ Your Library
+ Desktop Site
+ Find in Page
+ Private Tab
+ New Tab
+ Report Issue
diff --git a/buildSrc/src/main/java/Dependencies.kt b/buildSrc/src/main/java/Dependencies.kt
index 613dd60f9..c2471a89a 100644
--- a/buildSrc/src/main/java/Dependencies.kt
+++ b/buildSrc/src/main/java/Dependencies.kt
@@ -33,18 +33,32 @@ object Deps {
const val androidx_constraintlayout = "androidx.constraintlayout:constraintlayout:${Versions.androidx_constraint_layout}"
const val mozilla_concept_engine = "org.mozilla.components:concept-engine:${Versions.mozilla_android_components}"
+ const val mozilla_concept_tabstray = "org.mozilla.components:concept-tabstray:${Versions.mozilla_android_components}"
+ const val mozilla_concept_toolbar = "org.mozilla.components:concept-toolbar:${Versions.mozilla_android_components}"
const val mozilla_concept_storage = "org.mozilla.components:concept-storage:${Versions.mozilla_android_components}"
const val mozilla_browser_awesomebar = "org.mozilla.components:browser-awesomebar:${Versions.mozilla_android_components}"
- const val mozilla_browser_domains = "org.mozilla.components:browser-domains:${Versions.mozilla_android_components}"
const val mozilla_browser_engine_gecko_nightly = "org.mozilla.components:browser-engine-gecko-nightly:${Versions.mozilla_android_components}"
+ const val mozilla_browser_domains = "org.mozilla.components:browser-domains:${Versions.mozilla_android_components}"
+ const val mozilla_browser_search = "org.mozilla.components:browser-search:${Versions.mozilla_android_components}"
const val mozilla_browser_session = "org.mozilla.components:browser-session:${Versions.mozilla_android_components}"
+ const val mozilla_browser_tabstray = "org.mozilla.components:browser-tabstray:${Versions.mozilla_android_components}"
const val mozilla_browser_toolbar = "org.mozilla.components:browser-toolbar:${Versions.mozilla_android_components}"
+ const val mozilla_browser_menu = "org.mozilla.components:browser-menu:${Versions.mozilla_android_components}"
+ const val mozilla_browser_errorpages = "org.mozilla.components:browser-errorpages:${Versions.mozilla_android_components}"
+ const val mozilla_browser_storage_sync = "org.mozilla.components:browser-storage-sync:${Versions.mozilla_android_components}"
const val mozilla_feature_awesomebar = "org.mozilla.components:feature-awesomebar:${Versions.mozilla_android_components}"
+ const val mozilla_feature_contextmenu = "org.mozilla.components:feature-contextmenu:${Versions.mozilla_android_components}"
+ const val mozilla_feature_customtabs = "org.mozilla.components:feature-customtabs:${Versions.mozilla_android_components}"
const val mozilla_feature_intent = "org.mozilla.components:feature-intent:${Versions.mozilla_android_components}"
+ const val mozilla_feature_search = "org.mozilla.components:feature-search:${Versions.mozilla_android_components}"
const val mozilla_feature_session = "org.mozilla.components:feature-session:${Versions.mozilla_android_components}"
+ const val mozilla_feature_sync = "org.mozilla.components:feature-sync:${Versions.mozilla_android_components}"
+ const val mozilla_feature_tabs = "org.mozilla.components:feature-tabs:${Versions.mozilla_android_components}"
+ const val mozilla_feature_downloads = "org.mozilla.components:feature-downloads:${Versions.mozilla_android_components}"
const val mozilla_feature_storage = "org.mozilla.components:feature-storage:${Versions.mozilla_android_components}"
+ const val mozilla_feature_prompts = "org.mozilla.components:feature-prompts:${Versions.mozilla_android_components}"
const val mozilla_feature_toolbar = "org.mozilla.components:feature-toolbar:${Versions.mozilla_android_components}"
const val mozilla_support_ktx = "org.mozilla.components:support-ktx:${Versions.mozilla_android_components}"