From e961a9d63da6f7af7aae27aafe9bb0753a350ec9 Mon Sep 17 00:00:00 2001 From: Sawyer Blatz Date: Mon, 16 Sep 2019 08:58:19 -0700 Subject: [PATCH] For #1744: Adds clipboard provider to search engine screen --- .../org/mozilla/fenix/components/Utilities.kt | 5 ++ .../components/toolbar/BrowserToolbarView.kt | 24 +++----- .../mozilla/fenix/search/SearchFragment.kt | 25 +++++++- .../org/mozilla/fenix/search/SearchLayouts.kt | 4 +- .../fenix/search/awesomebar/AwesomeBarView.kt | 13 ---- .../mozilla/fenix/utils/ClipboardHandler.kt | 44 +++++++++++++ app/src/main/res/layout/fragment_search.xml | 61 ++++++++++++++++++- app/src/main/res/values/styles.xml | 5 ++ 8 files changed, 148 insertions(+), 33 deletions(-) create mode 100644 app/src/main/java/org/mozilla/fenix/utils/ClipboardHandler.kt diff --git a/app/src/main/java/org/mozilla/fenix/components/Utilities.kt b/app/src/main/java/org/mozilla/fenix/components/Utilities.kt index a6ecfb2e7..20f46417a 100644 --- a/app/src/main/java/org/mozilla/fenix/components/Utilities.kt +++ b/app/src/main/java/org/mozilla/fenix/components/Utilities.kt @@ -11,6 +11,7 @@ import mozilla.components.feature.intent.processing.TabIntentProcessor import mozilla.components.feature.search.SearchUseCases import mozilla.components.feature.session.SessionUseCases import org.mozilla.fenix.test.Mockable +import org.mozilla.fenix.utils.ClipboardHandler /** * Component group for miscellaneous components. @@ -39,4 +40,8 @@ class Utilities( val customTabIntentProcessor by lazy { CustomTabIntentProcessor(sessionManager, sessionUseCases.loadUrl, context.resources) } + + val clipboardHandler by lazy { + ClipboardHandler(context) + } } diff --git a/app/src/main/java/org/mozilla/fenix/components/toolbar/BrowserToolbarView.kt b/app/src/main/java/org/mozilla/fenix/components/toolbar/BrowserToolbarView.kt index 8ac9c4802..0fd81157e 100644 --- a/app/src/main/java/org/mozilla/fenix/components/toolbar/BrowserToolbarView.kt +++ b/app/src/main/java/org/mozilla/fenix/components/toolbar/BrowserToolbarView.kt @@ -1,9 +1,9 @@ +/* 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.ClipData -import android.content.ClipDescription.MIMETYPE_TEXT_PLAIN -import android.content.ClipboardManager -import android.content.Context.CLIPBOARD_SERVICE import android.view.Gravity import android.view.LayoutInflater import android.view.View @@ -54,7 +54,7 @@ class BrowserToolbarView( val isCustomTabSession = customTabSession != null view.setOnUrlLongClickListener { - val clipboard = view.context.getSystemService(CLIPBOARD_SERVICE) as ClipboardManager + val clipboard = view.context.components.utils.clipboardHandler val customView = LayoutInflater.from(view.context).inflate(R.layout.browser_toolbar_popup_window, null) val popupWindow = PopupWindow(customView, LinearLayout.LayoutParams.WRAP_CONTENT, @@ -64,22 +64,22 @@ class BrowserToolbarView( popupWindow.elevation = view.context.dimen(R.dimen.mozac_browser_menu_elevation).toFloat() - customView.paste.isVisible = clipboard.containsText() && !isCustomTabSession - customView.paste_and_go.isVisible = clipboard.containsText() && !isCustomTabSession + customView.paste.isVisible = !clipboard.url.isNullOrEmpty() && !isCustomTabSession + customView.paste_and_go.isVisible = !clipboard.url.isNullOrEmpty() && !isCustomTabSession customView.copy.setOnClickListener { popupWindow.dismiss() - clipboard.primaryClip = ClipData.newPlainText("Text", view.url.toString()) + clipboard.text = view.url.toString() } customView.paste.setOnClickListener { popupWindow.dismiss() - interactor.onBrowserToolbarPaste(clipboard.primaryClip?.getItemAt(0)?.text.toString()) + interactor.onBrowserToolbarPaste(clipboard.text!!) } customView.paste_and_go.setOnClickListener { popupWindow.dismiss() - interactor.onBrowserToolbarPasteAndGo(clipboard.primaryClip?.getItemAt(0)?.text.toString()) + interactor.onBrowserToolbarPasteAndGo(clipboard.text!!) } popupWindow.showAsDropDown(view, view.context.dimen(R.dimen.context_menu_x_offset), 0, Gravity.START) @@ -162,10 +162,6 @@ class BrowserToolbarView( // Intentionally leaving this as a stub for now since we don't actually want to update currently } - private fun ClipboardManager.containsText(): Boolean { - return (primaryClipDescription?.hasMimeType(MIMETYPE_TEXT_PLAIN) ?: false && primaryClip?.itemCount != 0) - } - companion object { private const val TOOLBAR_ELEVATION = 16 private const val PROGRESS_BOTTOM = 0 diff --git a/app/src/main/java/org/mozilla/fenix/search/SearchFragment.kt b/app/src/main/java/org/mozilla/fenix/search/SearchFragment.kt index 11174b269..88713747c 100644 --- a/app/src/main/java/org/mozilla/fenix/search/SearchFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/search/SearchFragment.kt @@ -37,6 +37,7 @@ import org.mozilla.fenix.HomeActivity import org.mozilla.fenix.R import org.mozilla.fenix.components.StoreProvider import org.mozilla.fenix.components.metrics.Event +import org.mozilla.fenix.ext.components import org.mozilla.fenix.ext.getColorFromAttr import org.mozilla.fenix.ext.getSpannable import org.mozilla.fenix.ext.requireComponents @@ -116,6 +117,7 @@ class SearchFragment : Fragment(), BackHandler { ) awesomeBarView = AwesomeBarView(view.search_layout, searchInteractor) + toolbarView = ToolbarView( view.toolbar_component_wrapper, searchInteractor, @@ -129,6 +131,7 @@ class SearchFragment : Fragment(), BackHandler { @ObsoleteCoroutinesApi @ExperimentalCoroutinesApi + @SuppressWarnings("LongMethod") override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) @@ -198,12 +201,22 @@ class SearchFragment : Fragment(), BackHandler { searchInteractor.turnOnStartedTyping() } + fill_link_from_clipboard.setOnClickListener { + (activity as HomeActivity) + .openToBrowserAndLoad( + searchTermOrURL = requireContext().components.utils.clipboardHandler.url ?: "", + newTab = searchStore.state.session == null, + from = BrowserDirection.FromSearch + ) + } + consumeFrom(searchStore) { awesomeBarView.update(it) toolbarView.update(it) updateSearchEngineIcon(it) updateSearchShortuctsIcon(it) updateSearchWithLabel(it) + updateClipboardSuggestion(it, requireContext().components.utils.clipboardHandler.url) } startPostponedEnterTransition() @@ -231,6 +244,8 @@ class SearchFragment : Fragment(), BackHandler { toolbarView.view.requestFocus() } + updateClipboardSuggestion(searchStore.state, requireContext().components.utils.clipboardHandler.url) + permissionDidUpdate = false (activity as AppCompatActivity).supportActionBar?.hide() } @@ -260,10 +275,18 @@ class SearchFragment : Fragment(), BackHandler { } private fun updateSearchWithLabel(searchState: SearchFragmentState) { - searchWithShortcuts.visibility = + search_with_shortcuts.visibility = if (searchState.showShortcutEnginePicker) View.VISIBLE else View.GONE } + private fun updateClipboardSuggestion(searchState: SearchFragmentState, clipboardUrl: String?) { + fill_link_from_clipboard.visibility = + if (searchState.showClipboardSuggestions && searchState.query.isEmpty() && !clipboardUrl.isNullOrEmpty()) + View.VISIBLE else View.GONE + + clipboard_url.text = clipboardUrl + } + private fun updateSearchShortuctsIcon(searchState: SearchFragmentState) { with(requireContext()) { val showShortcuts = searchState.showShortcutEnginePicker diff --git a/app/src/main/java/org/mozilla/fenix/search/SearchLayouts.kt b/app/src/main/java/org/mozilla/fenix/search/SearchLayouts.kt index e82bdbfe4..c018f39d7 100644 --- a/app/src/main/java/org/mozilla/fenix/search/SearchLayouts.kt +++ b/app/src/main/java/org/mozilla/fenix/search/SearchLayouts.kt @@ -63,7 +63,7 @@ internal fun SearchFragment.setOutOfExperimentConstraints(layout: ConstraintLayo BOTTOM to TOP of UNSET ) } - searchWithShortcuts { + fill_link_from_clipboard { connect( TOP to BOTTOM of toolbar_wrapper ) @@ -71,7 +71,7 @@ internal fun SearchFragment.setOutOfExperimentConstraints(layout: ConstraintLayo awesomeBar { connect( TOP to TOP of UNSET, - TOP to BOTTOM of searchWithShortcuts, + TOP to BOTTOM of search_with_shortcuts, BOTTOM to TOP of pillWrapper ) } diff --git a/app/src/main/java/org/mozilla/fenix/search/awesomebar/AwesomeBarView.kt b/app/src/main/java/org/mozilla/fenix/search/awesomebar/AwesomeBarView.kt index 1c3448136..bb59f7e69 100644 --- a/app/src/main/java/org/mozilla/fenix/search/awesomebar/AwesomeBarView.kt +++ b/app/src/main/java/org/mozilla/fenix/search/awesomebar/AwesomeBarView.kt @@ -16,7 +16,6 @@ import mozilla.components.browser.search.SearchEngine import mozilla.components.browser.session.Session import mozilla.components.concept.engine.EngineSession import mozilla.components.feature.awesomebar.provider.BookmarksStorageSuggestionProvider -import mozilla.components.feature.awesomebar.provider.ClipboardSuggestionProvider import mozilla.components.feature.awesomebar.provider.HistoryStorageSuggestionProvider import mozilla.components.feature.awesomebar.provider.SearchSuggestionProvider import mozilla.components.feature.awesomebar.provider.SessionSuggestionProvider @@ -80,7 +79,6 @@ class AwesomeBarView( override val containerView: View? get() = container - private val clipboardSuggestionProvider: ClipboardSuggestionProvider private val sessionProvider: SessionSuggestionProvider private val historyStorageProvider: HistoryStorageSuggestionProvider private val shortcutsEnginePickerProvider: ShortcutsSuggestionProvider @@ -118,13 +116,6 @@ class AwesomeBarView( val draw = getDrawable(R.drawable.ic_link)!! draw.setColorFilter(primaryTextColor, SRC_IN) - clipboardSuggestionProvider = ClipboardSuggestionProvider( - this, - loadUrlUseCase, - draw.toBitmap(), - getString(R.string.awesomebar_clipboard_title) - ) - sessionProvider = SessionSuggestionProvider( components.core.sessionManager, @@ -189,10 +180,6 @@ class AwesomeBarView( ) } - if (state.showClipboardSuggestions) { - view.addProviders(clipboardSuggestionProvider) - } - if (state.showHistorySuggestions) { view.addProviders(historyStorageProvider) } diff --git a/app/src/main/java/org/mozilla/fenix/utils/ClipboardHandler.kt b/app/src/main/java/org/mozilla/fenix/utils/ClipboardHandler.kt new file mode 100644 index 000000000..9078efd59 --- /dev/null +++ b/app/src/main/java/org/mozilla/fenix/utils/ClipboardHandler.kt @@ -0,0 +1,44 @@ +/* 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.utils + +import android.content.ClipData +import android.content.ClipboardManager +import android.content.Context +import mozilla.components.support.utils.WebURLFinder + +private const val MIME_TYPE_TEXT_PLAIN = "text/plain" + +class ClipboardHandler(context: Context) { + private val clipboard = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager + + var text: String? + get() { + if (clipboard.isPrimaryClipEmpty() || + !clipboard.isPrimaryClipPlainText()) { + return null + } + + return clipboard.firstPrimaryClipItem?.text.toString() + } + + set(value) { + clipboard.primaryClip = ClipData.newPlainText("Text", value) + } + + val url: String? + get() { + val finder = WebURLFinder(text) + return finder.bestWebURL() + } + + private fun ClipboardManager.isPrimaryClipPlainText() = + primaryClipDescription?.hasMimeType(MIME_TYPE_TEXT_PLAIN) ?: false + + private fun ClipboardManager.isPrimaryClipEmpty() = primaryClip?.itemCount == 0 + + private val ClipboardManager.firstPrimaryClipItem: ClipData.Item? + get() = primaryClip?.getItemAt(0) +} diff --git a/app/src/main/res/layout/fragment_search.xml b/app/src/main/res/layout/fragment_search.xml index 7b49ff27a..e4795b071 100644 --- a/app/src/main/res/layout/fragment_search.xml +++ b/app/src/main/res/layout/fragment_search.xml @@ -43,11 +43,66 @@ + + + + + + + + + + app:layout_constraintTop_toBottomOf="@id/fill_link_from_clipboard" /> 14sp + +