From 5b99139048cc92be849970cd6dc8a765831dd9af Mon Sep 17 00:00:00 2001 From: Mugurell Date: Tue, 17 Aug 2021 10:43:57 +0300 Subject: [PATCH] [fenix] For https://github.com/mozilla-mobile/fenix/issues/17917 - Migrate `home` from Kotlin synthetics to View Binding. --- .../java/org/mozilla/fenix/HomeActivity.kt | 12 +- .../org/mozilla/fenix/home/HomeFragment.kt | 110 ++++++++++-------- .../view/RecentBookmarkItemViewHolder.kt | 15 ++- .../view/RecentBookmarksViewHolder.kt | 10 +- .../home/sessioncontrol/SessionControlView.kt | 5 +- .../topsites/TopSiteItemViewHolder.kt | 23 ++-- .../res/layout/component_recent_bookmarks.xml | 4 +- .../view/RecentBookmarkItemViewHolderTest.kt | 24 ++-- .../view/RecentBookmarksViewHolderTest.kt | 14 +-- .../view/RecentTabViewHolderTest.kt | 30 +++-- .../view/RecentTabsHeaderViewHolderTest.kt | 14 +-- .../NoCollectionsMessageViewHolderTest.kt | 27 ++--- ...rivateBrowsingDescriptionViewHolderTest.kt | 12 +- .../viewholders/TopSiteViewHolderTest.kt | 12 +- .../topsites/TopSiteItemViewHolderTest.kt | 30 +++-- .../home/tips/ButtonTipViewHolderTest.kt | 20 ++-- .../fenix/perf/StartupReportFullyDrawnTest.kt | 11 +- 17 files changed, 187 insertions(+), 186 deletions(-) diff --git a/app/src/main/java/org/mozilla/fenix/HomeActivity.kt b/app/src/main/java/org/mozilla/fenix/HomeActivity.kt index a72ca29e0e..fc2017f61c 100644 --- a/app/src/main/java/org/mozilla/fenix/HomeActivity.kt +++ b/app/src/main/java/org/mozilla/fenix/HomeActivity.kt @@ -32,7 +32,6 @@ import androidx.navigation.NavDirections import androidx.navigation.fragment.NavHostFragment import androidx.navigation.ui.AppBarConfiguration import androidx.navigation.ui.NavigationUI -import kotlinx.android.synthetic.main.activity_home.* import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.delay 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.components.metrics.BreadcrumbsRecorder import org.mozilla.fenix.components.metrics.Event +import org.mozilla.fenix.databinding.ActivityHomeBinding import org.mozilla.fenix.exceptions.trackingprotection.TrackingProtectionExceptionsFragmentDirections import org.mozilla.fenix.ext.alreadyOnDestination import org.mozilla.fenix.ext.breadcrumb @@ -134,6 +134,7 @@ open class HomeActivity : LocaleAwareAppCompatActivity(), NavHostActivity { // components requires context to access. protected val homeActivityInitTimeStampNanoSeconds = SystemClock.elapsedRealtimeNanos() + private lateinit var binding: ActivityHomeBinding lateinit var themeManager: ThemeManager lateinit var browsingModeManager: BrowsingModeManager @@ -203,8 +204,9 @@ open class HomeActivity : LocaleAwareAppCompatActivity(), NavHostActivity { // Must be after we set the content view if (isVisuallyComplete) { + binding = ActivityHomeBinding.bind(window.decorView.findViewById(R.id.rootContainer)) components.performance.visualCompletenessQueue - .attachViewToRunVisualCompletenessQueueLater(WeakReference(rootContainer)) + .attachViewToRunVisualCompletenessQueueLater(WeakReference(binding.rootContainer)) } privateNotificationObserver = PrivateNotificationFeature( @@ -270,7 +272,7 @@ open class HomeActivity : LocaleAwareAppCompatActivity(), NavHostActivity { components.performance.visualCompletenessQueue, components.startupStateProvider, safeIntent, - rootContainer + binding.rootContainer ) } @@ -310,7 +312,7 @@ open class HomeActivity : LocaleAwareAppCompatActivity(), NavHostActivity { message = "onStart()" ) - ProfilerMarkers.homeActivityOnStart(rootContainer, components.core.engine.profiler) + ProfilerMarkers.homeActivityOnStart(binding.rootContainer, components.core.engine.profiler) } override fun onStop() { @@ -651,7 +653,7 @@ open class HomeActivity : LocaleAwareAppCompatActivity(), NavHostActivity { */ override fun getSupportActionBarAndInflateIfNecessary(): ActionBar { if (!isToolbarInflated) { - navigationToolbar = navigationToolbarStub.inflate() as Toolbar + navigationToolbar = binding.navigationToolbarStub.inflate() as Toolbar setSupportActionBar(navigationToolbar) // Add ids to this that we don't want to have a toolbar back button diff --git a/app/src/main/java/org/mozilla/fenix/home/HomeFragment.kt b/app/src/main/java/org/mozilla/fenix/home/HomeFragment.kt index 513d5a2692..f629a21450 100644 --- a/app/src/main/java/org/mozilla/fenix/home/HomeFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/home/HomeFragment.kt @@ -5,6 +5,7 @@ package org.mozilla.fenix.home import android.animation.Animator +import android.annotation.SuppressLint import android.content.Context import android.content.res.Configuration import android.graphics.drawable.BitmapDrawable @@ -41,10 +42,8 @@ import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView.SCROLL_STATE_IDLE import com.google.android.material.appbar.AppBarLayout +import com.google.android.material.button.MaterialButton 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.Main 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.toolbar.FenixTabCounterMenu import org.mozilla.fenix.components.toolbar.ToolbarPosition +import org.mozilla.fenix.databinding.FragmentHomeBinding import org.mozilla.fenix.ext.components import org.mozilla.fenix.ext.hideToolbar import org.mozilla.fenix.ext.metrics @@ -128,20 +128,24 @@ class HomeFragment : Fragment() { private val args by navArgs() private lateinit var bundleArgs: Bundle + private var _binding: FragmentHomeBinding? = null + private val binding get() = _binding!! + private val homeViewModel: HomeScreenViewModel by activityViewModels() private val snackbarAnchorView: View? get() = when (requireContext().settings().toolbarPosition) { - ToolbarPosition.BOTTOM -> toolbarLayout + ToolbarPosition.BOTTOM -> binding.toolbarLayout ToolbarPosition.TOP -> null } private val browsingModeManager get() = (activity as HomeActivity).browsingModeManager private val collectionStorageObserver = object : TabCollectionStorage.Observer { + @SuppressLint("NotifyDataSetChanged") override fun onCollectionRenamed(tabCollection: TabCollection, title: String) { lifecycleScope.launch(Main) { - view?.sessionControlRecyclerView?.adapter?.notifyDataSetChanged() + binding.sessionControlRecyclerView.adapter?.notifyDataSetChanged() } showRenamedSnackbar() } @@ -171,7 +175,7 @@ class HomeFragment : Fragment() { private val historyMetadataFeature = ViewBoundFeatureWrapper() @VisibleForTesting - internal var getMenuButton: () -> MenuButton? = { menuButton } + internal var getMenuButton: () -> MenuButton? = { binding.menuButton } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -196,13 +200,13 @@ class HomeFragment : Fragment() { inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? - ): View? { - val view = inflater.inflate(R.layout.fragment_home, container, false) + ): View { + _binding = FragmentHomeBinding.inflate(inflater, container, false) val activity = activity as HomeActivity val components = requireComponents currentMode = CurrentMode( - view.context, + requireContext(), onboarding, browsingModeManager, ::dispatchModeChanges @@ -242,7 +246,7 @@ class HomeFragment : Fragment() { config = ::getTopSitesConfig ), owner = viewLifecycleOwner, - view = view + view = binding.root ) if (FeatureFlags.showRecentTabsFeature) { @@ -252,7 +256,7 @@ class HomeFragment : Fragment() { homeStore = homeFragmentStore ), owner = viewLifecycleOwner, - view = view + view = binding.root ) } @@ -266,7 +270,7 @@ class HomeFragment : Fragment() { scope = viewLifecycleOwner.lifecycleScope ), owner = viewLifecycleOwner, - view = view + view = binding.root ) } @@ -278,7 +282,7 @@ class HomeFragment : Fragment() { scope = viewLifecycleOwner.lifecycleScope ), owner = viewLifecycleOwner, - view = view + view = binding.root ) } @@ -322,20 +326,20 @@ class HomeFragment : Fragment() { ) ) - updateLayout(view) + updateLayout(binding.root) sessionControlView = SessionControlView( - view.sessionControlRecyclerView, + binding.sessionControlRecyclerView, viewLifecycleOwner, sessionControlInteractor, homeViewModel ) - updateSessionControlView(view) + updateSessionControlView() - appBarLayout = view.homeAppBar + appBarLayout = binding.homeAppBar activity.themeManager.applyStatusBarTheme(activity) - return view + return binding.root } override fun onConfigurationChanged(newConfig: Configuration) { @@ -367,24 +371,24 @@ class HomeFragment : Fragment() { * 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. */ - private fun updateSessionControlView(view: View) { + private fun updateSessionControlView() { if (browsingModeManager.mode == BrowsingMode.Private) { - view.consumeFrom(homeFragmentStore, viewLifecycleOwner) { + binding.root.consumeFrom(homeFragmentStore, viewLifecycleOwner) { sessionControlView?.update(it) } } else { sessionControlView?.update(homeFragmentStore.state) - view.consumeFrom(homeFragmentStore, viewLifecycleOwner) { + binding.root.consumeFrom(homeFragmentStore, viewLifecycleOwner) { sessionControlView?.update(it) } } } private fun updateLayout(view: View) { - when (view.context.settings().toolbarPosition) { + when (requireContext().settings().toolbarPosition) { ToolbarPosition.TOP -> { - view.toolbarLayout.layoutParams = CoordinatorLayout.LayoutParams( + binding.toolbarLayout.layoutParams = CoordinatorLayout.LayoutParams( ConstraintLayout.LayoutParams.MATCH_PARENT, ConstraintLayout.LayoutParams.WRAP_CONTENT ).apply { @@ -392,21 +396,21 @@ class HomeFragment : Fragment() { } ConstraintSet().apply { - clone(view.toolbarLayout) - clear(view.bottom_bar.id, BOTTOM) - clear(view.bottomBarShadow.id, BOTTOM) - connect(view.bottom_bar.id, TOP, PARENT_ID, TOP) - connect(view.bottomBarShadow.id, TOP, view.bottom_bar.id, BOTTOM) - connect(view.bottomBarShadow.id, BOTTOM, PARENT_ID, BOTTOM) - applyTo(view.toolbarLayout) + clone(binding.toolbarLayout) + clear(binding.bottomBar.id, BOTTOM) + clear(binding.bottomBarShadow.id, BOTTOM) + connect(binding.bottomBar.id, TOP, PARENT_ID, TOP) + connect(binding.bottomBarShadow.id, TOP, binding.bottomBar.id, BOTTOM) + connect(binding.bottomBarShadow.id, BOTTOM, PARENT_ID, BOTTOM) + applyTo(binding.toolbarLayout) } - view.bottom_bar.background = AppCompatResources.getDrawable( + binding.bottomBar.background = AppCompatResources.getDrawable( view.context, view.context.theme.resolveAttribute(R.attr.bottomBarBackgroundTop) ) - view.homeAppBar.updateLayoutParams { + binding.homeAppBar.updateLayoutParams { topMargin = resources.getDimensionPixelSize(R.dimen.home_fragment_top_toolbar_header_margin) } @@ -422,24 +426,24 @@ class HomeFragment : Fragment() { context?.metrics?.track(Event.HomeScreenDisplayed) observeSearchEngineChanges() - createHomeMenu(requireContext(), WeakReference(view.menuButton)) - createTabCounterMenu(view) + createHomeMenu(requireContext(), WeakReference(binding.menuButton)) + createTabCounterMenu() - view.menuButton.setColorFilter( + binding.menuButton.setColorFilter( ContextCompat.getColor( 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.toolbar_wrapper.setOnClickListener { + binding.toolbarWrapper.setOnClickListener { navigateToSearch() requireComponents.analytics.metrics.track(Event.SearchBarTapped(Event.SearchBarTapped.Source.HOME)) } - view.toolbar_wrapper.setOnLongClickListener { + binding.toolbarWrapper.setOnLongClickListener { ToolbarPopupWindow.show( WeakReference(it), handlePasteAndGo = sessionControlInteractor::onPasteAndGo, @@ -449,7 +453,7 @@ class HomeFragment : Fragment() { true } - view.tab_button.setOnClickListener { + binding.tabButton.setOnClickListener { if (FeatureFlags.showStartOnHomeSettings) { requireComponents.analytics.metrics.track(Event.StartOnHomeOpenTabsTray) } @@ -457,7 +461,7 @@ class HomeFragment : Fragment() { } PrivateBrowsingButtonView( - privateBrowsingButton, + binding.privateBrowsingButton, browsingModeManager ) { newMode -> if (newMode == BrowsingMode.Private) { @@ -509,15 +513,15 @@ class HomeFragment : Fragment() { val searchIcon = BitmapDrawable(requireContext().resources, searchEngine.icon) searchIcon.setBounds(0, 0, iconSize, iconSize) - search_engine_icon?.setImageDrawable(searchIcon) + binding.searchEngineIcon.setImageDrawable(searchIcon) } 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 mode = browsingModeManager.mode @@ -530,7 +534,7 @@ class HomeFragment : Fragment() { } val tabCounterMenu = FenixTabCounterMenu( - view.context, + requireContext(), onItemTapped, iconColor = if (mode == BrowsingMode.Private) { ContextCompat.getColor(requireContext(), R.color.primary_text_private_theme) @@ -545,7 +549,7 @@ class HomeFragment : Fragment() { } tabCounterMenu.updateMenu(showOnly = inverseBrowsingMode) - view.tab_button.setOnLongClickListener { + binding.tabButton.setOnLongClickListener { tabCounterMenu.menuController.show(anchor = it) true } @@ -608,6 +612,7 @@ class HomeFragment : Fragment() { _sessionControlInteractor = null sessionControlView = null appBarLayout = null + _binding = null bundleArgs.clear() } @@ -663,7 +668,7 @@ class HomeFragment : Fragment() { isDisplayedWithBrowserToolbar = false ) .setText(it.context.getString(R.string.onboarding_firefox_account_sync_is_on)) - .setAnchorView(toolbarLayout) + .setAnchorView(binding.toolbarLayout) .show() } } @@ -774,12 +779,12 @@ class HomeFragment : Fragment() { } // We want to show the popup only after privateBrowsingButton is available. // Otherwise, we will encounter an activity token error. - privateBrowsingButton.post { + binding.privateBrowsingButton.post { runIfFragmentIsAttached { context.settings().showedPrivateModeContextualFeatureRecommender = true context.settings().lastCfrShownTimeInMillis = System.currentTimeMillis() 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 } - view?.tab_button?.setCountWithAnimation(tabCount) - view?.add_tabs_to_collections_button?.isVisible = tabCount > 0 + binding.tabButton.setCountWithAnimation(tabCount) + // The add_tabs_to_collections_button is added at runtime. We need to search for it in the same way. + sessionControlView?.view?.findViewById(R.id.add_tabs_to_collections_button) + ?.isVisible = tabCount > 0 } + @SuppressLint("NotifyDataSetChanged") private fun handleSwipedItemDeletionCancel() { - view?.sessionControlRecyclerView?.adapter?.notifyDataSetChanged() + binding.sessionControlRecyclerView.adapter?.notifyDataSetChanged() } companion object { diff --git a/app/src/main/java/org/mozilla/fenix/home/recentbookmarks/view/RecentBookmarkItemViewHolder.kt b/app/src/main/java/org/mozilla/fenix/home/recentbookmarks/view/RecentBookmarkItemViewHolder.kt index ddc484ef62..b75d4e3eaa 100644 --- a/app/src/main/java/org/mozilla/fenix/home/recentbookmarks/view/RecentBookmarkItemViewHolder.kt +++ b/app/src/main/java/org/mozilla/fenix/home/recentbookmarks/view/RecentBookmarkItemViewHolder.kt @@ -5,13 +5,10 @@ package org.mozilla.fenix.home.recentbookmarks.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.support.ktx.kotlin.tryGetHostFromUrl import org.mozilla.fenix.R +import org.mozilla.fenix.databinding.RecentBookmarkItemBinding import org.mozilla.fenix.ext.components import org.mozilla.fenix.ext.loadIntoView import org.mozilla.fenix.home.recentbookmarks.interactor.RecentBookmarksInteractor @@ -23,15 +20,17 @@ class RecentBookmarkItemViewHolder( ) : ViewHolder(view) { fun bind(bookmark: BookmarkNode) { - bookmark_title.text = bookmark.title ?: bookmark.url - bookmark_subtitle.text = bookmark.url?.tryGetHostFromUrl() ?: bookmark.title ?: "" + val binding = RecentBookmarkItemBinding.bind(view) - bookmark_item.setOnClickListener { + binding.bookmarkTitle.text = bookmark.title ?: bookmark.url + binding.bookmarkSubtitle.text = bookmark.url?.tryGetHostFromUrl() ?: bookmark.title ?: "" + + binding.bookmarkItem.setOnClickListener { interactor.onRecentBookmarkClicked(bookmark) } bookmark.url?.let { - view.context.components.core.icons.loadIntoView(favicon_image, it) + view.context.components.core.icons.loadIntoView(binding.faviconImage, it) } } diff --git a/app/src/main/java/org/mozilla/fenix/home/recentbookmarks/view/RecentBookmarksViewHolder.kt b/app/src/main/java/org/mozilla/fenix/home/recentbookmarks/view/RecentBookmarksViewHolder.kt index 0b408c9940..9ef6e0b9cd 100644 --- a/app/src/main/java/org/mozilla/fenix/home/recentbookmarks/view/RecentBookmarksViewHolder.kt +++ b/app/src/main/java/org/mozilla/fenix/home/recentbookmarks/view/RecentBookmarksViewHolder.kt @@ -8,10 +8,9 @@ import android.view.View import androidx.navigation.findNavController import androidx.recyclerview.widget.LinearLayoutManager 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 org.mozilla.fenix.R +import org.mozilla.fenix.databinding.ComponentRecentBookmarksBinding import org.mozilla.fenix.home.recentbookmarks.RecentBookmarksItemAdapter import org.mozilla.fenix.home.recentbookmarks.interactor.RecentBookmarksInteractor import org.mozilla.fenix.utils.view.ViewHolder @@ -24,14 +23,17 @@ class RecentBookmarksViewHolder( private val recentBookmarksAdapter = RecentBookmarksItemAdapter(interactor) init { + val recentBookmarksBinding = ComponentRecentBookmarksBinding.bind(view) + val recentBookmarksHeaderBinding = recentBookmarksBinding.recentBookmarksHeader + val linearLayoutManager = LinearLayoutManager(view.context, HORIZONTAL, false) - view.recent_bookmarks_list.apply { + recentBookmarksBinding.recentBookmarksList.apply { adapter = recentBookmarksAdapter layoutManager = linearLayoutManager } - showAllBookmarksButton.setOnClickListener { + recentBookmarksHeaderBinding.showAllBookmarksButton.setOnClickListener { dismissSearchDialogIfDisplayed() interactor.onShowAllBookmarksClicked() } diff --git a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/SessionControlView.kt b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/SessionControlView.kt index 28ffd20a8c..fcfed8b706 100644 --- a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/SessionControlView.kt +++ b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/SessionControlView.kt @@ -10,7 +10,6 @@ import androidx.lifecycle.LifecycleOwner import androidx.recyclerview.widget.ItemTouchHelper import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView -import kotlinx.android.extensions.LayoutContainer import mozilla.components.concept.storage.BookmarkNode import mozilla.components.browser.state.state.TabSessionState import mozilla.components.feature.tab.collections.TabCollection @@ -207,11 +206,11 @@ private fun collectionTabItems(collection: TabCollection) = } class SessionControlView( - override val containerView: View, + val containerView: View, viewLifecycleOwner: LifecycleOwner, interactor: SessionControlInteractor, private var homeScreenViewModel: HomeScreenViewModel -) : LayoutContainer { +) { val view: RecyclerView = containerView as RecyclerView diff --git a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/topsites/TopSiteItemViewHolder.kt b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/topsites/TopSiteItemViewHolder.kt index e337a2b4b6..1477f84222 100644 --- a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/topsites/TopSiteItemViewHolder.kt +++ b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/topsites/TopSiteItemViewHolder.kt @@ -10,7 +10,6 @@ import android.view.MotionEvent import android.view.View import android.widget.PopupWindow 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.item.SimpleBrowserMenuItem 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 org.mozilla.fenix.R import org.mozilla.fenix.components.metrics.Event +import org.mozilla.fenix.databinding.TopSiteItemBinding import org.mozilla.fenix.ext.components import org.mozilla.fenix.ext.loadIntoView import org.mozilla.fenix.home.sessioncontrol.TopSiteInteractor @@ -30,13 +30,14 @@ class TopSiteItemViewHolder( private val interactor: TopSiteInteractor ) : ViewHolder(view) { private lateinit var topSite: TopSite + private val binding = TopSiteItemBinding.bind(view) init { - top_site_item.setOnClickListener { + binding.topSiteItem.setOnClickListener { interactor.onSelectTopSite(topSite.url, topSite.type) } - top_site_item.setOnLongClickListener { + binding.topSiteItem.setOnLongClickListener { interactor.onTopSiteMenuOpened() it.context.components.analytics.metrics.track(Event.TopSiteLongPress(topSite.type)) @@ -62,30 +63,30 @@ class TopSiteItemViewHolder( } fun bind(topSite: TopSite) { - top_site_title.text = topSite.title + binding.topSiteTitle.text = topSite.title if (topSite.type == PINNED || topSite.type == DEFAULT) { 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 { - top_site_title.setCompoundDrawablesWithIntrinsicBounds(null, null, null, null) + binding.topSiteTitle.setCompoundDrawablesWithIntrinsicBounds(null, null, null, null) } when (topSite.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 -> { - favicon_image.setImageDrawable(getDrawable(itemView.context, R.drawable.ic_baidu)) + binding.faviconImage.setImageDrawable(getDrawable(itemView.context, R.drawable.ic_baidu)) } 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 -> { - favicon_image.setImageDrawable(getDrawable(itemView.context, R.drawable.ic_pdd)) + binding.faviconImage.setImageDrawable(getDrawable(itemView.context, R.drawable.ic_pdd)) } else -> { - itemView.context.components.core.icons.loadIntoView(favicon_image, topSite.url) + itemView.context.components.core.icons.loadIntoView(binding.faviconImage, topSite.url) } } diff --git a/app/src/main/res/layout/component_recent_bookmarks.xml b/app/src/main/res/layout/component_recent_bookmarks.xml index f567210589..a7f31d67fe 100644 --- a/app/src/main/res/layout/component_recent_bookmarks.xml +++ b/app/src/main/res/layout/component_recent_bookmarks.xml @@ -9,7 +9,9 @@ android:layout_height="wrap_content" android:orientation="vertical"> - + (R.id.top_site_title).compoundDrawables[0] + TopSiteItemViewHolder(binding.root, interactor).bind(defaultTopSite) + val pinIndicator = binding.topSiteTitle.compoundDrawables[0] assertNotNull(pinIndicator) } @@ -83,8 +79,8 @@ class TopSiteItemViewHolderTest { type = TopSite.Type.PINNED ) - TopSiteItemViewHolder(view, interactor).bind(pinnedTopSite) - val pinIndicator = view.findViewById(R.id.top_site_title).compoundDrawables[0] + TopSiteItemViewHolder(binding.root, interactor).bind(pinnedTopSite) + val pinIndicator = binding.topSiteTitle.compoundDrawables[0] assertNotNull(pinIndicator) } @@ -99,8 +95,8 @@ class TopSiteItemViewHolderTest { type = TopSite.Type.FRECENT ) - TopSiteItemViewHolder(view, interactor).bind(frecentTopSite) - val pinIndicator = view.findViewById(R.id.top_site_title).compoundDrawables[0] + TopSiteItemViewHolder(binding.root, interactor).bind(frecentTopSite) + val pinIndicator = binding.topSiteTitle.compoundDrawables[0] assertNull(pinIndicator) } diff --git a/app/src/test/java/org/mozilla/fenix/home/tips/ButtonTipViewHolderTest.kt b/app/src/test/java/org/mozilla/fenix/home/tips/ButtonTipViewHolderTest.kt index dc79303182..505109faad 100644 --- a/app/src/test/java/org/mozilla/fenix/home/tips/ButtonTipViewHolderTest.kt +++ b/app/src/test/java/org/mozilla/fenix/home/tips/ButtonTipViewHolderTest.kt @@ -16,7 +16,6 @@ import io.mockk.just import io.mockk.mockk import io.mockk.spyk import io.mockk.verify -import kotlinx.android.synthetic.main.button_tip_item.* import mozilla.components.support.test.robolectric.testContext import org.junit.Assert.assertEquals 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.tips.Tip import org.mozilla.fenix.components.tips.TipType +import org.mozilla.fenix.databinding.ButtonTipItemBinding import org.mozilla.fenix.helpers.FenixRobolectricTestRunner import org.mozilla.fenix.home.sessioncontrol.SessionControlInteractor import org.mozilla.fenix.utils.Settings @@ -43,6 +43,7 @@ class ButtonTipViewHolderTest { @MockK private lateinit var sharedPrefs: SharedPreferences @MockK private lateinit var sharedPrefsEditor: SharedPreferences.Editor private lateinit var viewHolder: ButtonTipViewHolder + private lateinit var binding: ButtonTipItemBinding @Before fun setup() { @@ -53,6 +54,7 @@ class ButtonTipViewHolderTest { ) viewHolder = ButtonTipViewHolder(view, interactor, metrics, settings) + binding = ButtonTipItemBinding.bind(view) every { view.context } returns activity every { activity.openToBrowserAndLoad(any(), any(), any()) } just Runs every { interactor.onCloseTip(any()) } just Runs @@ -66,9 +68,9 @@ class ButtonTipViewHolderTest { fun `text is displayed based on given tip`() { viewHolder.bind(defaultTip()) - assertEquals("Tip Title", viewHolder.tip_header_text.text) - assertEquals("Tip description", viewHolder.tip_description_text.text) - assertEquals("button", viewHolder.tip_button.text) + assertEquals("Tip Title", binding.tipHeaderText.text) + assertEquals("Tip description", binding.tipDescriptionText.text) + assertEquals("button", binding.tipButton.text) verify { metrics.track(Event.TipDisplayed("tipIdentifier")) } } @@ -77,16 +79,16 @@ class ButtonTipViewHolderTest { fun `learn more is hidden if learnMoreURL is null`() { viewHolder.bind(defaultTip(learnMoreUrl = null)) - assertTrue(viewHolder.tip_learn_more.isGone) + assertTrue(binding.tipLearnMore.isGone) } @Test fun `learn more is visible if learnMoreURL is not null`() { 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 { activity.openToBrowserAndLoad( searchTermOrURL = "https://learnmore.com", @@ -101,7 +103,7 @@ class ButtonTipViewHolderTest { val action = mockk<() -> Unit>(relaxed = true) viewHolder.bind(defaultTip(action)) - viewHolder.tip_button.performClick() + binding.tipButton.performClick() verify { action() } verify { metrics.track(Event.TipPressed("tipIdentifier")) } } @@ -111,7 +113,7 @@ class ButtonTipViewHolderTest { val tip = defaultTip() viewHolder.bind(tip) - viewHolder.tip_close.performClick() + binding.tipClose.performClick() verify { interactor.onCloseTip(tip) } verify { metrics.track(Event.TipClosed("tipIdentifier")) } verify { sharedPrefsEditor.putBoolean("tipIdentifier", false) } diff --git a/app/src/test/java/org/mozilla/fenix/perf/StartupReportFullyDrawnTest.kt b/app/src/test/java/org/mozilla/fenix/perf/StartupReportFullyDrawnTest.kt index 23b67713de..3dfa7b7712 100644 --- a/app/src/test/java/org/mozilla/fenix/perf/StartupReportFullyDrawnTest.kt +++ b/app/src/test/java/org/mozilla/fenix/perf/StartupReportFullyDrawnTest.kt @@ -4,6 +4,7 @@ package org.mozilla.fenix.perf +import android.view.LayoutInflater import android.view.View import android.view.ViewTreeObserver import android.widget.LinearLayout @@ -15,16 +16,21 @@ import io.mockk.impl.annotations.MockK import io.mockk.just import io.mockk.mockk import io.mockk.slot +import io.mockk.spyk 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.Test +import org.junit.runner.RunWith import org.mozilla.fenix.HomeActivity 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.perf.StartupTimelineStateMachine.StartupDestination import org.mozilla.fenix.perf.StartupTimelineStateMachine.StartupState +@RunWith(FenixRobolectricTestRunner::class) class StartupReportFullyDrawnTest { @MockK private lateinit var activity: HomeActivity @@ -37,9 +43,10 @@ class StartupReportFullyDrawnTest { @Before fun setup() { MockKAnnotations.init(this) + val binding = TopSiteItemBinding.inflate(LayoutInflater.from(testContext), rootContainer, false) + holderItemView = spyk(binding.root) every { activity.findViewById(R.id.rootContainer) } returns rootContainer every { holderItemView.context } returns activity - every { holderItemView.top_site_item } returns mockk(relaxed = true) holder = TopSiteItemViewHolder(holderItemView, mockk()) every { rootContainer.viewTreeObserver } returns viewTreeObserver every { holderItemView.viewTreeObserver } returns viewTreeObserver