For #17917 - Finish migrating all synthetics usages

upstream-sync
Mugurell 3 years ago committed by mergify[bot]
parent 5e13ead750
commit 621c388c12

@ -7,16 +7,17 @@ package org.mozilla.fenix.perf
import android.util.Log
import android.view.View
import android.view.ViewGroup
import android.widget.LinearLayout
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.view.children
import androidx.recyclerview.widget.RecyclerView
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.UiDevice
import kotlinx.android.synthetic.main.activity_home.*
import org.junit.Assert.assertEquals
import org.junit.Ignore
import org.junit.Rule
import org.junit.Test
import org.mozilla.fenix.R
import org.mozilla.fenix.ext.components
import org.mozilla.fenix.helpers.HomeActivityTestRule
@ -94,7 +95,7 @@ class StartupExcessiveResourceUseTest {
val actualRunBlocking = RunBlockingCounter.count.get()
val actualComponentInitCount = ComponentInitCount.count.get()
val rootView = activityTestRule.activity.rootContainer
val rootView = activityTestRule.activity.findViewById<LinearLayout>(R.id.rootContainer)
val actualViewHierarchyDepth = countAndLogViewHierarchyDepth(rootView, 1)
val actualRecyclerViewConstraintLayoutChildren = countRecyclerViewConstraintLayoutChildren(rootView, null)

@ -38,7 +38,7 @@ interface AddonDetailsInteractor {
/**
* Shows the details of an add-on.
*/
class AddonDetailsView(
class AddonDetailsBindingDelegate(
private val binding: FragmentAddOnDetailsBinding,
private val interactor: AddonDetailsInteractor
) {

@ -40,7 +40,7 @@ class AddonDetailsFragment : Fragment(R.layout.fragment_add_on_details), AddonDe
}
val binding = FragmentAddOnDetailsBinding.bind(view)
AddonDetailsView(binding, interactor = this).bind(args.addon)
AddonDetailsBindingDelegate(binding, interactor = this).bind(args.addon)
}
override fun openWebsite(addonSiteUrl: Uri) {

@ -10,13 +10,13 @@ import android.view.View
import android.view.ViewGroup
import androidx.navigation.fragment.findNavController
import androidx.navigation.fragment.navArgs
import kotlinx.android.synthetic.main.fragment_add_on_internal_settings.*
import kotlinx.coroutines.ExperimentalCoroutinesApi
import mozilla.components.browser.state.action.WebExtensionAction
import mozilla.components.concept.engine.EngineSession
import mozilla.components.concept.engine.EngineView
import mozilla.components.lib.state.ext.consumeFrom
import org.mozilla.fenix.R
import org.mozilla.fenix.databinding.FragmentAddOnInternalSettingsBinding
import org.mozilla.fenix.ext.requireComponents
import org.mozilla.fenix.ext.showToolbar
@ -57,10 +57,12 @@ class WebExtensionActionPopupFragment : AddonPopupBaseFragment(), EngineSession.
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val binding = FragmentAddOnInternalSettingsBinding.bind(view)
val session = engineSession
// If we have the session, render it otherwise consume it from the store.
if (session != null) {
addonSettingsEngineView.render(session)
binding.addonSettingsEngineView.render(session)
consumePopupSession()
} else {
consumeFrom(coreComponents.store) { state ->
@ -68,7 +70,7 @@ class WebExtensionActionPopupFragment : AddonPopupBaseFragment(), EngineSession.
val popupSession = extState.popupSession
if (popupSession != null) {
initializeSession(popupSession)
addonSettingsEngineView.render(popupSession)
binding.addonSettingsEngineView.render(popupSession)
popupSession.register(this)
consumePopupSession()
engineSession = popupSession

@ -484,7 +484,7 @@ abstract class BaseBrowserFragment :
)
val dynamicDownloadDialog = DynamicDownloadDialog(
container = binding.browserLayout,
context = context,
downloadState = downloadState,
metrics = requireComponents.analytics.metrics,
didFail = downloadJobStatus == DownloadState.Status.FAILED,
@ -492,7 +492,7 @@ abstract class BaseBrowserFragment :
onCannotOpenFile = {
showCannotOpenFileError(binding.browserLayout, context, it)
},
view = binding.viewDynamicDownloadDialog.root,
binding = binding.viewDynamicDownloadDialog,
toolbarHeight = toolbarHeight,
onDismiss = { sharedViewModel.downloadDialogState.remove(downloadState.sessionId) }
)
@ -885,7 +885,7 @@ abstract class BaseBrowserFragment :
{ sharedViewModel.downloadDialogState.remove(sessionId) }
DynamicDownloadDialog(
container = binding.browserLayout,
context = context,
downloadState = savedDownloadState.first,
metrics = requireComponents.analytics.metrics,
didFail = savedDownloadState.second,
@ -893,7 +893,7 @@ abstract class BaseBrowserFragment :
onCannotOpenFile = {
showCannotOpenFileError(binding.browserLayout, context, it)
},
view = binding.viewDynamicDownloadDialog.root,
binding = binding.viewDynamicDownloadDialog,
toolbarHeight = toolbarHeight,
onDismiss = onDismiss
).show()
@ -1158,7 +1158,7 @@ abstract class BaseBrowserFragment :
)
/**
* Returns the layout [android.binding.Gravity] for the quick settings and ETP dialog.
* Returns the layout [android.view.Gravity] for the quick settings and ETP dialog.
*/
protected fun getAppropriateLayoutGravity(): Int =
requireComponents.settings.toolbarPosition.androidGravity

@ -9,7 +9,6 @@ import android.os.Looper
import android.text.InputFilter
import android.view.KeyEvent
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.inputmethod.EditorInfo
import androidx.constraintlayout.widget.ConstraintSet
@ -18,7 +17,6 @@ import androidx.recyclerview.widget.RecyclerView
import androidx.transition.AutoTransition
import androidx.transition.Transition
import androidx.transition.TransitionManager
import kotlinx.android.extensions.LayoutContainer
import mozilla.components.feature.tab.collections.TabCollection
import mozilla.components.support.ktx.android.view.hideKeyboard
import mozilla.components.support.ktx.android.view.showKeyboard
@ -30,16 +28,15 @@ import org.mozilla.fenix.ext.toShortUrl
import org.mozilla.fenix.home.Tab
class CollectionCreationView(
container: ViewGroup,
private val container: ViewGroup,
private val interactor: CollectionCreationInteractor
) : LayoutContainer {
) {
private val binding = ComponentCollectionCreationBinding.inflate(
LayoutInflater.from(container.context),
container,
true
)
override val containerView: View = binding.root
private val bottomBarView = CollectionCreationBottomBarView(
interactor = interactor,
@ -82,12 +79,12 @@ class CollectionCreationView(
binding.tabList.run {
adapter = collectionCreationTabListAdapter
itemAnimator = null
layoutManager = LinearLayoutManager(containerView.context, RecyclerView.VERTICAL, true)
layoutManager = LinearLayoutManager(container.context, RecyclerView.VERTICAL, true)
}
binding.collectionsList.run {
adapter = collectionSaveListAdapter
layoutManager = LinearLayoutManager(containerView.context, RecyclerView.VERTICAL, true)
layoutManager = LinearLayoutManager(container.context, RecyclerView.VERTICAL, true)
}
}
@ -113,7 +110,7 @@ class CollectionCreationView(
}
private fun updateForSelectTabs(state: CollectionCreationState) {
containerView.context.components.analytics.metrics.track(Event.CollectionTabSelectOpened)
container.context.components.analytics.metrics.track(Event.CollectionTabSelectOpened)
binding.tabList.isClickable = true
@ -136,7 +133,7 @@ class CollectionCreationView(
}
selectTabsConstraints.clone(
containerView.context,
container.context,
R.layout.component_collection_creation
)
collectionCreationTabListAdapter.updateData(state.tabs, state.selectedTabs)
@ -146,7 +143,7 @@ class CollectionCreationView(
private fun updateForSelectCollection() {
binding.tabList.isClickable = false
selectCollectionConstraints.clone(
containerView.context,
container.context,
R.layout.component_collection_creation_select_collection
)
selectCollectionConstraints.applyTo(binding.collectionConstraintLayout)
@ -163,7 +160,7 @@ class CollectionCreationView(
private fun updateForNameCollection(state: CollectionCreationState) {
binding.tabList.isClickable = false
nameCollectionConstraints.clone(
containerView.context,
container.context,
R.layout.component_collection_creation_name_collection
)
nameCollectionConstraints.applyTo(binding.collectionConstraintLayout)
@ -186,7 +183,7 @@ class CollectionCreationView(
binding.nameCollectionEdittext.showKeyboard()
binding.nameCollectionEdittext.setText(
containerView.context.getString(
container.context.getString(
R.string.create_collection_default_name,
state.defaultCollectionNumber
)
@ -198,7 +195,7 @@ class CollectionCreationView(
binding.tabList.isClickable = false
state.selectedTabCollection?.let { tabCollection ->
val publicSuffixList = containerView.context.components.publicSuffixList
val publicSuffixList = container.context.components.publicSuffixList
tabCollection.tabs.map { tab ->
Tab(
sessionId = tab.id.toString(),
@ -211,7 +208,7 @@ class CollectionCreationView(
}
}
nameCollectionConstraints.clone(
containerView.context,
container.context,
R.layout.component_collection_creation_name_collection
)
nameCollectionConstraints.applyTo(binding.collectionConstraintLayout)

@ -18,8 +18,8 @@ import androidx.core.widget.TextViewCompat
import com.google.android.material.snackbar.BaseTransientBottomBar
import com.google.android.material.snackbar.ContentViewCallback
import com.google.android.material.snackbar.Snackbar
import kotlinx.android.synthetic.main.fenix_snackbar.view.*
import org.mozilla.fenix.R
import org.mozilla.fenix.databinding.FenixSnackbarBinding
import org.mozilla.fenix.ext.increaseTapArea
import org.mozilla.fenix.ext.settings
import org.mozilla.fenix.utils.Mockable
@ -27,20 +27,20 @@ import org.mozilla.fenix.utils.Mockable
@Mockable
class FenixSnackbar private constructor(
parent: ViewGroup,
content: View,
private val binding: FenixSnackbarBinding,
contentViewCallback: FenixSnackbarCallback,
isError: Boolean
) : BaseTransientBottomBar<FenixSnackbar>(parent, content, contentViewCallback) {
) : BaseTransientBottomBar<FenixSnackbar>(parent, binding.root, contentViewCallback) {
init {
view.setBackgroundColor(Color.TRANSPARENT)
setAppropriateBackground(isError)
content.snackbar_btn.increaseTapArea(actionButtonIncreaseDps)
binding.snackbarBtn.increaseTapArea(actionButtonIncreaseDps)
TextViewCompat.setAutoSizeTextTypeUniformWithConfiguration(
content.snackbar_text,
binding.snackbarText,
minTextSize,
maxTextSize,
stepGranularity,
@ -49,7 +49,7 @@ class FenixSnackbar private constructor(
}
fun setAppropriateBackground(isError: Boolean) {
view.snackbar_layout.background = if (isError) {
binding.snackbarLayout.background = if (isError) {
AppCompatResources.getDrawable(context, R.drawable.fenix_snackbar_error_background)
} else {
AppCompatResources.getDrawable(context, R.drawable.fenix_snackbar_background)
@ -57,7 +57,7 @@ class FenixSnackbar private constructor(
}
fun setText(text: String) = this.apply {
view.snackbar_text.text = text
binding.snackbarText.text = text
}
fun setLength(duration: Int) = this.apply {
@ -65,7 +65,7 @@ class FenixSnackbar private constructor(
}
fun setAction(text: String, action: () -> Unit) = this.apply {
view.snackbar_btn.apply {
binding.snackbarBtn.apply {
setText(text)
visibility = View.VISIBLE
setOnClickListener {
@ -110,7 +110,7 @@ class FenixSnackbar private constructor(
}
val inflater = LayoutInflater.from(parent.context)
val content = inflater.inflate(R.layout.fenix_snackbar, parent, false)
val binding = FenixSnackbarBinding.inflate(inflater, parent, false)
val durationOrAccessibleDuration =
if (parent.context.settings().accessibilityServicesEnabled) {
@ -119,12 +119,12 @@ class FenixSnackbar private constructor(
duration
}
val callback = FenixSnackbarCallback(content)
val callback = FenixSnackbarCallback(binding.root)
val shouldUseBottomToolbar = view.context.settings().shouldUseBottomToolbar
val toolbarHeight = view.resources.getDimensionPixelSize(R.dimen.browser_toolbar_height)
val dynamicToolbarEnabled = view.context.settings().isDynamicToolbarEnabled
return FenixSnackbar(parent, content, callback, isError).also {
return FenixSnackbar(parent, binding, callback, isError).also {
it.duration = durationOrAccessibleDuration
it.view.updatePadding(

@ -13,7 +13,6 @@ import androidx.annotation.VisibleForTesting
import androidx.coordinatorlayout.widget.CoordinatorLayout
import androidx.core.content.ContextCompat
import androidx.lifecycle.LifecycleOwner
import kotlinx.android.extensions.LayoutContainer
import kotlinx.coroutines.ExperimentalCoroutinesApi
import mozilla.components.browser.domains.autocomplete.ShippedDomainsProvider
import mozilla.components.browser.state.selector.selectedTab
@ -43,10 +42,7 @@ class BrowserToolbarView(
private val interactor: BrowserToolbarInteractor,
private val customTabSession: CustomTabSessionState?,
private val lifecycleOwner: LifecycleOwner
) : LayoutContainer {
override val containerView: View?
get() = container
) {
private val settings = container.context.settings()

@ -9,9 +9,9 @@ import android.view.View
import androidx.fragment.app.Fragment
import androidx.navigation.fragment.findNavController
import androidx.navigation.fragment.navArgs
import kotlinx.android.synthetic.main.fragment_crash_reporter.*
import mozilla.components.lib.crash.Crash
import org.mozilla.fenix.R
import org.mozilla.fenix.databinding.FragmentCrashReporterBinding
import org.mozilla.fenix.ext.hideToolbar
import org.mozilla.fenix.ext.increaseTapArea
import org.mozilla.fenix.ext.requireComponents
@ -25,10 +25,12 @@ class CrashReporterFragment : Fragment(R.layout.fragment_crash_reporter) {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val binding = FragmentCrashReporterBinding.bind(view)
val args: CrashReporterFragmentArgs by navArgs()
val crash = Crash.fromIntent(args.crashIntent)
title.text = getString(R.string.tab_crash_title_2, getString(R.string.app_name))
binding.title.text = getString(R.string.tab_crash_title_2, getString(R.string.app_name))
val controller = CrashReporterController(
crash,
@ -38,17 +40,17 @@ class CrashReporterFragment : Fragment(R.layout.fragment_crash_reporter) {
settings = requireContext().settings()
)
restoreTabButton.apply {
binding.restoreTabButton.apply {
increaseTapArea(TAP_INCREASE_DP)
setOnClickListener {
controller.handleCloseAndRestore(sendCrashCheckbox.isChecked)
controller.handleCloseAndRestore(binding.sendCrashCheckbox.isChecked)
}
}
closeTabButton.apply {
binding.closeTabButton.apply {
increaseTapArea(TAP_INCREASE_DP)
setOnClickListener {
controller.handleCloseAndRemove(sendCrashCheckbox.isChecked)
controller.handleCloseAndRemove(binding.sendCrashCheckbox.isChecked)
}
}
}

@ -8,7 +8,6 @@ import android.content.Intent
import androidx.annotation.VisibleForTesting
import androidx.navigation.NavDestination
import androidx.navigation.NavDirections
import kotlinx.android.synthetic.main.activity_home.*
import mozilla.components.browser.state.selector.findCustomTab
import mozilla.components.browser.state.state.SessionState
import mozilla.components.concept.engine.manifest.WebAppManifestParser

@ -11,10 +11,9 @@ import android.view.View
import androidx.coordinatorlayout.widget.CoordinatorLayout
import androidx.core.view.isVisible
import androidx.navigation.fragment.navArgs
import kotlinx.android.synthetic.main.component_browser_top_toolbar.*
import kotlinx.android.synthetic.main.fragment_browser.*
import kotlinx.coroutines.ExperimentalCoroutinesApi
import mozilla.components.browser.state.state.SessionState
import mozilla.components.browser.toolbar.BrowserToolbar
import mozilla.components.concept.engine.manifest.WebAppManifestParser
import mozilla.components.concept.engine.manifest.getOrNull
import mozilla.components.feature.contextmenu.ContextMenuCandidate
@ -58,6 +57,8 @@ class ExternalAppBrowserFragment : BaseBrowserFragment(), UserInteractionHandler
val customTabSessionId = customTabSessionId ?: return
val activity = requireActivity()
val components = activity.components
val toolbar = binding.root.findViewById<BrowserToolbar>(R.id.toolbar)
val manifest = args.webAppManifest?.let { json -> WebAppManifestParser().parse(json).getOrNull() }
customTabsIntegration.set(
@ -105,9 +106,9 @@ class ExternalAppBrowserFragment : BaseBrowserFragment(), UserInteractionHandler
browserToolbarView.view.isVisible = toolbarVisible
webAppToolbarShouldBeVisible = toolbarVisible
if (!toolbarVisible) {
engineView.setDynamicToolbarMaxHeight(0)
binding.engineView.setDynamicToolbarMaxHeight(0)
val browserEngine =
swipeRefresh.layoutParams as CoordinatorLayout.LayoutParams
binding.swipeRefresh.layoutParams as CoordinatorLayout.LayoutParams
browserEngine.bottomMargin = 0
}
},

@ -9,14 +9,13 @@ import android.view.View
import android.view.ViewGroup
import android.webkit.MimeTypeMap
import androidx.coordinatorlayout.widget.CoordinatorLayout
import kotlinx.android.extensions.LayoutContainer
import kotlinx.android.synthetic.main.download_dialog_layout.view.*
import mozilla.components.browser.state.state.content.DownloadState
import mozilla.components.feature.downloads.AbstractFetchDownloadService
import mozilla.components.feature.downloads.toMegabyteOrKilobyteString
import org.mozilla.fenix.R
import org.mozilla.fenix.components.metrics.Event
import org.mozilla.fenix.components.metrics.MetricController
import org.mozilla.fenix.databinding.DownloadDialogLayoutBinding
import org.mozilla.fenix.ext.metrics
import org.mozilla.fenix.ext.settings
@ -27,29 +26,26 @@ import org.mozilla.fenix.ext.settings
*/
@Suppress("LongParameterList")
class DynamicDownloadDialog(
private val container: ViewGroup,
private val context: Context,
private val downloadState: DownloadState?,
private val metrics: MetricController,
private val didFail: Boolean,
private val tryAgain: (String) -> Unit,
private val onCannotOpenFile: (DownloadState) -> Unit,
private val view: View,
private val binding: DownloadDialogLayoutBinding,
private val toolbarHeight: Int,
private val onDismiss: () -> Unit
) : LayoutContainer {
) {
override val containerView: View?
get() = container
private val settings = container.context.settings()
private val settings = context.settings()
init {
setupDownloadDialog(view)
setupDownloadDialog()
}
private fun setupDownloadDialog(view: View) {
private fun setupDownloadDialog() {
if (downloadState == null) return
view.apply {
binding.root.apply {
if (layoutParams is CoordinatorLayout.LayoutParams) {
(layoutParams as CoordinatorLayout.LayoutParams).apply {
@ -65,40 +61,40 @@ class DynamicDownloadDialog(
if (settings.shouldUseBottomToolbar) {
val params: ViewGroup.MarginLayoutParams =
view.layoutParams as ViewGroup.MarginLayoutParams
binding.root.layoutParams as ViewGroup.MarginLayoutParams
params.bottomMargin = toolbarHeight
}
if (didFail) {
view.download_dialog_title.text =
container.context.getString(R.string.mozac_feature_downloads_failed_notification_text2)
binding.downloadDialogTitle.text =
context.getString(R.string.mozac_feature_downloads_failed_notification_text2)
view.download_dialog_icon.setImageResource(
binding.downloadDialogIcon.setImageResource(
mozilla.components.feature.downloads.R.drawable.mozac_feature_download_ic_download_failed
)
view.download_dialog_action_button.apply {
binding.downloadDialogActionButton.apply {
text = context.getString(
mozilla.components.feature.downloads.R.string.mozac_feature_downloads_button_try_again
)
setOnClickListener {
tryAgain(downloadState.id)
context.metrics.track(Event.InAppNotificationDownloadTryAgain)
dismiss(view)
dismiss()
}
}
} else {
val titleText = container.context.getString(
val titleText = context.getString(
R.string.mozac_feature_downloads_completed_notification_text2
) + " (${downloadState.contentLength?.toMegabyteOrKilobyteString()})"
view.download_dialog_title.text = titleText
binding.downloadDialogTitle.text = titleText
view.download_dialog_icon.setImageResource(
binding.downloadDialogIcon.setImageResource(
mozilla.components.feature.downloads.R.drawable.mozac_feature_download_ic_download_complete
)
view.download_dialog_action_button.apply {
binding.downloadDialogActionButton.apply {
text = context.getString(
mozilla.components.feature.downloads.R.string.mozac_feature_downloads_button_open
)
@ -115,28 +111,28 @@ class DynamicDownloadDialog(
}
context.metrics.track(Event.InAppNotificationDownloadOpen)
dismiss(view)
dismiss()
}
}
}
view.download_dialog_close_button.setOnClickListener {
dismiss(view)
binding.downloadDialogCloseButton.setOnClickListener {
dismiss()
}
view.download_dialog_filename.text = downloadState.fileName
binding.downloadDialogFilename.text = downloadState.fileName
}
fun show() {
view.visibility = View.VISIBLE
binding.root.visibility = View.VISIBLE
(view.layoutParams as CoordinatorLayout.LayoutParams).apply {
(behavior as DynamicDownloadDialogBehavior).forceExpand(view)
(binding.root.layoutParams as CoordinatorLayout.LayoutParams).apply {
(behavior as DynamicDownloadDialogBehavior).forceExpand(binding.root)
}
}
private fun dismiss(view: View) {
view.visibility = View.GONE
private fun dismiss() {
binding.root.visibility = View.GONE
onDismiss()
}

@ -5,8 +5,8 @@
package org.mozilla.fenix.historymetadata.view
import android.view.View
import kotlinx.android.synthetic.main.history_metadata_group.*
import org.mozilla.fenix.R
import org.mozilla.fenix.databinding.HistoryMetadataGroupBinding
import org.mozilla.fenix.historymetadata.HistoryMetadataGroup
import org.mozilla.fenix.historymetadata.interactor.HistoryMetadataInteractor
import org.mozilla.fenix.utils.view.ViewHolder
@ -22,8 +22,10 @@ class HistoryMetadataGroupViewHolder(
private val interactor: HistoryMetadataInteractor
) : ViewHolder(view) {
private val binding = HistoryMetadataGroupBinding.bind(view)
fun bind(historyMetadataGroup: HistoryMetadataGroup) {
history_metadata_group_title.text = historyMetadataGroup.title
binding.historyMetadataGroupTitle.text = historyMetadataGroup.title
itemView.isActivated = historyMetadataGroup.expanded

@ -5,8 +5,8 @@
package org.mozilla.fenix.historymetadata.view
import android.view.View
import kotlinx.android.synthetic.main.history_metadata_header.*
import org.mozilla.fenix.R
import org.mozilla.fenix.databinding.HistoryMetadataHeaderBinding
import org.mozilla.fenix.historymetadata.interactor.HistoryMetadataInteractor
import org.mozilla.fenix.utils.view.ViewHolder
@ -22,7 +22,8 @@ class HistoryMetadataHeaderViewHolder(
) : ViewHolder(view) {
init {
show_all_button.setOnClickListener {
val binding = HistoryMetadataHeaderBinding.bind(view)
binding.showAllButton.setOnClickListener {
interactor.onHistoryMetadataShowAllClicked()
}
}

@ -5,11 +5,11 @@
package org.mozilla.fenix.historymetadata.view
import android.view.View
import kotlinx.android.synthetic.main.history_metadata_list_row.*
import mozilla.components.browser.icons.BrowserIcons
import mozilla.components.browser.state.state.ContentState
import mozilla.components.concept.storage.HistoryMetadata
import org.mozilla.fenix.R
import org.mozilla.fenix.databinding.HistoryMetadataListRowBinding
import org.mozilla.fenix.ext.components
import org.mozilla.fenix.ext.loadIntoView
import org.mozilla.fenix.historymetadata.interactor.HistoryMetadataInteractor
@ -29,14 +29,16 @@ class HistoryMetadataViewHolder(
private val icons: BrowserIcons = view.context.components.core.icons
) : ViewHolder(view) {
val binding = HistoryMetadataListRowBinding.bind(view)
fun bind(historyMetadata: HistoryMetadata) {
history_metadata_title.text = if (historyMetadata.title.isNullOrEmpty()) {
binding.historyMetadataTitle.text = if (historyMetadata.title.isNullOrEmpty()) {
historyMetadata.key.url
} else {
historyMetadata.title
}
icons.loadIntoView(history_metadata_icon, historyMetadata.key.url)
icons.loadIntoView(binding.historyMetadataIcon, historyMetadata.key.url)
itemView.setOnClickListener {
interactor.onHistoryMetadataItemClicked(historyMetadata.key.url, historyMetadata.key)

@ -8,15 +8,14 @@ import android.content.Context
import android.view.ViewGroup
import androidx.appcompat.widget.Toolbar
import androidx.core.content.ContextCompat
import kotlinx.android.extensions.LayoutContainer
import mozilla.components.support.ktx.android.content.getColorFromAttr
import org.mozilla.fenix.R
import org.mozilla.fenix.ext.asActivity
import org.mozilla.fenix.ext.setToolbarColors
open class LibraryPageView(
override val containerView: ViewGroup
) : LayoutContainer {
val containerView: ViewGroup
) {
protected val context: Context inline get() = containerView.context
protected val activity = context.asActivity()

@ -14,7 +14,6 @@ import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.lifecycle.lifecycleScope
import androidx.navigation.Navigation
import kotlinx.android.synthetic.main.fragment_edit_bookmark.*
import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.Dispatchers.Main
import kotlinx.coroutines.launch
@ -24,6 +23,7 @@ import mozilla.components.support.ktx.android.view.hideKeyboard
import mozilla.components.support.ktx.android.view.showKeyboard
import org.mozilla.fenix.R
import org.mozilla.fenix.components.metrics.Event
import org.mozilla.fenix.databinding.FragmentEditBookmarkBinding
import org.mozilla.fenix.ext.nav
import org.mozilla.fenix.ext.requireComponents
import org.mozilla.fenix.ext.showToolbar
@ -34,6 +34,8 @@ import org.mozilla.fenix.library.bookmarks.friendlyRootTitle
* Menu to create a new bookmark folder.
*/
class AddBookmarkFolderFragment : Fragment(R.layout.fragment_edit_bookmark) {
private var _binding: FragmentEditBookmarkBinding? = null
private val binding get() = _binding!!
private val sharedViewModel: BookmarksSharedViewModel by activityViewModels()
@ -46,10 +48,12 @@ class AddBookmarkFolderFragment : Fragment(R.layout.fragment_edit_bookmark) {
* Hides fields for bookmark items present in the shared layout file.
*/
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
bookmarkUrlLabel.visibility = GONE
bookmarkUrlEdit.visibility = GONE
inputLayoutBookmarkUrl.visibility = GONE
bookmarkNameEdit.showKeyboard()
_binding = FragmentEditBookmarkBinding.bind(view)
binding.bookmarkUrlLabel.visibility = GONE
binding.bookmarkUrlEdit.visibility = GONE
binding.inputLayoutBookmarkUrl.visibility = GONE
binding.bookmarkNameEdit.showKeyboard()
}
override fun onResume() {
@ -63,9 +67,9 @@ class AddBookmarkFolderFragment : Fragment(R.layout.fragment_edit_bookmark) {
?: requireComponents.core.bookmarksStorage.getBookmark(BookmarkRoot.Mobile.id)
}
bookmarkParentFolderSelector.text =
binding.bookmarkParentFolderSelector.text =
friendlyRootTitle(context, sharedViewModel.selectedFolder!!)
bookmarkParentFolderSelector.setOnClickListener {
binding.bookmarkParentFolderSelector.setOnClickListener {
nav(
R.id.bookmarkAddFolderFragment,
AddBookmarkFolderFragmentDirections
@ -79,7 +83,7 @@ class AddBookmarkFolderFragment : Fragment(R.layout.fragment_edit_bookmark) {
override fun onPause() {
super.onPause()
bookmarkNameEdit.hideKeyboard()
binding.bookmarkNameEdit.hideKeyboard()
}
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
@ -89,8 +93,8 @@ class AddBookmarkFolderFragment : Fragment(R.layout.fragment_edit_bookmark) {
override fun onOptionsItemSelected(item: MenuItem): Boolean {
return when (item.itemId) {
R.id.confirm_add_folder_button -> {
if (bookmarkNameEdit.text.isNullOrBlank()) {
bookmarkNameEdit.error =
if (binding.bookmarkNameEdit.text.isNullOrBlank()) {
binding.bookmarkNameEdit.error =
getString(R.string.bookmark_empty_title_error)
return true
}
@ -98,7 +102,7 @@ class AddBookmarkFolderFragment : Fragment(R.layout.fragment_edit_bookmark) {
viewLifecycleOwner.lifecycleScope.launch(IO) {
val newGuid = requireComponents.core.bookmarksStorage.addFolder(
sharedViewModel.selectedFolder!!.guid,
bookmarkNameEdit.text.toString(),
binding.bookmarkNameEdit.text.toString(),
null
)
sharedViewModel.selectedFolder =
@ -114,4 +118,10 @@ class AddBookmarkFolderFragment : Fragment(R.layout.fragment_edit_bookmark) {
else -> super.onOptionsItemSelected(item)
}
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
}

@ -23,8 +23,6 @@ import androidx.lifecycle.lifecycleScope
import androidx.navigation.Navigation
import androidx.navigation.fragment.findNavController
import androidx.navigation.fragment.navArgs
import kotlinx.android.synthetic.main.fragment_edit_bookmark.*
import kotlinx.android.synthetic.main.fragment_edit_bookmark.view.*
import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.Dispatchers.Main
import kotlinx.coroutines.launch
@ -40,6 +38,7 @@ import org.mozilla.fenix.NavHostActivity
import org.mozilla.fenix.R
import org.mozilla.fenix.components.FenixSnackbar
import org.mozilla.fenix.components.metrics.Event
import org.mozilla.fenix.databinding.FragmentEditBookmarkBinding
import org.mozilla.fenix.ext.components
import org.mozilla.fenix.ext.getRootView
import org.mozilla.fenix.ext.nav
@ -54,6 +53,8 @@ import org.mozilla.fenix.library.bookmarks.friendlyRootTitle
* Menu to edit the name, URL, and location of a bookmark item.
*/
class EditBookmarkFragment : Fragment(R.layout.fragment_edit_bookmark) {
private var _binding: FragmentEditBookmarkBinding? = null
private val binding get() = _binding!!
private val args by navArgs<EditBookmarkFragmentArgs>()
private val sharedViewModel: BookmarksSharedViewModel by activityViewModels()
@ -68,6 +69,9 @@ class EditBookmarkFragment : Fragment(R.layout.fragment_edit_bookmark) {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
_binding = FragmentEditBookmarkBinding.bind(view)
initToolbar()
viewLifecycleOwner.lifecycleScope.launch(Main) {
@ -95,9 +99,9 @@ class EditBookmarkFragment : Fragment(R.layout.fragment_edit_bookmark) {
when (bookmarkNode?.type) {
BookmarkNodeType.FOLDER -> {
activity?.title = getString(R.string.edit_bookmark_folder_fragment_title)
inputLayoutBookmarkUrl.visibility = View.GONE
bookmarkUrlEdit.visibility = View.GONE
bookmarkUrlLabel.visibility = View.GONE
binding.inputLayoutBookmarkUrl.visibility = View.GONE
binding.bookmarkUrlEdit.visibility = View.GONE
binding.bookmarkUrlLabel.visibility = View.GONE
}
BookmarkNodeType.ITEM -> {
activity?.title = getString(R.string.edit_bookmark_fragment_title)
@ -107,15 +111,15 @@ class EditBookmarkFragment : Fragment(R.layout.fragment_edit_bookmark) {
val currentBookmarkNode = bookmarkNode
if (currentBookmarkNode != null && currentBookmarkNode != bookmarkNodeBeforeReload) {
bookmarkNameEdit.setText(currentBookmarkNode.title)
bookmarkUrlEdit.setText(currentBookmarkNode.url)
binding.bookmarkNameEdit.setText(currentBookmarkNode.title)
binding.bookmarkUrlEdit.setText(currentBookmarkNode.url)
}
bookmarkParent?.let { node ->
bookmarkParentFolderSelector.text = friendlyRootTitle(context, node)
binding.bookmarkParentFolderSelector.text = friendlyRootTitle(context, node)
}
bookmarkParentFolderSelector.setOnClickListener {
binding.bookmarkParentFolderSelector.setOnClickListener {
sharedViewModel.selectedFolder = null
nav(
R.id.bookmarkEditFragment,
@ -131,22 +135,22 @@ class EditBookmarkFragment : Fragment(R.layout.fragment_edit_bookmark) {
)
}
view.bookmarkNameEdit.apply {
binding.bookmarkNameEdit.apply {
requestFocus()
placeCursorAtEnd()
showKeyboard()
}
view.bookmarkUrlEdit.addTextChangedListener(object : TextWatcher {
binding.bookmarkUrlEdit.addTextChangedListener(object : TextWatcher {
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
// NOOP
}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
bookmarkUrlEdit.onTextChanged(s)
binding.bookmarkUrlEdit.onTextChanged(s)
inputLayoutBookmarkUrl.error = null
inputLayoutBookmarkUrl.errorIconDrawable = null
binding.inputLayoutBookmarkUrl.error = null
binding.inputLayoutBookmarkUrl.errorIconDrawable = null
}
override fun afterTextChanged(s: Editable?) {
@ -169,9 +173,9 @@ class EditBookmarkFragment : Fragment(R.layout.fragment_edit_bookmark) {
override fun onPause() {
super.onPause()
bookmarkNameEdit.hideKeyboard()
bookmarkUrlEdit.hideKeyboard()
progress_bar_bookmark.visibility = View.GONE
binding.bookmarkNameEdit.hideKeyboard()
binding.bookmarkUrlEdit.hideKeyboard()
binding.progressBarBookmark.visibility = View.GONE
}
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
@ -234,9 +238,9 @@ class EditBookmarkFragment : Fragment(R.layout.fragment_edit_bookmark) {
}
private fun updateBookmarkFromTextChanges() {
progress_bar_bookmark.visibility = View.VISIBLE
val nameText = bookmarkNameEdit.text.toString()
val urlText = bookmarkUrlEdit.text.toString()
binding.progressBarBookmark.visibility = View.VISIBLE
val nameText = binding.bookmarkNameEdit.text.toString()
val urlText = binding.bookmarkUrlEdit.text.toString()
updateBookmarkNode(nameText, urlText)
}
@ -265,16 +269,16 @@ class EditBookmarkFragment : Fragment(R.layout.fragment_edit_bookmark) {
)
}
withContext(Main) {
inputLayoutBookmarkUrl.error = null
inputLayoutBookmarkUrl.errorIconDrawable = null
binding.inputLayoutBookmarkUrl.error = null
binding.inputLayoutBookmarkUrl.errorIconDrawable = null
findNavController().popBackStack()
}
} catch (e: UrlParseFailed) {
withContext(Main) {
inputLayoutBookmarkUrl.error = getString(R.string.bookmark_invalid_url_error)
inputLayoutBookmarkUrl.setErrorIconDrawable(R.drawable.mozac_ic_warning_with_bottom_padding)
inputLayoutBookmarkUrl.setErrorIconTintList(
binding.inputLayoutBookmarkUrl.error = getString(R.string.bookmark_invalid_url_error)
binding.inputLayoutBookmarkUrl.setErrorIconDrawable(R.drawable.mozac_ic_warning_with_bottom_padding)
binding.inputLayoutBookmarkUrl.setErrorIconTintList(
ColorStateList.valueOf(
ContextCompat.getColor(requireContext(), R.color.design_error)
)
@ -282,6 +286,12 @@ class EditBookmarkFragment : Fragment(R.layout.fragment_edit_bookmark) {
}
}
}
progress_bar_bookmark.visibility = View.INVISIBLE
binding.progressBarBookmark.visibility = View.INVISIBLE
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
}

@ -12,7 +12,6 @@ import androidx.core.view.updatePaddingRelative
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import kotlinx.android.extensions.LayoutContainer
import mozilla.components.concept.storage.BookmarkNode
import org.mozilla.fenix.R
import org.mozilla.fenix.library.LibrarySiteItemView
@ -61,9 +60,7 @@ class SelectBookmarkFolderAdapter(private val sharedViewModel: BookmarksSharedVi
class BookmarkFolderViewHolder(
val view: LibrarySiteItemView
) : RecyclerView.ViewHolder(view), LayoutContainer {
override val containerView get() = view
) : RecyclerView.ViewHolder(view) {
init {
view.displayAs(LibrarySiteItemView.ItemType.FOLDER)
@ -74,12 +71,12 @@ class SelectBookmarkFolderAdapter(private val sharedViewModel: BookmarksSharedVi
view.changeSelected(selected)
view.iconView.setImageDrawable(
AppCompatResources.getDrawable(
containerView.context,
view.context,
R.drawable.ic_folder_icon
)?.apply {
setTint(
ContextCompat.getColor(
containerView.context,
view.context,
R.color.primary_text_light_theme
)
)

@ -15,7 +15,6 @@ import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.navArgs
import kotlinx.android.synthetic.main.fragment_select_bookmark_folder.*
import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.Dispatchers.Main
import kotlinx.coroutines.launch
@ -23,6 +22,7 @@ import kotlinx.coroutines.withContext
import mozilla.appservices.places.BookmarkRoot
import mozilla.components.concept.storage.BookmarkNode
import org.mozilla.fenix.R
import org.mozilla.fenix.databinding.FragmentSelectBookmarkFolderBinding
import org.mozilla.fenix.ext.components
import org.mozilla.fenix.ext.nav
import org.mozilla.fenix.ext.showToolbar
@ -30,6 +30,8 @@ import org.mozilla.fenix.library.bookmarks.BookmarksSharedViewModel
import org.mozilla.fenix.library.bookmarks.DesktopFolders
class SelectBookmarkFolderFragment : Fragment() {
private var _binding: FragmentSelectBookmarkFolderBinding? = null
private val binding get() = _binding!!
private val sharedViewModel: BookmarksSharedViewModel by activityViewModels()
private var bookmarkNode: BookmarkNode? = null
@ -40,7 +42,15 @@ class SelectBookmarkFolderFragment : Fragment() {
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.fragment_select_bookmark_folder, container, false)
_binding = FragmentSelectBookmarkFolderBinding.inflate(inflater, container, false)
return binding.root
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
override fun onResume() {
@ -57,7 +67,7 @@ class SelectBookmarkFolderFragment : Fragment() {
?.let { DesktopFolders(context, showMobileRoot = true).withOptionalDesktopFolders(it) }
}
val adapter = SelectBookmarkFolderAdapter(sharedViewModel)
recylerViewBookmarkFolders.adapter = adapter
binding.recylerViewBookmarkFolders.adapter = adapter
adapter.updateData(bookmarkNode, args.hideFolderGuid)
}
}

@ -9,8 +9,6 @@ import android.view.ViewGroup
import androidx.core.view.isVisible
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.SimpleItemAnimator
import kotlinx.android.synthetic.main.component_downloads.*
import kotlinx.android.synthetic.main.component_downloads.view.*
import mozilla.components.support.base.feature.UserInteractionHandler
import org.mozilla.fenix.R
import org.mozilla.fenix.databinding.ComponentDownloadsBinding
@ -112,10 +110,10 @@ class DownloadView(
}
fun updateEmptyState(userHasDownloads: Boolean) {
download_list.isVisible = userHasDownloads
download_empty_view.isVisible = !userHasDownloads
binding.downloadList.isVisible = userHasDownloads
binding.downloadEmptyView.isVisible = !userHasDownloads
if (!userHasDownloads) {
download_empty_view.announceForAccessibility(context.getString(R.string.download_empty_message_1))
binding.downloadEmptyView.announceForAccessibility(context.getString(R.string.download_empty_message_1))
}
}

@ -15,7 +15,6 @@ import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import androidx.navigation.fragment.findNavController
import kotlinx.android.synthetic.main.fragment_recently_closed_tabs.view.*
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.map
@ -30,6 +29,7 @@ import org.mozilla.fenix.R
import org.mozilla.fenix.browser.browsingmode.BrowsingMode
import org.mozilla.fenix.components.FenixSnackbar
import org.mozilla.fenix.components.StoreProvider
import org.mozilla.fenix.databinding.FragmentRecentlyClosedTabsBinding
import org.mozilla.fenix.ext.getRootView
import org.mozilla.fenix.ext.requireComponents
import org.mozilla.fenix.ext.setTextColor
@ -100,8 +100,8 @@ class RecentlyClosedFragment : LibraryPageFragment<RecoverableTab>(), UserIntera
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val view = inflater.inflate(R.layout.fragment_recently_closed_tabs, container, false)
): View {
val binding = FragmentRecentlyClosedTabsBinding.inflate(inflater, container, false)
recentlyClosedFragmentStore = StoreProvider.get(this) {
RecentlyClosedFragmentStore(
RecentlyClosedFragmentState(
@ -126,10 +126,10 @@ class RecentlyClosedFragment : LibraryPageFragment<RecoverableTab>(), UserIntera
)
recentlyClosedInteractor = RecentlyClosedFragmentInteractor(recentlyClosedController)
_recentlyClosedFragmentView = RecentlyClosedFragmentView(
view.recentlyClosedLayout,
binding.recentlyClosedLayout,
recentlyClosedInteractor
)
return view
return binding.root
}
override fun onDestroyView() {

@ -5,15 +5,14 @@
package org.mozilla.fenix.library.recentlyclosed
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.content.res.AppCompatResources
import androidx.core.view.isVisible
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.SimpleItemAnimator
import kotlinx.android.synthetic.main.component_recently_closed.*
import mozilla.components.browser.state.state.recover.RecoverableTab
import org.mozilla.fenix.R
import org.mozilla.fenix.databinding.ComponentRecentlyClosedBinding
import org.mozilla.fenix.library.LibraryPageView
import org.mozilla.fenix.selection.SelectionInteractor
@ -74,19 +73,20 @@ class RecentlyClosedFragmentView(
private val interactor: RecentlyClosedFragmentInteractor
) : LibraryPageView(container) {
val view: View = LayoutInflater.from(container.context)
.inflate(R.layout.component_recently_closed, container, true)
private val binding = ComponentRecentlyClosedBinding.inflate(
LayoutInflater.from(container.context), container, true
)
private val recentlyClosedAdapter: RecentlyClosedAdapter = RecentlyClosedAdapter(interactor)
init {
recently_closed_list.apply {
binding.recentlyClosedList.apply {
layoutManager = LinearLayoutManager(containerView.context)
adapter = recentlyClosedAdapter
(itemAnimator as SimpleItemAnimator).supportsChangeAnimations = false
}
view_more_history.apply {
binding.viewMoreHistory.apply {
titleView.text =
containerView.context.getString(R.string.recently_closed_show_full_history)
urlView.isVisible = false
@ -106,8 +106,8 @@ class RecentlyClosedFragmentView(
fun update(state: RecentlyClosedFragmentState) {
state.apply {
recently_closed_empty_view.isVisible = items.isEmpty()
recently_closed_list.isVisible = items.isNotEmpty()
binding.recentlyClosedEmptyView.isVisible = items.isEmpty()
binding.recentlyClosedList.isVisible = items.isNotEmpty()
recentlyClosedAdapter.updateData(items, selectedTabs)

@ -6,10 +6,9 @@ package org.mozilla.fenix.library.recentlyclosed
import android.view.View
import androidx.recyclerview.widget.RecyclerView
import kotlinx.android.synthetic.main.history_list_item.view.*
import kotlinx.android.synthetic.main.library_site_item.view.*
import mozilla.components.browser.state.state.recover.RecoverableTab
import org.mozilla.fenix.R
import org.mozilla.fenix.databinding.HistoryListItemBinding
import org.mozilla.fenix.ext.hideAndDisable
import org.mozilla.fenix.ext.showAndEnable
import org.mozilla.fenix.selection.SelectionHolder
@ -22,6 +21,8 @@ class RecentlyClosedItemViewHolder(
private val selectionHolder: SelectionHolder<RecoverableTab>
) : RecyclerView.ViewHolder(view) {
private val binding = HistoryListItemBinding.bind(view)
private var item: RecoverableTab? = null
init {
@ -31,21 +32,21 @@ class RecentlyClosedItemViewHolder(
fun bind(
item: RecoverableTab
) {
itemView.history_layout.titleView.text =
binding.historyLayout.titleView.text =
if (item.title.isNotEmpty()) item.title else item.url
itemView.history_layout.urlView.text = item.url
binding.historyLayout.urlView.text = item.url
itemView.history_layout.setSelectionInteractor(item, selectionHolder, recentlyClosedFragmentInteractor)
itemView.history_layout.changeSelected(item in selectionHolder.selectedItems)
binding.historyLayout.setSelectionInteractor(item, selectionHolder, recentlyClosedFragmentInteractor)
binding.historyLayout.changeSelected(item in selectionHolder.selectedItems)
if (this.item?.url != item.url) {
itemView.history_layout.loadFavicon(item.url)
binding.historyLayout.loadFavicon(item.url)
}
if (selectionHolder.selectedItems.isEmpty()) {
itemView.overflow_menu.showAndEnable()
binding.historyLayout.overflowView.showAndEnable()
} else {
itemView.overflow_menu.hideAndDisable()
binding.historyLayout.overflowView.hideAndDisable()
}
this.item = item
@ -67,7 +68,7 @@ class RecentlyClosedItemViewHolder(
}
}
itemView.history_layout.attachMenu(historyMenu.menuController)
binding.historyLayout.attachMenu(historyMenu.menuController)
}
companion object {

@ -9,7 +9,6 @@ import android.os.Bundle
import android.view.View
import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.LinearLayoutManager
import kotlinx.android.synthetic.main.activity_migration.*
import mozilla.components.support.base.log.logger.Logger
import mozilla.components.support.ktx.android.content.getColorFromAttr
import mozilla.components.support.migration.AbstractMigrationProgressActivity
@ -21,6 +20,7 @@ import mozilla.components.support.migration.state.MigrationStore
import org.mozilla.fenix.HomeActivity
import org.mozilla.fenix.IntentReceiverActivity
import org.mozilla.fenix.R
import org.mozilla.fenix.databinding.ActivityMigrationBinding
import org.mozilla.fenix.ext.components
class MigrationProgressActivity : AbstractMigrationProgressActivity() {
@ -28,38 +28,43 @@ class MigrationProgressActivity : AbstractMigrationProgressActivity() {
private val statusAdapter = MigrationStatusAdapter()
override val store: MigrationStore by lazy { components.migrationStore }
private lateinit var binding: ActivityMigrationBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_migration)
binding = ActivityMigrationBinding.inflate(layoutInflater)
setContentView(binding.root)
init()
}
fun init() {
window.navigationBarColor = getColorFromAttr(R.attr.foundation)
val appName = migration_description.context.getString(R.string.app_name)
val appName = binding.migrationDescription.context.getString(R.string.app_name)
migration_description.apply {
binding.migrationDescription.apply {
text = context.getString(R.string.migration_description, appName)
}
migration_status_list.apply {
binding.migrationStatusList.apply {
val margin = resources.getDimensionPixelSize(R.dimen.migration_margin)
addItemDecoration(MigrationStatusItemDecoration(margin))
layoutManager = LinearLayoutManager(this@MigrationProgressActivity)
adapter = statusAdapter
}
migration_welcome_title.apply {
binding.migrationWelcomeTitle.apply {
text = context.getString(R.string.migration_title, appName)
}
migration_button_text_view.text = getString(R.string.migration_updating_app_button_text, appName)
binding.migrationButtonTextView.text = getString(R.string.migration_updating_app_button_text, appName)
}
override fun onMigrationCompleted(results: MigrationResults) {
// Enable clicking the finish button
migration_button.apply {
binding.migrationButton.apply {
setOnClickListener {
AbstractMigrationService.dismissNotification(context)
@ -79,12 +84,12 @@ class MigrationProgressActivity : AbstractMigrationProgressActivity() {
}
}
}
migration_button_text_view.apply {
binding.migrationButtonTextView.apply {
text = getString(R.string.migration_update_app_button, getString(R.string.app_name))
setTextColor(ContextCompat.getColor(context, R.color.white_color))
}
migration_button.setBackgroundResource(R.drawable.migration_button_background)
migration_button_progress_bar.visibility = View.INVISIBLE
binding.migrationButton.setBackgroundResource(R.drawable.migration_button_background)
binding.migrationButtonProgressBar.visibility = View.INVISIBLE
// Keep the results list up-to-date.
statusAdapter.updateData(results)
}

@ -13,10 +13,10 @@ import androidx.core.view.isInvisible
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import kotlinx.android.synthetic.main.migration_list_item.view.*
import mozilla.components.support.migration.Migration
import mozilla.components.support.migration.MigrationResults
import org.mozilla.fenix.R
import org.mozilla.fenix.databinding.MigrationListItemBinding
internal data class MigrationItem(
val migration: Migration,
@ -60,9 +60,11 @@ internal class MigrationStatusAdapter :
}
class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
private val context = view.context
private val title = view.migration_item_name
private val status = view.migration_status_image
private val binding = MigrationListItemBinding.bind(view)
private val title = binding.migrationItemName
private val status = binding.migrationStatusImage
fun bind(item: MigrationItem) {
// Get the resource ID for the item.

@ -7,7 +7,6 @@ package org.mozilla.fenix.nimbus.view
import android.view.ViewGroup
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import kotlinx.android.extensions.LayoutContainer
import mozilla.components.service.nimbus.ui.NimbusBranchAdapter
import org.mozilla.fenix.nimbus.NimbusBranchesState
import org.mozilla.fenix.nimbus.controller.NimbusBranchesController
@ -16,9 +15,9 @@ import org.mozilla.fenix.nimbus.controller.NimbusBranchesController
* View used for managing a Nimbus experiment's branches.
*/
class NimbusBranchesView(
override val containerView: ViewGroup,
private val containerView: ViewGroup,
val controller: NimbusBranchesController
) : LayoutContainer {
) {
private val nimbusAdapter = NimbusBranchAdapter(controller)

@ -35,9 +35,6 @@ import androidx.core.view.isVisible
import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.findNavController
import androidx.navigation.fragment.navArgs
import kotlinx.android.synthetic.main.fragment_search_dialog.*
import kotlinx.android.synthetic.main.fragment_search_dialog.view.*
import kotlinx.android.synthetic.main.search_suggestions_hint.view.*
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.map
@ -62,6 +59,8 @@ import org.mozilla.fenix.HomeActivity
import org.mozilla.fenix.R
import org.mozilla.fenix.components.metrics.Event
import org.mozilla.fenix.components.toolbar.ToolbarPosition
import org.mozilla.fenix.databinding.FragmentSearchDialogBinding
import org.mozilla.fenix.databinding.SearchSuggestionsHintBinding
import org.mozilla.fenix.ext.components
import org.mozilla.fenix.ext.requireComponents
import org.mozilla.fenix.ext.settings
@ -74,6 +73,9 @@ typealias SearchDialogFragmentStore = SearchFragmentStore
@SuppressWarnings("LargeClass", "TooManyFunctions")
class SearchDialogFragment : AppCompatDialogFragment(), UserInteractionHandler {
private var _binding: FragmentSearchDialogBinding? = null
private val binding get() = _binding!!
private var voiceSearchButtonAlreadyAdded: Boolean = false
private lateinit var interactor: SearchDialogInteractor
private lateinit var store: SearchDialogFragmentStore
@ -134,9 +136,9 @@ class SearchDialogFragment : AppCompatDialogFragment(), UserInteractionHandler {
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
): View {
val args by navArgs<SearchDialogFragmentArgs>()
val view = inflater.inflate(R.layout.fragment_search_dialog, container, false)
_binding = FragmentSearchDialogBinding.inflate(inflater, container, false)
val activity = requireActivity() as HomeActivity
val isPrivate = activity.browsingModeManager.mode.isPrivate
@ -187,12 +189,12 @@ class SearchDialogFragment : AppCompatDialogFragment(), UserInteractionHandler {
interactor,
historyStorageProvider(),
isPrivate,
view.toolbar,
binding.toolbar,
requireComponents.core.engine,
fromHomeFragment
)
val awesomeBar = view.awesome_bar
val awesomeBar = binding.awesomeBar
awesomeBar.customizeForBottomToolbar = requireContext().settings().shouldUseBottomToolbar
awesomeBarView = AwesomeBarView(
@ -202,8 +204,8 @@ class SearchDialogFragment : AppCompatDialogFragment(), UserInteractionHandler {
fromHomeFragment
)
view.awesome_bar.setOnTouchListener { _, _ ->
view.hideKeyboard()
binding.awesomeBar.setOnTouchListener { _, _ ->
binding.root.hideKeyboard()
false
}
@ -217,14 +219,14 @@ class SearchDialogFragment : AppCompatDialogFragment(), UserInteractionHandler {
if (fromHomeFragment) {
// When displayed above home, dispatches the touch events to scrim area to the HomeFragment
view.search_wrapper.background = ColorDrawable(Color.TRANSPARENT)
binding.searchWrapper.background = ColorDrawable(Color.TRANSPARENT)
dialog?.window?.decorView?.setOnTouchListener { _, event ->
requireActivity().dispatchTouchEvent(event)
false
}
}
return view
return binding.root
}
@ExperimentalCoroutinesApi
@ -244,13 +246,13 @@ class SearchDialogFragment : AppCompatDialogFragment(), UserInteractionHandler {
// When displayed above browser, dismisses dialog on clicking scrim area
if (findNavController().previousBackStackEntry?.destination?.id == R.id.browserFragment) {
search_wrapper.setOnClickListener {
binding.searchWrapper.setOnClickListener {
it.hideKeyboard()
dismissAllowingStateLoss()
}
}
view.search_engines_shortcut_button.setOnClickListener {
binding.searchEnginesShortcutButton.setOnClickListener {
interactor.onSearchShortcutsButtonClicked()
}
@ -260,18 +262,18 @@ class SearchDialogFragment : AppCompatDialogFragment(), UserInteractionHandler {
view = view
)
qr_scan_button.visibility = if (context?.hasCamera() == true) View.VISIBLE else View.GONE
binding.qrScanButton.visibility = if (context?.hasCamera() == true) View.VISIBLE else View.GONE
qr_scan_button.setOnClickListener {
binding.qrScanButton.setOnClickListener {
if (!requireContext().hasCamera()) { return@setOnClickListener }
view.hideKeyboard()
toolbarView.view.clearFocus()
if (requireContext().settings().shouldShowCameraPermissionPrompt) {
qrFeature.get()?.scan(R.id.search_wrapper)
qrFeature.get()?.scan(binding.searchWrapper.id)
} else {
if (requireContext().isPermissionGranted(Manifest.permission.CAMERA)) {
qrFeature.get()?.scan(R.id.search_wrapper)
qrFeature.get()?.scan(binding.searchWrapper.id)
} else {
interactor.onCameraPermissionsNeeded()
resetFocus()
@ -282,7 +284,7 @@ class SearchDialogFragment : AppCompatDialogFragment(), UserInteractionHandler {
requireContext().settings().setCameraPermissionNeededState = false
}
fill_link_from_clipboard.setOnClickListener {
binding.fillLinkFromClipboard.setOnClickListener {
requireComponents.analytics.metrics.track(Event.ClipboardSuggestionClicked)
view.hideKeyboard()
toolbarView.view.clearFocus()
@ -295,7 +297,9 @@ class SearchDialogFragment : AppCompatDialogFragment(), UserInteractionHandler {
}
val stubListener = ViewStub.OnInflateListener { _, inflated ->
inflated.learn_more.setOnClickListener {
val searchSuggestionHintBinding = SearchSuggestionsHintBinding.bind(inflated)
searchSuggestionHintBinding.learnMore.setOnClickListener {
(activity as HomeActivity)
.openToBrowserAndLoad(
searchTermOrURL = SupportUtils.getGenericSumoURLForTopic(
@ -306,7 +310,7 @@ class SearchDialogFragment : AppCompatDialogFragment(), UserInteractionHandler {
)
}
inflated.allow.setOnClickListener {
searchSuggestionHintBinding.allow.setOnClickListener {
inflated.visibility = View.GONE
requireContext().settings().also {
it.shouldShowSearchSuggestionsInPrivate = true
@ -317,7 +321,7 @@ class SearchDialogFragment : AppCompatDialogFragment(), UserInteractionHandler {
requireComponents.analytics.metrics.track(Event.PrivateBrowsingShowSearchSuggestions)
}
inflated.dismiss.setOnClickListener {
searchSuggestionHintBinding.dismiss.setOnClickListener {
inflated.visibility = View.GONE
requireContext().settings().also {
it.shouldShowSearchSuggestionsInPrivate = false
@ -325,14 +329,14 @@ class SearchDialogFragment : AppCompatDialogFragment(), UserInteractionHandler {
}
}
inflated.text.text =
searchSuggestionHintBinding.text.text =
getString(R.string.search_suggestions_onboarding_text, getString(R.string.app_name))
inflated.title.text =
searchSuggestionHintBinding.title.text =
getString(R.string.search_suggestions_onboarding_title)
}
view.search_suggestions_hint.setOnInflateListener((stubListener))
binding.searchSuggestionsHint.setOnInflateListener((stubListener))
if (view.context.settings().accessibilityServicesEnabled) {
updateAccessibilityTraversalOrder()
}
@ -344,7 +348,7 @@ class SearchDialogFragment : AppCompatDialogFragment(), UserInteractionHandler {
* query as consumeFrom may run several times on fragment start due to state updates.
* */
if (it.url != it.query) firstUpdate = false
awesome_bar?.visibility = if (shouldShowAwesomebar(it)) View.VISIBLE else View.INVISIBLE
binding.awesomeBar.visibility = if (shouldShowAwesomebar(it)) View.VISIBLE else View.INVISIBLE
updateSearchSuggestionsHintVisibility(it)
updateClipboardSuggestion(it, requireContext().components.clipboardHandler.url)
updateToolbarContentDescription(it)
@ -359,14 +363,14 @@ class SearchDialogFragment : AppCompatDialogFragment(), UserInteractionHandler {
!firstUpdate && searchFragmentState.query.isNotBlank() || searchFragmentState.showSearchShortcuts
private fun updateAccessibilityTraversalOrder() {
val searchWrapperId = search_wrapper.id
val searchWrapperId = binding.searchWrapper.id
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) {
qr_scan_button.accessibilityTraversalAfter = searchWrapperId
search_engines_shortcut_button.accessibilityTraversalAfter = searchWrapperId
fill_link_from_clipboard.accessibilityTraversalAfter = searchWrapperId
binding.qrScanButton.accessibilityTraversalAfter = searchWrapperId
binding.searchEnginesShortcutButton.accessibilityTraversalAfter = searchWrapperId
binding.fillLinkFromClipboard.accessibilityTraversalAfter = searchWrapperId
} else {
viewLifecycleOwner.lifecycleScope.launch {
search_wrapper.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED)
binding.searchWrapper.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED)
}
}
}
@ -376,6 +380,12 @@ class SearchDialogFragment : AppCompatDialogFragment(), UserInteractionHandler {
view?.hideKeyboard()
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
/*
* This way of dismissing the keyboard is needed to smoothly dismiss the keyboard while the dialog
* is also dismissing. For example, when clicking a top site on home while this dialog is showing.
@ -442,7 +452,7 @@ class SearchDialogFragment : AppCompatDialogFragment(), UserInteractionHandler {
requestPermissions(permissions, REQUEST_CODE_CAMERA_PERMISSIONS)
},
onScanResult = { result ->
qr_scan_button.isChecked = false
binding.qrScanButton.isChecked = false
activity?.let {
AlertDialog.Builder(it).apply {
val spannable = resources.getSpanned(
@ -487,7 +497,7 @@ class SearchDialogFragment : AppCompatDialogFragment(), UserInteractionHandler {
}
private fun resetFocus() {
qr_scan_button.isChecked = false
binding.qrScanButton.isChecked = false
toolbarView.view.edit.focus()
toolbarView.view.requestFocus()
}
@ -495,31 +505,31 @@ class SearchDialogFragment : AppCompatDialogFragment(), UserInteractionHandler {
private fun setupConstraints(view: View) {
if (view.context.settings().toolbarPosition == ToolbarPosition.BOTTOM) {
ConstraintSet().apply {
clone(search_wrapper)
clone(binding.searchWrapper)
clear(toolbar.id, TOP)
connect(toolbar.id, BOTTOM, PARENT_ID, BOTTOM)
clear(binding.toolbar.id, TOP)
connect(binding.toolbar.id, BOTTOM, PARENT_ID, BOTTOM)
clear(pill_wrapper.id, BOTTOM)
connect(pill_wrapper.id, BOTTOM, toolbar.id, TOP)
clear(binding.pillWrapper.id, BOTTOM)
connect(binding.pillWrapper.id, BOTTOM, binding.toolbar.id, TOP)
clear(awesome_bar.id, TOP)
clear(awesome_bar.id, BOTTOM)
connect(awesome_bar.id, TOP, search_suggestions_hint.id, BOTTOM)
connect(awesome_bar.id, BOTTOM, pill_wrapper.id, TOP)
clear(binding.awesomeBar.id, TOP)
clear(binding.awesomeBar.id, BOTTOM)
connect(binding.awesomeBar.id, TOP, binding.searchSuggestionsHint.id, BOTTOM)
connect(binding.awesomeBar.id, BOTTOM, binding.pillWrapper.id, TOP)
clear(search_suggestions_hint.id, TOP)
clear(search_suggestions_hint.id, BOTTOM)
connect(search_suggestions_hint.id, TOP, PARENT_ID, TOP)
connect(search_suggestions_hint.id, BOTTOM, search_hint_bottom_barrier.id, TOP)
clear(binding.searchSuggestionsHint.id, TOP)
clear(binding.searchSuggestionsHint.id, BOTTOM)
connect(binding.searchSuggestionsHint.id, TOP, PARENT_ID, TOP)
connect(binding.searchSuggestionsHint.id, BOTTOM, binding.searchHintBottomBarrier.id, TOP)
clear(fill_link_from_clipboard.id, TOP)
connect(fill_link_from_clipboard.id, BOTTOM, pill_wrapper.id, TOP)
clear(binding.fillLinkFromClipboard.id, TOP)
connect(binding.fillLinkFromClipboard.id, BOTTOM, binding.pillWrapper.id, TOP)
clear(fill_link_divider.id, TOP)
connect(fill_link_divider.id, BOTTOM, fill_link_from_clipboard.id, TOP)
clear(binding.fillLinkDivider.id, TOP)
connect(binding.fillLinkDivider.id, BOTTOM, binding.fillLinkFromClipboard.id, TOP)
applyTo(search_wrapper)
applyTo(binding.searchWrapper)
}
}
}
@ -530,8 +540,8 @@ class SearchDialogFragment : AppCompatDialogFragment(), UserInteractionHandler {
!state.showSearchShortcuts &&
state.url != state.query
findViewById<View>(R.id.search_suggestions_hint)?.isVisible = showHint
search_suggestions_hint_divider?.isVisible = showHint
binding.searchSuggestionsHint.isVisible = showHint
binding.searchSuggestionsHintDivider.isVisible = showHint
}
}
@ -581,17 +591,18 @@ class SearchDialogFragment : AppCompatDialogFragment(), UserInteractionHandler {
searchState.query.isEmpty() &&
!clipboardUrl.isNullOrEmpty() && !searchState.showSearchShortcuts
fill_link_from_clipboard.isVisible = shouldShowView
fill_link_divider.isVisible = shouldShowView
pill_wrapper_divider.isVisible =
binding.fillLinkFromClipboard.isVisible = shouldShowView
binding.fillLinkDivider.isVisible = shouldShowView
binding.pillWrapperDivider.isVisible =
!(shouldShowView && requireComponents.settings.shouldUseBottomToolbar)
clipboard_url.isVisible = shouldShowView
clipboard_title.isVisible = shouldShowView
link_icon.isVisible = shouldShowView
binding.clipboardUrl.isVisible = shouldShowView
binding.clipboardTitle.isVisible = shouldShowView
binding.linkIcon.isVisible = shouldShowView
clipboard_url.text = clipboardUrl
binding.clipboardUrl.text = clipboardUrl
fill_link_from_clipboard.contentDescription = "${clipboard_title.text}, ${clipboard_url.text}."
binding.fillLinkFromClipboard.contentDescription =
"${binding.clipboardTitle.text}, ${binding.clipboardUrl.text}."
if (clipboardUrl != null && !((activity as HomeActivity).browsingModeManager.mode.isPrivate)) {
requireComponents.core.engine.speculativeConnect(clipboardUrl)
@ -611,13 +622,13 @@ class SearchDialogFragment : AppCompatDialogFragment(), UserInteractionHandler {
private fun updateSearchShortcutsIcon(searchState: SearchFragmentState) {
view?.apply {
search_engines_shortcut_button.isVisible = searchState.areShortcutsAvailable
binding.searchEnginesShortcutButton.isVisible = searchState.areShortcutsAvailable
val showShortcuts = searchState.showSearchShortcuts
search_engines_shortcut_button.isChecked = showShortcuts
binding.searchEnginesShortcutButton.isChecked = showShortcuts
val color = if (showShortcuts) R.attr.contrastText else R.attr.primaryText
search_engines_shortcut_button.compoundDrawables[0]?.setTint(
binding.searchEnginesShortcutButton.compoundDrawables[0]?.setTint(
requireContext().getColorFromAttr(color)
)
}

@ -87,7 +87,6 @@ class TurnOnSyncFragment : Fragment(), AccountObserver {
override fun onDestroy() {
super.onDestroy()
requireComponents.analytics.metrics.track(Event.SyncAuthClosed)
_binding = null
}
override fun onResume() {
@ -139,6 +138,12 @@ class TurnOnSyncFragment : Fragment(), AccountObserver {
return binding.root
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
override fun onAuthenticated(account: OAuthAccount, authType: AuthType) {
// If we're in a `shouldLoginJustWithEmail = true` state, we won't have a view available,
// and can't display a snackbar.

@ -104,9 +104,14 @@ class LocaleSettingsFragment : Fragment() {
}
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
override fun onDestroy() {
super.onDestroy()
requireComponents.analytics.metrics.track(Event.SyncAuthClosed)
_binding = null
}
}

@ -7,44 +7,41 @@ package org.mozilla.fenix.settings.logins.view
import android.text.method.LinkMovementMethod
import android.view.LayoutInflater
import android.view.ViewGroup
import android.widget.FrameLayout
import androidx.core.view.isVisible
import androidx.recyclerview.widget.LinearLayoutManager
import kotlinx.android.extensions.LayoutContainer
import kotlinx.android.synthetic.main.component_saved_logins.view.*
import org.mozilla.fenix.R
import org.mozilla.fenix.databinding.ComponentSavedLoginsBinding
import org.mozilla.fenix.ext.addUnderline
import org.mozilla.fenix.settings.logins.LoginsListState
import org.mozilla.fenix.settings.logins.interactor.SavedLoginsInteractor
import org.mozilla.fenix.ext.addUnderline
/**
* View that contains and configures the Saved Logins List
*/
class SavedLoginsListView(
override val containerView: ViewGroup,
private val containerView: ViewGroup,
val interactor: SavedLoginsInteractor
) : LayoutContainer {
val view: FrameLayout = LayoutInflater.from(containerView.context)
.inflate(R.layout.component_saved_logins, containerView, true)
.findViewById(R.id.saved_logins_wrapper)
) {
private val binding = ComponentSavedLoginsBinding.inflate(
LayoutInflater.from(containerView.context), containerView, true
)
private val loginsAdapter = LoginsAdapter(interactor)
init {
view.saved_logins_list.apply {
binding.savedLoginsList.apply {
adapter = loginsAdapter
layoutManager = LinearLayoutManager(containerView.context)
itemAnimator = null
}
with(view.saved_passwords_empty_learn_more) {
with(binding.savedPasswordsEmptyLearnMore) {
movementMethod = LinkMovementMethod.getInstance()
addUnderline()
setOnClickListener { interactor.onLearnMoreClicked() }
}
with(view.saved_passwords_empty_message) {
with(binding.savedPasswordsEmptyMessage) {
val appName = context.getString(R.string.app_name)
text = String.format(
context.getString(
@ -57,11 +54,11 @@ class SavedLoginsListView(
fun update(state: LoginsListState) {
if (state.isLoading) {
view.progress_bar.isVisible = true
binding.progressBar.isVisible = true
} else {
view.progress_bar.isVisible = false
view.saved_logins_list.isVisible = state.loginList.isNotEmpty()
view.saved_passwords_empty_view.isVisible = state.loginList.isEmpty()
binding.progressBar.isVisible = false
binding.savedLoginsList.isVisible = state.loginList.isNotEmpty()
binding.savedPasswordsEmptyView.isVisible = state.loginList.isEmpty()
}
loginsAdapter.submitList(state.filteredItems)
}

@ -71,6 +71,12 @@ class ConnectionPanelDialogFragment : FenixDialogFragment() {
)
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
private fun getCurrentTab(): SessionState? {
return requireComponents.core.store.state.findTabOrCustomTab(args.sessionId)
}

@ -43,6 +43,7 @@ import org.mozilla.fenix.settings.PhoneFeature
* - website tracking protection.
* - website permission.
*/
@Suppress("TooManyFunctions")
class QuickSettingsSheetDialogFragment : FenixDialogFragment() {
private lateinit var quickSettingsStore: QuickSettingsFragmentStore
@ -131,6 +132,12 @@ class QuickSettingsSheetDialogFragment : FenixDialogFragment() {
}
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,

@ -8,10 +8,10 @@ import android.os.Bundle
import android.view.View
import androidx.appcompat.app.AlertDialog
import androidx.fragment.app.Fragment
import kotlinx.android.synthetic.main.fragment_add_new_device.*
import org.mozilla.fenix.BrowserDirection
import org.mozilla.fenix.HomeActivity
import org.mozilla.fenix.R
import org.mozilla.fenix.databinding.FragmentAddNewDeviceBinding
import org.mozilla.fenix.ext.showToolbar
import org.mozilla.fenix.settings.SupportUtils
@ -27,7 +27,9 @@ class AddNewDeviceFragment : Fragment(R.layout.fragment_add_new_device) {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
learn_button.setOnClickListener {
val binding = FragmentAddNewDeviceBinding.bind(view)
binding.learnButton.setOnClickListener {
(activity as HomeActivity).openToBrowserAndLoad(
searchTermOrURL = SupportUtils.getSumoURLForTopic(
requireContext(),
@ -38,7 +40,7 @@ class AddNewDeviceFragment : Fragment(R.layout.fragment_add_new_device) {
)
}
connect_button.setOnClickListener {
binding.connectButton.setOnClickListener {
AlertDialog.Builder(requireContext()).apply {
setMessage(R.string.sync_connect_device_dialog)
setPositiveButton(R.string.sync_confirmation_button) { dialog, _ -> dialog.cancel() }

@ -6,7 +6,6 @@ package org.mozilla.fenix.share
import android.view.LayoutInflater
import android.view.ViewGroup
import kotlinx.android.extensions.LayoutContainer
import mozilla.components.concept.sync.Device
import org.mozilla.fenix.databinding.ShareToAccountDevicesBinding
import org.mozilla.fenix.share.listadapters.AccountDevicesShareAdapter
@ -24,9 +23,9 @@ interface ShareToAccountDevicesInteractor {
}
class ShareToAccountDevicesView(
override val containerView: ViewGroup,
containerView: ViewGroup,
interactor: ShareToAccountDevicesInteractor
) : LayoutContainer {
) {
private val adapter = AccountDevicesShareAdapter(interactor)

@ -7,7 +7,6 @@ package org.mozilla.fenix.share
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import kotlinx.android.extensions.LayoutContainer
import org.mozilla.fenix.databinding.ShareToAppsBinding
import org.mozilla.fenix.share.listadapters.AppShareAdapter
import org.mozilla.fenix.share.listadapters.AppShareOption
@ -20,9 +19,9 @@ interface ShareToAppsInteractor {
}
class ShareToAppsView(
override val containerView: ViewGroup,
containerView: ViewGroup,
interactor: ShareToAppsInteractor
) : LayoutContainer {
) {
private val adapter = AppShareAdapter(interactor)
private val recentAdapter = AppShareAdapter(interactor)

@ -11,14 +11,17 @@ import android.view.ViewGroup
import androidx.core.widget.addTextChangedListener
import androidx.fragment.app.DialogFragment
import androidx.lifecycle.lifecycleScope
import kotlinx.android.synthetic.main.fragment_create_shortcut.*
import kotlinx.coroutines.launch
import mozilla.components.browser.state.selector.selectedTab
import org.mozilla.fenix.R
import org.mozilla.fenix.databinding.FragmentCreateShortcutBinding
import org.mozilla.fenix.ext.loadIntoView
import org.mozilla.fenix.ext.requireComponents
class CreateShortcutFragment : DialogFragment() {
private var _binding: FragmentCreateShortcutBinding? = null
private val binding get() = _binding!!
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setStyle(STYLE_NO_TITLE, R.style.CreateShortcutDialogStyle)
@ -28,7 +31,11 @@ class CreateShortcutFragment : DialogFragment() {
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? = inflater.inflate(R.layout.fragment_create_shortcut, container, false)
): View {
_binding = FragmentCreateShortcutBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
@ -38,29 +45,35 @@ class CreateShortcutFragment : DialogFragment() {
if (tab == null) {
dismiss()
} else {
requireComponents.core.icons.loadIntoView(favicon_image, tab.content.url)
requireComponents.core.icons.loadIntoView(binding.faviconImage, tab.content.url)
cancel_button.setOnClickListener { dismiss() }
add_button.setOnClickListener {
val text = shortcut_text.text.toString().trim()
binding.cancelButton.setOnClickListener { dismiss() }
binding.addButton.setOnClickListener {
val text = binding.shortcutText.text.toString().trim()
requireActivity().lifecycleScope.launch {
requireComponents.useCases.webAppUseCases.addToHomescreen(text)
}
dismiss()
}
shortcut_text.addTextChangedListener {
binding.shortcutText.addTextChangedListener {
updateAddButtonEnabledState()
}
shortcut_text.setText(tab.content.title)
binding.shortcutText.setText(tab.content.title)
}
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
private fun updateAddButtonEnabledState() {
val text = shortcut_text.text
add_button.isEnabled = text.isNotBlank()
add_button.alpha = if (text.isNotBlank()) ENABLED_ALPHA else DISABLED_ALPHA
val text = binding.shortcutText.text
binding.addButton.isEnabled = text.isNotBlank()
binding.addButton.alpha = if (text.isNotBlank()) ENABLED_ALPHA else DISABLED_ALPHA
}
companion object {

@ -10,9 +10,9 @@ import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.DialogFragment
import androidx.lifecycle.lifecycleScope
import kotlinx.android.synthetic.main.fragment_create_shortcut.*
import kotlinx.coroutines.launch
import org.mozilla.fenix.R
import org.mozilla.fenix.databinding.FragmentPwaOnboardingBinding
import org.mozilla.fenix.ext.requireComponents
/**
@ -33,9 +33,10 @@ class PwaOnboardingDialogFragment : DialogFragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val components = requireComponents
val binding = FragmentPwaOnboardingBinding.bind(view)
cancel_button.setOnClickListener { dismiss() }
add_button.setOnClickListener {
binding.cancelButton.setOnClickListener { dismiss() }
binding.addButton.setOnClickListener {
viewLifecycleOwner.lifecycleScope.launch {
components.useCases.webAppUseCases.addToHomescreen()
}.invokeOnCompletion {

@ -32,7 +32,6 @@ sealed class SyncedTabsViewHolder(itemView: View) : RecyclerView.ViewHolder(item
abstract fun <T : AdapterItem> bind(item: T, interactor: SyncedTabsView.Listener)
class TabViewHolder(itemView: View) : SyncedTabsViewHolder(itemView) {
override fun <T : AdapterItem> bind(item: T, interactor: SyncedTabsView.Listener) {
bindTab(item as AdapterItem.Tab)
@ -56,7 +55,6 @@ sealed class SyncedTabsViewHolder(itemView: View) : RecyclerView.ViewHolder(item
}
class ErrorViewHolder(itemView: View) : SyncedTabsViewHolder(itemView) {
override fun <T : AdapterItem> bind(item: T, interactor: SyncedTabsView.Listener) {
val errorItem = item as AdapterItem.Error
val binding = SyncTabsErrorRowBinding.bind(itemView)
@ -105,7 +103,6 @@ sealed class SyncedTabsViewHolder(itemView: View) : RecyclerView.ViewHolder(item
}
class TitleViewHolder(itemView: View) : SyncedTabsViewHolder(itemView) {
override fun <T : AdapterItem> bind(item: T, interactor: SyncedTabsView.Listener) {
val binding = ViewSyncedTabsTitleBinding.bind(itemView)
binding.refreshIcon.setOnClickListener { v ->

@ -12,7 +12,6 @@ import androidx.navigation.fragment.findNavController
import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.google.android.material.bottomsheet.BottomSheetDialog
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
import kotlinx.android.synthetic.main.fragment_tab_history_dialog.*
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.mapNotNull
@ -21,6 +20,7 @@ import mozilla.components.lib.state.ext.flowScoped
import mozilla.components.support.ktx.android.content.getColorFromAttr
import mozilla.components.support.ktx.kotlinx.coroutines.flow.ifChanged
import org.mozilla.fenix.R
import org.mozilla.fenix.databinding.FragmentTabHistoryDialogBinding
import org.mozilla.fenix.ext.requireComponents
class TabHistoryDialogFragment : BottomSheetDialogFragment() {
@ -37,6 +37,8 @@ class TabHistoryDialogFragment : BottomSheetDialogFragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val binding = FragmentTabHistoryDialogBinding.bind(view)
view.setBackgroundColor(view.context.getColorFromAttr(R.attr.foundation))
customTabSessionId = requireArguments().getString(EXTRA_SESSION_ID)
@ -47,7 +49,7 @@ class TabHistoryDialogFragment : BottomSheetDialogFragment() {
customTabId = customTabSessionId
)
val tabHistoryView = TabHistoryView(
container = tabHistoryLayout,
container = binding.tabHistoryLayout,
expandDialog = ::expand,
interactor = TabHistoryInteractor(controller)
)

@ -5,14 +5,11 @@
package org.mozilla.fenix.tabhistory
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import kotlinx.android.extensions.LayoutContainer
import kotlinx.android.synthetic.main.component_tabhistory.*
import mozilla.components.browser.state.state.content.HistoryState
import org.mozilla.fenix.R
import org.mozilla.fenix.databinding.ComponentTabhistoryBinding
interface TabHistoryViewInteractor {
@ -26,13 +23,12 @@ class TabHistoryView(
container: ViewGroup,
private val expandDialog: () -> Unit,
interactor: TabHistoryViewInteractor
) : LayoutContainer {
) {
override val containerView: View = LayoutInflater.from(container.context)
.inflate(R.layout.component_tabhistory, container, true)
private val binding = ComponentTabhistoryBinding.inflate(LayoutInflater.from(container.context), container, true)
private val adapter = TabHistoryAdapter(interactor)
private val layoutManager = object : LinearLayoutManager(containerView.context) {
private val layoutManager = object : LinearLayoutManager(container.context) {
private var shouldScrollToSelected = true
@ -45,10 +41,10 @@ class TabHistoryView(
// Force expansion of the dialog, otherwise scrolling to the current history item
// won't work when its position is near the bottom of the recyclerview.
expandDialog.invoke()
val itemView = tabHistoryRecyclerView.findViewHolderForLayoutPosition(
val itemView = binding.tabHistoryRecyclerView.findViewHolderForLayoutPosition(
findFirstCompletelyVisibleItemPosition()
)?.itemView
val offset = tabHistoryRecyclerView.height / 2 - (itemView?.height ?: 0) / 2
val offset = binding.tabHistoryRecyclerView.height / 2 - (itemView?.height ?: 0) / 2
scrollToPositionWithOffset(index, offset)
shouldScrollToSelected = false
}
@ -61,8 +57,8 @@ class TabHistoryView(
private var currentIndex: Int? = null
init {
tabHistoryRecyclerView.adapter = adapter
tabHistoryRecyclerView.layoutManager = layoutManager
binding.tabHistoryRecyclerView.adapter = adapter
binding.tabHistoryRecyclerView.layoutManager = layoutManager
}
fun updateState(historyState: HistoryState) {

@ -14,7 +14,6 @@ import android.widget.PopupWindow
import androidx.annotation.VisibleForTesting
import androidx.core.view.isVisible
import com.google.android.material.snackbar.Snackbar
import kotlinx.android.synthetic.main.browser_toolbar_popup_window.view.*
import mozilla.components.browser.state.selector.selectedTab
import mozilla.components.browser.state.store.BrowserStore
import org.mozilla.fenix.R
@ -23,6 +22,7 @@ import org.mozilla.fenix.components.metrics.Event
import org.mozilla.fenix.ext.components
import java.lang.ref.WeakReference
import mozilla.components.browser.state.selector.findCustomTab
import org.mozilla.fenix.databinding.BrowserToolbarPopupWindowBinding
object ToolbarPopupWindow {
fun show(
@ -38,10 +38,9 @@ object ToolbarPopupWindow {
val isCustomTabSession = customTabId != null
val customView = LayoutInflater.from(context)
.inflate(R.layout.browser_toolbar_popup_window, null)
val binding = BrowserToolbarPopupWindowBinding.inflate(LayoutInflater.from(context))
val popupWindow = PopupWindow(
customView,
binding.root,
LinearLayout.LayoutParams.WRAP_CONTENT,
context.resources.getDimensionPixelSize(R.dimen.context_menu_height),
true
@ -53,13 +52,13 @@ object ToolbarPopupWindow {
// See: https://github.com/mozilla-mobile/fenix/issues/10027
popupWindow.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
customView.copy.isVisible = copyVisible
binding.copy.isVisible = copyVisible
customView.paste.isVisible = !clipboard.text.isNullOrEmpty() && !isCustomTabSession
customView.paste_and_go.isVisible =
binding.paste.isVisible = !clipboard.text.isNullOrEmpty() && !isCustomTabSession
binding.pasteAndGo.isVisible =
!clipboard.text.isNullOrEmpty() && !isCustomTabSession
customView.copy.setOnClickListener {
binding.copy.setOnClickListener {
popupWindow.dismiss()
clipboard.text = getUrlForClipboard(
it.context.components.core.store,
@ -78,12 +77,12 @@ object ToolbarPopupWindow {
context.components.analytics.metrics.track(Event.CopyUrlUsed)
}
customView.paste.setOnClickListener {
binding.paste.setOnClickListener {
popupWindow.dismiss()
handlePaste(clipboard.text!!)
}
customView.paste_and_go.setOnClickListener {
binding.pasteAndGo.setOnClickListener {
popupWindow.dismiss()
handlePasteAndGo(clipboard.text!!)
}

@ -6,12 +6,11 @@ package org.mozilla.fenix.utils.view
import android.view.View
import androidx.recyclerview.widget.RecyclerView
import kotlinx.android.extensions.LayoutContainer
/**
* A base class for all recycler view holders supporting Android Extensions-style view access.
* This allows views to be used without an `itemView.<id>` prefix, and additionally caches them.
*/
abstract class ViewHolder(
override val containerView: View
) : RecyclerView.ViewHolder(containerView), LayoutContainer
val containerView: View
) : RecyclerView.ViewHolder(containerView)

@ -21,12 +21,12 @@ import org.mozilla.fenix.databinding.FragmentAddOnDetailsBinding
import org.mozilla.fenix.helpers.FenixRobolectricTestRunner
@RunWith(FenixRobolectricTestRunner::class)
class AddonDetailsViewTest {
class AddonDetailsBindingDelegateTest {
private lateinit var view: View
private lateinit var binding: FragmentAddOnDetailsBinding
private lateinit var interactor: AddonDetailsInteractor
private lateinit var detailsView: AddonDetailsView
private lateinit var detailsBindingDelegate: AddonDetailsBindingDelegate
private val baseAddon = Addon(
id = "",
translatableDescription = mapOf(
@ -41,19 +41,19 @@ class AddonDetailsViewTest {
view = binding.root
interactor = mockk(relaxed = true)
detailsView = AddonDetailsView(binding, interactor)
detailsBindingDelegate = AddonDetailsBindingDelegate(binding, interactor)
}
@Test
fun `bind addons rating`() {
detailsView.bind(
detailsBindingDelegate.bind(
baseAddon.copy(
rating = null
)
)
assertEquals(0f, binding.ratingView.rating)
detailsView.bind(
detailsBindingDelegate.bind(
baseAddon.copy(
rating = Addon.Rating(
average = 4.3f,
@ -68,7 +68,7 @@ class AddonDetailsViewTest {
@Test
fun `bind addons website`() {
detailsView.bind(
detailsBindingDelegate.bind(
baseAddon.copy(
siteUrl = "https://mozilla.org"
)
@ -81,7 +81,7 @@ class AddonDetailsViewTest {
@Test
fun `bind addons last updated`() {
detailsView.bind(baseAddon)
detailsBindingDelegate.bind(baseAddon)
assertEquals("Nov 23, 2020", binding.lastUpdatedText.text)
}
@ -93,7 +93,7 @@ class AddonDetailsViewTest {
installedState = null
)
detailsView.bind(addon1)
detailsBindingDelegate.bind(addon1)
assertEquals("1.0.0", binding.versionText.text)
binding.versionText.performLongClick()
verify(exactly = 0) { interactor.showUpdaterDialog(addon1) }
@ -106,7 +106,7 @@ class AddonDetailsViewTest {
optionsPageUrl = null
)
)
detailsView.bind(addon2)
detailsBindingDelegate.bind(addon2)
assertEquals("2.0.0", binding.versionText.text)
binding.versionText.performLongClick()
verify { interactor.showUpdaterDialog(addon2) }
@ -115,7 +115,7 @@ class AddonDetailsViewTest {
@Test
fun `bind addons authors`() {
val baseAuthor = Addon.Author("", "", "", "")
detailsView.bind(
detailsBindingDelegate.bind(
baseAddon.copy(
authors = listOf(
baseAuthor.copy(name = " Sarah Jane"),
@ -129,7 +129,7 @@ class AddonDetailsViewTest {
@Test
fun `bind addons details`() {
detailsView.bind(baseAddon)
detailsBindingDelegate.bind(baseAddon)
assertEquals(
"Some blank addon\nwith a blank line",

@ -12,7 +12,6 @@ import io.mockk.every
import io.mockk.just
import io.mockk.mockk
import io.mockk.verify
import kotlinx.android.synthetic.main.collection_tab_list_row.*
import mozilla.components.support.test.robolectric.testContext
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
@ -20,6 +19,7 @@ import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mozilla.fenix.databinding.CollectionTabListRowBinding
import org.mozilla.fenix.helpers.FenixRobolectricTestRunner
import org.mozilla.fenix.home.Tab
@ -67,20 +67,21 @@ class CollectionCreationTabListAdapterTest {
val holder = adapter.createViewHolder(FrameLayout(testContext), 0)
adapter.bindViewHolder(holder, 0)
val binding = CollectionTabListRowBinding.bind(holder.itemView)
assertEquals("Mozilla", holder.tab_title.text)
assertEquals("mozilla.org", holder.hostname.text)
assertFalse(holder.tab_selected_checkbox.isInvisible)
assertEquals("Mozilla", binding.tabTitle.text)
assertEquals("mozilla.org", binding.hostname.text)
assertFalse(binding.tabSelectedCheckbox.isInvisible)
assertTrue(holder.itemView.isClickable)
every { interactor.addTabToSelection(mozillaTab) } just Runs
every { interactor.removeTabFromSelection(mozillaTab) } just Runs
assertFalse(holder.tab_selected_checkbox.isChecked)
assertFalse(binding.tabSelectedCheckbox.isChecked)
holder.tab_selected_checkbox.isChecked = true
binding.tabSelectedCheckbox.isChecked = true
verify { interactor.addTabToSelection(mozillaTab) }
holder.tab_selected_checkbox.isChecked = false
binding.tabSelectedCheckbox.isChecked = false
verify { interactor.removeTabFromSelection(mozillaTab) }
}
@ -96,10 +97,11 @@ class CollectionCreationTabListAdapterTest {
val holder = adapter.createViewHolder(FrameLayout(testContext), 0)
adapter.bindViewHolder(holder, 0)
val binding = CollectionTabListRowBinding.bind(holder.itemView)
assertEquals("Mozilla", holder.tab_title.text)
assertEquals("mozilla.org", holder.hostname.text)
assertTrue(holder.tab_selected_checkbox.isInvisible)
assertEquals("Mozilla", binding.tabTitle.text)
assertEquals("mozilla.org", binding.hostname.text)
assertTrue(binding.tabSelectedCheckbox.isInvisible)
assertFalse(holder.itemView.isClickable)
}

@ -11,13 +11,13 @@ import io.mockk.every
import io.mockk.just
import io.mockk.mockk
import io.mockk.verify
import kotlinx.android.synthetic.main.collections_list_item.view.*
import mozilla.components.feature.tab.collections.TabCollection
import mozilla.components.support.test.robolectric.testContext
import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mozilla.fenix.databinding.CollectionsListItemBinding
import org.mozilla.fenix.helpers.FenixRobolectricTestRunner
@RunWith(FenixRobolectricTestRunner::class)
@ -70,9 +70,10 @@ class SaveCollectionListAdapterTest {
val holder = adapter.createViewHolder(parent, 0)
adapter.bindViewHolder(holder, 0)
val binding = CollectionsListItemBinding.bind(holder.itemView)
assertEquals("Collection", holder.itemView.collection_item.text)
assertEquals("mozilla.org, firefox.com", holder.itemView.collection_description.text)
assertEquals("Collection", binding.collectionItem.text)
assertEquals("mozilla.org, firefox.com", binding.collectionDescription.text)
holder.itemView.performClick()
verify { interactor.selectCollection(collection, emptyList()) }

@ -5,10 +5,8 @@
package org.mozilla.fenix.historymetadata.view
import android.view.LayoutInflater
import android.view.View
import io.mockk.mockk
import io.mockk.verify
import kotlinx.android.synthetic.main.history_metadata_group.view.*
import mozilla.components.concept.storage.DocumentType
import mozilla.components.concept.storage.HistoryMetadata
import mozilla.components.concept.storage.HistoryMetadataKey
@ -17,6 +15,7 @@ import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mozilla.fenix.databinding.HistoryMetadataGroupBinding
import org.mozilla.fenix.helpers.FenixRobolectricTestRunner
import org.mozilla.fenix.historymetadata.HistoryMetadataGroup
import org.mozilla.fenix.historymetadata.interactor.HistoryMetadataInteractor
@ -24,7 +23,7 @@ import org.mozilla.fenix.historymetadata.interactor.HistoryMetadataInteractor
@RunWith(FenixRobolectricTestRunner::class)
class HistoryMetadataGroupViewHolderTest {
private lateinit var view: View
private lateinit var binding: HistoryMetadataGroupBinding
private lateinit var interactor: HistoryMetadataInteractor
private val historyEntry = HistoryMetadata(
@ -42,23 +41,23 @@ class HistoryMetadataGroupViewHolderTest {
@Before
fun setup() {
view = LayoutInflater.from(testContext).inflate(HistoryMetadataGroupViewHolder.LAYOUT_ID, null)
binding = HistoryMetadataGroupBinding.inflate(LayoutInflater.from(testContext))
interactor = mockk(relaxed = true)
}
@Test
fun `GIVEN a history metadata group on bind THEN set the title text and isActivated state`() {
HistoryMetadataGroupViewHolder(view, interactor).bind(historyGroup)
HistoryMetadataGroupViewHolder(binding.root, interactor).bind(historyGroup)
assertEquals(historyGroup.title, view.history_metadata_group_title.text)
assertEquals(historyGroup.expanded, view.isActivated)
assertEquals(historyGroup.title, binding.historyMetadataGroupTitle.text)
assertEquals(historyGroup.expanded, binding.root.isActivated)
}
@Test
fun `WHEN a history metadata group is clicked THEN interactor is called`() {
HistoryMetadataGroupViewHolder(view, interactor).bind(historyGroup)
HistoryMetadataGroupViewHolder(binding.root, interactor).bind(historyGroup)
view.performClick()
binding.root.performClick()
verify { interactor.onToggleHistoryMetadataGroupExpanded(historyGroup) }
}

@ -5,34 +5,33 @@
package org.mozilla.fenix.historymetadata.view
import android.view.LayoutInflater
import android.view.View
import io.mockk.mockk
import io.mockk.verify
import kotlinx.android.synthetic.main.history_metadata_header.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.databinding.HistoryMetadataHeaderBinding
import org.mozilla.fenix.helpers.FenixRobolectricTestRunner
import org.mozilla.fenix.home.sessioncontrol.SessionControlInteractor
@RunWith(FenixRobolectricTestRunner::class)
class HistoryMetadataHeaderViewHolderTest {
private lateinit var view: View
private lateinit var binding: HistoryMetadataHeaderBinding
private lateinit var interactor: SessionControlInteractor
@Before
fun setup() {
view = LayoutInflater.from(testContext).inflate(HistoryMetadataHeaderViewHolder.LAYOUT_ID, null)
binding = HistoryMetadataHeaderBinding.inflate(LayoutInflater.from(testContext))
interactor = mockk(relaxed = true)
}
@Test
fun `WHEN show all button is clicked THEN interactor is called`() {
HistoryMetadataHeaderViewHolder(view, interactor)
HistoryMetadataHeaderViewHolder(binding.root, interactor)
view.show_all_button.performClick()
binding.showAllButton.performClick()
verify { interactor.onHistoryMetadataShowAllClicked() }
}

@ -5,11 +5,9 @@
package org.mozilla.fenix.historymetadata.view
import android.view.LayoutInflater
import android.view.View
import io.mockk.every
import io.mockk.mockk
import io.mockk.verify
import kotlinx.android.synthetic.main.history_metadata_list_row.view.*
import mozilla.components.browser.icons.BrowserIcons
import mozilla.components.browser.icons.IconRequest
import mozilla.components.concept.storage.DocumentType
@ -20,13 +18,14 @@ import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mozilla.fenix.databinding.HistoryMetadataListRowBinding
import org.mozilla.fenix.helpers.FenixRobolectricTestRunner
import org.mozilla.fenix.home.sessioncontrol.SessionControlInteractor
@RunWith(FenixRobolectricTestRunner::class)
class HistoryMetadataViewHolderTest {
private lateinit var view: View
private lateinit var binding: HistoryMetadataListRowBinding
private lateinit var interactor: SessionControlInteractor
private lateinit var icons: BrowserIcons
@ -41,28 +40,28 @@ class HistoryMetadataViewHolderTest {
@Before
fun setup() {
view = LayoutInflater.from(testContext).inflate(HistoryMetadataViewHolder.LAYOUT_ID, null)
binding = HistoryMetadataListRowBinding.inflate(LayoutInflater.from(testContext))
interactor = mockk(relaxed = true)
icons = mockk(relaxed = true)
every { icons.loadIntoView(view.history_metadata_icon, any()) } returns mockk()
every { icons.loadIntoView(binding.historyMetadataIcon, any()) } returns mockk()
}
@Test
fun `GIVEN a history metadata on bind THEN set the title text and load the tab icon`() {
HistoryMetadataViewHolder(view, interactor, icons).bind(historyEntry)
HistoryMetadataViewHolder(binding.root, interactor, icons).bind(historyEntry)
assertEquals(historyEntry.title, view.history_metadata_title.text)
assertEquals(historyEntry.title, binding.historyMetadataTitle.text)
verify { icons.loadIntoView(view.history_metadata_icon, IconRequest(historyEntry.key.url)) }
verify { icons.loadIntoView(binding.historyMetadataIcon, IconRequest(historyEntry.key.url)) }
}
@Test
fun `WHEN a history metadata item is clicked THEN interactor is called`() {
HistoryMetadataViewHolder(view, interactor, icons).bind(historyEntry)
HistoryMetadataViewHolder(binding.root, interactor, icons).bind(historyEntry)
view.performClick()
binding.root.performClick()
verify { interactor.onHistoryMetadataItemClicked(historyEntry.key.url, historyEntry.key) }
}
@ -78,8 +77,8 @@ class HistoryMetadataViewHolderTest {
documentType = DocumentType.Regular
)
HistoryMetadataViewHolder(view, interactor, icons).bind(historyEntryWithoutTitle)
HistoryMetadataViewHolder(binding.root, interactor, icons).bind(historyEntryWithoutTitle)
assertEquals(historyEntry.key.url, view.history_metadata_title.text)
assertEquals(historyEntry.key.url, binding.historyMetadataTitle.text)
}
}

@ -6,7 +6,6 @@ package org.mozilla.fenix.migration
import android.view.View
import android.widget.FrameLayout
import kotlinx.android.synthetic.main.migration_list_item.view.*
import mozilla.components.support.migration.Migration
import mozilla.components.support.migration.MigrationRun
import mozilla.components.support.test.robolectric.testContext
@ -14,6 +13,7 @@ import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mozilla.fenix.databinding.MigrationListItemBinding
import org.mozilla.fenix.helpers.FenixRobolectricTestRunner
@RunWith(FenixRobolectricTestRunner::class)
@ -50,14 +50,16 @@ class MigrationStatusAdapterTest {
val holder1 = adapter.createViewHolder(FrameLayout(testContext), 0)
val holder2 = adapter.createViewHolder(FrameLayout(testContext), 0)
val binding1 = MigrationListItemBinding.bind(holder1.itemView)
val binding2 = MigrationListItemBinding.bind(holder2.itemView)
adapter.bindViewHolder(holder1, 0)
adapter.bindViewHolder(holder2, 1)
assertEquals("Settings", holder1.itemView.migration_item_name.text)
assertEquals(View.INVISIBLE, holder1.itemView.migration_status_image.visibility)
assertEquals("Settings", binding1.migrationItemName.text)
assertEquals(View.INVISIBLE, binding1.migrationStatusImage.visibility)
assertEquals("History", holder2.itemView.migration_item_name.text)
assertEquals(View.VISIBLE, holder2.itemView.migration_status_image.visibility)
assertEquals("Migration completed", holder2.itemView.migration_status_image.contentDescription)
assertEquals("History", binding2.migrationItemName.text)
assertEquals(View.VISIBLE, binding2.migrationStatusImage.visibility)
assertEquals("Migration completed", binding2.migrationStatusImage.contentDescription)
}
}

@ -8,7 +8,6 @@ import android.view.LayoutInflater
import io.mockk.Called
import io.mockk.mockk
import io.mockk.verify
import kotlinx.android.synthetic.main.account_share_list_item.view.*
import mozilla.components.concept.sync.Device
import mozilla.components.concept.sync.DeviceType
import mozilla.components.support.test.robolectric.testContext
@ -17,6 +16,7 @@ import org.junit.Assert.assertFalse
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mozilla.fenix.databinding.AccountShareListItemBinding
import org.mozilla.fenix.helpers.FenixRobolectricTestRunner
import org.mozilla.fenix.share.ShareToAccountDevicesInteractor
import org.mozilla.fenix.share.listadapters.SyncShareOption
@ -34,6 +34,7 @@ class AccountDeviceViewHolderTest {
subscriptionExpired = false,
subscription = null
)
private lateinit var binding: AccountShareListItemBinding
private lateinit var viewHolder: AccountDeviceViewHolder
private lateinit var interactor: ShareToAccountDevicesInteractor
@ -41,14 +42,14 @@ class AccountDeviceViewHolderTest {
fun setup() {
interactor = mockk(relaxUnitFun = true)
val view = LayoutInflater.from(testContext).inflate(AccountDeviceViewHolder.LAYOUT_ID, null)
viewHolder = AccountDeviceViewHolder(view, interactor)
binding = AccountShareListItemBinding.inflate(LayoutInflater.from(testContext))
viewHolder = AccountDeviceViewHolder(binding.root, interactor)
}
@Test
fun `bind SignIn option`() {
viewHolder.bind(SyncShareOption.SignIn)
assertEquals("Sign in to Sync", viewHolder.itemView.deviceName.text)
assertEquals("Sign in to Sync", binding.deviceName.text)
viewHolder.itemView.performClick()
verify { interactor.onSignIn() }
@ -58,7 +59,7 @@ class AccountDeviceViewHolderTest {
@Test
fun `bind Reconnect option`() {
viewHolder.bind(SyncShareOption.Reconnect)
assertEquals("Reconnect to Sync", viewHolder.itemView.deviceName.text)
assertEquals("Reconnect to Sync", binding.deviceName.text)
viewHolder.itemView.performClick()
verify { interactor.onReauth() }
@ -68,7 +69,7 @@ class AccountDeviceViewHolderTest {
@Test
fun `bind Offline option`() {
viewHolder.bind(SyncShareOption.Offline)
assertEquals("Offline", viewHolder.itemView.deviceName.text)
assertEquals("Offline", binding.deviceName.text)
viewHolder.itemView.performClick()
verify { interactor wasNot Called }
@ -78,7 +79,7 @@ class AccountDeviceViewHolderTest {
@Test
fun `bind AddNewDevice option`() {
viewHolder.bind(SyncShareOption.AddNewDevice)
assertEquals("Connect another device", viewHolder.itemView.deviceName.text)
assertEquals("Connect another device", binding.deviceName.text)
viewHolder.itemView.performClick()
verify { interactor.onAddNewDevice() }
@ -89,7 +90,7 @@ class AccountDeviceViewHolderTest {
fun `bind SendAll option`() {
val devices = listOf<Device>(mockk())
viewHolder.bind(SyncShareOption.SendAll(devices))
assertEquals("Send to all devices", viewHolder.itemView.deviceName.text)
assertEquals("Send to all devices", binding.deviceName.text)
viewHolder.itemView.performClick()
verify { interactor.onShareToAllDevices(devices) }
@ -103,7 +104,7 @@ class AccountDeviceViewHolderTest {
displayName = "Mobile"
)
viewHolder.bind(SyncShareOption.SingleDevice(device))
assertEquals("Mobile", viewHolder.itemView.deviceName.text)
assertEquals("Mobile", binding.deviceName.text)
viewHolder.itemView.performClick()
verify { interactor.onShareToDevice(device) }
@ -117,7 +118,7 @@ class AccountDeviceViewHolderTest {
displayName = "Desktop"
)
viewHolder.bind(SyncShareOption.SingleDevice(device))
assertEquals("Desktop", viewHolder.itemView.deviceName.text)
assertEquals("Desktop", binding.deviceName.text)
viewHolder.itemView.performClick()
verify { interactor.onShareToDevice(device) }

@ -9,13 +9,13 @@ import androidx.appcompat.content.res.AppCompatResources.getDrawable
import io.mockk.Called
import io.mockk.mockk
import io.mockk.verify
import kotlinx.android.synthetic.main.app_share_list_item.view.*
import mozilla.components.support.test.robolectric.testContext
import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mozilla.fenix.R
import org.mozilla.fenix.databinding.AppShareListItemBinding
import org.mozilla.fenix.helpers.FenixRobolectricTestRunner
import org.mozilla.fenix.share.ShareToAppsInteractor
import org.mozilla.fenix.share.listadapters.AppShareOption
@ -23,6 +23,7 @@ import org.mozilla.fenix.share.listadapters.AppShareOption
@RunWith(FenixRobolectricTestRunner::class)
class AppViewHolderTest {
private lateinit var binding: AppShareListItemBinding
private lateinit var viewHolder: AppViewHolder
private lateinit var interactor: ShareToAppsInteractor
@ -30,8 +31,8 @@ class AppViewHolderTest {
fun setup() {
interactor = mockk(relaxUnitFun = true)
val view = LayoutInflater.from(testContext).inflate(AppViewHolder.LAYOUT_ID, null)
viewHolder = AppViewHolder(view, interactor)
binding = AppShareListItemBinding.inflate(LayoutInflater.from(testContext))
viewHolder = AppViewHolder(binding.root, interactor)
}
@Test
@ -44,8 +45,8 @@ class AppViewHolderTest {
)
viewHolder.bind(app)
assertEquals("Pocket", viewHolder.itemView.appName.text)
assertEquals(app.icon, viewHolder.itemView.appIcon.drawable)
assertEquals("Pocket", binding.appName.text)
assertEquals(app.icon, binding.appIcon.drawable)
}
@Test

Loading…
Cancel
Save