1
0
Fork 0

For #4155: Adds popup menu for browser toolbar

master
Sawyer Blatz 2019-09-04 13:56:39 -07:00
parent 017fb625dd
commit fdd2b59fb8
13 changed files with 152 additions and 16 deletions

View File

@ -17,6 +17,14 @@ open class BrowserToolbarInteractor(
private val browserToolbarController: BrowserToolbarController
) : BrowserToolbarViewInteractor {
override fun onBrowserToolbarPaste(text: String) {
browserToolbarController.handleToolbarPaste(text)
}
override fun onBrowserToolbarPasteAndGo(text: String) {
browserToolbarController.handleToolbarPasteAndGo(text)
}
override fun onBrowserToolbarClicked() {
browserToolbarController.handleToolbarClick()
}

View File

@ -34,6 +34,8 @@ import org.mozilla.fenix.quickactionsheet.QuickActionSheetBehavior
* An interface that handles the view manipulation of the BrowserToolbar, triggered by the Interactor
*/
interface BrowserToolbarController {
fun handleToolbarPaste(text: String)
fun handleToolbarPasteAndGo(text: String)
fun handleToolbarItemInteraction(item: ToolbarMenu.Item)
fun handleToolbarClick()
}
@ -51,6 +53,21 @@ class DefaultBrowserToolbarController(
private val bottomSheetBehavior: QuickActionSheetBehavior<NestedScrollView>
) : BrowserToolbarController {
override fun handleToolbarPaste(text: String) {
navController.nav(
R.id.browserFragment,
BrowserFragmentDirections.actionBrowserFragmentToSearchFragment(
sessionId = customTabSession?.id ?: context.components.core.sessionManager.selectedSession?.id,
pastedText = text
)
)
}
override fun handleToolbarPasteAndGo(text: String) {
context.components.core.sessionManager.selectedSession?.searchTerms = ""
context.components.useCases.sessionUseCases.loadUrl(text)
}
override fun handleToolbarClick() {
context.components.analytics.metrics.track(
Event.SearchBarTapped(Event.SearchBarTapped.Source.BROWSER)
@ -58,7 +75,8 @@ class DefaultBrowserToolbarController(
navController.nav(
R.id.browserFragment,
BrowserFragmentDirections.actionBrowserFragmentToSearchFragment(
customTabSession?.id ?: context.components.core.sessionManager.selectedSession?.id
customTabSession?.id ?: context.components.core.sessionManager.selectedSession?.id,
pastedText = null
)
)
}
@ -101,12 +119,18 @@ class DefaultBrowserToolbarController(
}
}
ToolbarMenu.Item.NewTab -> {
val directions = BrowserFragmentDirections.actionBrowserFragmentToSearchFragment(null)
val directions = BrowserFragmentDirections.actionBrowserFragmentToSearchFragment(
sessionId = null,
pastedText = null
)
navController.nav(R.id.browserFragment, directions)
browsingModeManager.mode = BrowsingMode.Normal
}
ToolbarMenu.Item.NewPrivateTab -> {
val directions = BrowserFragmentDirections.actionBrowserFragmentToSearchFragment(null)
val directions = BrowserFragmentDirections.actionBrowserFragmentToSearchFragment(
sessionId = null,
pastedText = null
)
navController.nav(R.id.browserFragment, directions)
browsingModeManager.mode = BrowsingMode.Private
}

View File

@ -1,8 +1,13 @@
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.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.PopupMenu
import androidx.core.content.ContextCompat
import kotlinx.android.extensions.LayoutContainer
import mozilla.components.browser.domains.autocomplete.ShippedDomainsProvider
@ -11,11 +16,13 @@ import mozilla.components.browser.toolbar.BrowserToolbar
import mozilla.components.support.ktx.android.util.dpToFloat
import mozilla.components.support.ktx.android.util.dpToPx
import org.mozilla.fenix.R
import org.mozilla.fenix.theme.ThemeManager
import org.mozilla.fenix.customtabs.CustomTabToolbarMenu
import org.mozilla.fenix.ext.components
import org.mozilla.fenix.theme.ThemeManager
interface BrowserToolbarViewInteractor {
fun onBrowserToolbarPaste(text: String)
fun onBrowserToolbarPasteAndGo(text: String)
fun onBrowserToolbarClicked()
fun onBrowserToolbarMenuItemTapped(item: ToolbarMenu.Item)
}
@ -39,6 +46,36 @@ class BrowserToolbarView(
val toolbarIntegration: ToolbarIntegration
init {
view.setOnUrlLongClickListener {
val popup = PopupMenu(view.context, view)
popup.menuInflater.inflate(R.menu.browser_toolbar_popup_menu, popup.menu)
popup.show()
val clipboard = view.context.getSystemService(CLIPBOARD_SERVICE) as ClipboardManager
popup.menu.findItem(R.id.paste)?.isVisible = clipboard.containsText()
popup.menu.findItem(R.id.paste_and_go)?.isVisible = clipboard.containsText()
popup.setOnMenuItemClickListener {
when (it.itemId) {
R.id.copy -> {
clipboard.primaryClip = ClipData.newPlainText("Text", view.url.toString())
}
R.id.paste -> {
interactor.onBrowserToolbarPaste(clipboard.primaryClip?.getItemAt(0)?.text.toString())
}
R.id.paste_and_go -> {
interactor.onBrowserToolbarPasteAndGo(clipboard.primaryClip?.getItemAt(0)?.text.toString())
}
}
true
}
true
}
with(container.context) {
val sessionManager = components.core.sessionManager
val isCustomTabSession = customTabSession != null
@ -115,6 +152,10 @@ 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

@ -233,7 +233,11 @@ class HomeFragment : Fragment(), AccountObserver {
view.toolbar_wrapper.setOnClickListener {
invokePendingDeleteJobs()
onboarding.finish()
val directions = HomeFragmentDirections.actionHomeFragmentToSearchFragment(null, true)
val directions = HomeFragmentDirections.actionHomeFragmentToSearchFragment(
sessionId = null,
showShortcutEnginePicker = true,
pastedText = null
)
val extras =
FragmentNavigator.Extras.Builder()
.addSharedElement(toolbar_wrapper, "toolbar_wrapper_transition")
@ -384,7 +388,11 @@ class HomeFragment : Fragment(), AccountObserver {
}
is TabAction.Add -> {
invokePendingDeleteJobs()
val directions = HomeFragmentDirections.actionHomeFragmentToSearchFragment(null, true)
val directions = HomeFragmentDirections.actionHomeFragmentToSearchFragment(
sessionId = null,
showShortcutEnginePicker = true,
pastedText = null
)
nav(R.id.homeFragment, directions)
}
is TabAction.ShareTabs -> {

View File

@ -24,7 +24,11 @@ class StartSearchIntentProcessor(
out.putExtra(HomeActivity.OPEN_TO_SEARCH, false)
metrics.track(Event.SearchWidgetNewTabPressed)
val directions = NavGraphDirections.actionGlobalSearch(sessionId = null, showShortcutEnginePicker = true)
val directions = NavGraphDirections.actionGlobalSearch(
sessionId = null,
showShortcutEnginePicker = true,
pastedText = null
)
navController.nav(null, directions)
true
} else {

View File

@ -74,6 +74,10 @@ class SearchFragment : Fragment(), BackHandler {
?.let { it.sessionId }
?.let(requireComponents.core.sessionManager::findSessionById)
val pastedText = arguments
?.let(SearchFragmentArgs.Companion::fromBundle)
?.let { it.pastedText }
val displayShortcutEnginePicker = arguments
?.let(SearchFragmentArgs.Companion::fromBundle)
?.let { it.showShortcutEnginePicker } ?: false
@ -95,7 +99,8 @@ class SearchFragment : Fragment(), BackHandler {
showClipboardSuggestions = requireContext().settings.shouldShowClipboardSuggestions,
showHistorySuggestions = requireContext().settings.shouldShowHistorySuggestions,
showBookmarkSuggestions = requireContext().settings.shouldShowBookmarkSuggestions,
session = session
session = session,
pastedText = pastedText
)
)
}

View File

@ -41,6 +41,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 session The current session if available
* @property pastedText The text pasted from the long press toolbar menu
*/
data class SearchFragmentState(
val query: String,
@ -51,7 +52,8 @@ data class SearchFragmentState(
val showClipboardSuggestions: Boolean,
val showHistorySuggestions: Boolean,
val showBookmarkSuggestions: Boolean,
val session: Session?
val session: Session?,
val pastedText: String? = null
) : State
/**

View File

@ -111,8 +111,14 @@ class ToolbarView(
fun update(searchState: SearchFragmentState) {
if (!isInitialized) {
view.url = searchState.query
view.setSearchTerms(searchState.session?.searchTerms ?: "")
view.url = searchState.pastedText ?: searchState.query
/* Only set the search terms if pasted text is null so that the search term doesn't
overwrite pastedText when view enters `editMode` */
if (searchState.pastedText.isNullOrEmpty()) {
view.setSearchTerms(searchState.session?.searchTerms ?: "")
}
view.editMode()
isInitialized = true
}

View File

@ -0,0 +1,18 @@
<?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/. -->
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/copy"
android:iconTint="?primaryText"
android:title="@string/browser_toolbar_long_press_popup_copy"/>
<item
android:id="@+id/paste"
android:iconTint="?primaryText"
android:title="@string/browser_toolbar_long_press_popup_paste"/>
<item
android:id="@+id/paste_and_go"
android:iconTint="?primaryText"
android:title="@string/browser_toolbar_long_press_popup_paste_and_go"/>
</menu>

View File

@ -106,6 +106,10 @@
android:id="@+id/action_searchFragment_to_searchEngineFragment"
app:destination="@id/searchEngineFragment"
app:popUpTo="@+id/searchFragment" />
<argument
android:name="pastedText"
app:argType="string"
app:nullable="true"/>
</fragment>
<fragment

View File

@ -422,6 +422,7 @@
<!-- Content Description for session item share button -->
<string name="content_description_session_share">Share session</string>
<string name="paste">Paste</string>
<!-- Bookmarks -->
<!-- Content description for bookmarks library menu -->
<string name="bookmark_menu_content_description">Bookmark menu</string>
@ -871,4 +872,12 @@
<string name="add_to_homescreen_cancel">Cancel</string>
<!-- Add button text for the Add to Homescreen dialog -->
<string name="add_to_homescreen_add">Add</string>
<!-- Browser long press popup menu -->
<!-- Copy the current url -->
<string name="browser_toolbar_long_press_popup_copy">Copy</string>
<!-- Paste & go the text in the clipboard. '&amp;' is replaced with the ampersand symbol: & -->
<string name="browser_toolbar_long_press_popup_paste_and_go">Paste &amp; Go</string>
<!-- Paste the text in the clipboard -->
<string name="browser_toolbar_long_press_popup_paste">Paste</string>
</resources>

View File

@ -97,7 +97,10 @@ class DefaultBrowserToolbarControllerTest {
verify {
navController.nav(
R.id.browserFragment,
BrowserFragmentDirections.actionBrowserFragmentToSearchFragment("1")
BrowserFragmentDirections.actionBrowserFragmentToSearchFragment(
sessionId = "1",
pastedText = null
)
)
}
}
@ -243,7 +246,7 @@ class DefaultBrowserToolbarControllerTest {
verify { metrics.track(Event.BrowserMenuItemTapped(Event.BrowserMenuItemTapped.Item.NEW_PRIVATE_TAB)) }
verify {
val directions = BrowserFragmentDirections
.actionBrowserFragmentToSearchFragment(null)
.actionBrowserFragmentToSearchFragment(sessionId = null, pastedText = null)
navController.nav(R.id.browserFragment, directions)
}
verify { browsingModeManager.mode = BrowsingMode.Private }
@ -310,7 +313,7 @@ class DefaultBrowserToolbarControllerTest {
verify { metrics.track(Event.BrowserMenuItemTapped(Event.BrowserMenuItemTapped.Item.NEW_TAB)) }
verify {
val directions = BrowserFragmentDirections
.actionBrowserFragmentToSearchFragment(null)
.actionBrowserFragmentToSearchFragment(sessionId = null, pastedText = null)
navController.nav(R.id.browserFragment, directions)
}
verify { browsingModeManager.mode = BrowsingMode.Normal }
@ -337,7 +340,7 @@ class DefaultBrowserToolbarControllerTest {
verify { viewModel.previousFragmentId = R.id.browserFragment }
verify {
val directions = BrowserFragmentDirections
.actionBrowserFragmentToSearchFragment(null)
.actionBrowserFragmentToSearchFragment(sessionId = null, pastedText = null)
navController.nav(R.id.browserFragment, directions)
}
}

View File

@ -65,7 +65,11 @@ class StartSearchIntentProcessorTest {
verify { metrics.track(Event.SearchWidgetNewTabPressed) }
verify {
navController.navigate(
NavGraphDirections.actionGlobalSearch(sessionId = null, showShortcutEnginePicker = true)
NavGraphDirections.actionGlobalSearch(
sessionId = null,
showShortcutEnginePicker = true,
pastedText = null
)
)
}
verify { out.putExtra(HomeActivity.OPEN_TO_SEARCH, false) }