From 46b27b6ad9224be8fd5e50a69de15ae84dad7d49 Mon Sep 17 00:00:00 2001 From: Gabriel Luong Date: Tue, 26 Sep 2023 11:59:43 -0400 Subject: [PATCH] Bug 1849073 - Part 2: Remove the History Search Dialog --- .../org/mozilla/fenix/BrowserDirection.kt | 1 - .../java/org/mozilla/fenix/HomeActivity.kt | 2 - .../library/history/HistoryController.kt | 13 +- .../fenix/library/history/HistoryFragment.kt | 1 - .../history/HistorySearchController.kt | 47 --- .../history/HistorySearchDialogFragment.kt | 314 ------------------ .../history/HistorySearchDialogInteractor.kt | 30 -- .../history/HistorySearchFragmentStore.kt | 53 --- .../awesomebar/AwesomeBarInteractor.kt | 20 -- .../history/awesomebar/AwesomeBarView.kt | 61 ---- .../history/awesomebar/AwesomeBarWrapper.kt | 106 ------ .../library/history/toolbar/ToolbarView.kt | 124 ------- .../layout/fragment_history_search_dialog.xml | 111 ------- app/src/main/res/navigation/nav_graph.xml | 12 - .../library/history/HistoryControllerTest.kt | 5 +- .../history/HistorySearchControllerTest.kt | 103 ------ .../HistorySearchDialogInteractorTest.kt | 50 --- .../history/HistorySearchFragmentStoreTest.kt | 39 --- .../DefaultReviewQualityCheckServiceTest.kt | 2 + 19 files changed, 7 insertions(+), 1087 deletions(-) delete mode 100644 app/src/main/java/org/mozilla/fenix/library/history/HistorySearchController.kt delete mode 100644 app/src/main/java/org/mozilla/fenix/library/history/HistorySearchDialogFragment.kt delete mode 100644 app/src/main/java/org/mozilla/fenix/library/history/HistorySearchDialogInteractor.kt delete mode 100644 app/src/main/java/org/mozilla/fenix/library/history/HistorySearchFragmentStore.kt delete mode 100644 app/src/main/java/org/mozilla/fenix/library/history/awesomebar/AwesomeBarInteractor.kt delete mode 100644 app/src/main/java/org/mozilla/fenix/library/history/awesomebar/AwesomeBarView.kt delete mode 100644 app/src/main/java/org/mozilla/fenix/library/history/awesomebar/AwesomeBarWrapper.kt delete mode 100644 app/src/main/java/org/mozilla/fenix/library/history/toolbar/ToolbarView.kt delete mode 100644 app/src/main/res/layout/fragment_history_search_dialog.xml delete mode 100644 app/src/test/java/org/mozilla/fenix/library/history/HistorySearchControllerTest.kt delete mode 100644 app/src/test/java/org/mozilla/fenix/library/history/HistorySearchDialogInteractorTest.kt delete mode 100644 app/src/test/java/org/mozilla/fenix/library/history/HistorySearchFragmentStoreTest.kt diff --git a/app/src/main/java/org/mozilla/fenix/BrowserDirection.kt b/app/src/main/java/org/mozilla/fenix/BrowserDirection.kt index 9cc7ad3a1b..d51ef204f2 100644 --- a/app/src/main/java/org/mozilla/fenix/BrowserDirection.kt +++ b/app/src/main/java/org/mozilla/fenix/BrowserDirection.kt @@ -21,7 +21,6 @@ enum class BrowserDirection(@IdRes val fragmentId: Int) { FromSettings(R.id.settingsFragment), FromBookmarks(R.id.bookmarkFragment), FromHistory(R.id.historyFragment), - FromHistorySearchDialog(R.id.historySearchDialogFragment), FromHistoryMetadataGroup(R.id.historyMetadataGroupFragment), FromTrackingProtectionExceptions(R.id.trackingProtectionExceptionsFragment), FromAbout(R.id.aboutFragment), diff --git a/app/src/main/java/org/mozilla/fenix/HomeActivity.kt b/app/src/main/java/org/mozilla/fenix/HomeActivity.kt index 3a1fce6f92..737ebb4a92 100644 --- a/app/src/main/java/org/mozilla/fenix/HomeActivity.kt +++ b/app/src/main/java/org/mozilla/fenix/HomeActivity.kt @@ -999,8 +999,6 @@ open class HomeActivity : LocaleAwareAppCompatActivity(), NavHostActivity { BookmarkFragmentDirections.actionGlobalBrowser(customTabSessionId) BrowserDirection.FromHistory -> HistoryFragmentDirections.actionGlobalBrowser(customTabSessionId) - BrowserDirection.FromHistorySearchDialog -> - SearchDialogFragmentDirections.actionGlobalBrowser(customTabSessionId) BrowserDirection.FromHistoryMetadataGroup -> HistoryMetadataGroupFragmentDirections.actionGlobalBrowser(customTabSessionId) BrowserDirection.FromTrackingProtectionExceptions -> diff --git a/app/src/main/java/org/mozilla/fenix/library/history/HistoryController.kt b/app/src/main/java/org/mozilla/fenix/library/history/HistoryController.kt index 1f8824bd1f..2464d3004c 100644 --- a/app/src/main/java/org/mozilla/fenix/library/history/HistoryController.kt +++ b/app/src/main/java/org/mozilla/fenix/library/history/HistoryController.kt @@ -24,7 +24,6 @@ import org.mozilla.fenix.components.history.DefaultPagedHistoryProvider import org.mozilla.fenix.ext.components import org.mozilla.fenix.ext.navigateSafe import org.mozilla.fenix.library.history.HistoryFragment.DeleteConfirmationDialogFragment -import org.mozilla.fenix.utils.Settings import org.mozilla.fenix.GleanMetrics.History as GleanHistory @Suppress("TooManyFunctions") @@ -71,7 +70,6 @@ class DefaultHistoryController( delete: (Set) -> suspend (context: Context) -> Unit, ) -> Unit, private val syncHistory: suspend () -> Unit, - private val settings: Settings, ) : HistoryController { override fun handleOpen(item: History) { @@ -117,13 +115,10 @@ class DefaultHistoryController( } override fun handleSearch() { - val directions = if (settings.showUnifiedSearchFeature) { - HistoryFragmentDirections.actionGlobalSearchDialog(null) - } else { - HistoryFragmentDirections.actionGlobalHistorySearchDialog() - } - - navController.navigateSafe(R.id.historyFragment, directions) + navController.navigateSafe( + R.id.historyFragment, + HistoryFragmentDirections.actionGlobalSearchDialog(sessionId = null), + ) } override fun handleDeleteTimeRange() { diff --git a/app/src/main/java/org/mozilla/fenix/library/history/HistoryFragment.kt b/app/src/main/java/org/mozilla/fenix/library/history/HistoryFragment.kt index e037c9e88e..59f74471ce 100644 --- a/app/src/main/java/org/mozilla/fenix/library/history/HistoryFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/library/history/HistoryFragment.kt @@ -156,7 +156,6 @@ class HistoryFragment : LibraryPageFragment(), UserInteractionHandler, deleteSnackbar = ::deleteSnackbar, onTimeFrameDeleted = ::onTimeFrameDeleted, syncHistory = ::syncHistory, - settings = requireContext().components.settings, ) historyInteractor = DefaultHistoryInteractor( historyController, diff --git a/app/src/main/java/org/mozilla/fenix/library/history/HistorySearchController.kt b/app/src/main/java/org/mozilla/fenix/library/history/HistorySearchController.kt deleted file mode 100644 index 347bfb0bf9..0000000000 --- a/app/src/main/java/org/mozilla/fenix/library/history/HistorySearchController.kt +++ /dev/null @@ -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.history - -import mozilla.components.concept.engine.EngineSession.LoadUrlFlags -import mozilla.components.service.glean.private.NoExtras -import org.mozilla.fenix.BrowserDirection -import org.mozilla.fenix.GleanMetrics.History -import org.mozilla.fenix.HomeActivity - -/** - * An interface that handles the view manipulation of the History Search, triggered by the Interactor - */ -interface HistorySearchController { - fun handleEditingCancelled() - fun handleTextChanged(text: String) - fun handleUrlTapped(url: String, flags: LoadUrlFlags = LoadUrlFlags.none()) -} - -class HistorySearchDialogController( - private val activity: HomeActivity, - private val fragmentStore: HistorySearchFragmentStore, - private val clearToolbarFocus: () -> Unit, -) : HistorySearchController { - - override fun handleEditingCancelled() { - clearToolbarFocus() - } - - override fun handleTextChanged(text: String) { - fragmentStore.dispatch(HistorySearchFragmentAction.UpdateQuery(text)) - } - - override fun handleUrlTapped(url: String, flags: LoadUrlFlags) { - History.searchResultTapped.record(NoExtras()) - clearToolbarFocus() - - activity.openToBrowserAndLoad( - searchTermOrURL = url, - newTab = true, - from = BrowserDirection.FromHistorySearchDialog, - flags = flags, - ) - } -} diff --git a/app/src/main/java/org/mozilla/fenix/library/history/HistorySearchDialogFragment.kt b/app/src/main/java/org/mozilla/fenix/library/history/HistorySearchDialogFragment.kt deleted file mode 100644 index 4c524be17d..0000000000 --- a/app/src/main/java/org/mozilla/fenix/library/history/HistorySearchDialogFragment.kt +++ /dev/null @@ -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.history - -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.FragmentHistorySearchDialogBinding -import org.mozilla.fenix.databinding.SearchSuggestionsHintBinding -import org.mozilla.fenix.ext.settings -import org.mozilla.fenix.library.history.awesomebar.AwesomeBarView -import org.mozilla.fenix.library.history.toolbar.ToolbarView -import org.mozilla.fenix.settings.SupportUtils - -@Suppress("TooManyFunctions", "LargeClass") -class HistorySearchDialogFragment : AppCompatDialogFragment(), UserInteractionHandler { - private var _binding: FragmentHistorySearchDialogBinding? = null - private val binding get() = _binding!! - - private lateinit var interactor: HistorySearchDialogInteractor - private lateinit var store: HistorySearchFragmentStore - 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@HistorySearchDialogFragment.onBackPressed() - } - } - } - - override fun onCreateView( - inflater: LayoutInflater, - container: ViewGroup?, - savedInstanceState: Bundle?, - ): View { - _binding = FragmentHistorySearchDialogBinding.inflate(inflater, container, false) - val activity = requireActivity() as HomeActivity - - store = HistorySearchFragmentStore( - createInitialHistorySearchFragmentState(), - ) - - interactor = HistorySearchDialogInteractor( - HistorySearchDialogController( - 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.FromHistorySearchDialog, - ) - } - - 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 -} diff --git a/app/src/main/java/org/mozilla/fenix/library/history/HistorySearchDialogInteractor.kt b/app/src/main/java/org/mozilla/fenix/library/history/HistorySearchDialogInteractor.kt deleted file mode 100644 index 8c95e13f57..0000000000 --- a/app/src/main/java/org/mozilla/fenix/library/history/HistorySearchDialogInteractor.kt +++ /dev/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.history - -import mozilla.components.concept.engine.EngineSession.LoadUrlFlags -import org.mozilla.fenix.library.history.awesomebar.AwesomeBarInteractor -import org.mozilla.fenix.library.history.toolbar.ToolbarInteractor - -/** - * Interactor for the history search - * Provides implementations for the AwesomeBarView and ToolbarView - */ -class HistorySearchDialogInteractor( - private val historySearchController: HistorySearchDialogController, -) : AwesomeBarInteractor, ToolbarInteractor { - - override fun onEditingCanceled() { - historySearchController.handleEditingCancelled() - } - - override fun onTextChanged(text: String) { - historySearchController.handleTextChanged(text) - } - - override fun onUrlTapped(url: String, flags: LoadUrlFlags) { - historySearchController.handleUrlTapped(url, flags) - } -} diff --git a/app/src/main/java/org/mozilla/fenix/library/history/HistorySearchFragmentStore.kt b/app/src/main/java/org/mozilla/fenix/library/history/HistorySearchFragmentStore.kt deleted file mode 100644 index 9086f9beb3..0000000000 --- a/app/src/main/java/org/mozilla/fenix/library/history/HistorySearchFragmentStore.kt +++ /dev/null @@ -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.history - -import mozilla.components.lib.state.Action -import mozilla.components.lib.state.State -import mozilla.components.lib.state.Store - -/** - * The [Store] for holding the [HistorySearchFragmentState] and applying [HistorySearchFragmentAction]s. - */ -class HistorySearchFragmentStore( - initialState: HistorySearchFragmentState, -) : Store( - initialState, - ::historySearchStateReducer, -) - -/** - * The state for the History Search Screen - * - * @property query The current search query string - */ -data class HistorySearchFragmentState( - val query: String, -) : State - -fun createInitialHistorySearchFragmentState(): HistorySearchFragmentState { - return HistorySearchFragmentState(query = "") -} - -/** - * Actions to dispatch through the [HistorySearchFragmentStore] to modify [HistorySearchFragmentState] - * through the reducer. - */ -sealed class HistorySearchFragmentAction : Action { - data class UpdateQuery(val query: String) : HistorySearchFragmentAction() -} - -/** - * The [HistorySearchFragmentState] Reducer. - */ -private fun historySearchStateReducer( - state: HistorySearchFragmentState, - action: HistorySearchFragmentAction, -): HistorySearchFragmentState { - return when (action) { - is HistorySearchFragmentAction.UpdateQuery -> - state.copy(query = action.query) - } -} diff --git a/app/src/main/java/org/mozilla/fenix/library/history/awesomebar/AwesomeBarInteractor.kt b/app/src/main/java/org/mozilla/fenix/library/history/awesomebar/AwesomeBarInteractor.kt deleted file mode 100644 index 1aa1819d5b..0000000000 --- a/app/src/main/java/org/mozilla/fenix/library/history/awesomebar/AwesomeBarInteractor.kt +++ /dev/null @@ -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.history.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()) -} diff --git a/app/src/main/java/org/mozilla/fenix/library/history/awesomebar/AwesomeBarView.kt b/app/src/main/java/org/mozilla/fenix/library/history/awesomebar/AwesomeBarView.kt deleted file mode 100644 index 02dfc58d2d..0000000000 --- a/app/src/main/java/org/mozilla/fenix/library/history/awesomebar/AwesomeBarView.kt +++ /dev/null @@ -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.history.awesomebar - -import mozilla.components.concept.engine.EngineSession -import mozilla.components.feature.awesomebar.provider.CombinedHistorySuggestionProvider -import mozilla.components.feature.session.SessionUseCases -import org.mozilla.fenix.HomeActivity -import org.mozilla.fenix.browser.browsingmode.BrowsingMode -import org.mozilla.fenix.components.Core.Companion.METADATA_SHORTCUT_SUGGESTION_LIMIT -import org.mozilla.fenix.ext.components -import org.mozilla.fenix.library.history.HistorySearchFragmentState - -/** - * View that contains and configures the BrowserAwesomeBar - */ -class AwesomeBarView( - activity: HomeActivity, - val interactor: AwesomeBarInteractor, - val view: AwesomeBarWrapper, -) { - private val combinedHistoryProvider: CombinedHistorySuggestionProvider - - private val loadUrlUseCase = object : SessionUseCases.LoadUrlUseCase { - override fun invoke( - url: String, - flags: EngineSession.LoadUrlFlags, - additionalHeaders: Map?, - ) { - interactor.onUrlTapped(url, flags) - } - } - - init { - val components = activity.components - - val engineForSpeculativeConnects = when (activity.browsingModeManager.mode) { - BrowsingMode.Normal -> components.core.engine - BrowsingMode.Private -> null - } - - combinedHistoryProvider = - CombinedHistorySuggestionProvider( - historyStorage = components.core.historyStorage, - historyMetadataStorage = components.core.historyStorage, - loadUrlUseCase = loadUrlUseCase, - icons = components.core.icons, - engine = engineForSpeculativeConnects, - maxNumberOfSuggestions = METADATA_SHORTCUT_SUGGESTION_LIMIT, - showEditSuggestion = false, - ) - - view.addProviders(combinedHistoryProvider) - } - - fun update(state: HistorySearchFragmentState) { - view.onInputChanged(state.query) - } -} diff --git a/app/src/main/java/org/mozilla/fenix/library/history/awesomebar/AwesomeBarWrapper.kt b/app/src/main/java/org/mozilla/fenix/library/history/awesomebar/AwesomeBarWrapper.kt deleted file mode 100644 index 6d9fd2f9a2..0000000000 --- a/app/src/main/java/org/mozilla/fenix/library/history/awesomebar/AwesomeBarWrapper.kt +++ /dev/null @@ -1,106 +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.history.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.R -import org.mozilla.fenix.ext.components -import org.mozilla.fenix.ext.settings -import org.mozilla.fenix.theme.FirefoxTheme -import org.mozilla.fenix.theme.ThemeManager - -/** - * This wrapper wraps the `AwesomeBar()` composable and exposes it as a `View` and `concept-awesomebar` - * implementation to be integrated in the view hierarchy of [HistorySearchDialogFragment] 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()) - 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 = ThemeManager.resolveAttributeColor(R.attr.textPrimary), - description = ThemeManager.resolveAttributeColor(R.attr.textSecondary), - autocompleteIcon = ThemeManager.resolveAttributeColor(R.attr.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 - } -} diff --git a/app/src/main/java/org/mozilla/fenix/library/history/toolbar/ToolbarView.kt b/app/src/main/java/org/mozilla/fenix/library/history/toolbar/ToolbarView.kt deleted file mode 100644 index 1b7a807799..0000000000 --- a/app/src/main/java/org/mozilla/fenix/library/history/toolbar/ToolbarView.kt +++ /dev/null @@ -1,124 +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.history.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.history.HistorySearchFragmentState - -/** - * 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.history_search_1) - - 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: HistorySearchFragmentState) { - 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 historySearchIcon = AppCompatResources.getDrawable(context, R.drawable.ic_history) - - historySearchIcon?.let { - view.edit.setIcon(historySearchIcon, context.getString(R.string.history_search_1)) - } - } -} diff --git a/app/src/main/res/layout/fragment_history_search_dialog.xml b/app/src/main/res/layout/fragment_history_search_dialog.xml deleted file mode 100644 index 21fa8e0909..0000000000 --- a/app/src/main/res/layout/fragment_history_search_dialog.xml +++ /dev/null @@ -1,111 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - diff --git a/app/src/main/res/navigation/nav_graph.xml b/app/src/main/res/navigation/nav_graph.xml index bf081d3639..22347f9f1d 100644 --- a/app/src/main/res/navigation/nav_graph.xml +++ b/app/src/main/res/navigation/nav_graph.xml @@ -335,20 +335,8 @@ android:name="org.mozilla.fenix.library.history.HistoryFragment" android:label="@string/library_history" tools:layout="@layout/fragment_history"> - - - - - diff --git a/app/src/test/java/org/mozilla/fenix/library/history/HistoryControllerTest.kt b/app/src/test/java/org/mozilla/fenix/library/history/HistoryControllerTest.kt index a00c729de1..f75f22b0e6 100644 --- a/app/src/test/java/org/mozilla/fenix/library/history/HistoryControllerTest.kt +++ b/app/src/test/java/org/mozilla/fenix/library/history/HistoryControllerTest.kt @@ -30,7 +30,6 @@ import org.mozilla.fenix.components.AppStore import org.mozilla.fenix.components.history.DefaultPagedHistoryProvider import org.mozilla.fenix.ext.navigateSafe import org.mozilla.fenix.helpers.FenixRobolectricTestRunner -import org.mozilla.fenix.utils.Settings @RunWith(FenixRobolectricTestRunner::class) class HistoryControllerTest { @@ -56,7 +55,6 @@ class HistoryControllerTest { private val state: HistoryFragmentState = mockk(relaxed = true) private val navController: NavController = mockk(relaxed = true) private val historyProvider: DefaultPagedHistoryProvider = mockk(relaxed = true) - private val settings: Settings = mockk(relaxed = true) @Before fun setUp() { @@ -146,7 +144,7 @@ class HistoryControllerTest { verify { navController.navigateSafe( R.id.historyFragment, - HistoryFragmentDirections.actionGlobalHistorySearchDialog(), + HistoryFragmentDirections.actionGlobalSearchDialog(sessionId = null), ) } } @@ -269,7 +267,6 @@ class HistoryControllerTest { invalidateOptionsMenu, { items, _, _ -> deleteHistoryItems.invoke(items) }, syncHistory, - settings, ) } } diff --git a/app/src/test/java/org/mozilla/fenix/library/history/HistorySearchControllerTest.kt b/app/src/test/java/org/mozilla/fenix/library/history/HistorySearchControllerTest.kt deleted file mode 100644 index cbd1ee8629..0000000000 --- a/app/src/test/java/org/mozilla/fenix/library/history/HistorySearchControllerTest.kt +++ /dev/null @@ -1,103 +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.history - -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.support.test.robolectric.testContext -import mozilla.telemetry.glean.testing.GleanTestRule -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.History -import org.mozilla.fenix.HomeActivity -import org.mozilla.fenix.helpers.FenixRobolectricTestRunner - -@RunWith(FenixRobolectricTestRunner::class) -class HistorySearchControllerTest { - - @get:Rule - val gleanTestRule = GleanTestRule(testContext) - - @MockK(relaxed = true) - private lateinit var activity: HomeActivity - - @MockK(relaxed = true) - private lateinit var store: HistorySearchFragmentStore - - @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(HistorySearchFragmentAction.UpdateQuery(text)) } - } - - @Test - fun `WHEN text is changed to empty THEN update query action is dispatched`() { - val text = "" - - createController().handleTextChanged(text) - - verify { store.dispatch(HistorySearchFragmentAction.UpdateQuery(text)) } - } - - @Test - fun `WHEN url is tapped THEN openToBrowserAndLoad is called`() { - val url = "https://www.google.com/" - val flags = EngineSession.LoadUrlFlags.none() - assertNull(History.searchResultTapped.testGetValue()) - - createController().handleUrlTapped(url, flags) - createController().handleUrlTapped(url) - - assertNotNull(History.searchResultTapped.testGetValue()) - assertNull(History.searchResultTapped.testGetValue()!!.last().extra) - verify { - activity.openToBrowserAndLoad( - searchTermOrURL = url, - newTab = true, - from = BrowserDirection.FromHistorySearchDialog, - flags = flags, - ) - } - } - - private fun createController( - clearToolbarFocus: () -> Unit = { }, - ): HistorySearchDialogController { - return HistorySearchDialogController( - activity = activity, - fragmentStore = store, - clearToolbarFocus = clearToolbarFocus, - ) - } -} diff --git a/app/src/test/java/org/mozilla/fenix/library/history/HistorySearchDialogInteractorTest.kt b/app/src/test/java/org/mozilla/fenix/library/history/HistorySearchDialogInteractorTest.kt deleted file mode 100644 index 6a9c10d541..0000000000 --- a/app/src/test/java/org/mozilla/fenix/library/history/HistorySearchDialogInteractorTest.kt +++ /dev/null @@ -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.history - -import io.mockk.mockk -import io.mockk.verify -import kotlinx.coroutines.test.runTest -import org.junit.Before -import org.junit.Test - -class HistorySearchDialogInteractorTest { - - lateinit var searchController: HistorySearchDialogController - lateinit var interactor: HistorySearchDialogInteractor - - @Before - fun setup() { - searchController = mockk(relaxed = true) - interactor = HistorySearchDialogInteractor( - 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") - } - } -} diff --git a/app/src/test/java/org/mozilla/fenix/library/history/HistorySearchFragmentStoreTest.kt b/app/src/test/java/org/mozilla/fenix/library/history/HistorySearchFragmentStoreTest.kt deleted file mode 100644 index 19937b7eee..0000000000 --- a/app/src/test/java/org/mozilla/fenix/library/history/HistorySearchFragmentStoreTest.kt +++ /dev/null @@ -1,39 +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.history - -import io.mockk.impl.annotations.MockK -import kotlinx.coroutines.test.runTest -import org.junit.Assert.assertEquals -import org.junit.Assert.assertNotSame -import org.junit.Test -import org.mozilla.fenix.components.Components - -class HistorySearchFragmentStoreTest { - - @MockK(relaxed = true) - private lateinit var components: Components - - @Test - fun `GIVEN createInitialHistorySearchFragmentState THEN query is empty`() { - val expected = HistorySearchFragmentState(query = "") - - assertEquals( - expected, - createInitialHistorySearchFragmentState(), - ) - } - - @Test - fun updateQuery() = runTest { - val initialState = HistorySearchFragmentState(query = "") - val store = HistorySearchFragmentStore(initialState) - val query = "test query" - - store.dispatch(HistorySearchFragmentAction.UpdateQuery(query)).join() - assertNotSame(initialState, store.state) - assertEquals(query, store.state.query) - } -} diff --git a/app/src/test/java/org/mozilla/fenix/shopping/middleware/DefaultReviewQualityCheckServiceTest.kt b/app/src/test/java/org/mozilla/fenix/shopping/middleware/DefaultReviewQualityCheckServiceTest.kt index cfb72a9091..8de08b762c 100644 --- a/app/src/test/java/org/mozilla/fenix/shopping/middleware/DefaultReviewQualityCheckServiceTest.kt +++ b/app/src/test/java/org/mozilla/fenix/shopping/middleware/DefaultReviewQualityCheckServiceTest.kt @@ -15,10 +15,12 @@ import mozilla.components.support.test.mock import mozilla.components.support.test.rule.MainCoroutineRule import org.junit.Assert.assertEquals import org.junit.Assert.assertNull +import org.junit.Ignore import org.junit.Rule import org.junit.Test import org.mockito.Mockito.doAnswer +@Ignore("Intermittent failure, see https://bugzilla.mozilla.org/show_bug.cgi?id=1849525.") class DefaultReviewQualityCheckServiceTest { @get:Rule