2
0
mirror of https://github.com/fork-maintainers/iceraven-browser synced 2024-11-17 15:26:23 +00:00

[fenix] For https://github.com/mozilla-mobile/fenix/issues/17917 - Migrate home from Kotlin synthetics to View Binding.

This commit is contained in:
Mugurell 2021-08-17 10:43:57 +03:00 committed by mergify[bot]
parent b38f717c4b
commit 5b99139048
17 changed files with 187 additions and 186 deletions

View File

@ -32,7 +32,6 @@ import androidx.navigation.NavDirections
import androidx.navigation.fragment.NavHostFragment import androidx.navigation.fragment.NavHostFragment
import androidx.navigation.ui.AppBarConfiguration import androidx.navigation.ui.AppBarConfiguration
import androidx.navigation.ui.NavigationUI import androidx.navigation.ui.NavigationUI
import kotlinx.android.synthetic.main.activity_home.*
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
@ -74,6 +73,7 @@ import org.mozilla.fenix.browser.browsingmode.BrowsingModeManager
import org.mozilla.fenix.browser.browsingmode.DefaultBrowsingModeManager import org.mozilla.fenix.browser.browsingmode.DefaultBrowsingModeManager
import org.mozilla.fenix.components.metrics.BreadcrumbsRecorder import org.mozilla.fenix.components.metrics.BreadcrumbsRecorder
import org.mozilla.fenix.components.metrics.Event import org.mozilla.fenix.components.metrics.Event
import org.mozilla.fenix.databinding.ActivityHomeBinding
import org.mozilla.fenix.exceptions.trackingprotection.TrackingProtectionExceptionsFragmentDirections import org.mozilla.fenix.exceptions.trackingprotection.TrackingProtectionExceptionsFragmentDirections
import org.mozilla.fenix.ext.alreadyOnDestination import org.mozilla.fenix.ext.alreadyOnDestination
import org.mozilla.fenix.ext.breadcrumb import org.mozilla.fenix.ext.breadcrumb
@ -134,6 +134,7 @@ open class HomeActivity : LocaleAwareAppCompatActivity(), NavHostActivity {
// components requires context to access. // components requires context to access.
protected val homeActivityInitTimeStampNanoSeconds = SystemClock.elapsedRealtimeNanos() protected val homeActivityInitTimeStampNanoSeconds = SystemClock.elapsedRealtimeNanos()
private lateinit var binding: ActivityHomeBinding
lateinit var themeManager: ThemeManager lateinit var themeManager: ThemeManager
lateinit var browsingModeManager: BrowsingModeManager lateinit var browsingModeManager: BrowsingModeManager
@ -203,8 +204,9 @@ open class HomeActivity : LocaleAwareAppCompatActivity(), NavHostActivity {
// Must be after we set the content view // Must be after we set the content view
if (isVisuallyComplete) { if (isVisuallyComplete) {
binding = ActivityHomeBinding.bind(window.decorView.findViewById(R.id.rootContainer))
components.performance.visualCompletenessQueue components.performance.visualCompletenessQueue
.attachViewToRunVisualCompletenessQueueLater(WeakReference(rootContainer)) .attachViewToRunVisualCompletenessQueueLater(WeakReference(binding.rootContainer))
} }
privateNotificationObserver = PrivateNotificationFeature( privateNotificationObserver = PrivateNotificationFeature(
@ -270,7 +272,7 @@ open class HomeActivity : LocaleAwareAppCompatActivity(), NavHostActivity {
components.performance.visualCompletenessQueue, components.performance.visualCompletenessQueue,
components.startupStateProvider, components.startupStateProvider,
safeIntent, safeIntent,
rootContainer binding.rootContainer
) )
} }
@ -310,7 +312,7 @@ open class HomeActivity : LocaleAwareAppCompatActivity(), NavHostActivity {
message = "onStart()" message = "onStart()"
) )
ProfilerMarkers.homeActivityOnStart(rootContainer, components.core.engine.profiler) ProfilerMarkers.homeActivityOnStart(binding.rootContainer, components.core.engine.profiler)
} }
override fun onStop() { override fun onStop() {
@ -651,7 +653,7 @@ open class HomeActivity : LocaleAwareAppCompatActivity(), NavHostActivity {
*/ */
override fun getSupportActionBarAndInflateIfNecessary(): ActionBar { override fun getSupportActionBarAndInflateIfNecessary(): ActionBar {
if (!isToolbarInflated) { if (!isToolbarInflated) {
navigationToolbar = navigationToolbarStub.inflate() as Toolbar navigationToolbar = binding.navigationToolbarStub.inflate() as Toolbar
setSupportActionBar(navigationToolbar) setSupportActionBar(navigationToolbar)
// Add ids to this that we don't want to have a toolbar back button // Add ids to this that we don't want to have a toolbar back button

View File

@ -5,6 +5,7 @@
package org.mozilla.fenix.home package org.mozilla.fenix.home
import android.animation.Animator import android.animation.Animator
import android.annotation.SuppressLint
import android.content.Context import android.content.Context
import android.content.res.Configuration import android.content.res.Configuration
import android.graphics.drawable.BitmapDrawable import android.graphics.drawable.BitmapDrawable
@ -41,10 +42,8 @@ import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.RecyclerView.SCROLL_STATE_IDLE import androidx.recyclerview.widget.RecyclerView.SCROLL_STATE_IDLE
import com.google.android.material.appbar.AppBarLayout import com.google.android.material.appbar.AppBarLayout
import com.google.android.material.button.MaterialButton
import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar
import kotlinx.android.synthetic.main.fragment_home.*
import kotlinx.android.synthetic.main.fragment_home.view.*
import kotlinx.android.synthetic.main.no_collections_message.view.*
import kotlinx.coroutines.Dispatchers.IO import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.Dispatchers.Main import kotlinx.coroutines.Dispatchers.Main
import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.ExperimentalCoroutinesApi
@ -92,6 +91,7 @@ import org.mozilla.fenix.components.tips.Tip
import org.mozilla.fenix.components.tips.providers.MasterPasswordTipProvider import org.mozilla.fenix.components.tips.providers.MasterPasswordTipProvider
import org.mozilla.fenix.components.toolbar.FenixTabCounterMenu import org.mozilla.fenix.components.toolbar.FenixTabCounterMenu
import org.mozilla.fenix.components.toolbar.ToolbarPosition import org.mozilla.fenix.components.toolbar.ToolbarPosition
import org.mozilla.fenix.databinding.FragmentHomeBinding
import org.mozilla.fenix.ext.components import org.mozilla.fenix.ext.components
import org.mozilla.fenix.ext.hideToolbar import org.mozilla.fenix.ext.hideToolbar
import org.mozilla.fenix.ext.metrics import org.mozilla.fenix.ext.metrics
@ -128,20 +128,24 @@ class HomeFragment : Fragment() {
private val args by navArgs<HomeFragmentArgs>() private val args by navArgs<HomeFragmentArgs>()
private lateinit var bundleArgs: Bundle private lateinit var bundleArgs: Bundle
private var _binding: FragmentHomeBinding? = null
private val binding get() = _binding!!
private val homeViewModel: HomeScreenViewModel by activityViewModels() private val homeViewModel: HomeScreenViewModel by activityViewModels()
private val snackbarAnchorView: View? private val snackbarAnchorView: View?
get() = when (requireContext().settings().toolbarPosition) { get() = when (requireContext().settings().toolbarPosition) {
ToolbarPosition.BOTTOM -> toolbarLayout ToolbarPosition.BOTTOM -> binding.toolbarLayout
ToolbarPosition.TOP -> null ToolbarPosition.TOP -> null
} }
private val browsingModeManager get() = (activity as HomeActivity).browsingModeManager private val browsingModeManager get() = (activity as HomeActivity).browsingModeManager
private val collectionStorageObserver = object : TabCollectionStorage.Observer { private val collectionStorageObserver = object : TabCollectionStorage.Observer {
@SuppressLint("NotifyDataSetChanged")
override fun onCollectionRenamed(tabCollection: TabCollection, title: String) { override fun onCollectionRenamed(tabCollection: TabCollection, title: String) {
lifecycleScope.launch(Main) { lifecycleScope.launch(Main) {
view?.sessionControlRecyclerView?.adapter?.notifyDataSetChanged() binding.sessionControlRecyclerView.adapter?.notifyDataSetChanged()
} }
showRenamedSnackbar() showRenamedSnackbar()
} }
@ -171,7 +175,7 @@ class HomeFragment : Fragment() {
private val historyMetadataFeature = ViewBoundFeatureWrapper<HistoryMetadataFeature>() private val historyMetadataFeature = ViewBoundFeatureWrapper<HistoryMetadataFeature>()
@VisibleForTesting @VisibleForTesting
internal var getMenuButton: () -> MenuButton? = { menuButton } internal var getMenuButton: () -> MenuButton? = { binding.menuButton }
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
@ -196,13 +200,13 @@ class HomeFragment : Fragment() {
inflater: LayoutInflater, inflater: LayoutInflater,
container: ViewGroup?, container: ViewGroup?,
savedInstanceState: Bundle? savedInstanceState: Bundle?
): View? { ): View {
val view = inflater.inflate(R.layout.fragment_home, container, false) _binding = FragmentHomeBinding.inflate(inflater, container, false)
val activity = activity as HomeActivity val activity = activity as HomeActivity
val components = requireComponents val components = requireComponents
currentMode = CurrentMode( currentMode = CurrentMode(
view.context, requireContext(),
onboarding, onboarding,
browsingModeManager, browsingModeManager,
::dispatchModeChanges ::dispatchModeChanges
@ -242,7 +246,7 @@ class HomeFragment : Fragment() {
config = ::getTopSitesConfig config = ::getTopSitesConfig
), ),
owner = viewLifecycleOwner, owner = viewLifecycleOwner,
view = view view = binding.root
) )
if (FeatureFlags.showRecentTabsFeature) { if (FeatureFlags.showRecentTabsFeature) {
@ -252,7 +256,7 @@ class HomeFragment : Fragment() {
homeStore = homeFragmentStore homeStore = homeFragmentStore
), ),
owner = viewLifecycleOwner, owner = viewLifecycleOwner,
view = view view = binding.root
) )
} }
@ -266,7 +270,7 @@ class HomeFragment : Fragment() {
scope = viewLifecycleOwner.lifecycleScope scope = viewLifecycleOwner.lifecycleScope
), ),
owner = viewLifecycleOwner, owner = viewLifecycleOwner,
view = view view = binding.root
) )
} }
@ -278,7 +282,7 @@ class HomeFragment : Fragment() {
scope = viewLifecycleOwner.lifecycleScope scope = viewLifecycleOwner.lifecycleScope
), ),
owner = viewLifecycleOwner, owner = viewLifecycleOwner,
view = view view = binding.root
) )
} }
@ -322,20 +326,20 @@ class HomeFragment : Fragment() {
) )
) )
updateLayout(view) updateLayout(binding.root)
sessionControlView = SessionControlView( sessionControlView = SessionControlView(
view.sessionControlRecyclerView, binding.sessionControlRecyclerView,
viewLifecycleOwner, viewLifecycleOwner,
sessionControlInteractor, sessionControlInteractor,
homeViewModel homeViewModel
) )
updateSessionControlView(view) updateSessionControlView()
appBarLayout = view.homeAppBar appBarLayout = binding.homeAppBar
activity.themeManager.applyStatusBarTheme(activity) activity.themeManager.applyStatusBarTheme(activity)
return view return binding.root
} }
override fun onConfigurationChanged(newConfig: Configuration) { override fun onConfigurationChanged(newConfig: Configuration) {
@ -367,24 +371,24 @@ class HomeFragment : Fragment() {
* data in our store. The [View.consumeFrom] coroutine dispatch * data in our store. The [View.consumeFrom] coroutine dispatch
* doesn't get run right away which means that we won't draw on the first layout pass. * doesn't get run right away which means that we won't draw on the first layout pass.
*/ */
private fun updateSessionControlView(view: View) { private fun updateSessionControlView() {
if (browsingModeManager.mode == BrowsingMode.Private) { if (browsingModeManager.mode == BrowsingMode.Private) {
view.consumeFrom(homeFragmentStore, viewLifecycleOwner) { binding.root.consumeFrom(homeFragmentStore, viewLifecycleOwner) {
sessionControlView?.update(it) sessionControlView?.update(it)
} }
} else { } else {
sessionControlView?.update(homeFragmentStore.state) sessionControlView?.update(homeFragmentStore.state)
view.consumeFrom(homeFragmentStore, viewLifecycleOwner) { binding.root.consumeFrom(homeFragmentStore, viewLifecycleOwner) {
sessionControlView?.update(it) sessionControlView?.update(it)
} }
} }
} }
private fun updateLayout(view: View) { private fun updateLayout(view: View) {
when (view.context.settings().toolbarPosition) { when (requireContext().settings().toolbarPosition) {
ToolbarPosition.TOP -> { ToolbarPosition.TOP -> {
view.toolbarLayout.layoutParams = CoordinatorLayout.LayoutParams( binding.toolbarLayout.layoutParams = CoordinatorLayout.LayoutParams(
ConstraintLayout.LayoutParams.MATCH_PARENT, ConstraintLayout.LayoutParams.MATCH_PARENT,
ConstraintLayout.LayoutParams.WRAP_CONTENT ConstraintLayout.LayoutParams.WRAP_CONTENT
).apply { ).apply {
@ -392,21 +396,21 @@ class HomeFragment : Fragment() {
} }
ConstraintSet().apply { ConstraintSet().apply {
clone(view.toolbarLayout) clone(binding.toolbarLayout)
clear(view.bottom_bar.id, BOTTOM) clear(binding.bottomBar.id, BOTTOM)
clear(view.bottomBarShadow.id, BOTTOM) clear(binding.bottomBarShadow.id, BOTTOM)
connect(view.bottom_bar.id, TOP, PARENT_ID, TOP) connect(binding.bottomBar.id, TOP, PARENT_ID, TOP)
connect(view.bottomBarShadow.id, TOP, view.bottom_bar.id, BOTTOM) connect(binding.bottomBarShadow.id, TOP, binding.bottomBar.id, BOTTOM)
connect(view.bottomBarShadow.id, BOTTOM, PARENT_ID, BOTTOM) connect(binding.bottomBarShadow.id, BOTTOM, PARENT_ID, BOTTOM)
applyTo(view.toolbarLayout) applyTo(binding.toolbarLayout)
} }
view.bottom_bar.background = AppCompatResources.getDrawable( binding.bottomBar.background = AppCompatResources.getDrawable(
view.context, view.context,
view.context.theme.resolveAttribute(R.attr.bottomBarBackgroundTop) view.context.theme.resolveAttribute(R.attr.bottomBarBackgroundTop)
) )
view.homeAppBar.updateLayoutParams<ViewGroup.MarginLayoutParams> { binding.homeAppBar.updateLayoutParams<ViewGroup.MarginLayoutParams> {
topMargin = topMargin =
resources.getDimensionPixelSize(R.dimen.home_fragment_top_toolbar_header_margin) resources.getDimensionPixelSize(R.dimen.home_fragment_top_toolbar_header_margin)
} }
@ -422,24 +426,24 @@ class HomeFragment : Fragment() {
context?.metrics?.track(Event.HomeScreenDisplayed) context?.metrics?.track(Event.HomeScreenDisplayed)
observeSearchEngineChanges() observeSearchEngineChanges()
createHomeMenu(requireContext(), WeakReference(view.menuButton)) createHomeMenu(requireContext(), WeakReference(binding.menuButton))
createTabCounterMenu(view) createTabCounterMenu()
view.menuButton.setColorFilter( binding.menuButton.setColorFilter(
ContextCompat.getColor( ContextCompat.getColor(
requireContext(), requireContext(),
ThemeManager.resolveAttribute(R.attr.primaryText, requireContext()) ThemeManager.resolveAttribute(R.attr.primaryText, requireContext())
) )
) )
view.toolbar.compoundDrawablePadding = binding.toolbar.compoundDrawablePadding =
view.resources.getDimensionPixelSize(R.dimen.search_bar_search_engine_icon_padding) view.resources.getDimensionPixelSize(R.dimen.search_bar_search_engine_icon_padding)
view.toolbar_wrapper.setOnClickListener { binding.toolbarWrapper.setOnClickListener {
navigateToSearch() navigateToSearch()
requireComponents.analytics.metrics.track(Event.SearchBarTapped(Event.SearchBarTapped.Source.HOME)) requireComponents.analytics.metrics.track(Event.SearchBarTapped(Event.SearchBarTapped.Source.HOME))
} }
view.toolbar_wrapper.setOnLongClickListener { binding.toolbarWrapper.setOnLongClickListener {
ToolbarPopupWindow.show( ToolbarPopupWindow.show(
WeakReference(it), WeakReference(it),
handlePasteAndGo = sessionControlInteractor::onPasteAndGo, handlePasteAndGo = sessionControlInteractor::onPasteAndGo,
@ -449,7 +453,7 @@ class HomeFragment : Fragment() {
true true
} }
view.tab_button.setOnClickListener { binding.tabButton.setOnClickListener {
if (FeatureFlags.showStartOnHomeSettings) { if (FeatureFlags.showStartOnHomeSettings) {
requireComponents.analytics.metrics.track(Event.StartOnHomeOpenTabsTray) requireComponents.analytics.metrics.track(Event.StartOnHomeOpenTabsTray)
} }
@ -457,7 +461,7 @@ class HomeFragment : Fragment() {
} }
PrivateBrowsingButtonView( PrivateBrowsingButtonView(
privateBrowsingButton, binding.privateBrowsingButton,
browsingModeManager browsingModeManager
) { newMode -> ) { newMode ->
if (newMode == BrowsingMode.Private) { if (newMode == BrowsingMode.Private) {
@ -509,15 +513,15 @@ class HomeFragment : Fragment() {
val searchIcon = val searchIcon =
BitmapDrawable(requireContext().resources, searchEngine.icon) BitmapDrawable(requireContext().resources, searchEngine.icon)
searchIcon.setBounds(0, 0, iconSize, iconSize) searchIcon.setBounds(0, 0, iconSize, iconSize)
search_engine_icon?.setImageDrawable(searchIcon) binding.searchEngineIcon.setImageDrawable(searchIcon)
} else { } else {
search_engine_icon.setImageDrawable(null) binding.searchEngineIcon.setImageDrawable(null)
} }
} }
} }
} }
private fun createTabCounterMenu(view: View) { private fun createTabCounterMenu() {
val browsingModeManager = (activity as HomeActivity).browsingModeManager val browsingModeManager = (activity as HomeActivity).browsingModeManager
val mode = browsingModeManager.mode val mode = browsingModeManager.mode
@ -530,7 +534,7 @@ class HomeFragment : Fragment() {
} }
val tabCounterMenu = FenixTabCounterMenu( val tabCounterMenu = FenixTabCounterMenu(
view.context, requireContext(),
onItemTapped, onItemTapped,
iconColor = if (mode == BrowsingMode.Private) { iconColor = if (mode == BrowsingMode.Private) {
ContextCompat.getColor(requireContext(), R.color.primary_text_private_theme) ContextCompat.getColor(requireContext(), R.color.primary_text_private_theme)
@ -545,7 +549,7 @@ class HomeFragment : Fragment() {
} }
tabCounterMenu.updateMenu(showOnly = inverseBrowsingMode) tabCounterMenu.updateMenu(showOnly = inverseBrowsingMode)
view.tab_button.setOnLongClickListener { binding.tabButton.setOnLongClickListener {
tabCounterMenu.menuController.show(anchor = it) tabCounterMenu.menuController.show(anchor = it)
true true
} }
@ -608,6 +612,7 @@ class HomeFragment : Fragment() {
_sessionControlInteractor = null _sessionControlInteractor = null
sessionControlView = null sessionControlView = null
appBarLayout = null appBarLayout = null
_binding = null
bundleArgs.clear() bundleArgs.clear()
} }
@ -663,7 +668,7 @@ class HomeFragment : Fragment() {
isDisplayedWithBrowserToolbar = false isDisplayedWithBrowserToolbar = false
) )
.setText(it.context.getString(R.string.onboarding_firefox_account_sync_is_on)) .setText(it.context.getString(R.string.onboarding_firefox_account_sync_is_on))
.setAnchorView(toolbarLayout) .setAnchorView(binding.toolbarLayout)
.show() .show()
} }
} }
@ -774,12 +779,12 @@ class HomeFragment : Fragment() {
} }
// We want to show the popup only after privateBrowsingButton is available. // We want to show the popup only after privateBrowsingButton is available.
// Otherwise, we will encounter an activity token error. // Otherwise, we will encounter an activity token error.
privateBrowsingButton.post { binding.privateBrowsingButton.post {
runIfFragmentIsAttached { runIfFragmentIsAttached {
context.settings().showedPrivateModeContextualFeatureRecommender = true context.settings().showedPrivateModeContextualFeatureRecommender = true
context.settings().lastCfrShownTimeInMillis = System.currentTimeMillis() context.settings().lastCfrShownTimeInMillis = System.currentTimeMillis()
privateBrowsingRecommend.showAsDropDown( privateBrowsingRecommend.showAsDropDown(
privateBrowsingButton, 0, CFR_Y_OFFSET, Gravity.TOP or Gravity.END binding.privateBrowsingButton, 0, CFR_Y_OFFSET, Gravity.TOP or Gravity.END
) )
} }
} }
@ -1105,12 +1110,15 @@ class HomeFragment : Fragment() {
browserState.normalTabs.size browserState.normalTabs.size
} }
view?.tab_button?.setCountWithAnimation(tabCount) binding.tabButton.setCountWithAnimation(tabCount)
view?.add_tabs_to_collections_button?.isVisible = tabCount > 0 // The add_tabs_to_collections_button is added at runtime. We need to search for it in the same way.
sessionControlView?.view?.findViewById<MaterialButton>(R.id.add_tabs_to_collections_button)
?.isVisible = tabCount > 0
} }
@SuppressLint("NotifyDataSetChanged")
private fun handleSwipedItemDeletionCancel() { private fun handleSwipedItemDeletionCancel() {
view?.sessionControlRecyclerView?.adapter?.notifyDataSetChanged() binding.sessionControlRecyclerView.adapter?.notifyDataSetChanged()
} }
companion object { companion object {

View File

@ -5,13 +5,10 @@
package org.mozilla.fenix.home.recentbookmarks.view package org.mozilla.fenix.home.recentbookmarks.view
import android.view.View import android.view.View
import kotlinx.android.synthetic.main.recent_bookmark_item.bookmark_title
import kotlinx.android.synthetic.main.recent_bookmark_item.bookmark_subtitle
import kotlinx.android.synthetic.main.recent_bookmark_item.bookmark_item
import kotlinx.android.synthetic.main.recent_bookmark_item.favicon_image
import mozilla.components.concept.storage.BookmarkNode import mozilla.components.concept.storage.BookmarkNode
import mozilla.components.support.ktx.kotlin.tryGetHostFromUrl import mozilla.components.support.ktx.kotlin.tryGetHostFromUrl
import org.mozilla.fenix.R import org.mozilla.fenix.R
import org.mozilla.fenix.databinding.RecentBookmarkItemBinding
import org.mozilla.fenix.ext.components import org.mozilla.fenix.ext.components
import org.mozilla.fenix.ext.loadIntoView import org.mozilla.fenix.ext.loadIntoView
import org.mozilla.fenix.home.recentbookmarks.interactor.RecentBookmarksInteractor import org.mozilla.fenix.home.recentbookmarks.interactor.RecentBookmarksInteractor
@ -23,15 +20,17 @@ class RecentBookmarkItemViewHolder(
) : ViewHolder(view) { ) : ViewHolder(view) {
fun bind(bookmark: BookmarkNode) { fun bind(bookmark: BookmarkNode) {
bookmark_title.text = bookmark.title ?: bookmark.url val binding = RecentBookmarkItemBinding.bind(view)
bookmark_subtitle.text = bookmark.url?.tryGetHostFromUrl() ?: bookmark.title ?: ""
bookmark_item.setOnClickListener { binding.bookmarkTitle.text = bookmark.title ?: bookmark.url
binding.bookmarkSubtitle.text = bookmark.url?.tryGetHostFromUrl() ?: bookmark.title ?: ""
binding.bookmarkItem.setOnClickListener {
interactor.onRecentBookmarkClicked(bookmark) interactor.onRecentBookmarkClicked(bookmark)
} }
bookmark.url?.let { bookmark.url?.let {
view.context.components.core.icons.loadIntoView(favicon_image, it) view.context.components.core.icons.loadIntoView(binding.faviconImage, it)
} }
} }

View File

@ -8,10 +8,9 @@ import android.view.View
import androidx.navigation.findNavController import androidx.navigation.findNavController
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.LinearLayoutManager.HORIZONTAL import androidx.recyclerview.widget.LinearLayoutManager.HORIZONTAL
import kotlinx.android.synthetic.main.component_recent_bookmarks.view.*
import kotlinx.android.synthetic.main.recent_bookmarks_header.*
import mozilla.components.concept.storage.BookmarkNode import mozilla.components.concept.storage.BookmarkNode
import org.mozilla.fenix.R import org.mozilla.fenix.R
import org.mozilla.fenix.databinding.ComponentRecentBookmarksBinding
import org.mozilla.fenix.home.recentbookmarks.RecentBookmarksItemAdapter import org.mozilla.fenix.home.recentbookmarks.RecentBookmarksItemAdapter
import org.mozilla.fenix.home.recentbookmarks.interactor.RecentBookmarksInteractor import org.mozilla.fenix.home.recentbookmarks.interactor.RecentBookmarksInteractor
import org.mozilla.fenix.utils.view.ViewHolder import org.mozilla.fenix.utils.view.ViewHolder
@ -24,14 +23,17 @@ class RecentBookmarksViewHolder(
private val recentBookmarksAdapter = RecentBookmarksItemAdapter(interactor) private val recentBookmarksAdapter = RecentBookmarksItemAdapter(interactor)
init { init {
val recentBookmarksBinding = ComponentRecentBookmarksBinding.bind(view)
val recentBookmarksHeaderBinding = recentBookmarksBinding.recentBookmarksHeader
val linearLayoutManager = LinearLayoutManager(view.context, HORIZONTAL, false) val linearLayoutManager = LinearLayoutManager(view.context, HORIZONTAL, false)
view.recent_bookmarks_list.apply { recentBookmarksBinding.recentBookmarksList.apply {
adapter = recentBookmarksAdapter adapter = recentBookmarksAdapter
layoutManager = linearLayoutManager layoutManager = linearLayoutManager
} }
showAllBookmarksButton.setOnClickListener { recentBookmarksHeaderBinding.showAllBookmarksButton.setOnClickListener {
dismissSearchDialogIfDisplayed() dismissSearchDialogIfDisplayed()
interactor.onShowAllBookmarksClicked() interactor.onShowAllBookmarksClicked()
} }

View File

@ -10,7 +10,6 @@ import androidx.lifecycle.LifecycleOwner
import androidx.recyclerview.widget.ItemTouchHelper import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import kotlinx.android.extensions.LayoutContainer
import mozilla.components.concept.storage.BookmarkNode import mozilla.components.concept.storage.BookmarkNode
import mozilla.components.browser.state.state.TabSessionState import mozilla.components.browser.state.state.TabSessionState
import mozilla.components.feature.tab.collections.TabCollection import mozilla.components.feature.tab.collections.TabCollection
@ -207,11 +206,11 @@ private fun collectionTabItems(collection: TabCollection) =
} }
class SessionControlView( class SessionControlView(
override val containerView: View, val containerView: View,
viewLifecycleOwner: LifecycleOwner, viewLifecycleOwner: LifecycleOwner,
interactor: SessionControlInteractor, interactor: SessionControlInteractor,
private var homeScreenViewModel: HomeScreenViewModel private var homeScreenViewModel: HomeScreenViewModel
) : LayoutContainer { ) {
val view: RecyclerView = containerView as RecyclerView val view: RecyclerView = containerView as RecyclerView

View File

@ -10,7 +10,6 @@ import android.view.MotionEvent
import android.view.View import android.view.View
import android.widget.PopupWindow import android.widget.PopupWindow
import androidx.appcompat.content.res.AppCompatResources.getDrawable import androidx.appcompat.content.res.AppCompatResources.getDrawable
import kotlinx.android.synthetic.main.top_site_item.*
import mozilla.components.browser.menu.BrowserMenuBuilder import mozilla.components.browser.menu.BrowserMenuBuilder
import mozilla.components.browser.menu.item.SimpleBrowserMenuItem import mozilla.components.browser.menu.item.SimpleBrowserMenuItem
import mozilla.components.feature.top.sites.TopSite import mozilla.components.feature.top.sites.TopSite
@ -19,6 +18,7 @@ import mozilla.components.feature.top.sites.TopSite.Type.FRECENT
import mozilla.components.feature.top.sites.TopSite.Type.PINNED import mozilla.components.feature.top.sites.TopSite.Type.PINNED
import org.mozilla.fenix.R import org.mozilla.fenix.R
import org.mozilla.fenix.components.metrics.Event import org.mozilla.fenix.components.metrics.Event
import org.mozilla.fenix.databinding.TopSiteItemBinding
import org.mozilla.fenix.ext.components import org.mozilla.fenix.ext.components
import org.mozilla.fenix.ext.loadIntoView import org.mozilla.fenix.ext.loadIntoView
import org.mozilla.fenix.home.sessioncontrol.TopSiteInteractor import org.mozilla.fenix.home.sessioncontrol.TopSiteInteractor
@ -30,13 +30,14 @@ class TopSiteItemViewHolder(
private val interactor: TopSiteInteractor private val interactor: TopSiteInteractor
) : ViewHolder(view) { ) : ViewHolder(view) {
private lateinit var topSite: TopSite private lateinit var topSite: TopSite
private val binding = TopSiteItemBinding.bind(view)
init { init {
top_site_item.setOnClickListener { binding.topSiteItem.setOnClickListener {
interactor.onSelectTopSite(topSite.url, topSite.type) interactor.onSelectTopSite(topSite.url, topSite.type)
} }
top_site_item.setOnLongClickListener { binding.topSiteItem.setOnLongClickListener {
interactor.onTopSiteMenuOpened() interactor.onTopSiteMenuOpened()
it.context.components.analytics.metrics.track(Event.TopSiteLongPress(topSite.type)) it.context.components.analytics.metrics.track(Event.TopSiteLongPress(topSite.type))
@ -62,30 +63,30 @@ class TopSiteItemViewHolder(
} }
fun bind(topSite: TopSite) { fun bind(topSite: TopSite) {
top_site_title.text = topSite.title binding.topSiteTitle.text = topSite.title
if (topSite.type == PINNED || topSite.type == DEFAULT) { if (topSite.type == PINNED || topSite.type == DEFAULT) {
val pinIndicator = getDrawable(itemView.context, R.drawable.ic_new_pin) val pinIndicator = getDrawable(itemView.context, R.drawable.ic_new_pin)
top_site_title.setCompoundDrawablesWithIntrinsicBounds(pinIndicator, null, null, null) binding.topSiteTitle.setCompoundDrawablesWithIntrinsicBounds(pinIndicator, null, null, null)
} else { } else {
top_site_title.setCompoundDrawablesWithIntrinsicBounds(null, null, null, null) binding.topSiteTitle.setCompoundDrawablesWithIntrinsicBounds(null, null, null, null)
} }
when (topSite.url) { when (topSite.url) {
SupportUtils.POCKET_TRENDING_URL -> { SupportUtils.POCKET_TRENDING_URL -> {
favicon_image.setImageDrawable(getDrawable(itemView.context, R.drawable.ic_pocket)) binding.faviconImage.setImageDrawable(getDrawable(itemView.context, R.drawable.ic_pocket))
} }
SupportUtils.BAIDU_URL -> { SupportUtils.BAIDU_URL -> {
favicon_image.setImageDrawable(getDrawable(itemView.context, R.drawable.ic_baidu)) binding.faviconImage.setImageDrawable(getDrawable(itemView.context, R.drawable.ic_baidu))
} }
SupportUtils.JD_URL -> { SupportUtils.JD_URL -> {
favicon_image.setImageDrawable(getDrawable(itemView.context, R.drawable.ic_jd)) binding.faviconImage.setImageDrawable(getDrawable(itemView.context, R.drawable.ic_jd))
} }
SupportUtils.PDD_URL -> { SupportUtils.PDD_URL -> {
favicon_image.setImageDrawable(getDrawable(itemView.context, R.drawable.ic_pdd)) binding.faviconImage.setImageDrawable(getDrawable(itemView.context, R.drawable.ic_pdd))
} }
else -> { else -> {
itemView.context.components.core.icons.loadIntoView(favicon_image, topSite.url) itemView.context.components.core.icons.loadIntoView(binding.faviconImage, topSite.url)
} }
} }

View File

@ -9,7 +9,9 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="vertical"> android:orientation="vertical">
<include layout="@layout/recent_bookmarks_header" /> <include
android:id="@+id/recent_bookmarks_header"
layout="@layout/recent_bookmarks_header" />
<androidx.recyclerview.widget.RecyclerView <androidx.recyclerview.widget.RecyclerView
android:id="@+id/recent_bookmarks_list" android:id="@+id/recent_bookmarks_list"

View File

@ -5,9 +5,7 @@
package org.mozilla.fenix.home.recentbookmarks.view package org.mozilla.fenix.home.recentbookmarks.view
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View
import io.mockk.mockk import io.mockk.mockk
import kotlinx.android.synthetic.main.recent_bookmark_item.view.*
import mozilla.components.concept.storage.BookmarkNode import mozilla.components.concept.storage.BookmarkNode
import mozilla.components.concept.storage.BookmarkNodeType import mozilla.components.concept.storage.BookmarkNodeType
import mozilla.components.support.ktx.kotlin.tryGetHostFromUrl import mozilla.components.support.ktx.kotlin.tryGetHostFromUrl
@ -16,13 +14,14 @@ import org.junit.Assert
import org.junit.Before import org.junit.Before
import org.junit.Test import org.junit.Test
import org.junit.runner.RunWith import org.junit.runner.RunWith
import org.mozilla.fenix.databinding.RecentBookmarkItemBinding
import org.mozilla.fenix.helpers.FenixRobolectricTestRunner import org.mozilla.fenix.helpers.FenixRobolectricTestRunner
import org.mozilla.fenix.home.sessioncontrol.SessionControlInteractor import org.mozilla.fenix.home.sessioncontrol.SessionControlInteractor
@RunWith(FenixRobolectricTestRunner::class) @RunWith(FenixRobolectricTestRunner::class)
class RecentBookmarkItemViewHolderTest { class RecentBookmarkItemViewHolderTest {
private lateinit var view: View private lateinit var binding: RecentBookmarkItemBinding
private lateinit var interactor: SessionControlInteractor private lateinit var interactor: SessionControlInteractor
private val bookmarkNoUrl = BookmarkNode( private val bookmarkNoUrl = BookmarkNode(
@ -60,37 +59,36 @@ class RecentBookmarkItemViewHolderTest {
@Before @Before
fun setup() { fun setup() {
view = LayoutInflater.from(testContext) binding = RecentBookmarkItemBinding.inflate(LayoutInflater.from(testContext))
.inflate(RecentBookmarkItemViewHolder.LAYOUT_ID, null)
interactor = mockk(relaxed = true) interactor = mockk(relaxed = true)
} }
@Test @Test
fun `GIVEN a bookmark exists in the list THEN set the title text and subtitle from item`() { fun `GIVEN a bookmark exists in the list THEN set the title text and subtitle from item`() {
RecentBookmarkItemViewHolder(view, interactor).bind(bookmarkWithUrl) RecentBookmarkItemViewHolder(binding.root, interactor).bind(bookmarkWithUrl)
val hostFromUrl = bookmarkWithUrl.url?.tryGetHostFromUrl() val hostFromUrl = bookmarkWithUrl.url?.tryGetHostFromUrl()
Assert.assertEquals(bookmarkWithUrl.title, view.bookmark_title.text) Assert.assertEquals(bookmarkWithUrl.title, binding.bookmarkTitle.text)
Assert.assertEquals(hostFromUrl, view.bookmark_subtitle.text) Assert.assertEquals(hostFromUrl, binding.bookmarkSubtitle.text)
} }
@Test @Test
fun `WHEN there is no url for the bookmark THEN do not load an icon `() { fun `WHEN there is no url for the bookmark THEN do not load an icon `() {
val viewHolder = RecentBookmarkItemViewHolder(view, interactor) val viewHolder = RecentBookmarkItemViewHolder(binding.root, interactor)
Assert.assertNull(view.favicon_image.drawable) Assert.assertNull(binding.faviconImage.drawable)
viewHolder.bind(bookmarkNoUrl) viewHolder.bind(bookmarkNoUrl)
Assert.assertNull(view.favicon_image.drawable) Assert.assertNull(binding.faviconImage.drawable)
} }
@Test @Test
fun `WHEN a bookmark does not have a title THEN show the url`() { fun `WHEN a bookmark does not have a title THEN show the url`() {
RecentBookmarkItemViewHolder(view, interactor).bind(bookmarkNoTitle) RecentBookmarkItemViewHolder(binding.root, interactor).bind(bookmarkNoTitle)
Assert.assertEquals(bookmarkNoTitle.url, view.bookmark_title.text) Assert.assertEquals(bookmarkNoTitle.url, binding.bookmarkTitle.text)
} }
} }

View File

@ -5,24 +5,23 @@
package org.mozilla.fenix.home.recentbookmarks.view package org.mozilla.fenix.home.recentbookmarks.view
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View
import androidx.navigation.Navigation import androidx.navigation.Navigation
import io.mockk.mockk import io.mockk.mockk
import io.mockk.verify import io.mockk.verify
import kotlinx.android.synthetic.main.recent_bookmarks_header.view.*
import mozilla.components.concept.storage.BookmarkNode import mozilla.components.concept.storage.BookmarkNode
import mozilla.components.concept.storage.BookmarkNodeType import mozilla.components.concept.storage.BookmarkNodeType
import mozilla.components.support.test.robolectric.testContext import mozilla.components.support.test.robolectric.testContext
import org.junit.Before import org.junit.Before
import org.junit.Test import org.junit.Test
import org.junit.runner.RunWith import org.junit.runner.RunWith
import org.mozilla.fenix.databinding.ComponentRecentBookmarksBinding
import org.mozilla.fenix.helpers.FenixRobolectricTestRunner import org.mozilla.fenix.helpers.FenixRobolectricTestRunner
import org.mozilla.fenix.home.sessioncontrol.SessionControlInteractor import org.mozilla.fenix.home.sessioncontrol.SessionControlInteractor
@RunWith(FenixRobolectricTestRunner::class) @RunWith(FenixRobolectricTestRunner::class)
class RecentBookmarksViewHolderTest { class RecentBookmarksViewHolderTest {
private lateinit var view: View private lateinit var binding: ComponentRecentBookmarksBinding
private lateinit var interactor: SessionControlInteractor private lateinit var interactor: SessionControlInteractor
private val bookmark = BookmarkNode( private val bookmark = BookmarkNode(
@ -38,16 +37,15 @@ class RecentBookmarksViewHolderTest {
@Before @Before
fun setup() { fun setup() {
view = LayoutInflater.from(testContext) binding = ComponentRecentBookmarksBinding.inflate(LayoutInflater.from(testContext))
.inflate(RecentBookmarksViewHolder.LAYOUT_ID, null) Navigation.setViewNavController(binding.root, mockk(relaxed = true))
Navigation.setViewNavController(view, mockk(relaxed = true))
interactor = mockk(relaxed = true) interactor = mockk(relaxed = true)
} }
@Test @Test
fun `WHEN show all bookmarks button is clicked THEN interactor is called`() { fun `WHEN show all bookmarks button is clicked THEN interactor is called`() {
RecentBookmarksViewHolder(view, interactor).bind(listOf(bookmark)) RecentBookmarksViewHolder(binding.root, interactor).bind(listOf(bookmark))
view.showAllBookmarksButton.performClick() binding.recentBookmarksHeader.showAllBookmarksButton.performClick()
verify { interactor.onShowAllBookmarksClicked() } verify { interactor.onShowAllBookmarksClicked() }
} }

View File

@ -5,13 +5,10 @@
package org.mozilla.fenix.home.recenttabs.view package org.mozilla.fenix.home.recenttabs.view
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View
import androidx.core.graphics.drawable.toBitmap import androidx.core.graphics.drawable.toBitmap
import io.mockk.every import io.mockk.every
import io.mockk.mockk import io.mockk.mockk
import io.mockk.verify import io.mockk.verify
import kotlinx.android.synthetic.main.recent_tabs_list_row.*
import kotlinx.android.synthetic.main.recent_tabs_list_row.view.*
import mozilla.components.browser.icons.BrowserIcons import mozilla.components.browser.icons.BrowserIcons
import mozilla.components.browser.icons.IconRequest import mozilla.components.browser.icons.IconRequest
import mozilla.components.browser.state.state.createTab import mozilla.components.browser.state.state.createTab
@ -23,13 +20,14 @@ import org.junit.Before
import org.junit.Test import org.junit.Test
import org.junit.runner.RunWith import org.junit.runner.RunWith
import org.mozilla.fenix.R import org.mozilla.fenix.R
import org.mozilla.fenix.databinding.RecentTabsListRowBinding
import org.mozilla.fenix.helpers.FenixRobolectricTestRunner import org.mozilla.fenix.helpers.FenixRobolectricTestRunner
import org.mozilla.fenix.home.sessioncontrol.SessionControlInteractor import org.mozilla.fenix.home.sessioncontrol.SessionControlInteractor
@RunWith(FenixRobolectricTestRunner::class) @RunWith(FenixRobolectricTestRunner::class)
class RecentTabViewHolderTest { class RecentTabViewHolderTest {
private lateinit var view: View private lateinit var binding: RecentTabsListRowBinding
private lateinit var interactor: SessionControlInteractor private lateinit var interactor: SessionControlInteractor
private lateinit var icons: BrowserIcons private lateinit var icons: BrowserIcons
@ -40,27 +38,27 @@ class RecentTabViewHolderTest {
@Before @Before
fun setup() { fun setup() {
view = LayoutInflater.from(testContext).inflate(RecentTabViewHolder.LAYOUT_ID, null) binding = RecentTabsListRowBinding.inflate(LayoutInflater.from(testContext))
interactor = mockk(relaxed = true) interactor = mockk(relaxed = true)
icons = mockk(relaxed = true) icons = mockk(relaxed = true)
every { icons.loadIntoView(view.recent_tab_icon, any()) } returns mockk() every { icons.loadIntoView(binding.recentTabIcon, any()) } returns mockk()
} }
@Test @Test
fun `GIVEN a new recent tab on bind THEN set the title text and load the tab icon`() { fun `GIVEN a new recent tab on bind THEN set the title text and load the tab icon`() {
RecentTabViewHolder(view, interactor, icons).bindTab(tab) RecentTabViewHolder(binding.root, interactor, icons).bindTab(tab)
assertEquals(tab.content.title, view.recent_tab_title.text) assertEquals(tab.content.title, binding.recentTabTitle.text)
verify { icons.loadIntoView(view.recent_tab_icon, IconRequest(tab.content.url)) } verify { icons.loadIntoView(binding.recentTabIcon, IconRequest(tab.content.url)) }
} }
@Test @Test
fun `WHEN a recent tab item is clicked THEN interactor is called`() { fun `WHEN a recent tab item is clicked THEN interactor is called`() {
RecentTabViewHolder(view, interactor, icons).bindTab(tab) RecentTabViewHolder(binding.root, interactor, icons).bindTab(tab)
view.performClick() binding.root.performClick()
verify { interactor.onRecentTabClicked(tab.id) } verify { interactor.onRecentTabClicked(tab.id) }
} }
@ -69,21 +67,21 @@ class RecentTabViewHolderTest {
fun `WHEN a recent tab icon exists THEN load it`() { fun `WHEN a recent tab icon exists THEN load it`() {
val bitmap = testContext.getDrawable(R.drawable.ic_search)!!.toBitmap() val bitmap = testContext.getDrawable(R.drawable.ic_search)!!.toBitmap()
val tabWithIcon = tab.copy(content = tab.content.copy(icon = bitmap)) val tabWithIcon = tab.copy(content = tab.content.copy(icon = bitmap))
val viewHolder = RecentTabViewHolder(view, interactor, icons) val viewHolder = RecentTabViewHolder(binding.root, interactor, icons)
assertNull(view.recent_tab_icon.drawable) assertNull(binding.recentTabIcon.drawable)
viewHolder.bindTab(tabWithIcon) viewHolder.bindTab(tabWithIcon)
assertNotNull(view.recent_tab_icon.drawable) assertNotNull(binding.recentTabIcon.drawable)
} }
@Test @Test
fun `WHEN a recent tab does not have a title THEN show the url`() { fun `WHEN a recent tab does not have a title THEN show the url`() {
val tabWithoutTitle = createTab(url = "https://mozilla.org") val tabWithoutTitle = createTab(url = "https://mozilla.org")
RecentTabViewHolder(view, interactor, icons).bindTab(tabWithoutTitle) RecentTabViewHolder(binding.root, interactor, icons).bindTab(tabWithoutTitle)
assertEquals(tabWithoutTitle.content.url, view.recent_tab_title.text) assertEquals(tabWithoutTitle.content.url, binding.recentTabTitle.text)
} }
} }

View File

@ -5,37 +5,35 @@
package org.mozilla.fenix.home.recenttabs.view package org.mozilla.fenix.home.recenttabs.view
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View
import androidx.navigation.Navigation import androidx.navigation.Navigation
import io.mockk.mockk import io.mockk.mockk
import io.mockk.verify import io.mockk.verify
import kotlinx.android.synthetic.main.recent_tabs_header.view.*
import mozilla.components.support.test.robolectric.testContext import mozilla.components.support.test.robolectric.testContext
import org.junit.Before import org.junit.Before
import org.junit.Test import org.junit.Test
import org.junit.runner.RunWith import org.junit.runner.RunWith
import org.mozilla.fenix.databinding.RecentTabsHeaderBinding
import org.mozilla.fenix.helpers.FenixRobolectricTestRunner import org.mozilla.fenix.helpers.FenixRobolectricTestRunner
import org.mozilla.fenix.home.sessioncontrol.SessionControlInteractor import org.mozilla.fenix.home.sessioncontrol.SessionControlInteractor
@RunWith(FenixRobolectricTestRunner::class) @RunWith(FenixRobolectricTestRunner::class)
class RecentTabsHeaderViewHolderTest { class RecentTabsHeaderViewHolderTest {
private lateinit var view: View private lateinit var binding: RecentTabsHeaderBinding
private lateinit var interactor: SessionControlInteractor private lateinit var interactor: SessionControlInteractor
@Before @Before
fun setup() { fun setup() {
view = LayoutInflater.from(testContext) binding = RecentTabsHeaderBinding.inflate(LayoutInflater.from(testContext))
.inflate(RecentTabsHeaderViewHolder.LAYOUT_ID, null) Navigation.setViewNavController(binding.root, mockk(relaxed = true))
Navigation.setViewNavController(view, mockk(relaxed = true))
interactor = mockk(relaxed = true) interactor = mockk(relaxed = true)
} }
@Test @Test
fun `WHEN show all button is clicked THEN interactor is called`() { fun `WHEN show all button is clicked THEN interactor is called`() {
RecentTabsHeaderViewHolder(view, interactor) RecentTabsHeaderViewHolder(binding.root, interactor)
view.show_all_button.performClick() binding.showAllButton.performClick()
verify { interactor.onRecentTabShowAllClicked() } verify { interactor.onRecentTabShowAllClicked() }
} }

View File

@ -5,13 +5,10 @@
package org.mozilla.fenix.home.sessioncontrol.viewholders package org.mozilla.fenix.home.sessioncontrol.viewholders
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View
import androidx.appcompat.view.ContextThemeWrapper
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.LifecycleOwner
import io.mockk.mockk import io.mockk.mockk
import io.mockk.verify import io.mockk.verify
import kotlinx.android.synthetic.main.no_collections_message.view.*
import mozilla.components.browser.state.state.BrowserState import mozilla.components.browser.state.state.BrowserState
import mozilla.components.browser.state.state.createTab import mozilla.components.browser.state.state.createTab
import mozilla.components.browser.state.store.BrowserStore import mozilla.components.browser.state.store.BrowserStore
@ -21,14 +18,14 @@ import org.junit.Assert.assertTrue
import org.junit.Before import org.junit.Before
import org.junit.Test import org.junit.Test
import org.junit.runner.RunWith import org.junit.runner.RunWith
import org.mozilla.fenix.R import org.mozilla.fenix.databinding.NoCollectionsMessageBinding
import org.mozilla.fenix.helpers.FenixRobolectricTestRunner import org.mozilla.fenix.helpers.FenixRobolectricTestRunner
import org.mozilla.fenix.home.sessioncontrol.CollectionInteractor import org.mozilla.fenix.home.sessioncontrol.CollectionInteractor
@RunWith(FenixRobolectricTestRunner::class) @RunWith(FenixRobolectricTestRunner::class)
class NoCollectionsMessageViewHolderTest { class NoCollectionsMessageViewHolderTest {
private lateinit var view: View private lateinit var binding: NoCollectionsMessageBinding
private val store: BrowserStore = BrowserStore( private val store: BrowserStore = BrowserStore(
initialState = BrowserState( initialState = BrowserState(
listOf( listOf(
@ -41,9 +38,7 @@ class NoCollectionsMessageViewHolderTest {
@Before @Before
fun setup() { fun setup() {
val appCompatContext = ContextThemeWrapper(testContext, R.style.NormalTheme) binding = NoCollectionsMessageBinding.inflate(LayoutInflater.from(testContext))
view = LayoutInflater.from(appCompatContext)
.inflate(NoCollectionsMessageViewHolder.LAYOUT_ID, null)
lifecycleOwner = mockk(relaxed = true) lifecycleOwner = mockk(relaxed = true)
interactor = mockk(relaxed = true) interactor = mockk(relaxed = true)
} }
@ -51,31 +46,31 @@ class NoCollectionsMessageViewHolderTest {
@Test @Test
fun `hide add to collection button when there are no tabs open`() { fun `hide add to collection button when there are no tabs open`() {
val noTabsStore = BrowserStore() val noTabsStore = BrowserStore()
NoCollectionsMessageViewHolder(view, lifecycleOwner, noTabsStore, interactor) NoCollectionsMessageViewHolder(binding.root, lifecycleOwner, noTabsStore, interactor)
assertFalse(view.add_tabs_to_collections_button.isVisible) assertFalse(binding.addTabsToCollectionsButton.isVisible)
} }
@Test @Test
fun `show add to collection button when there are tabs`() { fun `show add to collection button when there are tabs`() {
NoCollectionsMessageViewHolder(view, lifecycleOwner, store, interactor) NoCollectionsMessageViewHolder(binding.root, lifecycleOwner, store, interactor)
assertTrue(view.add_tabs_to_collections_button.isVisible) assertTrue(binding.addTabsToCollectionsButton.isVisible)
} }
@Test @Test
fun `call interactor on click`() { fun `call interactor on click`() {
NoCollectionsMessageViewHolder(view, lifecycleOwner, store, interactor) NoCollectionsMessageViewHolder(binding.root, lifecycleOwner, store, interactor)
view.add_tabs_to_collections_button.performClick() binding.addTabsToCollectionsButton.performClick()
verify { interactor.onAddTabsToCollectionTapped() } verify { interactor.onAddTabsToCollectionTapped() }
} }
@Test @Test
fun `hide view and change setting on remove placeholder click`() { fun `hide view and change setting on remove placeholder click`() {
NoCollectionsMessageViewHolder(view, lifecycleOwner, store, interactor) NoCollectionsMessageViewHolder(binding.root, lifecycleOwner, store, interactor)
view.remove_collection_placeholder.performClick() binding.removeCollectionPlaceholder.performClick()
verify { verify {
interactor.onRemoveCollectionsPlaceholder() interactor.onRemoveCollectionsPlaceholder()
} }

View File

@ -5,35 +5,33 @@
package org.mozilla.fenix.home.sessioncontrol.viewholders package org.mozilla.fenix.home.sessioncontrol.viewholders
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View
import io.mockk.mockk import io.mockk.mockk
import io.mockk.verify import io.mockk.verify
import kotlinx.android.synthetic.main.private_browsing_description.view.*
import mozilla.components.support.test.robolectric.testContext import mozilla.components.support.test.robolectric.testContext
import org.junit.Before import org.junit.Before
import org.junit.Test import org.junit.Test
import org.junit.runner.RunWith import org.junit.runner.RunWith
import org.mozilla.fenix.databinding.PrivateBrowsingDescriptionBinding
import org.mozilla.fenix.helpers.FenixRobolectricTestRunner import org.mozilla.fenix.helpers.FenixRobolectricTestRunner
import org.mozilla.fenix.home.sessioncontrol.TabSessionInteractor import org.mozilla.fenix.home.sessioncontrol.TabSessionInteractor
@RunWith(FenixRobolectricTestRunner::class) @RunWith(FenixRobolectricTestRunner::class)
class PrivateBrowsingDescriptionViewHolderTest { class PrivateBrowsingDescriptionViewHolderTest {
private lateinit var view: View private lateinit var binding: PrivateBrowsingDescriptionBinding
private lateinit var interactor: TabSessionInteractor private lateinit var interactor: TabSessionInteractor
@Before @Before
fun setup() { fun setup() {
view = LayoutInflater.from(testContext) binding = PrivateBrowsingDescriptionBinding.inflate(LayoutInflater.from(testContext))
.inflate(PrivateBrowsingDescriptionViewHolder.LAYOUT_ID, null)
interactor = mockk(relaxed = true) interactor = mockk(relaxed = true)
} }
@Test @Test
fun `call interactor on click`() { fun `call interactor on click`() {
PrivateBrowsingDescriptionViewHolder(view, interactor) PrivateBrowsingDescriptionViewHolder(binding.root, interactor)
view.private_session_common_myths.performClick() binding.privateSessionCommonMyths.performClick()
verify { interactor.onPrivateBrowsingLearnMoreClicked() } verify { interactor.onPrivateBrowsingLearnMoreClicked() }
} }
} }

View File

@ -5,34 +5,32 @@
package org.mozilla.fenix.home.sessioncontrol.viewholders package org.mozilla.fenix.home.sessioncontrol.viewholders
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View
import io.mockk.mockk import io.mockk.mockk
import kotlinx.android.synthetic.main.component_top_sites.view.*
import mozilla.components.feature.top.sites.TopSite import mozilla.components.feature.top.sites.TopSite
import mozilla.components.support.test.robolectric.testContext import mozilla.components.support.test.robolectric.testContext
import org.junit.Assert.assertEquals import org.junit.Assert.assertEquals
import org.junit.Before import org.junit.Before
import org.junit.Test import org.junit.Test
import org.junit.runner.RunWith import org.junit.runner.RunWith
import org.mozilla.fenix.databinding.ComponentTopSitesBinding
import org.mozilla.fenix.helpers.FenixRobolectricTestRunner import org.mozilla.fenix.helpers.FenixRobolectricTestRunner
import org.mozilla.fenix.home.sessioncontrol.TopSiteInteractor import org.mozilla.fenix.home.sessioncontrol.TopSiteInteractor
@RunWith(FenixRobolectricTestRunner::class) @RunWith(FenixRobolectricTestRunner::class)
class TopSiteViewHolderTest { class TopSiteViewHolderTest {
private lateinit var view: View private lateinit var binding: ComponentTopSitesBinding
private lateinit var interactor: TopSiteInteractor private lateinit var interactor: TopSiteInteractor
@Before @Before
fun setup() { fun setup() {
view = LayoutInflater.from(testContext) binding = ComponentTopSitesBinding.inflate(LayoutInflater.from(testContext))
.inflate(TopSiteViewHolder.LAYOUT_ID, null)
interactor = mockk() interactor = mockk()
} }
@Test @Test
fun `binds list of top sites`() { fun `binds list of top sites`() {
TopSiteViewHolder(view, interactor).bind( TopSiteViewHolder(binding.root, interactor).bind(
listOf( listOf(
TopSite( TopSite(
id = 1L, id = 1L,
@ -44,6 +42,6 @@ class TopSiteViewHolderTest {
) )
) )
assertEquals(1, view.top_sites_list.adapter!!.itemCount) assertEquals(1, binding.topSitesList.adapter!!.itemCount)
} }
} }

View File

@ -5,11 +5,8 @@
package org.mozilla.fenix.home.sessioncontrol.viewholders.topsites package org.mozilla.fenix.home.sessioncontrol.viewholders.topsites
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View
import android.widget.TextView
import io.mockk.mockk import io.mockk.mockk
import io.mockk.verify import io.mockk.verify
import kotlinx.android.synthetic.main.top_site_item.view.*
import mozilla.components.feature.top.sites.TopSite import mozilla.components.feature.top.sites.TopSite
import mozilla.components.support.test.robolectric.testContext import mozilla.components.support.test.robolectric.testContext
import org.junit.Assert.assertNotNull import org.junit.Assert.assertNotNull
@ -17,14 +14,14 @@ import org.junit.Assert.assertNull
import org.junit.Before import org.junit.Before
import org.junit.Test import org.junit.Test
import org.junit.runner.RunWith import org.junit.runner.RunWith
import org.mozilla.fenix.R import org.mozilla.fenix.databinding.TopSiteItemBinding
import org.mozilla.fenix.helpers.FenixRobolectricTestRunner import org.mozilla.fenix.helpers.FenixRobolectricTestRunner
import org.mozilla.fenix.home.sessioncontrol.TopSiteInteractor import org.mozilla.fenix.home.sessioncontrol.TopSiteInteractor
@RunWith(FenixRobolectricTestRunner::class) @RunWith(FenixRobolectricTestRunner::class)
class TopSiteItemViewHolderTest { class TopSiteItemViewHolderTest {
private lateinit var view: View private lateinit var binding: TopSiteItemBinding
private lateinit var interactor: TopSiteInteractor private lateinit var interactor: TopSiteInteractor
private val pocket = TopSite( private val pocket = TopSite(
id = 1L, id = 1L,
@ -36,24 +33,23 @@ class TopSiteItemViewHolderTest {
@Before @Before
fun setup() { fun setup() {
view = LayoutInflater.from(testContext) binding = TopSiteItemBinding.inflate(LayoutInflater.from(testContext))
.inflate(TopSiteItemViewHolder.LAYOUT_ID, null)
interactor = mockk(relaxed = true) interactor = mockk(relaxed = true)
} }
@Test @Test
fun `calls interactor on click`() { fun `calls interactor on click`() {
TopSiteItemViewHolder(view, interactor).bind(pocket) TopSiteItemViewHolder(binding.root, interactor).bind(pocket)
view.top_site_item.performClick() binding.topSiteItem.performClick()
verify { interactor.onSelectTopSite("https://getpocket.com", TopSite.Type.DEFAULT) } verify { interactor.onSelectTopSite("https://getpocket.com", TopSite.Type.DEFAULT) }
} }
@Test @Test
fun `calls interactor on long click`() { fun `calls interactor on long click`() {
TopSiteItemViewHolder(view, interactor).bind(pocket) TopSiteItemViewHolder(binding.root, interactor).bind(pocket)
view.top_site_item.performLongClick() binding.topSiteItem.performLongClick()
verify { interactor.onTopSiteMenuOpened() } verify { interactor.onTopSiteMenuOpened() }
} }
@ -67,8 +63,8 @@ class TopSiteItemViewHolderTest {
type = TopSite.Type.DEFAULT type = TopSite.Type.DEFAULT
) )
TopSiteItemViewHolder(view, interactor).bind(defaultTopSite) TopSiteItemViewHolder(binding.root, interactor).bind(defaultTopSite)
val pinIndicator = view.findViewById<TextView>(R.id.top_site_title).compoundDrawables[0] val pinIndicator = binding.topSiteTitle.compoundDrawables[0]
assertNotNull(pinIndicator) assertNotNull(pinIndicator)
} }
@ -83,8 +79,8 @@ class TopSiteItemViewHolderTest {
type = TopSite.Type.PINNED type = TopSite.Type.PINNED
) )
TopSiteItemViewHolder(view, interactor).bind(pinnedTopSite) TopSiteItemViewHolder(binding.root, interactor).bind(pinnedTopSite)
val pinIndicator = view.findViewById<TextView>(R.id.top_site_title).compoundDrawables[0] val pinIndicator = binding.topSiteTitle.compoundDrawables[0]
assertNotNull(pinIndicator) assertNotNull(pinIndicator)
} }
@ -99,8 +95,8 @@ class TopSiteItemViewHolderTest {
type = TopSite.Type.FRECENT type = TopSite.Type.FRECENT
) )
TopSiteItemViewHolder(view, interactor).bind(frecentTopSite) TopSiteItemViewHolder(binding.root, interactor).bind(frecentTopSite)
val pinIndicator = view.findViewById<TextView>(R.id.top_site_title).compoundDrawables[0] val pinIndicator = binding.topSiteTitle.compoundDrawables[0]
assertNull(pinIndicator) assertNull(pinIndicator)
} }

View File

@ -16,7 +16,6 @@ import io.mockk.just
import io.mockk.mockk import io.mockk.mockk
import io.mockk.spyk import io.mockk.spyk
import io.mockk.verify import io.mockk.verify
import kotlinx.android.synthetic.main.button_tip_item.*
import mozilla.components.support.test.robolectric.testContext import mozilla.components.support.test.robolectric.testContext
import org.junit.Assert.assertEquals import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue import org.junit.Assert.assertTrue
@ -29,6 +28,7 @@ import org.mozilla.fenix.components.metrics.Event
import org.mozilla.fenix.components.metrics.MetricController import org.mozilla.fenix.components.metrics.MetricController
import org.mozilla.fenix.components.tips.Tip import org.mozilla.fenix.components.tips.Tip
import org.mozilla.fenix.components.tips.TipType import org.mozilla.fenix.components.tips.TipType
import org.mozilla.fenix.databinding.ButtonTipItemBinding
import org.mozilla.fenix.helpers.FenixRobolectricTestRunner import org.mozilla.fenix.helpers.FenixRobolectricTestRunner
import org.mozilla.fenix.home.sessioncontrol.SessionControlInteractor import org.mozilla.fenix.home.sessioncontrol.SessionControlInteractor
import org.mozilla.fenix.utils.Settings import org.mozilla.fenix.utils.Settings
@ -43,6 +43,7 @@ class ButtonTipViewHolderTest {
@MockK private lateinit var sharedPrefs: SharedPreferences @MockK private lateinit var sharedPrefs: SharedPreferences
@MockK private lateinit var sharedPrefsEditor: SharedPreferences.Editor @MockK private lateinit var sharedPrefsEditor: SharedPreferences.Editor
private lateinit var viewHolder: ButtonTipViewHolder private lateinit var viewHolder: ButtonTipViewHolder
private lateinit var binding: ButtonTipItemBinding
@Before @Before
fun setup() { fun setup() {
@ -53,6 +54,7 @@ class ButtonTipViewHolderTest {
) )
viewHolder = ButtonTipViewHolder(view, interactor, metrics, settings) viewHolder = ButtonTipViewHolder(view, interactor, metrics, settings)
binding = ButtonTipItemBinding.bind(view)
every { view.context } returns activity every { view.context } returns activity
every { activity.openToBrowserAndLoad(any(), any(), any()) } just Runs every { activity.openToBrowserAndLoad(any(), any(), any()) } just Runs
every { interactor.onCloseTip(any()) } just Runs every { interactor.onCloseTip(any()) } just Runs
@ -66,9 +68,9 @@ class ButtonTipViewHolderTest {
fun `text is displayed based on given tip`() { fun `text is displayed based on given tip`() {
viewHolder.bind(defaultTip()) viewHolder.bind(defaultTip())
assertEquals("Tip Title", viewHolder.tip_header_text.text) assertEquals("Tip Title", binding.tipHeaderText.text)
assertEquals("Tip description", viewHolder.tip_description_text.text) assertEquals("Tip description", binding.tipDescriptionText.text)
assertEquals("button", viewHolder.tip_button.text) assertEquals("button", binding.tipButton.text)
verify { metrics.track(Event.TipDisplayed("tipIdentifier")) } verify { metrics.track(Event.TipDisplayed("tipIdentifier")) }
} }
@ -77,16 +79,16 @@ class ButtonTipViewHolderTest {
fun `learn more is hidden if learnMoreURL is null`() { fun `learn more is hidden if learnMoreURL is null`() {
viewHolder.bind(defaultTip(learnMoreUrl = null)) viewHolder.bind(defaultTip(learnMoreUrl = null))
assertTrue(viewHolder.tip_learn_more.isGone) assertTrue(binding.tipLearnMore.isGone)
} }
@Test @Test
fun `learn more is visible if learnMoreURL is not null`() { fun `learn more is visible if learnMoreURL is not null`() {
viewHolder.bind(defaultTip(learnMoreUrl = "https://learnmore.com")) viewHolder.bind(defaultTip(learnMoreUrl = "https://learnmore.com"))
assertTrue(viewHolder.tip_learn_more.isVisible) assertTrue(binding.tipLearnMore.isVisible)
viewHolder.tip_learn_more.performClick() binding.tipLearnMore.performClick()
verify { verify {
activity.openToBrowserAndLoad( activity.openToBrowserAndLoad(
searchTermOrURL = "https://learnmore.com", searchTermOrURL = "https://learnmore.com",
@ -101,7 +103,7 @@ class ButtonTipViewHolderTest {
val action = mockk<() -> Unit>(relaxed = true) val action = mockk<() -> Unit>(relaxed = true)
viewHolder.bind(defaultTip(action)) viewHolder.bind(defaultTip(action))
viewHolder.tip_button.performClick() binding.tipButton.performClick()
verify { action() } verify { action() }
verify { metrics.track(Event.TipPressed("tipIdentifier")) } verify { metrics.track(Event.TipPressed("tipIdentifier")) }
} }
@ -111,7 +113,7 @@ class ButtonTipViewHolderTest {
val tip = defaultTip() val tip = defaultTip()
viewHolder.bind(tip) viewHolder.bind(tip)
viewHolder.tip_close.performClick() binding.tipClose.performClick()
verify { interactor.onCloseTip(tip) } verify { interactor.onCloseTip(tip) }
verify { metrics.track(Event.TipClosed("tipIdentifier")) } verify { metrics.track(Event.TipClosed("tipIdentifier")) }
verify { sharedPrefsEditor.putBoolean("tipIdentifier", false) } verify { sharedPrefsEditor.putBoolean("tipIdentifier", false) }

View File

@ -4,6 +4,7 @@
package org.mozilla.fenix.perf package org.mozilla.fenix.perf
import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewTreeObserver import android.view.ViewTreeObserver
import android.widget.LinearLayout import android.widget.LinearLayout
@ -15,16 +16,21 @@ import io.mockk.impl.annotations.MockK
import io.mockk.just import io.mockk.just
import io.mockk.mockk import io.mockk.mockk
import io.mockk.slot import io.mockk.slot
import io.mockk.spyk
import io.mockk.verify import io.mockk.verify
import kotlinx.android.synthetic.main.top_site_item.view.* import mozilla.components.support.test.robolectric.testContext
import org.junit.Before import org.junit.Before
import org.junit.Test import org.junit.Test
import org.junit.runner.RunWith
import org.mozilla.fenix.HomeActivity import org.mozilla.fenix.HomeActivity
import org.mozilla.fenix.R import org.mozilla.fenix.R
import org.mozilla.fenix.databinding.TopSiteItemBinding
import org.mozilla.fenix.helpers.FenixRobolectricTestRunner
import org.mozilla.fenix.home.sessioncontrol.viewholders.topsites.TopSiteItemViewHolder import org.mozilla.fenix.home.sessioncontrol.viewholders.topsites.TopSiteItemViewHolder
import org.mozilla.fenix.perf.StartupTimelineStateMachine.StartupDestination import org.mozilla.fenix.perf.StartupTimelineStateMachine.StartupDestination
import org.mozilla.fenix.perf.StartupTimelineStateMachine.StartupState import org.mozilla.fenix.perf.StartupTimelineStateMachine.StartupState
@RunWith(FenixRobolectricTestRunner::class)
class StartupReportFullyDrawnTest { class StartupReportFullyDrawnTest {
@MockK private lateinit var activity: HomeActivity @MockK private lateinit var activity: HomeActivity
@ -37,9 +43,10 @@ class StartupReportFullyDrawnTest {
@Before @Before
fun setup() { fun setup() {
MockKAnnotations.init(this) MockKAnnotations.init(this)
val binding = TopSiteItemBinding.inflate(LayoutInflater.from(testContext), rootContainer, false)
holderItemView = spyk(binding.root)
every { activity.findViewById<LinearLayout>(R.id.rootContainer) } returns rootContainer every { activity.findViewById<LinearLayout>(R.id.rootContainer) } returns rootContainer
every { holderItemView.context } returns activity every { holderItemView.context } returns activity
every { holderItemView.top_site_item } returns mockk(relaxed = true)
holder = TopSiteItemViewHolder(holderItemView, mockk()) holder = TopSiteItemViewHolder(holderItemView, mockk())
every { rootContainer.viewTreeObserver } returns viewTreeObserver every { rootContainer.viewTreeObserver } returns viewTreeObserver
every { holderItemView.viewTreeObserver } returns viewTreeObserver every { holderItemView.viewTreeObserver } returns viewTreeObserver