Skip to content

Commit

Permalink
For mozilla-mobile#22271 Improve URL accessing from the clipboard for…
Browse files Browse the repository at this point in the history
… Android 12 and above.
  • Loading branch information
Amejia481 committed Nov 9, 2021
1 parent c026d8b commit 3acd292
Show file tree
Hide file tree
Showing 7 changed files with 74 additions and 39 deletions.
59 changes: 38 additions & 21 deletions app/src/main/java/org/mozilla/fenix/search/SearchDialogFragment.kt
Original file line number Diff line number Diff line change
Expand Up @@ -290,12 +290,25 @@ class SearchDialogFragment : AppCompatDialogFragment(), UserInteractionHandler {
requireComponents.analytics.metrics.track(Event.ClipboardSuggestionClicked)
view.hideKeyboard()
toolbarView.view.clearFocus()
(activity as HomeActivity)
.openToBrowserAndLoad(
searchTermOrURL = requireContext().components.clipboardHandler.url ?: "",
newTab = store.state.tabId == null,
from = BrowserDirection.FromSearchDialog
)
val clipboardUrl = requireContext().components.clipboardHandler.extractURL() ?: ""

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
toolbarView.view.edit.updateUrl(clipboardUrl)
toolbarView.view.edit.focus()
binding.fillLinkFromClipboard.isVisible = false
binding.fillLinkDivider.isVisible = false
binding.pillWrapperDivider.isVisible = false
binding.clipboardUrl.isVisible = false
binding.clipboardTitle.isVisible = false
binding.linkIcon.isVisible = false
} else {
(activity as HomeActivity)
.openToBrowserAndLoad(
searchTermOrURL = clipboardUrl,
newTab = store.state.tabId == null,
from = BrowserDirection.FromSearchDialog
)
}
}

val stubListener = ViewStub.OnInflateListener { _, inflated ->
Expand Down Expand Up @@ -389,12 +402,12 @@ class SearchDialogFragment : AppCompatDialogFragment(), UserInteractionHandler {
flow.map { state ->
val shouldShowView = state.showClipboardSuggestions &&
state.query.isEmpty() &&
!state.clipboardUrl.isNullOrEmpty() && !state.showSearchShortcuts
Pair(shouldShowView, state.clipboardUrl)
state.clipboardHasUrl && !state.showSearchShortcuts
Pair(shouldShowView, state.clipboardHasUrl)
}
.ifChanged()
.collect { (shouldShowView, clipboardUrl) ->
updateClipboardSuggestion(shouldShowView, clipboardUrl)
.collect { (shouldShowView) ->
updateClipboardSuggestion(shouldShowView)
}
}

Expand All @@ -418,9 +431,8 @@ class SearchDialogFragment : AppCompatDialogFragment(), UserInteractionHandler {
// We delay querying the clipboard by posting this code to the main thread message queue,
// because ClipboardManager will return null if the does app not have input focus yet.
lifecycleScope.launch(Dispatchers.Cached) {
context?.components?.clipboardHandler?.url?.let { clipboardUrl ->
store.dispatch(SearchFragmentAction.UpdateClipboardUrl(clipboardUrl))
}
val hasUrl = context?.components?.clipboardHandler?.containsURL() ?: false
store.dispatch(SearchFragmentAction.UpdateClipboardHasUrl(hasUrl))
}
}
}
Expand Down Expand Up @@ -655,25 +667,30 @@ class SearchDialogFragment : AppCompatDialogFragment(), UserInteractionHandler {
private fun isSpeechAvailable(): Boolean = speechIntent.resolveActivity(requireContext().packageManager) != null

private fun updateClipboardSuggestion(
shouldShowView: Boolean,
clipboardUrl: String?
shouldShowView: Boolean
) {
binding.fillLinkFromClipboard.isVisible = shouldShowView
binding.fillLinkDivider.isVisible = shouldShowView
binding.pillWrapperDivider.isVisible =
!(shouldShowView && requireComponents.settings.shouldUseBottomToolbar)
binding.clipboardUrl.isVisible = shouldShowView
binding.clipboardTitle.isVisible = shouldShowView
binding.linkIcon.isVisible = shouldShowView

binding.clipboardUrl.text = clipboardUrl

binding.fillLinkFromClipboard.contentDescription =
"${binding.clipboardTitle.text}, ${binding.clipboardUrl.text}."
val contentDescription = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
"${binding.clipboardTitle.text}."
} else {
val clipboardUrl = context?.components?.clipboardHandler?.extractURL()

if (clipboardUrl != null && !((activity as HomeActivity).browsingModeManager.mode.isPrivate)) {
requireComponents.core.engine.speculativeConnect(clipboardUrl)
if (clipboardUrl != null && !((activity as HomeActivity).browsingModeManager.mode.isPrivate)) {
requireComponents.core.engine.speculativeConnect(clipboardUrl)
}
binding.clipboardUrl.text = clipboardUrl
binding.clipboardUrl.isVisible = shouldShowView
"${binding.clipboardTitle.text}, ${binding.clipboardUrl.text}."
}

binding.fillLinkFromClipboard.contentDescription = contentDescription
}

private fun updateToolbarContentDescription(source: SearchEngineSource) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ sealed class SearchEngineSource {
* @property showHistorySuggestions Whether or not to show history suggestions in the AwesomeBar
* @property showBookmarkSuggestions Whether or not to show the bookmark suggestion in the AwesomeBar
* @property pastedText The text pasted from the long press toolbar menu
* @property clipboardUrl The URL in the clipboard of the user - if there's any; otherwise `null`.
* @property clipboardHasUrl The URL in the clipboard of the user - if there's any; otherwise `null`.
*/
data class SearchFragmentState(
val query: String,
Expand All @@ -81,7 +81,7 @@ data class SearchFragmentState(
val tabId: String?,
val pastedText: String? = null,
val searchAccessPoint: Event.PerformedSearch.SearchAccessPoint?,
val clipboardUrl: String? = null
val clipboardHasUrl: Boolean = false
) : State

fun createInitialSearchFragmentState(
Expand Down Expand Up @@ -131,7 +131,7 @@ sealed class SearchFragmentAction : Action {
data class ShowSearchShortcutEnginePicker(val show: Boolean) : SearchFragmentAction()
data class AllowSearchSuggestionsInPrivateModePrompt(val show: Boolean) : SearchFragmentAction()
data class UpdateQuery(val query: String) : SearchFragmentAction()
data class UpdateClipboardUrl(val url: String?) : SearchFragmentAction()
data class UpdateClipboardHasUrl(val hasUrl: Boolean) : SearchFragmentAction()

/**
* Updates the local `SearchFragmentState` from the global `SearchState` in `BrowserStore`.
Expand Down Expand Up @@ -172,9 +172,9 @@ private fun searchStateReducer(state: SearchFragmentState, action: SearchFragmen
}
)
}
is SearchFragmentAction.UpdateClipboardUrl -> {
is SearchFragmentAction.UpdateClipboardHasUrl -> {
state.copy(
clipboardUrl = action.url
clipboardHasUrl = action.hasUrl
)
}
}
Expand Down
24 changes: 18 additions & 6 deletions app/src/main/java/org/mozilla/fenix/utils/ClipboardHandler.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ package org.mozilla.fenix.utils
import android.content.ClipData
import android.content.ClipboardManager
import android.content.Context
import android.os.Build
import android.view.textclassifier.TextClassifier
import androidx.annotation.VisibleForTesting
import androidx.core.content.getSystemService
import mozilla.components.support.utils.SafeUrl
Expand Down Expand Up @@ -37,13 +39,23 @@ class ClipboardHandler(val context: Context) {
clipboard.setPrimaryClip(ClipData.newPlainText("Text", value))
}

val url: String?
get() {
return text?.let {
val finder = WebURLFinder(it)
finder.bestWebURL()
}
fun extractURL(): String? {
return text?.let{
val finder = WebURLFinder(it)
finder.bestWebURL()
}
}

@Suppress("MagicNumber")
internal fun containsURL(): Boolean {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
val description = clipboard.primaryClipDescription
val score = description?.getConfidenceScore(TextClassifier.TYPE_URL) ?: 0F
score >= 0.7F
} else {
!extractURL().isNullOrEmpty()
}
}

private fun ClipboardManager.isPrimaryClipPlainText() =
primaryClipDescription?.hasMimeType(MIME_TYPE_TEXT_PLAIN) ?: false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ object ToolbarPopupWindow {
) {
val context = view.get()?.context ?: return
val clipboard = context.components.clipboardHandler
if (!copyVisible && clipboard.text.isNullOrEmpty()) return
if (!copyVisible && !clipboard.containsURL()) return

val isCustomTabSession = customTabId != null

Expand All @@ -54,9 +54,9 @@ object ToolbarPopupWindow {

binding.copy.isVisible = copyVisible

binding.paste.isVisible = !clipboard.text.isNullOrEmpty() && !isCustomTabSession
binding.paste.isVisible = clipboard.containsURL() && !isCustomTabSession
binding.pasteAndGo.isVisible =
!clipboard.text.isNullOrEmpty() && !isCustomTabSession
clipboard.containsURL() && !isCustomTabSession

binding.copy.setOnClickListener {
popupWindow.dismiss()
Expand Down
1 change: 0 additions & 1 deletion app/src/main/res/layout/fragment_search_dialog.xml
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,6 @@
android:clickable="false"
android:focusable="false"
android:importantForAccessibility="no"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="@+id/fill_link_from_clipboard"
app:layout_constraintStart_toStartOf="@+id/fill_link_from_clipboard"
app:layout_constraintTop_toTopOf="@+id/fill_link_from_clipboard"
Expand Down
7 changes: 7 additions & 0 deletions app/src/main/res/values-v31/dimens.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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/. -->
<resources xmlns:tools="http://schemas.android.com/tools">
<dimen name="search_fragment_clipboard_item_height">49dp</dimen>
</resources>
Original file line number Diff line number Diff line change
Expand Up @@ -203,15 +203,15 @@ class SearchFragmentStoreTest {
val initialState = emptyDefaultState()
val store = SearchFragmentStore(initialState)

assertNull(store.state.clipboardUrl)
assertNull(store.state.clipboardHasUrl)

store.dispatch(
SearchFragmentAction.UpdateClipboardUrl("https://www.mozilla.org")
SearchFragmentAction.UpdateClipboardHasUrl("https://www.mozilla.org")
).joinBlocking()

assertEquals(
"https://www.mozilla.org",
store.state.clipboardUrl
store.state.clipboardHasUrl
)
}

Expand Down

0 comments on commit 3acd292

Please sign in to comment.