1
0
Fork 0

For #1744: Adds clipboard provider to search engine screen

master
Sawyer Blatz 2019-09-16 08:58:19 -07:00 committed by Emily Kager
parent 5a4b7aec00
commit e961a9d63d
8 changed files with 148 additions and 33 deletions

View File

@ -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)
}
}

View File

@ -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

View File

@ -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

View File

@ -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
)
}

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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"

View File

@ -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>