For #1744: Adds clipboard provider to search engine screen
parent
5a4b7aec00
commit
e961a9d63d
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
|
@ -43,11 +43,66 @@
|
|||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/fill_link_from_clipboard"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="56dp"
|
||||
android:focusable="true"
|
||||
android:clickable="true"
|
||||
android:background="?selectableItemBackground"
|
||||
android:paddingStart="20dp"
|
||||
android:paddingEnd="8dp"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintEnd_toEndOf="@id/toolbar_wrapper"
|
||||
app:layout_constraintTop_toBottomOf="@id/toolbar_wrapper"
|
||||
app:layout_constraintStart_toStartOf="@id/toolbar_wrapper">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/link_icon"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:tint="?primaryText"
|
||||
android:src="@drawable/ic_link"
|
||||
android:importantForAccessibility="no"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/clipboard_title"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:textSize="15sp"
|
||||
android:textColor="?primaryText"
|
||||
android:maxLines="1"
|
||||
android:ellipsize="end"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toTopOf="@id/clipboard_url"
|
||||
app:layout_constraintStart_toEndOf="@id/link_icon"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintVertical_chainStyle="packed"
|
||||
android:text="@string/awesomebar_clipboard_title"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/clipboard_url"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="?accent"
|
||||
android:textAppearance="@style/Subtitle12TextStyle"
|
||||
android:maxLines="1"
|
||||
android:ellipsize="end"
|
||||
app:layout_constraintTop_toBottomOf="@id/clipboard_title"
|
||||
app:layout_constraintStart_toStartOf="@id/clipboard_title"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintVertical_chainStyle="packed"/>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/searchWithShortcuts"
|
||||
android:id="@+id/search_with_shortcuts"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="15dp"
|
||||
android:fontFamily="Inter UI"
|
||||
android:letterSpacing="0.15"
|
||||
android:text="@string/search_shortcuts_search_with"
|
||||
|
@ -57,7 +112,7 @@
|
|||
android:textStyle="bold"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintStart_toStartOf="@id/toolbar_wrapper"
|
||||
app:layout_constraintTop_toBottomOf="@id/toolbar_wrapper" />
|
||||
app:layout_constraintTop_toBottomOf="@id/fill_link_from_clipboard" />
|
||||
|
||||
<View
|
||||
android:id="@+id/search_divider"
|
||||
|
|
|
@ -258,6 +258,11 @@
|
|||
<item name="android:textSize">14sp</item>
|
||||
</style>
|
||||
|
||||
<style name="Subtitle12TextStyle" parent="TextAppearance.MaterialComponents.Body1">
|
||||
<item name="android:textColor">?secondaryText</item>
|
||||
<item name="android:textSize">12sp</item>
|
||||
</style>
|
||||
|
||||
<style name="ToolbarTitleTextStyle" parent="HeaderTextStyle">
|
||||
<item name="android:textSize">20sp</item>
|
||||
</style>
|
||||
|
|
Loading…
Reference in New Issue