Bug 1849073 - Part 1: Remove the Bookmark Search Dialog

fenix/120.0
Gabriel Luong 7 months ago committed by mergify[bot]
parent 5367c93ef6
commit 08be473cbf

@ -20,7 +20,6 @@ enum class BrowserDirection(@IdRes val fragmentId: Int) {
FromSearchDialog(R.id.searchDialogFragment),
FromSettings(R.id.settingsFragment),
FromBookmarks(R.id.bookmarkFragment),
FromBookmarkSearchDialog(R.id.bookmarkSearchDialogFragment),
FromHistory(R.id.historyFragment),
FromHistorySearchDialog(R.id.historySearchDialogFragment),
FromHistoryMetadataGroup(R.id.historyMetadataGroupFragment),

@ -997,8 +997,6 @@ open class HomeActivity : LocaleAwareAppCompatActivity(), NavHostActivity {
SettingsFragmentDirections.actionGlobalBrowser(customTabSessionId)
BrowserDirection.FromBookmarks ->
BookmarkFragmentDirections.actionGlobalBrowser(customTabSessionId)
BrowserDirection.FromBookmarkSearchDialog ->
SearchDialogFragmentDirections.actionGlobalBrowser(customTabSessionId)
BrowserDirection.FromHistory ->
HistoryFragmentDirections.actionGlobalBrowser(customTabSessionId)
BrowserDirection.FromHistorySearchDialog ->

@ -28,7 +28,6 @@ import org.mozilla.fenix.ext.bookmarkStorage
import org.mozilla.fenix.ext.components
import org.mozilla.fenix.ext.nav
import org.mozilla.fenix.ext.navigateSafe
import org.mozilla.fenix.utils.Settings
@VisibleForTesting
internal const val WARN_OPEN_ALL_SIZE = 15
@ -86,7 +85,6 @@ class DefaultBookmarkController(
private val deleteBookmarkFolder: (Set<BookmarkNode>) -> Unit,
private val showTabTray: (Boolean) -> Unit,
private val warnLargeOpenAll: (Int, () -> Unit) -> Unit,
private val settings: Settings,
) : BookmarkController {
private val resources: Resources = activity.resources
@ -256,13 +254,10 @@ class DefaultBookmarkController(
}
override fun handleSearch() {
val directions = if (settings.showUnifiedSearchFeature) {
BookmarkFragmentDirections.actionGlobalSearchDialog(sessionId = null)
} else {
BookmarkFragmentDirections.actionBookmarkFragmentToBookmarkSearchDialogFragment()
}
navController.navigateSafe(R.id.bookmarkFragment, directions)
navController.navigateSafe(
R.id.bookmarkFragment,
BookmarkFragmentDirections.actionGlobalSearchDialog(sessionId = null),
)
}
private fun openInNewTabAndShow(

@ -107,7 +107,6 @@ class BookmarkFragment : LibraryPageFragment<BookmarkNode>(), UserInteractionHan
deleteBookmarkFolder = ::showRemoveFolderDialog,
showTabTray = ::showTabTray,
warnLargeOpenAll = ::warnLargeOpenAll,
settings = requireComponents.settings,
),
)

@ -1,47 +0,0 @@
/* 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.library.bookmarks
import mozilla.components.concept.engine.EngineSession.LoadUrlFlags
import mozilla.components.service.glean.private.NoExtras
import org.mozilla.fenix.BrowserDirection
import org.mozilla.fenix.GleanMetrics.BookmarksManagement
import org.mozilla.fenix.HomeActivity
/**
* An interface that handles the view manipulation of the Bookmark Search, triggered by the Interactor
*/
interface BookmarkSearchController {
fun handleEditingCancelled()
fun handleTextChanged(text: String)
fun handleUrlTapped(url: String, flags: LoadUrlFlags = LoadUrlFlags.none())
}
class BookmarkSearchDialogController(
private val activity: HomeActivity,
private val fragmentStore: BookmarkSearchFragmentStore,
private val clearToolbarFocus: () -> Unit,
) : BookmarkSearchController {
override fun handleEditingCancelled() {
clearToolbarFocus()
}
override fun handleTextChanged(text: String) {
fragmentStore.dispatch(BookmarkSearchFragmentAction.UpdateQuery(text))
}
override fun handleUrlTapped(url: String, flags: LoadUrlFlags) {
BookmarksManagement.searchResultTapped.record(NoExtras())
clearToolbarFocus()
activity.openToBrowserAndLoad(
searchTermOrURL = url,
newTab = true,
from = BrowserDirection.FromBookmarkSearchDialog,
flags = flags,
)
}
}

@ -1,314 +0,0 @@
/* 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.library.bookmarks
import android.annotation.SuppressLint
import android.app.Activity
import android.app.Dialog
import android.content.Context
import android.content.DialogInterface
import android.content.Intent
import android.os.Build
import android.os.Bundle
import android.speech.RecognizerIntent
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.ViewStub
import android.view.accessibility.AccessibilityEvent
import android.view.inputmethod.InputMethodManager
import androidx.activity.result.ActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatDialogFragment
import androidx.appcompat.content.res.AppCompatResources
import androidx.constraintlayout.widget.ConstraintProperties.BOTTOM
import androidx.constraintlayout.widget.ConstraintProperties.PARENT_ID
import androidx.constraintlayout.widget.ConstraintProperties.TOP
import androidx.constraintlayout.widget.ConstraintSet
import androidx.core.view.isVisible
import androidx.lifecycle.lifecycleScope
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
import mozilla.components.browser.toolbar.BrowserToolbar
import mozilla.components.lib.state.ext.consumeFlow
import mozilla.components.lib.state.ext.consumeFrom
import mozilla.components.support.base.feature.UserInteractionHandler
import mozilla.components.support.ktx.android.view.hideKeyboard
import org.mozilla.fenix.BrowserDirection
import org.mozilla.fenix.HomeActivity
import org.mozilla.fenix.R
import org.mozilla.fenix.components.toolbar.ToolbarPosition
import org.mozilla.fenix.databinding.FragmentBookmarkSearchDialogBinding
import org.mozilla.fenix.databinding.SearchSuggestionsHintBinding
import org.mozilla.fenix.ext.settings
import org.mozilla.fenix.library.bookmarks.awesomebar.AwesomeBarView
import org.mozilla.fenix.library.bookmarks.toolbar.ToolbarView
import org.mozilla.fenix.settings.SupportUtils
@Suppress("TooManyFunctions", "LargeClass")
class BookmarkSearchDialogFragment : AppCompatDialogFragment(), UserInteractionHandler {
private var _binding: FragmentBookmarkSearchDialogBinding? = null
private val binding get() = _binding!!
private lateinit var interactor: BookmarkSearchDialogInteractor
private lateinit var store: BookmarkSearchFragmentStore
private lateinit var toolbarView: ToolbarView
private lateinit var awesomeBarView: AwesomeBarView
private val speechIntent = Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH)
private var voiceSearchButtonAlreadyAdded = false
private var dialogHandledAction = false
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setStyle(STYLE_NO_TITLE, R.style.SearchDialogStyle)
}
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
return object : Dialog(requireContext(), this.theme) {
@Deprecated("Deprecated in Java")
override fun onBackPressed() {
this@BookmarkSearchDialogFragment.onBackPressed()
}
}
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?,
): View {
_binding = FragmentBookmarkSearchDialogBinding.inflate(inflater, container, false)
val activity = requireActivity() as HomeActivity
store = BookmarkSearchFragmentStore(
createInitialBookmarkSearchFragmentState(),
)
interactor = BookmarkSearchDialogInteractor(
BookmarkSearchDialogController(
activity = activity,
fragmentStore = store,
clearToolbarFocus = {
dialogHandledAction = true
toolbarView.view.hideKeyboard()
toolbarView.view.clearFocus()
},
),
)
toolbarView = ToolbarView(
context = requireContext(),
interactor = interactor,
isPrivate = false,
view = binding.toolbar,
)
val awesomeBar = binding.awesomeBar
awesomeBarView = AwesomeBarView(
activity,
interactor,
awesomeBar,
)
awesomeBarView.view.setOnEditSuggestionListener(toolbarView.view::setSearchTerms)
return binding.root
}
@SuppressLint("ClickableViewAccessibility")
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
setupConstraints(view)
binding.searchWrapper.setOnTouchListener { _, _ ->
dismissAllowingStateLoss()
true
}
val stubListener = ViewStub.OnInflateListener { _, inflated ->
val searchSuggestionHintBinding = SearchSuggestionsHintBinding.bind(inflated)
searchSuggestionHintBinding.learnMore.setOnClickListener {
(activity as HomeActivity)
.openToBrowserAndLoad(
searchTermOrURL = SupportUtils.getGenericSumoURLForTopic(
SupportUtils.SumoTopic.SEARCH_SUGGESTION,
),
newTab = true,
from = BrowserDirection.FromBookmarkSearchDialog,
)
}
searchSuggestionHintBinding.allow.setOnClickListener {
inflated.visibility = View.GONE
requireContext().settings().also {
it.shouldShowSearchSuggestionsInPrivate = true
it.showSearchSuggestionsInPrivateOnboardingFinished = true
}
}
searchSuggestionHintBinding.dismiss.setOnClickListener {
inflated.visibility = View.GONE
requireContext().settings().also {
it.shouldShowSearchSuggestionsInPrivate = false
it.showSearchSuggestionsInPrivateOnboardingFinished = true
}
}
searchSuggestionHintBinding.text.text =
getString(R.string.search_suggestions_onboarding_text, getString(R.string.app_name))
searchSuggestionHintBinding.title.text =
getString(R.string.search_suggestions_onboarding_title)
}
binding.searchSuggestionsHintDivider.isVisible = false
binding.searchSuggestionsHint.isVisible = false
binding.searchSuggestionsHint.setOnInflateListener((stubListener))
if (view.context.settings().accessibilityServicesEnabled) {
updateAccessibilityTraversalOrder()
}
addVoiceSearchButton()
observeAwesomeBarState()
consumeFrom(store) {
toolbarView.update(it)
awesomeBarView.update(it)
}
}
private fun observeAwesomeBarState() = consumeFlow(store) { flow ->
flow.map { state -> state.query.isNotBlank() }
.distinctUntilChanged()
.collect { shouldShowAwesomebar ->
binding.awesomeBar.visibility = if (shouldShowAwesomebar) {
View.VISIBLE
} else {
View.INVISIBLE
}
}
}
private fun updateAccessibilityTraversalOrder() {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP_MR1) {
viewLifecycleOwner.lifecycleScope.launch {
binding.searchWrapper.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED)
}
}
}
override fun onPause() {
super.onPause()
view?.hideKeyboard()
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
/*
* This way of dismissing the keyboard is needed to smoothly dismiss the keyboard while the dialog
* is also dismissing.
*/
private fun hideDeviceKeyboard() {
// If the interactor/controller has handled a search event itself, it will hide the keyboard.
if (!dialogHandledAction) {
val imm =
requireContext().getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
imm.hideSoftInputFromWindow(view?.windowToken, InputMethodManager.HIDE_IMPLICIT_ONLY)
}
}
override fun onDismiss(dialog: DialogInterface) {
super.onDismiss(dialog)
hideDeviceKeyboard()
}
override fun onBackPressed(): Boolean {
view?.hideKeyboard()
dismissAllowingStateLoss()
return true
}
private fun setupConstraints(view: View) {
if (view.context.settings().toolbarPosition == ToolbarPosition.BOTTOM) {
ConstraintSet().apply {
clone(binding.searchWrapper)
clear(binding.toolbar.id, TOP)
connect(binding.toolbar.id, BOTTOM, PARENT_ID, BOTTOM)
clear(binding.pillWrapper.id, BOTTOM)
connect(binding.pillWrapper.id, BOTTOM, binding.toolbar.id, TOP)
clear(binding.awesomeBar.id, TOP)
clear(binding.awesomeBar.id, BOTTOM)
connect(binding.awesomeBar.id, TOP, binding.searchSuggestionsHint.id, BOTTOM)
connect(binding.awesomeBar.id, BOTTOM, binding.pillWrapper.id, TOP)
clear(binding.searchSuggestionsHint.id, TOP)
clear(binding.searchSuggestionsHint.id, BOTTOM)
connect(binding.searchSuggestionsHint.id, TOP, PARENT_ID, TOP)
connect(binding.searchSuggestionsHint.id, BOTTOM, binding.searchHintBottomBarrier.id, TOP)
applyTo(binding.searchWrapper)
}
}
}
private val startVoiceSearchForResult =
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result: ActivityResult ->
if (result.resultCode == Activity.RESULT_OK) {
val intent = result.data
intent?.getStringArrayListExtra(RecognizerIntent.EXTRA_RESULTS)?.first()?.also {
toolbarView.view.edit.updateUrl(url = it, shouldHighlight = true)
interactor.onTextChanged(it)
toolbarView.view.edit.focus()
}
}
}
private fun addVoiceSearchButton() {
val shouldShowVoiceSearch = isSpeechAvailable() &&
requireContext().settings().shouldShowVoiceSearch
if (voiceSearchButtonAlreadyAdded || !shouldShowVoiceSearch) return
toolbarView.view.addEditActionEnd(
BrowserToolbar.Button(
imageDrawable = AppCompatResources.getDrawable(requireContext(), R.drawable.ic_microphone)!!,
contentDescription = requireContext().getString(R.string.voice_search_content_description),
visible = { true },
listener = ::launchVoiceSearch,
),
)
voiceSearchButtonAlreadyAdded = true
}
private fun launchVoiceSearch() {
// Note if a user disables speech while the app is on the search fragment
// the voice button will still be available and *will* cause a crash if tapped,
// since the `visible` call is only checked on create. In order to avoid extra complexity
// around such a small edge case, we make the button have no functionality in this case.
if (!isSpeechAvailable()) { return }
speechIntent.apply {
putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, RecognizerIntent.LANGUAGE_MODEL_FREE_FORM)
putExtra(RecognizerIntent.EXTRA_PROMPT, requireContext().getString(R.string.voice_search_explainer))
}
startVoiceSearchForResult.launch(speechIntent)
}
private fun isSpeechAvailable(): Boolean = speechIntent.resolveActivity(requireContext().packageManager) != null
}

@ -1,30 +0,0 @@
/* 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.library.bookmarks
import mozilla.components.concept.engine.EngineSession.LoadUrlFlags
import org.mozilla.fenix.library.bookmarks.awesomebar.AwesomeBarInteractor
import org.mozilla.fenix.library.bookmarks.toolbar.ToolbarInteractor
/**
* Interactor for the bookmark search
* Provides implementations for the AwesomeBarView and ToolbarView
*/
class BookmarkSearchDialogInteractor(
private val bookmarkSearchController: BookmarkSearchDialogController,
) : AwesomeBarInteractor, ToolbarInteractor {
override fun onEditingCanceled() {
bookmarkSearchController.handleEditingCancelled()
}
override fun onTextChanged(text: String) {
bookmarkSearchController.handleTextChanged(text)
}
override fun onUrlTapped(url: String, flags: LoadUrlFlags) {
bookmarkSearchController.handleUrlTapped(url, flags)
}
}

@ -1,53 +0,0 @@
/* 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.library.bookmarks
import mozilla.components.lib.state.Action
import mozilla.components.lib.state.State
import mozilla.components.lib.state.Store
/**
* The [Store] for holding the [BookmarkSearchFragmentState] and applying [BookmarkSearchFragmentAction]s.
*/
class BookmarkSearchFragmentStore(
initialState: BookmarkSearchFragmentState,
) : Store<BookmarkSearchFragmentState, BookmarkSearchFragmentAction>(
initialState,
::bookmarkSearchStateReducer,
)
/**
* The state for the Bookmark Search Screen
*
* @property query The current search query string
*/
data class BookmarkSearchFragmentState(
val query: String,
) : State
fun createInitialBookmarkSearchFragmentState(): BookmarkSearchFragmentState {
return BookmarkSearchFragmentState(query = "")
}
/**
* Actions to dispatch through the [BookmarkSearchFragmentStore] to modify [BookmarkSearchFragmentState]
* through the reducer.
*/
sealed class BookmarkSearchFragmentAction : Action {
data class UpdateQuery(val query: String) : BookmarkSearchFragmentAction()
}
/**
* The [BookmarkSearchFragmentState] Reducer.
*/
private fun bookmarkSearchStateReducer(
state: BookmarkSearchFragmentState,
action: BookmarkSearchFragmentAction,
): BookmarkSearchFragmentState {
return when (action) {
is BookmarkSearchFragmentAction.UpdateQuery ->
state.copy(query = action.query)
}
}

@ -1,20 +0,0 @@
/* 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.library.bookmarks.awesomebar
import mozilla.components.concept.engine.EngineSession.LoadUrlFlags
/**
* Interface for the AwesomeBarView Interactor. This interface is implemented by objects that want
* to respond to user interaction on the AwesomebarView
*/
interface AwesomeBarInteractor {
/**
* Called whenever a suggestion containing a URL is tapped
* @param url the url the suggestion was providing
*/
fun onUrlTapped(url: String, flags: LoadUrlFlags = LoadUrlFlags.none())
}

@ -1,61 +0,0 @@
/* 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.library.bookmarks.awesomebar
import androidx.appcompat.content.res.AppCompatResources.getDrawable
import mozilla.components.concept.engine.EngineSession
import mozilla.components.feature.awesomebar.provider.BookmarksStorageSuggestionProvider
import mozilla.components.feature.session.SessionUseCases
import org.mozilla.fenix.HomeActivity
import org.mozilla.fenix.R
import org.mozilla.fenix.browser.browsingmode.BrowsingMode
import org.mozilla.fenix.ext.components
import org.mozilla.fenix.library.bookmarks.BookmarkSearchFragmentState
/**
* View that contains and configures the BrowserAwesomeBar
*/
class AwesomeBarView(
activity: HomeActivity,
val interactor: AwesomeBarInteractor,
val view: AwesomeBarWrapper,
) {
private val bookmarksStorageSuggestionProvider: BookmarksStorageSuggestionProvider
private val loadUrlUseCase = object : SessionUseCases.LoadUrlUseCase {
override fun invoke(
url: String,
flags: EngineSession.LoadUrlFlags,
additionalHeaders: Map<String, String>?,
) {
interactor.onUrlTapped(url, flags)
}
}
init {
val components = activity.components
val engineForSpeculativeConnects = when (activity.browsingModeManager.mode) {
BrowsingMode.Normal -> components.core.engine
BrowsingMode.Private -> null
}
bookmarksStorageSuggestionProvider =
BookmarksStorageSuggestionProvider(
bookmarksStorage = components.core.bookmarksStorage,
loadUrlUseCase = loadUrlUseCase,
icons = components.core.icons,
indicatorIcon = getDrawable(activity, R.drawable.ic_search_results_bookmarks),
engine = engineForSpeculativeConnects,
showEditSuggestion = false,
)
view.addProviders(bookmarksStorageSuggestionProvider)
}
fun update(state: BookmarkSearchFragmentState) {
view.onInputChanged(state.query)
}
}

@ -1,104 +0,0 @@
/* 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.library.bookmarks.awesomebar
import android.content.Context
import android.util.AttributeSet
import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateOf
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.AbstractComposeView
import mozilla.components.compose.browser.awesomebar.AwesomeBar
import mozilla.components.compose.browser.awesomebar.AwesomeBarDefaults
import mozilla.components.compose.browser.awesomebar.AwesomeBarOrientation
import mozilla.components.concept.awesomebar.AwesomeBar
import mozilla.components.support.ktx.android.view.hideKeyboard
import org.mozilla.fenix.ext.components
import org.mozilla.fenix.ext.settings
import org.mozilla.fenix.theme.FirefoxTheme
/**
* This wrapper wraps the `AwesomeBar()` composable and exposes it as a `View` and `concept-awesomebar`
* implementation to be integrated in the view hierarchy of [BookmarkSearchDialogFragment] until more parts
* of that screen have been refactored to use Jetpack Compose.
*/
class AwesomeBarWrapper @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0,
) : AbstractComposeView(context, attrs, defStyleAttr), AwesomeBar {
private val providers = mutableStateOf(emptyList<AwesomeBar.SuggestionProvider>())
private val text = mutableStateOf("")
private var onEditSuggestionListener: ((String) -> Unit)? = null
private var onStopListener: (() -> Unit)? = null
@Composable
override fun Content() {
if (providers.value.isEmpty()) {
return
}
val orientation = if (context.settings().shouldUseBottomToolbar) {
AwesomeBarOrientation.BOTTOM
} else {
AwesomeBarOrientation.TOP
}
FirefoxTheme {
AwesomeBar(
text = text.value,
providers = providers.value,
orientation = orientation,
colors = AwesomeBarDefaults.colors(
background = Color.Transparent,
title = FirefoxTheme.colors.textPrimary,
description = FirefoxTheme.colors.textSecondary,
autocompleteIcon = FirefoxTheme.colors.textSecondary,
),
onSuggestionClicked = { suggestion ->
suggestion.onSuggestionClicked?.invoke()
onStopListener?.invoke()
},
onAutoComplete = { suggestion ->
onEditSuggestionListener?.invoke(suggestion.editSuggestion!!)
},
onScroll = { hideKeyboard() },
profiler = context.components.core.engine.profiler,
)
}
}
override fun addProviders(vararg providers: AwesomeBar.SuggestionProvider) {
val newProviders = this.providers.value.toMutableList()
newProviders.addAll(providers)
this.providers.value = newProviders
}
override fun containsProvider(provider: AwesomeBar.SuggestionProvider): Boolean {
return providers.value.any { current -> current.id == provider.id }
}
override fun onInputChanged(text: String) {
this.text.value = text
}
override fun removeAllProviders() {
providers.value = emptyList()
}
override fun removeProviders(vararg providers: AwesomeBar.SuggestionProvider) {
val newProviders = this.providers.value.toMutableList()
newProviders.removeAll(providers)
this.providers.value = newProviders
}
override fun setOnEditSuggestionListener(listener: (String) -> Unit) {
onEditSuggestionListener = listener
}
override fun setOnStopListener(listener: () -> Unit) {
onStopListener = listener
}
}

@ -1,125 +0,0 @@
/* 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.library.bookmarks.toolbar
import android.content.Context
import androidx.annotation.VisibleForTesting
import androidx.appcompat.content.res.AppCompatResources
import androidx.core.content.ContextCompat
import mozilla.components.browser.toolbar.BrowserToolbar
import mozilla.components.support.ktx.android.content.getColorFromAttr
import mozilla.components.support.ktx.android.content.res.resolveAttribute
import mozilla.components.support.ktx.android.view.hideKeyboard
import org.mozilla.fenix.R
import org.mozilla.fenix.library.bookmarks.BookmarkSearchFragmentState
/**
* Interface for the Toolbar Interactor. This interface is implemented by objects that want
* to respond to user interaction on the [ToolbarView]
*/
interface ToolbarInteractor {
/**
* Called when a user removes focus from the [ToolbarView]
*/
fun onEditingCanceled()
/**
* Called whenever the text inside the [ToolbarView] changes
* @param text the current text displayed by [ToolbarView]
*/
fun onTextChanged(text: String)
}
/**
* View that contains and configures the BrowserToolbar to only be used in its editing mode.
*/
@Suppress("LongParameterList")
class ToolbarView(
private val context: Context,
private val interactor: ToolbarInteractor,
private val isPrivate: Boolean,
val view: BrowserToolbar,
) {
@VisibleForTesting
internal var isInitialized = false
init {
view.apply {
editMode()
background = AppCompatResources.getDrawable(
context,
context.theme.resolveAttribute(R.attr.layer1),
)
edit.hint = context.getString(R.string.bookmark_search)
edit.colors = edit.colors.copy(
text = context.getColorFromAttr(R.attr.textPrimary),
hint = context.getColorFromAttr(R.attr.textSecondary),
suggestionBackground = ContextCompat.getColor(
context,
R.color.suggestion_highlight_color,
),
clear = context.getColorFromAttr(R.attr.textPrimary),
)
edit.setUrlBackground(
AppCompatResources.getDrawable(context, R.drawable.search_url_background),
)
private = isPrivate
setOnUrlCommitListener {
hideKeyboard()
// We need to return false to not show display mode
false
}
setDefaultIcon()
setOnEditListener(
object : mozilla.components.concept.toolbar.Toolbar.OnEditListener {
override fun onCancelEditing(): Boolean {
interactor.onEditingCanceled()
// We need to return false to not show display mode
return false
}
override fun onTextChanged(text: String) {
url = text
interactor.onTextChanged(text)
}
},
)
}
}
fun update(state: BookmarkSearchFragmentState) {
if (!isInitialized) {
view.url = state.query
view.setSearchTerms(state.query)
// We must trigger an onTextChanged so when search terms are set when transitioning to `editMode`
// we have the most up to date text
interactor.onTextChanged(view.url.toString())
view.editMode()
isInitialized = true
}
}
private fun setDefaultIcon() {
val bookmarkSearchIcon =
AppCompatResources.getDrawable(context, R.drawable.ic_bookmarks_menu)
bookmarkSearchIcon?.let {
view.edit.setIcon(bookmarkSearchIcon, context.getString(R.string.bookmark_search))
}
}
}

@ -1,111 +0,0 @@
<?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/. -->
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/search_wrapper"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
android:background="?attr/scrimBackground">
<mozilla.components.browser.toolbar.BrowserToolbar
android:id="@+id/toolbar"
android:layout_width="0dp"
android:layout_height="@dimen/browser_toolbar_height"
android:background="@drawable/toolbar_background_top"
android:clickable="true"
android:focusable="true"
android:focusableInTouchMode="true"
app:layout_scrollFlags="scroll|enterAlways|snap|exitUntilCollapsed"
app:browserToolbarClearColor="?attr/textPrimary"
app:browserToolbarInsecureColor="?attr/textPrimary"
app:browserToolbarMenuColor="?attr/textPrimary"
app:browserToolbarProgressBarGravity="bottom"
app:browserToolbarSecureColor="?attr/textPrimary"
app:browserToolbarTrackingProtectionAndSecurityIndicatorSeparatorColor="?borderPrimary"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
<ViewStub
android:id="@+id/search_suggestions_hint"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:inflatedId="@id/search_suggestions_hint"
android:layout="@layout/search_suggestions_hint"
app:layout_constrainedHeight="true"
app:layout_constraintBottom_toTopOf="@id/search_hint_bottom_barrier"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/toolbar"/>
<View
android:id="@+id/search_suggestions_hint_divider"
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="?borderPrimary"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="@id/search_suggestions_hint"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<androidx.constraintlayout.widget.Barrier
android:id="@+id/search_hint_bottom_barrier"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:barrierDirection="top"
app:constraint_referenced_ids="awesome_bar,pill_wrapper"/>
<org.mozilla.fenix.library.bookmarks.awesomebar.AwesomeBarWrapper
android:id="@+id/awesome_bar"
android:layout_width="0dp"
android:layout_height="0dp"
android:fadingEdge="horizontal"
android:fadingEdgeLength="40dp"
android:nestedScrollingEnabled="false"
android:requiresFadingEdge="vertical"
android:background="?attr/layer1"
android:visibility="invisible"
app:layout_constraintBottom_toTopOf="@+id/pill_wrapper"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/search_suggestions_hint" />
<ImageView
android:id="@+id/link_icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/search_fragment_clipboard_item_horizontal_margin"
android:clickable="false"
android:focusable="false"
android:importantForAccessibility="no"
android:visibility="gone"
app:srcCompat="@drawable/ic_link"
tools:visibility="visible" />
<View
android:id="@+id/pill_wrapper_divider"
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="?borderPrimary"
app:layout_constraintBottom_toTopOf="@id/pill_wrapper" />
<View
android:id="@+id/pill_wrapper"
android:layout_width="0dp"
android:layout_height="0dp"
android:background="?attr/layer1"
android:importantForAccessibility="no"
android:paddingStart="@dimen/search_fragment_pill_padding_start"
android:paddingTop="@dimen/search_fragment_pill_padding_vertical"
android:paddingEnd="@dimen/search_fragment_pill_padding_end"
android:paddingBottom="@dimen/search_fragment_pill_padding_vertical"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

@ -382,19 +382,8 @@
<action
android:id="@+id/action_bookmarkFragment_to_bookmarkAddFolderFragment"
app:destination="@id/bookmarkAddFolderFragment" />
<action
android:id="@+id/action_bookmarkFragment_to_bookmarkSearchDialogFragment"
app:destination="@id/bookmarkSearchDialogFragment"
app:popUpTo="@id/bookmarkSearchDialogFragment"
app:popUpToInclusive="true" />
</fragment>
<dialog
android:id="@+id/bookmarkSearchDialogFragment"
android:name="org.mozilla.fenix.library.bookmarks.BookmarkSearchDialogFragment"
tools:layout="@layout/fragment_bookmark_search_dialog">
</dialog>
<fragment
android:id="@+id/bookmarkEditFragment"
android:name="org.mozilla.fenix.library.bookmarks.edit.EditBookmarkFragment"

@ -45,7 +45,6 @@ import org.mozilla.fenix.browser.browsingmode.BrowsingMode
import org.mozilla.fenix.components.Services
import org.mozilla.fenix.ext.bookmarkStorage
import org.mozilla.fenix.ext.components
import org.mozilla.fenix.utils.Settings
@Suppress("TooManyFunctions", "LargeClass")
class BookmarkControllerTest {
@ -65,7 +64,6 @@ class BookmarkControllerTest {
private val addNewTabUseCase: TabsUseCases.AddNewTabUseCase = mockk(relaxed = true)
private val navBackStackEntry: NavBackStackEntry = mockk(relaxed = true)
private val navDestination: NavDestination = mockk(relaxed = true)
private val settings: Settings = mockk(relaxed = true)
private val item =
BookmarkNode(BookmarkNodeType.ITEM, "456", "123", 0u, "Mozilla", "http://mozilla.org", 0, null)
@ -249,7 +247,7 @@ class BookmarkControllerTest {
verify {
navController.navigate(
BookmarkFragmentDirections.actionBookmarkFragmentToBookmarkSearchDialogFragment(),
BookmarkFragmentDirections.actionGlobalSearchDialog(sessionId = null),
)
}
}
@ -562,7 +560,6 @@ class BookmarkControllerTest {
deleteBookmarkFolder = deleteBookmarkFolder,
showTabTray = showTabTray,
warnLargeOpenAll = warnLargeOpenAll,
settings = settings,
)
}
}

@ -1,104 +0,0 @@
/* 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.library.bookmarks
import io.mockk.MockKAnnotations
import io.mockk.impl.annotations.MockK
import io.mockk.verify
import kotlinx.coroutines.test.runTest
import mozilla.components.concept.engine.EngineSession
import mozilla.components.service.glean.testing.GleanTestRule
import mozilla.components.support.test.robolectric.testContext
import org.junit.Assert.assertNotNull
import org.junit.Assert.assertNull
import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mozilla.fenix.BrowserDirection
import org.mozilla.fenix.GleanMetrics.BookmarksManagement
import org.mozilla.fenix.HomeActivity
import org.mozilla.fenix.helpers.FenixRobolectricTestRunner
@RunWith(FenixRobolectricTestRunner::class)
class BookmarkSearchControllerTest {
@get:Rule
val gleanTestRule = GleanTestRule(testContext)
@MockK(relaxed = true)
private lateinit var activity: HomeActivity
@MockK(relaxed = true)
private lateinit var store: BookmarkSearchFragmentStore
@Before
fun setUp() {
MockKAnnotations.init(this)
}
@Test
fun `WHEN editing is cancelled THEN clearToolbarFocus is called`() = runTest {
var clearToolbarFocusInvoked = false
createController(
clearToolbarFocus = {
clearToolbarFocusInvoked = true
},
).handleEditingCancelled()
assertTrue(clearToolbarFocusInvoked)
}
@Test
fun `WHEN text changed THEN update query action is dispatched`() {
val text = "fenix"
createController().handleTextChanged(text)
verify { store.dispatch(BookmarkSearchFragmentAction.UpdateQuery(text)) }
}
@Test
fun `WHEN text is changed to empty THEN update query action is dispatched`() {
val text = ""
createController().handleTextChanged(text)
verify { store.dispatch(BookmarkSearchFragmentAction.UpdateQuery(text)) }
}
@Test
fun `WHEN url is tapped THEN openToBrowserAndLoad is called`() {
val url = "https://www.google.com/"
val flags = EngineSession.LoadUrlFlags.none()
assertNull(BookmarksManagement.searchResultTapped.testGetValue())
createController().handleUrlTapped(url, flags)
createController().handleUrlTapped(url)
verify {
activity.openToBrowserAndLoad(
searchTermOrURL = url,
newTab = true,
from = BrowserDirection.FromBookmarkSearchDialog,
flags = flags,
)
}
assertNotNull(BookmarksManagement.searchResultTapped.testGetValue())
}
private fun createController(
clearToolbarFocus: () -> Unit = { },
): BookmarkSearchDialogController {
return BookmarkSearchDialogController(
activity = activity,
fragmentStore = store,
clearToolbarFocus = clearToolbarFocus,
)
}
}

@ -1,50 +0,0 @@
/* 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.library.bookmarks
import io.mockk.mockk
import io.mockk.verify
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
class BookmarkSearchDialogInteractorTest {
lateinit var searchController: BookmarkSearchDialogController
lateinit var interactor: BookmarkSearchDialogInteractor
@Before
fun setup() {
searchController = mockk(relaxed = true)
interactor = BookmarkSearchDialogInteractor(
searchController,
)
}
@Test
fun onEditingCanceled() = runTest {
interactor.onEditingCanceled()
verify {
searchController.handleEditingCancelled()
}
}
@Test
fun onTextChanged() {
interactor.onTextChanged("test")
verify { searchController.handleTextChanged("test") }
}
@Test
fun onUrlTapped() {
interactor.onUrlTapped("test")
verify {
searchController.handleUrlTapped("test")
}
}
}

@ -1,34 +0,0 @@
/* 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.library.bookmarks
import kotlinx.coroutines.test.runTest
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNotSame
import org.junit.Test
class BookmarkSearchFragmentStoreTest {
@Test
fun `GIVEN createInitialBookmarkSearchFragmentState THEN query is empty`() {
val expected = BookmarkSearchFragmentState(query = "")
assertEquals(
expected,
createInitialBookmarkSearchFragmentState(),
)
}
@Test
fun updateQuery() = runTest {
val initialState = BookmarkSearchFragmentState(query = "")
val store = BookmarkSearchFragmentStore(initialState)
val query = "test query"
store.dispatch(BookmarkSearchFragmentAction.UpdateQuery(query)).join()
assertNotSame(initialState, store.state)
assertEquals(query, store.state.query)
}
}

@ -83,9 +83,6 @@
<ID>UndocumentedPublicClass:BookmarkFragmentStore.kt$BookmarkFragmentStore : Store</ID>
<ID>UndocumentedPublicClass:BookmarkItemMenu.kt$BookmarkItemMenu</ID>
<ID>UndocumentedPublicClass:BookmarkItemMenu.kt$BookmarkItemMenu$Item</ID>
<ID>UndocumentedPublicClass:BookmarkSearchController.kt$BookmarkSearchDialogController : BookmarkSearchController</ID>
<ID>UndocumentedPublicClass:BookmarkSearchDialogFragment.kt$BookmarkSearchDialogFragment : AppCompatDialogFragmentUserInteractionHandler</ID>
<ID>UndocumentedPublicClass:BookmarkSearchFragmentStore.kt$BookmarkSearchFragmentAction$UpdateQuery : BookmarkSearchFragmentAction</ID>
<ID>UndocumentedPublicClass:BookmarkView.kt$BookmarkView : LibraryPageViewUserInteractionHandler</ID>
<ID>UndocumentedPublicClass:BookmarksUseCase.kt$BookmarksUseCase$AddBookmarksUseCase</ID>
<ID>UndocumentedPublicClass:BottomSpacerViewHolder.kt$BottomSpacerViewHolder : ViewHolder</ID>
@ -262,15 +259,6 @@
<ID>UndocumentedPublicClass:NoCollectionsMessageViewHolder.kt$NoCollectionsMessageViewHolder : ViewHolder</ID>
<ID>UndocumentedPublicClass:NormalBrowserTrayList.kt$NormalBrowserTrayList : AbstractBrowserTrayList</ID>
<ID>UndocumentedPublicClass:OnSharedPreferenceChangeListener.kt$OnSharedPreferenceChangeListener : OnSharedPreferenceChangeListenerDefaultLifecycleObserver</ID>
<ID>UndocumentedPublicClass:OnboardingFinishViewHolder.kt$OnboardingFinishViewHolder : ViewHolder</ID>
<ID>UndocumentedPublicClass:OnboardingHeaderViewHolder.kt$OnboardingHeaderViewHolder : ViewHolder</ID>
<ID>UndocumentedPublicClass:OnboardingManualSignInViewHolder.kt$OnboardingManualSignInViewHolder : ViewHolder</ID>
<ID>UndocumentedPublicClass:OnboardingPrivacyNoticeViewHolder.kt$OnboardingPrivacyNoticeViewHolder : ViewHolder</ID>
<ID>UndocumentedPublicClass:OnboardingRadioButton.kt$OnboardingRadioButton : AppCompatRadioButtonGroupableRadioButton</ID>
<ID>UndocumentedPublicClass:OnboardingSectionHeaderViewHolder.kt$OnboardingSectionHeaderViewHolder : ViewHolder</ID>
<ID>UndocumentedPublicClass:OnboardingThemePickerViewHolder.kt$OnboardingThemePickerViewHolder : ViewHolder</ID>
<ID>UndocumentedPublicClass:OnboardingToolbarPositionPickerViewHolder.kt$OnboardingToolbarPositionPickerViewHolder : ViewHolder</ID>
<ID>UndocumentedPublicClass:OnboardingTrackingProtectionViewHolder.kt$OnboardingTrackingProtectionViewHolder : ViewHolder</ID>
<ID>UndocumentedPublicClass:PagedHistoryProvider.kt$HistoryDB$Group : HistoryDB</ID>
<ID>UndocumentedPublicClass:PagedHistoryProvider.kt$HistoryDB$Metadata : HistoryDB</ID>
<ID>UndocumentedPublicClass:PagedHistoryProvider.kt$HistoryDB$Regular : HistoryDB</ID>
@ -492,7 +480,6 @@
<ID>UndocumentedPublicFunction:AddonPermissionDetailsBindingDelegate.kt$AddonPermissionDetailsBindingDelegate$fun bind(addon: Addon)</ID>
<ID>UndocumentedPublicFunction:AppRequestInterceptor.kt$AppRequestInterceptor$fun setNavigationController(navController: NavController)</ID>
<ID>UndocumentedPublicFunction:AppViewHolder.kt$AppViewHolder$fun bind(item: AppShareOption)</ID>
<ID>UndocumentedPublicFunction:AwesomeBarView.kt$AwesomeBarView$fun update(state: BookmarkSearchFragmentState)</ID>
<ID>UndocumentedPublicFunction:AwesomeBarView.kt$AwesomeBarView$fun update(state: HistorySearchFragmentState)</ID>
<ID>UndocumentedPublicFunction:AwesomeBarView.kt$AwesomeBarView$fun update(state: SearchFragmentState)</ID>
<ID>UndocumentedPublicFunction:AwesomeBarView.kt$AwesomeBarView$fun updateSuggestionProvidersVisibility( state: SearchProviderState, )</ID>
@ -519,10 +506,6 @@
<ID>UndocumentedPublicFunction:BookmarkController.kt$BookmarkController$fun handleSelectionModeSwitch()</ID>
<ID>UndocumentedPublicFunction:BookmarkFragmentStore.kt$operator fun BookmarkNode.contains(item: BookmarkNode): Boolean</ID>
<ID>UndocumentedPublicFunction:BookmarkNodeViewHolder.kt$BookmarkNodeViewHolder$fun bind( item: BookmarkNode, mode: BookmarkFragmentState.Mode, payload: BookmarkPayload, )</ID>
<ID>UndocumentedPublicFunction:BookmarkSearchController.kt$BookmarkSearchController$fun handleEditingCancelled()</ID>
<ID>UndocumentedPublicFunction:BookmarkSearchController.kt$BookmarkSearchController$fun handleTextChanged(text: String)</ID>
<ID>UndocumentedPublicFunction:BookmarkSearchController.kt$BookmarkSearchController$fun handleUrlTapped(url: String, flags: LoadUrlFlags = LoadUrlFlags.none())</ID>
<ID>UndocumentedPublicFunction:BookmarkSearchFragmentStore.kt$fun createInitialBookmarkSearchFragmentState(): BookmarkSearchFragmentState</ID>
<ID>UndocumentedPublicFunction:BookmarkView.kt$BookmarkView$fun update(state: BookmarkFragmentState)</ID>
<ID>UndocumentedPublicFunction:BrowserAnimator.kt$BrowserAnimator$fun beginAnimateInIfNecessary()</ID>
<ID>UndocumentedPublicFunction:BrowserAnimator.kt$BrowserAnimator.Companion$fun getToolbarNavOptions(context: Context): NavOptions</ID>
@ -707,9 +690,6 @@
<ID>UndocumentedPublicFunction:NavController.kt$fun NavController.navigateSafe( @IdRes resId: Int, directions: NavDirections, )</ID>
<ID>UndocumentedPublicFunction:NimbusBranchesView.kt$NimbusBranchesView$fun update(state: NimbusBranchesState)</ID>
<ID>UndocumentedPublicFunction:NotificationManager.kt$NotificationManager$fun showReceivedTabs(context: Context, device: Device?, tabs: List&lt;TabData&gt;)</ID>
<ID>UndocumentedPublicFunction:OnboardingRadioButton.kt$OnboardingRadioButton$fun addIllustration(illustration: ImageView)</ID>
<ID>UndocumentedPublicFunction:OnboardingRadioButton.kt$OnboardingRadioButton$fun onClickListener(listener: () -&gt; Unit)</ID>
<ID>UndocumentedPublicFunction:OnboardingSectionHeaderViewHolder.kt$OnboardingSectionHeaderViewHolder$fun bind(labelBuilder: (Context) -&gt; String)</ID>
<ID>UndocumentedPublicFunction:PagerIndicator.kt$fun Context.dpToPx(value: Float): Int</ID>
<ID>UndocumentedPublicFunction:PagerIndicator.kt$fun View.dpToPx(value: Float): Int</ID>
<ID>UndocumentedPublicFunction:PasswordRevealHelper.kt$fun togglePasswordReveal(passwordText: TextView, revealPasswordButton: ImageButton)</ID>
@ -793,7 +773,6 @@
<ID>UndocumentedPublicFunction:SessionControlAdapter.kt$AdapterItem$open fun contentsSameAs(other: AdapterItem)</ID>
<ID>UndocumentedPublicFunction:SessionControlView.kt$SessionControlView$fun update(state: AppState, shouldReportMetrics: Boolean = false)</ID>
<ID>UndocumentedPublicFunction:Settings.kt$Settings$@VisibleForTesting(otherwise = PRIVATE) fun setStrictETP()</ID>
<ID>UndocumentedPublicFunction:Settings.kt$Settings$fun addSearchWidgetInstalled(count: Int)</ID>
<ID>UndocumentedPublicFunction:Settings.kt$Settings$fun amoCollectionOverrideConfigured(): Boolean</ID>
<ID>UndocumentedPublicFunction:Settings.kt$Settings$fun getDeleteDataOnQuit(type: DeleteBrowsingDataOnQuitType): Boolean</ID>
<ID>UndocumentedPublicFunction:Settings.kt$Settings$fun getSitePermissionsCustomSettingsRules(): SitePermissionsRules</ID>
@ -875,7 +854,6 @@
<ID>UndocumentedPublicFunction:TabLayout.kt$fun TabLayout.isPrivateModeSelected(): Boolean</ID>
<ID>UndocumentedPublicFunction:TabLayout.kt$fun TabLayout.isSyncedModeSelected(): Boolean</ID>
<ID>UndocumentedPublicFunction:TabLayoutMediator.kt$TabLayoutMediator$fun selectTabAtPosition(position: Int)</ID>
<ID>UndocumentedPublicFunction:TabPreview.kt$TabPreview$fun loadPreviewThumbnail(thumbnailId: String)</ID>
<ID>UndocumentedPublicFunction:TabSessionState.kt$fun TabSessionState.isActive(maxActiveTime: Long): Boolean</ID>
<ID>UndocumentedPublicFunction:TabsTrayStore.kt$Page.Companion$fun positionToPage(position: Int): Page</ID>
<ID>UndocumentedPublicFunction:ThemeManager.kt$ThemeManager$fun applyStatusBarTheme(window: Window, context: Context)</ID>
@ -885,7 +863,6 @@
<ID>UndocumentedPublicFunction:ToolbarIntegration.kt$ToolbarIntegration$fun invalidateMenu()</ID>
<ID>UndocumentedPublicFunction:ToolbarPopupWindow.kt$ToolbarPopupWindow$fun show( view: WeakReference&lt;View&gt;, customTabId: String? = null, handlePasteAndGo: (String) -&gt; Unit, handlePaste: (String) -&gt; Unit, copyVisible: Boolean = true, )</ID>
<ID>UndocumentedPublicFunction:ToolbarView.kt$ToolbarView$fun update(searchState: SearchFragmentState)</ID>
<ID>UndocumentedPublicFunction:ToolbarView.kt$ToolbarView$fun update(state: BookmarkSearchFragmentState)</ID>
<ID>UndocumentedPublicFunction:ToolbarView.kt$ToolbarView$fun update(state: HistorySearchFragmentState)</ID>
<ID>UndocumentedPublicFunction:TopPlaceholderViewHolder.kt$TopPlaceholderViewHolder$fun bind()</ID>
<ID>UndocumentedPublicFunction:TopSiteItemViewHolder.kt$TopSiteItemViewHolder$fun bind(topSite: TopSite, position: Int)</ID>

Loading…
Cancel
Save