From 5a3fc1325f5ef7d5c6792d4934c12db6b44077b1 Mon Sep 17 00:00:00 2001 From: Emily Kager Date: Wed, 23 Jan 2019 14:48:15 -0800 Subject: [PATCH] Adds toolbar with autocomplete and menu to BrowserFragment --- app/build.gradle | 3 +- .../mozilla/fenix/browser/BrowserFragment.kt | 12 +- .../mozilla/fenix/components/Components.kt | 8 ++ .../org/mozilla/fenix/components/Search.kt | 4 +- .../org/mozilla/fenix/components/UseCases.kt | 18 ++- .../fenix/components/toolbar/Toolbar.kt | 106 ++++++++++++++++++ .../components/toolbar/ToolbarIntegration.kt | 28 +++++ .../java/org/mozilla/fenix/ext/Context.kt | 50 ++++++++- .../java/org/mozilla/fenix/ext/Fragment.kt | 10 +- app/src/main/res/drawable/ic_home.xml | 15 +++ app/src/main/res/layout/fragment_browser.xml | 2 +- app/src/main/res/navigation/nav_graph.xml | 9 +- app/src/main/res/values/colors.xml | 2 + app/src/main/res/values/strings.xml | 9 ++ buildSrc/src/main/java/Dependencies.kt | 16 ++- 15 files changed, 274 insertions(+), 18 deletions(-) create mode 100644 app/src/main/java/org/mozilla/fenix/components/toolbar/Toolbar.kt create mode 100644 app/src/main/res/drawable/ic_home.xml 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}"