From 0010d42f82b2d2bdb276eb2cab000d0e09a6b264 Mon Sep 17 00:00:00 2001
From: t895 <clombardo169@gmail.com>
Date: Sat, 17 Feb 2024 19:34:34 -0500
Subject: [PATCH] android: Use extension functions for view visibility and text
 marquee

---
 .../yuzu/yuzu_emu/adapters/DriverAdapter.kt   | 26 +++++----------
 .../yuzu/yuzu_emu/adapters/FolderAdapter.kt   | 10 ++----
 .../org/yuzu/yuzu_emu/adapters/GameAdapter.kt | 11 ++-----
 .../adapters/GamePropertiesAdapter.kt         | 28 ++++++----------
 .../yuzu_emu/adapters/HomeSettingAdapter.kt   | 14 +++-----
 .../yuzu_emu/adapters/InstallableAdapter.kt   | 14 +++-----
 .../yuzu/yuzu_emu/adapters/LicenseAdapter.kt  |  4 +--
 .../yuzu/yuzu_emu/adapters/SetupAdapter.kt    |  6 ++--
 .../settings/ui/SettingsSearchFragment.kt     |  9 +++--
 .../ui/viewholder/DateTimeViewHolder.kt       | 22 ++++---------
 .../ui/viewholder/InputProfileViewHolder.kt   |  9 ++---
 .../settings/ui/viewholder/InputViewHolder.kt | 29 +++++-----------
 .../ui/viewholder/RunnableViewHolder.kt       | 17 ++++------
 .../ui/viewholder/SingleChoiceViewHolder.kt   | 23 +++++--------
 .../ui/viewholder/SliderViewHolder.kt         | 21 ++++--------
 .../ui/viewholder/SubmenuViewHolder.kt        | 17 ++++------
 .../ui/viewholder/SwitchSettingViewHolder.kt  | 19 ++++-------
 .../yuzu_emu/fragments/EmulationFragment.kt   | 27 ++++++++-------
 .../yuzu_emu/fragments/GameInfoFragment.kt    |  3 +-
 .../fragments/GamePropertiesFragment.kt       | 10 ++----
 .../fragments/ProgressDialogFragment.kt       |  7 ++--
 .../yuzu/yuzu_emu/fragments/SearchFragment.kt | 13 ++------
 .../yuzu/yuzu_emu/fragments/SetupFragment.kt  |  9 ++---
 .../org/yuzu/yuzu_emu/ui/GamesFragment.kt     | 10 +++---
 .../org/yuzu/yuzu_emu/ui/main/MainActivity.kt | 19 +++++------
 .../java/org/yuzu/yuzu_emu/utils/ViewUtils.kt | 33 +++++++++++++++++++
 .../main/res/layout/card_driver_option.xml    |  9 -----
 .../app/src/main/res/layout/card_folder.xml   |  3 --
 .../app/src/main/res/layout/card_game.xml     |  3 --
 .../main/res/layout/card_simple_outlined.xml  |  3 --
 .../res/layout/fragment_game_properties.xml   |  3 --
 31 files changed, 166 insertions(+), 265 deletions(-)

diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/DriverAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/DriverAdapter.kt
index f218c76ef..50663ad91 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/DriverAdapter.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/DriverAdapter.kt
@@ -3,15 +3,15 @@
 
 package org.yuzu.yuzu_emu.adapters
 
-import android.text.TextUtils
 import android.view.LayoutInflater
-import android.view.View
 import android.view.ViewGroup
 import org.yuzu.yuzu_emu.R
 import org.yuzu.yuzu_emu.databinding.CardDriverOptionBinding
 import org.yuzu.yuzu_emu.features.settings.model.StringSetting
 import org.yuzu.yuzu_emu.model.Driver
 import org.yuzu.yuzu_emu.model.DriverViewModel
+import org.yuzu.yuzu_emu.utils.ViewUtils.marquee
+import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
 import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
 
 class DriverAdapter(private val driverViewModel: DriverViewModel) :
@@ -44,25 +44,15 @@ class DriverAdapter(private val driverViewModel: DriverViewModel) :
                 }
 
                 // Delay marquee by 3s
-                title.postDelayed(
-                    {
-                        title.isSelected = true
-                        title.ellipsize = TextUtils.TruncateAt.MARQUEE
-                        version.isSelected = true
-                        version.ellipsize = TextUtils.TruncateAt.MARQUEE
-                        description.isSelected = true
-                        description.ellipsize = TextUtils.TruncateAt.MARQUEE
-                    },
-                    3000
-                )
+                title.marquee()
+                version.marquee()
+                description.marquee()
                 title.text = model.title
                 version.text = model.version
                 description.text = model.description
-                if (model.title != binding.root.context.getString(R.string.system_gpu_driver)) {
-                    buttonDelete.visibility = View.VISIBLE
-                } else {
-                    buttonDelete.visibility = View.GONE
-                }
+                buttonDelete.setVisible(
+                    model.title != binding.root.context.getString(R.string.system_gpu_driver)
+                )
             }
         }
     }
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/FolderAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/FolderAdapter.kt
index 3d8f0bda8..5cbd15d2a 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/FolderAdapter.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/FolderAdapter.kt
@@ -4,7 +4,6 @@
 package org.yuzu.yuzu_emu.adapters
 
 import android.net.Uri
-import android.text.TextUtils
 import android.view.LayoutInflater
 import android.view.ViewGroup
 import androidx.fragment.app.FragmentActivity
@@ -12,6 +11,7 @@ import org.yuzu.yuzu_emu.databinding.CardFolderBinding
 import org.yuzu.yuzu_emu.fragments.GameFolderPropertiesDialogFragment
 import org.yuzu.yuzu_emu.model.GameDir
 import org.yuzu.yuzu_emu.model.GamesViewModel
+import org.yuzu.yuzu_emu.utils.ViewUtils.marquee
 import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
 
 class FolderAdapter(val activity: FragmentActivity, val gamesViewModel: GamesViewModel) :
@@ -29,13 +29,7 @@ class FolderAdapter(val activity: FragmentActivity, val gamesViewModel: GamesVie
         override fun bind(model: GameDir) {
             binding.apply {
                 path.text = Uri.parse(model.uriString).path
-                path.postDelayed(
-                    {
-                        path.isSelected = true
-                        path.ellipsize = TextUtils.TruncateAt.MARQUEE
-                    },
-                    3000
-                )
+                path.marquee()
 
                 buttonEdit.setOnClickListener {
                     GameFolderPropertiesDialogFragment.newInstance(model)
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt
index 85c8249e6..b1f247ac3 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt
@@ -4,7 +4,6 @@
 package org.yuzu.yuzu_emu.adapters
 
 import android.net.Uri
-import android.text.TextUtils
 import android.view.LayoutInflater
 import android.view.ViewGroup
 import android.widget.ImageView
@@ -27,6 +26,7 @@ import org.yuzu.yuzu_emu.databinding.CardGameBinding
 import org.yuzu.yuzu_emu.model.Game
 import org.yuzu.yuzu_emu.model.GamesViewModel
 import org.yuzu.yuzu_emu.utils.GameIconUtils
+import org.yuzu.yuzu_emu.utils.ViewUtils.marquee
 import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
 
 class GameAdapter(private val activity: AppCompatActivity) :
@@ -44,14 +44,7 @@ class GameAdapter(private val activity: AppCompatActivity) :
 
             binding.textGameTitle.text = model.title.replace("[\\t\\n\\r]+".toRegex(), " ")
 
-            binding.textGameTitle.postDelayed(
-                {
-                    binding.textGameTitle.ellipsize = TextUtils.TruncateAt.MARQUEE
-                    binding.textGameTitle.isSelected = true
-                },
-                3000
-            )
-
+            binding.textGameTitle.marquee()
             binding.cardGame.setOnClickListener { onClick(model) }
             binding.cardGame.setOnLongClickListener { onLongClick(model) }
         }
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GamePropertiesAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GamePropertiesAdapter.kt
index 0046d5314..017306875 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GamePropertiesAdapter.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GamePropertiesAdapter.kt
@@ -3,9 +3,7 @@
 
 package org.yuzu.yuzu_emu.adapters
 
-import android.text.TextUtils
 import android.view.LayoutInflater
-import android.view.View
 import android.view.ViewGroup
 import androidx.core.content.res.ResourcesCompat
 import androidx.lifecycle.Lifecycle
@@ -18,6 +16,8 @@ import org.yuzu.yuzu_emu.databinding.CardSimpleOutlinedBinding
 import org.yuzu.yuzu_emu.model.GameProperty
 import org.yuzu.yuzu_emu.model.InstallableProperty
 import org.yuzu.yuzu_emu.model.SubmenuProperty
+import org.yuzu.yuzu_emu.utils.ViewUtils.marquee
+import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
 import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
 
 class GamePropertiesAdapter(
@@ -76,23 +76,19 @@ class GamePropertiesAdapter(
                 )
             )
 
-            binding.details.postDelayed({
-                binding.details.isSelected = true
-                binding.details.ellipsize = TextUtils.TruncateAt.MARQUEE
-            }, 3000)
-
+            binding.details.marquee()
             if (submenuProperty.details != null) {
-                binding.details.visibility = View.VISIBLE
+                binding.details.setVisible(true)
                 binding.details.text = submenuProperty.details.invoke()
             } else if (submenuProperty.detailsFlow != null) {
-                binding.details.visibility = View.VISIBLE
+                binding.details.setVisible(true)
                 viewLifecycle.lifecycleScope.launch {
                     viewLifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
                         submenuProperty.detailsFlow.collect { binding.details.text = it }
                     }
                 }
             } else {
-                binding.details.visibility = View.GONE
+                binding.details.setVisible(false)
             }
         }
     }
@@ -112,14 +108,10 @@ class GamePropertiesAdapter(
                 )
             )
 
-            if (installableProperty.install != null) {
-                binding.buttonInstall.visibility = View.VISIBLE
-                binding.buttonInstall.setOnClickListener { installableProperty.install.invoke() }
-            }
-            if (installableProperty.export != null) {
-                binding.buttonExport.visibility = View.VISIBLE
-                binding.buttonExport.setOnClickListener { installableProperty.export.invoke() }
-            }
+            binding.buttonInstall.setVisible(installableProperty.install != null)
+            binding.buttonInstall.setOnClickListener { installableProperty.install?.invoke() }
+            binding.buttonExport.setVisible(installableProperty.export != null)
+            binding.buttonExport.setOnClickListener { installableProperty.export?.invoke() }
         }
     }
 
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/HomeSettingAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/HomeSettingAdapter.kt
index b512845d5..9234a4901 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/HomeSettingAdapter.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/HomeSettingAdapter.kt
@@ -3,9 +3,7 @@
 
 package org.yuzu.yuzu_emu.adapters
 
-import android.text.TextUtils
 import android.view.LayoutInflater
-import android.view.View
 import android.view.ViewGroup
 import androidx.appcompat.app.AppCompatActivity
 import androidx.core.content.ContextCompat
@@ -19,6 +17,8 @@ import org.yuzu.yuzu_emu.R
 import org.yuzu.yuzu_emu.databinding.CardHomeOptionBinding
 import org.yuzu.yuzu_emu.fragments.MessageDialogFragment
 import org.yuzu.yuzu_emu.model.HomeSetting
+import org.yuzu.yuzu_emu.utils.ViewUtils.marquee
+import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
 import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
 
 class HomeSettingAdapter(
@@ -64,13 +64,7 @@ class HomeSettingAdapter(
                     model.details.collect { updateOptionDetails(it) }
                 }
             }
-            binding.optionDetail.postDelayed(
-                {
-                    binding.optionDetail.ellipsize = TextUtils.TruncateAt.MARQUEE
-                    binding.optionDetail.isSelected = true
-                },
-                3000
-            )
+            binding.optionDetail.marquee()
 
             binding.root.setOnClickListener { onClick(model) }
         }
@@ -90,7 +84,7 @@ class HomeSettingAdapter(
         private fun updateOptionDetails(detailString: String) {
             if (detailString.isNotEmpty()) {
                 binding.optionDetail.text = detailString
-                binding.optionDetail.visibility = View.VISIBLE
+                binding.optionDetail.setVisible(true)
             }
         }
     }
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/InstallableAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/InstallableAdapter.kt
index 4218c4e52..1ba75fa2f 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/InstallableAdapter.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/InstallableAdapter.kt
@@ -4,10 +4,10 @@
 package org.yuzu.yuzu_emu.adapters
 
 import android.view.LayoutInflater
-import android.view.View
 import android.view.ViewGroup
 import org.yuzu.yuzu_emu.databinding.CardInstallableBinding
 import org.yuzu.yuzu_emu.model.Installable
+import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
 import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
 
 class InstallableAdapter(installables: List<Installable>) :
@@ -26,14 +26,10 @@ class InstallableAdapter(installables: List<Installable>) :
             binding.title.setText(model.titleId)
             binding.description.setText(model.descriptionId)
 
-            if (model.install != null) {
-                binding.buttonInstall.visibility = View.VISIBLE
-                binding.buttonInstall.setOnClickListener { model.install.invoke() }
-            }
-            if (model.export != null) {
-                binding.buttonExport.visibility = View.VISIBLE
-                binding.buttonExport.setOnClickListener { model.export.invoke() }
-            }
+            binding.buttonInstall.setVisible(model.install != null)
+            binding.buttonInstall.setOnClickListener { model.install?.invoke() }
+            binding.buttonExport.setVisible(model.export != null)
+            binding.buttonExport.setOnClickListener { model.export?.invoke() }
         }
     }
 }
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/LicenseAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/LicenseAdapter.kt
index 38bb1f96f..1379968f9 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/LicenseAdapter.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/LicenseAdapter.kt
@@ -4,12 +4,12 @@
 package org.yuzu.yuzu_emu.adapters
 
 import android.view.LayoutInflater
-import android.view.View
 import android.view.ViewGroup
 import androidx.appcompat.app.AppCompatActivity
 import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding
 import org.yuzu.yuzu_emu.fragments.LicenseBottomSheetDialogFragment
 import org.yuzu.yuzu_emu.model.License
+import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
 import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
 
 class LicenseAdapter(private val activity: AppCompatActivity, licenses: List<License>) :
@@ -25,7 +25,7 @@ class LicenseAdapter(private val activity: AppCompatActivity, licenses: List<Lic
             binding.apply {
                 textSettingName.text = root.context.getString(model.titleId)
                 textSettingDescription.text = root.context.getString(model.descriptionId)
-                textSettingValue.visibility = View.GONE
+                textSettingValue.setVisible(false)
 
                 root.setOnClickListener { onClick(model) }
             }
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/SetupAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/SetupAdapter.kt
index 02118e1a8..a5f610b31 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/SetupAdapter.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/SetupAdapter.kt
@@ -5,7 +5,6 @@ package org.yuzu.yuzu_emu.adapters
 
 import android.text.Html
 import android.view.LayoutInflater
-import android.view.View
 import android.view.ViewGroup
 import androidx.appcompat.app.AppCompatActivity
 import androidx.core.content.res.ResourcesCompat
@@ -17,6 +16,7 @@ import org.yuzu.yuzu_emu.model.SetupCallback
 import org.yuzu.yuzu_emu.model.SetupPage
 import org.yuzu.yuzu_emu.model.StepState
 import org.yuzu.yuzu_emu.utils.ViewUtils
+import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
 import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
 
 class SetupAdapter(val activity: AppCompatActivity, pages: List<SetupPage>) :
@@ -30,8 +30,8 @@ class SetupAdapter(val activity: AppCompatActivity, pages: List<SetupPage>) :
         AbstractViewHolder<SetupPage>(binding), SetupCallback {
         override fun bind(model: SetupPage) {
             if (model.stepCompleted.invoke() == StepState.COMPLETE) {
-                binding.buttonAction.visibility = View.INVISIBLE
-                binding.textConfirmation.visibility = View.VISIBLE
+                binding.buttonAction.setVisible(visible = false, gone = false)
+                binding.textConfirmation.setVisible(true)
             }
 
             binding.icon.setImageDrawable(
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsSearchFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsSearchFragment.kt
index 51740a2ac..c4c1d563a 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsSearchFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsSearchFragment.kt
@@ -27,6 +27,7 @@ import org.yuzu.yuzu_emu.R
 import org.yuzu.yuzu_emu.databinding.FragmentSettingsSearchBinding
 import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
 import org.yuzu.yuzu_emu.utils.NativeConfig
+import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
 import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
 
 class SettingsSearchFragment : Fragment() {
@@ -106,10 +107,9 @@ class SettingsSearchFragment : Fragment() {
 
     private fun search() {
         val searchTerm = binding.searchText.text.toString().lowercase()
-        binding.clearButton.visibility =
-            if (searchTerm.isEmpty()) View.INVISIBLE else View.VISIBLE
+        binding.clearButton.setVisible(visible = searchTerm.isNotEmpty(), gone = false)
         if (searchTerm.isEmpty()) {
-            binding.noResultsView.visibility = View.VISIBLE
+            binding.noResultsView.setVisible(visible = false, gone = false)
             settingsAdapter?.submitList(emptyList())
             return
         }
@@ -136,8 +136,7 @@ class SettingsSearchFragment : Fragment() {
             optionalSetting
         }
         settingsAdapter?.submitList(sortedList)
-        binding.noResultsView.visibility =
-            if (sortedList.isEmpty()) View.VISIBLE else View.INVISIBLE
+        binding.noResultsView.setVisible(visible = sortedList.isEmpty(), gone = false)
     }
 
     private fun focusSearch() {
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/DateTimeViewHolder.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/DateTimeViewHolder.kt
index a43f7b1fe..367db7fd2 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/DateTimeViewHolder.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/DateTimeViewHolder.kt
@@ -14,6 +14,7 @@ import org.yuzu.yuzu_emu.features.settings.model.view.DateTimeSetting
 import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
 import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter
 import org.yuzu.yuzu_emu.utils.NativeConfig
+import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
 
 class DateTimeViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAdapter) :
     SettingViewHolder(binding.root, adapter) {
@@ -22,27 +23,18 @@ class DateTimeViewHolder(val binding: ListItemSettingBinding, adapter: SettingsA
     override fun bind(item: SettingsItem) {
         setting = item as DateTimeSetting
         binding.textSettingName.text = item.title
-        if (setting.description.isNotEmpty()) {
-            binding.textSettingDescription.text = item.description
-            binding.textSettingDescription.visibility = View.VISIBLE
-        } else {
-            binding.textSettingDescription.visibility = View.GONE
-        }
-
-        binding.textSettingValue.visibility = View.VISIBLE
+        binding.textSettingDescription.setVisible(item.description.isNotEmpty())
+        binding.textSettingDescription.text = item.description
+        binding.textSettingValue.setVisible(true)
         val epochTime = setting.getValue()
         val instant = Instant.ofEpochMilli(epochTime * 1000)
         val zonedTime = ZonedDateTime.ofInstant(instant, ZoneId.of("UTC"))
         val dateFormatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM)
         binding.textSettingValue.text = dateFormatter.format(zonedTime)
 
-        binding.buttonClear.visibility = if (setting.setting.global ||
-            !NativeConfig.isPerGameConfigLoaded()
-        ) {
-            View.GONE
-        } else {
-            View.VISIBLE
-        }
+        binding.buttonClear.setVisible(
+            !setting.setting.global || NativeConfig.isPerGameConfigLoaded()
+        )
         binding.buttonClear.setOnClickListener {
             adapter.onClearClick(setting, bindingAdapterPosition)
         }
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/InputProfileViewHolder.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/InputProfileViewHolder.kt
index 81161d5d3..86af3a226 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/InputProfileViewHolder.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/InputProfileViewHolder.kt
@@ -9,6 +9,7 @@ import org.yuzu.yuzu_emu.features.settings.model.view.InputProfileSetting
 import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
 import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter
 import org.yuzu.yuzu_emu.R
+import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
 
 class InputProfileViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAdapter) :
     SettingViewHolder(binding.root, adapter) {
@@ -20,10 +21,10 @@ class InputProfileViewHolder(val binding: ListItemSettingBinding, adapter: Setti
         binding.textSettingValue.text =
             setting.getCurrentProfile().ifEmpty { binding.root.context.getString(R.string.not_set) }
 
-        binding.textSettingDescription.visibility = View.GONE
-        binding.buttonClear.visibility = View.GONE
-        binding.icon.visibility = View.GONE
-        binding.buttonClear.visibility = View.GONE
+        binding.textSettingDescription.setVisible(false)
+        binding.buttonClear.setVisible(false)
+        binding.icon.setVisible(false)
+        binding.buttonClear.setVisible(false)
     }
 
     override fun onClick(clicked: View) =
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/InputViewHolder.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/InputViewHolder.kt
index 1f1f08190..9d9047804 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/InputViewHolder.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/InputViewHolder.kt
@@ -12,6 +12,7 @@ import org.yuzu.yuzu_emu.features.settings.model.view.InputSetting
 import org.yuzu.yuzu_emu.features.settings.model.view.ModifierInputSetting
 import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
 import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter
+import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
 
 class InputViewHolder(val binding: ListItemSettingInputBinding, adapter: SettingsAdapter) :
     SettingViewHolder(binding.root, adapter) {
@@ -22,38 +23,26 @@ class InputViewHolder(val binding: ListItemSettingInputBinding, adapter: Setting
         binding.textSettingName.text = setting.title
         binding.textSettingValue.text = setting.getSelectedValue()
 
-        binding.buttonOptions.visibility = when (item) {
+        when (item) {
             is AnalogInputSetting -> {
                 val param = NativeInput.getStickParam(item.playerIndex, item.nativeAnalog)
-                if (
+                binding.buttonOptions.setVisible(
                     param.get("engine", "") == "analog_from_button" ||
-                    param.has("axis_x") || param.has("axis_y")
-                ) {
-                    View.VISIBLE
-                } else {
-                    View.GONE
-                }
+                        param.has("axis_x") || param.has("axis_y")
+                )
             }
 
             is ButtonInputSetting -> {
                 val param = NativeInput.getButtonParam(item.playerIndex, item.nativeButton)
-                if (
+                binding.buttonOptions.setVisible(
                     param.has("code") || param.has("button") || param.has("hat") ||
-                    param.has("axis")
-                ) {
-                    View.VISIBLE
-                } else {
-                    View.GONE
-                }
+                        param.has("axis")
+                )
             }
 
             is ModifierInputSetting -> {
                 val params = NativeInput.getStickParam(item.playerIndex, item.nativeAnalog)
-                if (params.has("modifier")) {
-                    View.VISIBLE
-                } else {
-                    View.GONE
-                }
+                binding.buttonOptions.setVisible(params.has("modifier"))
             }
         }
 
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/RunnableViewHolder.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/RunnableViewHolder.kt
index 2841520a5..fc2ffb515 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/RunnableViewHolder.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/RunnableViewHolder.kt
@@ -9,6 +9,7 @@ import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding
 import org.yuzu.yuzu_emu.features.settings.model.view.RunnableSetting
 import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
 import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter
+import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
 
 class RunnableViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAdapter) :
     SettingViewHolder(binding.root, adapter) {
@@ -16,8 +17,8 @@ class RunnableViewHolder(val binding: ListItemSettingBinding, adapter: SettingsA
 
     override fun bind(item: SettingsItem) {
         setting = item as RunnableSetting
+        binding.icon.setVisible(setting.iconId != 0)
         if (setting.iconId != 0) {
-            binding.icon.visibility = View.VISIBLE
             binding.icon.setImageDrawable(
                 ResourcesCompat.getDrawable(
                     binding.icon.resources,
@@ -25,19 +26,13 @@ class RunnableViewHolder(val binding: ListItemSettingBinding, adapter: SettingsA
                     binding.icon.context.theme
                 )
             )
-        } else {
-            binding.icon.visibility = View.GONE
         }
 
         binding.textSettingName.text = setting.title
-        if (setting.description.isNotEmpty()) {
-            binding.textSettingDescription.setText(item.descriptionId)
-            binding.textSettingDescription.visibility = View.VISIBLE
-        } else {
-            binding.textSettingDescription.visibility = View.GONE
-        }
-        binding.textSettingValue.visibility = View.GONE
-        binding.buttonClear.visibility = View.GONE
+        binding.textSettingDescription.setVisible(setting.description.isNotEmpty())
+        binding.textSettingDescription.text = item.description
+        binding.textSettingValue.setVisible(false)
+        binding.buttonClear.setVisible(false)
 
         setStyle(setting.isEditable, binding)
     }
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SingleChoiceViewHolder.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SingleChoiceViewHolder.kt
index 9705d428c..e2fe0b072 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SingleChoiceViewHolder.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SingleChoiceViewHolder.kt
@@ -11,6 +11,7 @@ import org.yuzu.yuzu_emu.features.settings.model.view.SingleChoiceSetting
 import org.yuzu.yuzu_emu.features.settings.model.view.StringSingleChoiceSetting
 import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter
 import org.yuzu.yuzu_emu.utils.NativeConfig
+import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
 
 class SingleChoiceViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAdapter) :
     SettingViewHolder(binding.root, adapter) {
@@ -19,14 +20,10 @@ class SingleChoiceViewHolder(val binding: ListItemSettingBinding, adapter: Setti
     override fun bind(item: SettingsItem) {
         setting = item
         binding.textSettingName.text = setting.title
-        if (item.description.isNotEmpty()) {
-            binding.textSettingDescription.text = item.description
-            binding.textSettingDescription.visibility = View.VISIBLE
-        } else {
-            binding.textSettingDescription.visibility = View.GONE
-        }
+        binding.textSettingDescription.setVisible(item.description.isNotEmpty())
+        binding.textSettingDescription.text = item.description
 
-        binding.textSettingValue.visibility = View.VISIBLE
+        binding.textSettingValue.setVisible(true)
         when (item) {
             is SingleChoiceSetting -> {
                 val resMgr = binding.textSettingValue.context.resources
@@ -48,16 +45,12 @@ class SingleChoiceViewHolder(val binding: ListItemSettingBinding, adapter: Setti
             }
         }
         if (binding.textSettingValue.text.isEmpty()) {
-            binding.textSettingValue.visibility = View.GONE
+            binding.textSettingValue.setVisible(false)
         }
 
-        binding.buttonClear.visibility = if (setting.setting.global ||
-            !NativeConfig.isPerGameConfigLoaded()
-        ) {
-            View.GONE
-        } else {
-            View.VISIBLE
-        }
+        binding.buttonClear.setVisible(
+            !setting.setting.global || NativeConfig.isPerGameConfigLoaded()
+        )
         binding.buttonClear.setOnClickListener {
             adapter.onClearClick(setting, bindingAdapterPosition)
         }
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SliderViewHolder.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SliderViewHolder.kt
index fcfac040e..a37b59b44 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SliderViewHolder.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SliderViewHolder.kt
@@ -10,6 +10,7 @@ import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
 import org.yuzu.yuzu_emu.features.settings.model.view.SliderSetting
 import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter
 import org.yuzu.yuzu_emu.utils.NativeConfig
+import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
 
 class SliderViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAdapter) :
     SettingViewHolder(binding.root, adapter) {
@@ -18,26 +19,18 @@ class SliderViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAda
     override fun bind(item: SettingsItem) {
         setting = item as SliderSetting
         binding.textSettingName.text = setting.title
-        if (item.description.isNotEmpty()) {
-            binding.textSettingDescription.text = setting.description
-            binding.textSettingDescription.visibility = View.VISIBLE
-        } else {
-            binding.textSettingDescription.visibility = View.GONE
-        }
-        binding.textSettingValue.visibility = View.VISIBLE
+        binding.textSettingDescription.setVisible(item.description.isNotEmpty())
+        binding.textSettingDescription.text = setting.description
+        binding.textSettingValue.setVisible(true)
         binding.textSettingValue.text = String.format(
             binding.textSettingValue.context.getString(R.string.value_with_units),
             setting.getSelectedValue(),
             setting.units
         )
 
-        binding.buttonClear.visibility = if (setting.setting.global ||
-            !NativeConfig.isPerGameConfigLoaded()
-        ) {
-            View.GONE
-        } else {
-            View.VISIBLE
-        }
+        binding.buttonClear.setVisible(
+            !setting.setting.global || NativeConfig.isPerGameConfigLoaded()
+        )
         binding.buttonClear.setOnClickListener {
             adapter.onClearClick(setting, bindingAdapterPosition)
         }
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SubmenuViewHolder.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SubmenuViewHolder.kt
index 165c765b3..f7a9c08c3 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SubmenuViewHolder.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SubmenuViewHolder.kt
@@ -9,6 +9,7 @@ import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding
 import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
 import org.yuzu.yuzu_emu.features.settings.model.view.SubmenuSetting
 import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter
+import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
 
 class SubmenuViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAdapter) :
     SettingViewHolder(binding.root, adapter) {
@@ -16,8 +17,8 @@ class SubmenuViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAd
 
     override fun bind(item: SettingsItem) {
         setting = item as SubmenuSetting
+        binding.icon.setVisible(setting.iconId != 0)
         if (setting.iconId != 0) {
-            binding.icon.visibility = View.VISIBLE
             binding.icon.setImageDrawable(
                 ResourcesCompat.getDrawable(
                     binding.icon.resources,
@@ -25,19 +26,13 @@ class SubmenuViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAd
                     binding.icon.context.theme
                 )
             )
-        } else {
-            binding.icon.visibility = View.GONE
         }
 
         binding.textSettingName.text = setting.title
-        if (setting.description.isNotEmpty()) {
-            binding.textSettingDescription.text = setting.description
-            binding.textSettingDescription.visibility = View.VISIBLE
-        } else {
-            binding.textSettingDescription.visibility = View.GONE
-        }
-        binding.textSettingValue.visibility = View.GONE
-        binding.buttonClear.visibility = View.GONE
+        binding.textSettingDescription.setVisible(setting.description.isNotEmpty())
+        binding.textSettingDescription.text = setting.description
+        binding.textSettingValue.setVisible(false)
+        binding.buttonClear.setVisible(false)
     }
 
     override fun onClick(clicked: View) {
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SwitchSettingViewHolder.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SwitchSettingViewHolder.kt
index f779a7b60..53f7b301f 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SwitchSettingViewHolder.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SwitchSettingViewHolder.kt
@@ -10,6 +10,7 @@ import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
 import org.yuzu.yuzu_emu.features.settings.model.view.SwitchSetting
 import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter
 import org.yuzu.yuzu_emu.utils.NativeConfig
+import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
 
 class SwitchSettingViewHolder(val binding: ListItemSettingSwitchBinding, adapter: SettingsAdapter) :
     SettingViewHolder(binding.root, adapter) {
@@ -19,12 +20,8 @@ class SwitchSettingViewHolder(val binding: ListItemSettingSwitchBinding, adapter
     override fun bind(item: SettingsItem) {
         setting = item as SwitchSetting
         binding.textSettingName.text = setting.title
-        if (setting.description.isNotEmpty()) {
-            binding.textSettingDescription.text = setting.description
-            binding.textSettingDescription.visibility = View.VISIBLE
-        } else {
-            binding.textSettingDescription.visibility = View.GONE
-        }
+        binding.textSettingDescription.setVisible(setting.description.isNotEmpty())
+        binding.textSettingDescription.text = setting.description
 
         binding.switchWidget.setOnCheckedChangeListener(null)
         binding.switchWidget.isChecked = setting.getIsChecked(setting.needsRuntimeGlobal)
@@ -32,13 +29,9 @@ class SwitchSettingViewHolder(val binding: ListItemSettingSwitchBinding, adapter
             adapter.onBooleanClick(setting, binding.switchWidget.isChecked, bindingAdapterPosition)
         }
 
-        binding.buttonClear.visibility = if (setting.setting.global ||
-            !NativeConfig.isPerGameConfigLoaded()
-        ) {
-            View.GONE
-        } else {
-            View.VISIBLE
-        }
+        binding.buttonClear.setVisible(
+            !setting.setting.global || NativeConfig.isPerGameConfigLoaded()
+        )
         binding.buttonClear.setOnClickListener {
             adapter.onClearClick(setting, bindingAdapterPosition)
         }
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt
index c737ed5e8..aedc128d6 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt
@@ -63,6 +63,7 @@ import org.yuzu.yuzu_emu.model.EmulationViewModel
 import org.yuzu.yuzu_emu.overlay.model.OverlayControl
 import org.yuzu.yuzu_emu.overlay.model.OverlayLayout
 import org.yuzu.yuzu_emu.utils.*
+import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
 import java.lang.NullPointerException
 
 class EmulationFragment : Fragment(), SurfaceHolder.Callback {
@@ -500,14 +501,12 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
                 binding.drawerLayout.close()
             }
             if (showInputOverlay) {
-                binding.surfaceInputOverlay.visibility = View.INVISIBLE
+                binding.surfaceInputOverlay.setVisible(visible = false, gone = false)
             }
         } else {
-            if (showInputOverlay && emulationViewModel.emulationStarted.value) {
-                binding.surfaceInputOverlay.visibility = View.VISIBLE
-            } else {
-                binding.surfaceInputOverlay.visibility = View.INVISIBLE
-            }
+            binding.surfaceInputOverlay.setVisible(
+                showInputOverlay && emulationViewModel.emulationStarted.value
+            )
             if (!isInFoldableLayout) {
                 if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) {
                     binding.surfaceInputOverlay.layout = OverlayLayout.Portrait
@@ -544,7 +543,9 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
     }
 
     private fun updateShowFpsOverlay() {
-        if (BooleanSetting.SHOW_PERFORMANCE_OVERLAY.getBoolean()) {
+        val showOverlay = BooleanSetting.SHOW_PERFORMANCE_OVERLAY.getBoolean()
+        binding.showFpsText.setVisible(showOverlay)
+        if (showOverlay) {
             val SYSTEM_FPS = 0
             val FPS = 1
             val FRAMETIME = 2
@@ -564,17 +565,17 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
                 }
             }
             perfStatsUpdateHandler.post(perfStatsUpdater!!)
-            binding.showFpsText.visibility = View.VISIBLE
         } else {
             if (perfStatsUpdater != null) {
                 perfStatsUpdateHandler.removeCallbacks(perfStatsUpdater!!)
             }
-            binding.showFpsText.visibility = View.GONE
         }
     }
 
     private fun updateThermalOverlay() {
-        if (BooleanSetting.SHOW_THERMAL_OVERLAY.getBoolean()) {
+        val showOverlay = BooleanSetting.SHOW_THERMAL_OVERLAY.getBoolean()
+        binding.showThermalsText.setVisible(showOverlay)
+        if (showOverlay) {
             thermalStatsUpdater = {
                 if (emulationViewModel.emulationStarted.value &&
                     !emulationViewModel.isEmulationStopping.value
@@ -596,12 +597,10 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
                 }
             }
             thermalStatsUpdateHandler.post(thermalStatsUpdater!!)
-            binding.showThermalsText.visibility = View.VISIBLE
         } else {
             if (thermalStatsUpdater != null) {
                 thermalStatsUpdateHandler.removeCallbacks(thermalStatsUpdater!!)
             }
-            binding.showThermalsText.visibility = View.GONE
         }
     }
 
@@ -870,12 +869,12 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
                     }
             }
         }
-        binding.doneControlConfig.visibility = View.VISIBLE
+        binding.doneControlConfig.setVisible(false)
         binding.surfaceInputOverlay.setIsInEditMode(true)
     }
 
     private fun stopConfiguringControls() {
-        binding.doneControlConfig.visibility = View.GONE
+        binding.doneControlConfig.setVisible(false)
         binding.surfaceInputOverlay.setIsInEditMode(false)
         // Unlock the orientation if it was locked for editing
         if (IntSetting.RENDERER_SCREEN_LAYOUT.getInt() == EmulationOrientation.Unspecified.int) {
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GameInfoFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GameInfoFragment.kt
index dbd56e84f..97a8954bb 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GameInfoFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GameInfoFragment.kt
@@ -27,6 +27,7 @@ import org.yuzu.yuzu_emu.databinding.FragmentGameInfoBinding
 import org.yuzu.yuzu_emu.model.GameVerificationResult
 import org.yuzu.yuzu_emu.model.HomeViewModel
 import org.yuzu.yuzu_emu.utils.GameMetadata
+import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
 import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
 
 class GameInfoFragment : Fragment() {
@@ -85,7 +86,7 @@ class GameInfoFragment : Fragment() {
                     copyToClipboard(getString(R.string.developer), args.game.developer)
                 }
             } else {
-                developer.visibility = View.GONE
+                developer.setVisible(false)
             }
 
             version.setHint(R.string.version)
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GamePropertiesFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GamePropertiesFragment.kt
index 3ea5e16ca..c4da1a65d 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GamePropertiesFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GamePropertiesFragment.kt
@@ -7,7 +7,6 @@ import android.annotation.SuppressLint
 import android.content.pm.ShortcutInfo
 import android.content.pm.ShortcutManager
 import android.os.Bundle
-import android.text.TextUtils
 import android.view.LayoutInflater
 import android.view.View
 import android.view.ViewGroup
@@ -46,6 +45,7 @@ import org.yuzu.yuzu_emu.utils.FileUtil
 import org.yuzu.yuzu_emu.utils.GameIconUtils
 import org.yuzu.yuzu_emu.utils.GpuDriverHelper
 import org.yuzu.yuzu_emu.utils.MemoryUtil
+import org.yuzu.yuzu_emu.utils.ViewUtils.marquee
 import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
 import java.io.BufferedOutputStream
 import java.io.File
@@ -107,13 +107,7 @@ class GamePropertiesFragment : Fragment() {
 
         GameIconUtils.loadGameIcon(args.game, binding.imageGameScreen)
         binding.title.text = args.game.title
-        binding.title.postDelayed(
-            {
-                binding.title.ellipsize = TextUtils.TruncateAt.MARQUEE
-                binding.title.isSelected = true
-            },
-            3000
-        )
+        binding.title.marquee()
 
         binding.buttonStart.setOnClickListener {
             LaunchGameDialogFragment.newInstance(args.game)
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/ProgressDialogFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/ProgressDialogFragment.kt
index d201cb80c..ae29e9cd1 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/ProgressDialogFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/ProgressDialogFragment.kt
@@ -22,6 +22,7 @@ import kotlinx.coroutines.launch
 import org.yuzu.yuzu_emu.R
 import org.yuzu.yuzu_emu.databinding.DialogProgressBarBinding
 import org.yuzu.yuzu_emu.model.TaskViewModel
+import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
 
 class ProgressDialogFragment : DialogFragment() {
     private val taskViewModel: TaskViewModel by activityViewModels()
@@ -120,10 +121,8 @@ class ProgressDialogFragment : DialogFragment() {
             launch {
                 repeatOnLifecycle(Lifecycle.State.CREATED) {
                     taskViewModel.message.collect {
-                        if (it.isEmpty()) {
-                            binding.message.visibility = View.GONE
-                        } else {
-                            binding.message.visibility = View.VISIBLE
+                        binding.message.setVisible(it.isNotEmpty())
+                        if (it.isNotEmpty()) {
                             binding.message.text = it
                         }
                     }
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SearchFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SearchFragment.kt
index 20b10b1a0..9f6509605 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SearchFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SearchFragment.kt
@@ -35,6 +35,7 @@ import org.yuzu.yuzu_emu.layout.AutofitGridLayoutManager
 import org.yuzu.yuzu_emu.model.Game
 import org.yuzu.yuzu_emu.model.GamesViewModel
 import org.yuzu.yuzu_emu.model.HomeViewModel
+import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
 
 class SearchFragment : Fragment() {
     private var _binding: FragmentSearchBinding? = null
@@ -81,11 +82,7 @@ class SearchFragment : Fragment() {
         binding.chipGroup.setOnCheckedStateChangeListener { _, _ -> filterAndSearch() }
 
         binding.searchText.doOnTextChanged { text: CharSequence?, _: Int, _: Int, _: Int ->
-            if (text.toString().isNotEmpty()) {
-                binding.clearButton.visibility = View.VISIBLE
-            } else {
-                binding.clearButton.visibility = View.INVISIBLE
-            }
+            binding.clearButton.setVisible(text.toString().isNotEmpty())
             filterAndSearch()
         }
 
@@ -109,11 +106,7 @@ class SearchFragment : Fragment() {
                 repeatOnLifecycle(Lifecycle.State.CREATED) {
                     gamesViewModel.searchedGames.collect {
                         (binding.gridGamesSearch.adapter as GameAdapter).submitList(it)
-                        if (it.isEmpty()) {
-                            binding.noResultsView.visibility = View.VISIBLE
-                        } else {
-                            binding.noResultsView.visibility = View.GONE
-                        }
+                        binding.noResultsView.setVisible(it.isEmpty())
                     }
                 }
             }
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SetupFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SetupFragment.kt
index ebf41a639..eb279d309 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SetupFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SetupFragment.kt
@@ -46,6 +46,7 @@ import org.yuzu.yuzu_emu.ui.main.MainActivity
 import org.yuzu.yuzu_emu.utils.DirectoryInitialization
 import org.yuzu.yuzu_emu.utils.NativeConfig
 import org.yuzu.yuzu_emu.utils.ViewUtils
+import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
 
 class SetupFragment : Fragment() {
     private var _binding: FragmentSetupBinding? = null
@@ -292,12 +293,8 @@ class SetupFragment : Fragment() {
             val backIsVisible = savedInstanceState.getBoolean(KEY_BACK_VISIBILITY)
             hasBeenWarned = savedInstanceState.getBooleanArray(KEY_HAS_BEEN_WARNED)!!
 
-            if (nextIsVisible) {
-                binding.buttonNext.visibility = View.VISIBLE
-            }
-            if (backIsVisible) {
-                binding.buttonBack.visibility = View.VISIBLE
-            }
+            binding.buttonNext.setVisible(nextIsVisible)
+            binding.buttonBack.setVisible(backIsVisible)
         } else {
             hasBeenWarned = BooleanArray(pages.size)
         }
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/GamesFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/GamesFragment.kt
index 23ca49b53..b3248585e 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/GamesFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/GamesFragment.kt
@@ -26,6 +26,7 @@ import org.yuzu.yuzu_emu.databinding.FragmentGamesBinding
 import org.yuzu.yuzu_emu.layout.AutofitGridLayoutManager
 import org.yuzu.yuzu_emu.model.GamesViewModel
 import org.yuzu.yuzu_emu.model.HomeViewModel
+import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
 import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
 
 class GamesFragment : Fragment() {
@@ -93,11 +94,10 @@ class GamesFragment : Fragment() {
                 repeatOnLifecycle(Lifecycle.State.RESUMED) {
                     gamesViewModel.isReloading.collect {
                         binding.swipeRefresh.isRefreshing = it
-                        if (gamesViewModel.games.value.isEmpty() && !it) {
-                            binding.noticeText.visibility = View.VISIBLE
-                        } else {
-                            binding.noticeText.visibility = View.INVISIBLE
-                        }
+                        binding.noticeText.setVisible(
+                            visible = gamesViewModel.games.value.isEmpty() && !it,
+                            gone = false
+                        )
                     }
                 }
             }
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt
index 4df4ac4c6..703fbaf3e 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt
@@ -47,6 +47,7 @@ import org.yuzu.yuzu_emu.model.InstallResult
 import org.yuzu.yuzu_emu.model.TaskState
 import org.yuzu.yuzu_emu.model.TaskViewModel
 import org.yuzu.yuzu_emu.utils.*
+import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
 import java.io.BufferedInputStream
 import java.io.BufferedOutputStream
 import java.util.zip.ZipEntry
@@ -139,8 +140,8 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
 
         // Prevents navigation from being drawn for a short time on recreation if set to hidden
         if (!homeViewModel.navigationVisible.value.first) {
-            binding.navigationView.visibility = View.INVISIBLE
-            binding.statusBarShade.visibility = View.INVISIBLE
+            binding.navigationView.setVisible(visible = false, gone = false)
+            binding.statusBarShade.setVisible(visible = false, gone = false)
         }
 
         lifecycleScope.apply {
@@ -214,18 +215,14 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
 
     private fun showNavigation(visible: Boolean, animated: Boolean) {
         if (!animated) {
-            if (visible) {
-                binding.navigationView.visibility = View.VISIBLE
-            } else {
-                binding.navigationView.visibility = View.INVISIBLE
-            }
+            binding.navigationView.setVisible(visible)
             return
         }
 
         val smallLayout = resources.getBoolean(R.bool.small_layout)
         binding.navigationView.animate().apply {
             if (visible) {
-                binding.navigationView.visibility = View.VISIBLE
+                binding.navigationView.setVisible(true)
                 duration = 300
                 interpolator = PathInterpolator(0.05f, 0.7f, 0.1f, 1f)
 
@@ -264,7 +261,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
             }
         }.withEndAction {
             if (!visible) {
-                binding.navigationView.visibility = View.INVISIBLE
+                binding.navigationView.setVisible(visible = false, gone = false)
             }
         }.start()
     }
@@ -272,7 +269,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
     private fun showStatusBarShade(visible: Boolean) {
         binding.statusBarShade.animate().apply {
             if (visible) {
-                binding.statusBarShade.visibility = View.VISIBLE
+                binding.statusBarShade.setVisible(true)
                 binding.statusBarShade.translationY = binding.statusBarShade.height.toFloat() * -2
                 duration = 300
                 translationY(0f)
@@ -284,7 +281,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
             }
         }.withEndAction {
             if (!visible) {
-                binding.statusBarShade.visibility = View.INVISIBLE
+                binding.statusBarShade.setVisible(visible = false, gone = false)
             }
         }.start()
     }
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ViewUtils.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ViewUtils.kt
index ffbfa9337..244091aec 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ViewUtils.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ViewUtils.kt
@@ -3,8 +3,10 @@
 
 package org.yuzu.yuzu_emu.utils
 
+import android.text.TextUtils
 import android.view.View
 import android.view.ViewGroup
+import android.widget.TextView
 
 object ViewUtils {
     fun showView(view: View, length: Long = 300) {
@@ -57,4 +59,35 @@ object ViewUtils {
         }
         this.layoutParams = layoutParams
     }
+
+    /**
+     * Shows or hides a view.
+     * @param visible Whether a view will be made View.VISIBLE or View.INVISIBLE/GONE.
+     * @param gone Optional parameter for hiding a view. Uses View.GONE if true and View.INVISIBLE otherwise.
+     */
+    fun View.setVisible(visible: Boolean, gone: Boolean = true) {
+        visibility = if (visible) {
+            View.VISIBLE
+        } else {
+            if (gone) {
+                View.GONE
+            } else {
+                View.INVISIBLE
+            }
+        }
+    }
+
+    /**
+     * Starts a marquee on some text.
+     * @param delay Optional parameter for changing the start delay. 3 seconds of delay by default.
+     */
+    fun TextView.marquee(delay: Long = 3000) {
+        ellipsize = null
+        marqueeRepeatLimit = -1
+        isSingleLine = true
+        postDelayed({
+            ellipsize = TextUtils.TruncateAt.MARQUEE
+            isSelected = true
+        }, delay)
+    }
 }
diff --git a/src/android/app/src/main/res/layout/card_driver_option.xml b/src/android/app/src/main/res/layout/card_driver_option.xml
index bda524f0f..09e26990b 100644
--- a/src/android/app/src/main/res/layout/card_driver_option.xml
+++ b/src/android/app/src/main/res/layout/card_driver_option.xml
@@ -39,10 +39,7 @@
                 style="@style/TextAppearance.Material3.TitleMedium"
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
-                android:ellipsize="none"
-                android:marqueeRepeatLimit="marquee_forever"
                 android:requiresFadingEdge="horizontal"
-                android:singleLine="true"
                 android:textAlignment="viewStart"
                 tools:text="@string/select_gpu_driver_default" />
 
@@ -52,10 +49,7 @@
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
                 android:layout_marginTop="6dp"
-                android:ellipsize="none"
-                android:marqueeRepeatLimit="marquee_forever"
                 android:requiresFadingEdge="horizontal"
-                android:singleLine="true"
                 android:textAlignment="viewStart"
                 tools:text="@string/install_gpu_driver_description" />
 
@@ -65,10 +59,7 @@
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
                 android:layout_marginTop="6dp"
-                android:ellipsize="none"
-                android:marqueeRepeatLimit="marquee_forever"
                 android:requiresFadingEdge="horizontal"
-                android:singleLine="true"
                 android:textAlignment="viewStart"
                 tools:text="@string/install_gpu_driver_description" />
 
diff --git a/src/android/app/src/main/res/layout/card_folder.xml b/src/android/app/src/main/res/layout/card_folder.xml
index ed4a7ca8f..e3a5f1a86 100644
--- a/src/android/app/src/main/res/layout/card_folder.xml
+++ b/src/android/app/src/main/res/layout/card_folder.xml
@@ -21,10 +21,7 @@
             android:layout_width="0dp"
             android:layout_height="wrap_content"
             android:layout_gravity="center_vertical|start"
-            android:ellipsize="none"
-            android:marqueeRepeatLimit="marquee_forever"
             android:requiresFadingEdge="horizontal"
-            android:singleLine="true"
             android:textAlignment="viewStart"
             app:layout_constraintBottom_toBottomOf="parent"
             app:layout_constraintEnd_toStartOf="@+id/button_layout"
diff --git a/src/android/app/src/main/res/layout/card_game.xml b/src/android/app/src/main/res/layout/card_game.xml
index 6340171ec..411b50315 100644
--- a/src/android/app/src/main/res/layout/card_game.xml
+++ b/src/android/app/src/main/res/layout/card_game.xml
@@ -40,10 +40,7 @@
                 android:layout_width="0dp"
                 android:layout_height="wrap_content"
                 android:layout_marginTop="8dp"
-                android:ellipsize="none"
-                android:marqueeRepeatLimit="marquee_forever"
                 android:requiresFadingEdge="horizontal"
-                android:singleLine="true"
                 android:textAlignment="center"
                 android:textSize="14sp"
                 app:layout_constraintEnd_toEndOf="@+id/image_game_screen"
diff --git a/src/android/app/src/main/res/layout/card_simple_outlined.xml b/src/android/app/src/main/res/layout/card_simple_outlined.xml
index b73930e7e..e29df6a2d 100644
--- a/src/android/app/src/main/res/layout/card_simple_outlined.xml
+++ b/src/android/app/src/main/res/layout/card_simple_outlined.xml
@@ -59,9 +59,6 @@
                 android:textAlignment="viewStart"
                 android:textSize="14sp"
                 android:textStyle="bold"
-                android:singleLine="true"
-                android:marqueeRepeatLimit="marquee_forever"
-                android:ellipsize="none"
                 android:requiresFadingEdge="horizontal"
                 android:layout_marginTop="6dp"
                 android:visibility="gone"
diff --git a/src/android/app/src/main/res/layout/fragment_game_properties.xml b/src/android/app/src/main/res/layout/fragment_game_properties.xml
index 436ebd79d..5e3f3cf28 100644
--- a/src/android/app/src/main/res/layout/fragment_game_properties.xml
+++ b/src/android/app/src/main/res/layout/fragment_game_properties.xml
@@ -76,10 +76,7 @@
                 android:layout_marginTop="12dp"
                 android:layout_marginBottom="12dp"
                 android:layout_marginHorizontal="16dp"
-                android:ellipsize="none"
-                android:marqueeRepeatLimit="marquee_forever"
                 android:requiresFadingEdge="horizontal"
-                android:singleLine="true"
                 android:textAlignment="center"
                 tools:text="deko_basic" />