android: Use view binding

master
Charles Lombardo 2023-03-20 00:21:52 +07:00 committed by bunnei
parent e49e6cac7e
commit 72679c7bae
16 changed files with 189 additions and 284 deletions

@ -8,11 +8,9 @@ import android.content.DialogInterface
import android.content.Intent
import android.graphics.Rect
import android.os.Bundle
import android.view.LayoutInflater
import android.view.MotionEvent
import android.view.View
import android.view.WindowManager
import android.widget.TextView
import androidx.activity.OnBackPressedCallback
import androidx.annotation.IntDef
import androidx.appcompat.app.AppCompatActivity
@ -21,10 +19,10 @@ import androidx.fragment.app.FragmentActivity
import androidx.fragment.app.FragmentManager
import androidx.preference.PreferenceManager
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.slider.Slider
import com.google.android.material.slider.Slider.OnChangeListener
import org.yuzu.yuzu_emu.NativeLibrary
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.databinding.DialogSliderBinding
import org.yuzu.yuzu_emu.features.settings.model.Settings
import org.yuzu.yuzu_emu.fragments.EmulationFragment
import org.yuzu.yuzu_emu.fragments.MenuFragment
@ -139,27 +137,22 @@ open class EmulationActivity : AppCompatActivity() {
}
private fun adjustScale() {
val inflater = LayoutInflater.from(this)
val view = inflater.inflate(R.layout.dialog_slider, null)
val slider = view.findViewById<Slider>(R.id.slider)
val textValue = view.findViewById<TextView>(R.id.text_value)
val units = view.findViewById<TextView>(R.id.text_units)
slider.valueTo = 150F
slider.value = PreferenceManager.getDefaultSharedPreferences(applicationContext)
val sliderBinding = DialogSliderBinding.inflate(layoutInflater)
sliderBinding.slider.valueTo = 150F
sliderBinding.slider.value = PreferenceManager.getDefaultSharedPreferences(applicationContext)
.getInt(Settings.PREF_CONTROL_SCALE, 50).toFloat()
slider.addOnChangeListener(OnChangeListener { _, value, _ ->
textValue.text = value.toString()
sliderBinding.slider.addOnChangeListener(OnChangeListener { _, value, _ ->
sliderBinding.textValue.text = value.toString()
setControlScale(value.toInt())
})
textValue.text = slider.value.toString()
units.text = "%"
sliderBinding.textValue.text = sliderBinding.slider.value.toString()
sliderBinding.textUnits.text = "%"
MaterialAlertDialogBuilder(this)
.setTitle(R.string.emulation_control_scale)
.setView(view)
.setView(sliderBinding.root)
.setNegativeButton(android.R.string.cancel, null)
.setPositiveButton(android.R.string.ok) { _: DialogInterface?, _: Int ->
setControlScale(slider.value.toInt())
setControlScale(sliderBinding.slider.value.toInt())
}
.setNeutralButton(R.string.slider_default) { _: DialogInterface?, _: Int ->
setControlScale(50)

@ -24,6 +24,8 @@ import kotlinx.coroutines.withContext
import org.yuzu.yuzu_emu.NativeLibrary
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.activities.EmulationActivity.Companion.launch
import org.yuzu.yuzu_emu.databinding.CardGameBinding
import org.yuzu.yuzu_emu.model.Game
import org.yuzu.yuzu_emu.model.GameDatabase
import org.yuzu.yuzu_emu.utils.Log
import org.yuzu.yuzu_emu.viewholders.GameViewHolder
@ -51,25 +53,24 @@ class GameAdapter(private val activity: AppCompatActivity) : RecyclerView.Adapte
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): GameViewHolder {
// Create a new view.
val gameCard = LayoutInflater.from(parent.context)
.inflate(R.layout.card_game, parent, false)
gameCard.setOnClickListener(this)
val binding = CardGameBinding.inflate(LayoutInflater.from(parent.context))
binding.root.setOnClickListener(this)
// Use that view to create a ViewHolder.
return GameViewHolder(gameCard)
return GameViewHolder(binding)
}
override fun onBindViewHolder(holder: GameViewHolder, position: Int) {
if (isDatasetValid) {
if (cursor!!.moveToPosition(position)) {
holder.imageIcon.scaleType = ImageView.ScaleType.CENTER_CROP
holder.binding.imageGameScreen.scaleType = ImageView.ScaleType.CENTER_CROP
activity.lifecycleScope.launch {
withContext(Dispatchers.IO) {
val uri =
Uri.parse(cursor!!.getString(GameDatabase.GAME_COLUMN_PATH)).toString()
val bitmap = decodeGameIcon(uri)
withContext(Dispatchers.Main) {
holder.imageIcon.load(bitmap) {
holder.binding.imageGameScreen.load(bitmap) {
error(R.drawable.no_icon)
crossfade(true)
}
@ -77,20 +78,23 @@ class GameAdapter(private val activity: AppCompatActivity) : RecyclerView.Adapte
}
}
holder.textGameTitle.text =
holder.binding.textGameTitle.text =
cursor!!.getString(GameDatabase.GAME_COLUMN_TITLE)
.replace("[\\t\\n\\r]+".toRegex(), " ")
holder.textGameCaption.text = cursor!!.getString(GameDatabase.GAME_COLUMN_CAPTION)
holder.binding.textGameCaption.text = cursor!!.getString(GameDatabase.GAME_COLUMN_CAPTION)
// TODO These shouldn't be necessary once the move to a DB-based model is complete.
holder.gameId = cursor!!.getString(GameDatabase.GAME_COLUMN_GAME_ID)
holder.path = cursor!!.getString(GameDatabase.GAME_COLUMN_PATH)
holder.title = cursor!!.getString(GameDatabase.GAME_COLUMN_TITLE)
holder.description = cursor!!.getString(GameDatabase.GAME_COLUMN_DESCRIPTION)
holder.regions = cursor!!.getString(GameDatabase.GAME_COLUMN_REGIONS)
holder.company = cursor!!.getString(GameDatabase.GAME_COLUMN_CAPTION)
val game = Game(
cursor!!.getString(GameDatabase.GAME_COLUMN_TITLE),
cursor!!.getString(GameDatabase.GAME_COLUMN_DESCRIPTION),
cursor!!.getString(GameDatabase.GAME_COLUMN_REGIONS),
cursor!!.getString(GameDatabase.GAME_COLUMN_PATH),
cursor!!.getString(GameDatabase.GAME_COLUMN_GAME_ID),
cursor!!.getString(GameDatabase.GAME_COLUMN_CAPTION)
)
holder.game = game
val backgroundColorId =
if (isValidGame(holder.path!!)) R.attr.colorSurface else R.attr.colorErrorContainer
if (isValidGame(holder.game.path)) R.attr.colorSurface else R.attr.colorErrorContainer
val itemView = holder.itemView
itemView.setBackgroundColor(
MaterialColors.getColor(
@ -177,7 +181,7 @@ class GameAdapter(private val activity: AppCompatActivity) : RecyclerView.Adapte
*/
override fun onClick(view: View) {
val holder = view.tag as GameViewHolder
launch((view.context as FragmentActivity), holder.path, holder.title)
launch((view.context as FragmentActivity), holder.game.path, holder.game.title)
}
private fun isValidGame(path: String): Boolean {

@ -9,7 +9,6 @@ import android.content.IntentFilter
import android.os.Bundle
import android.view.Menu
import android.view.View
import android.widget.FrameLayout
import android.widget.Toast
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
@ -18,11 +17,11 @@ import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.updatePadding
import androidx.localbroadcastmanager.content.LocalBroadcastManager
import com.google.android.material.appbar.AppBarLayout
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.progressindicator.LinearProgressIndicator
import org.yuzu.yuzu_emu.NativeLibrary
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.databinding.ActivitySettingsBinding
import org.yuzu.yuzu_emu.databinding.DialogProgressBarBinding
import org.yuzu.yuzu_emu.features.settings.model.Settings
import org.yuzu.yuzu_emu.features.settings.ui.SettingsFragment.Companion.newInstance
import org.yuzu.yuzu_emu.utils.*
@ -31,11 +30,15 @@ class SettingsActivity : AppCompatActivity(), SettingsActivityView {
private val presenter = SettingsActivityPresenter(this)
private var dialog: AlertDialog? = null
private lateinit var binding: ActivitySettingsBinding
override fun onCreate(savedInstanceState: Bundle?) {
ThemeHelper.setTheme(this)
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_settings)
binding = ActivitySettingsBinding.inflate(layoutInflater)
setContentView(binding.root)
WindowCompat.setDecorFitsSystemWindows(window, false)
@ -45,7 +48,7 @@ class SettingsActivity : AppCompatActivity(), SettingsActivityView {
presenter.onCreate(savedInstanceState, menuTag!!, gameID!!)
// Show "Back" button in the action bar for navigation
setSupportActionBar(findViewById(R.id.toolbar_settings))
setSupportActionBar(binding.toolbarSettings)
supportActionBar!!.setDisplayHomeAsUpEnabled(true)
setInsets()
@ -138,13 +141,12 @@ class SettingsActivity : AppCompatActivity(), SettingsActivityView {
override fun showLoading() {
if (dialog == null) {
val root = layoutInflater.inflate(R.layout.dialog_progress_bar, null)
val progressBar = root.findViewById<LinearProgressIndicator>(R.id.progress_bar)
progressBar.isIndeterminate = true
val loadingBinding = DialogProgressBarBinding.inflate(layoutInflater)
loadingBinding.progressBar.isIndeterminate = true
dialog = MaterialAlertDialogBuilder(this)
.setTitle(R.string.load_settings)
.setView(root)
.setView(loadingBinding.root)
.setCancelable(false)
.create()
}
@ -195,12 +197,10 @@ class SettingsActivity : AppCompatActivity(), SettingsActivityView {
get() = supportFragmentManager.findFragmentByTag(FRAGMENT_TAG) as SettingsFragment?
private fun setInsets() {
val appBar = findViewById<AppBarLayout>(R.id.appbar_settings)
val frame = findViewById<FrameLayout>(R.id.frame_content)
ViewCompat.setOnApplyWindowInsetsListener(frame) { view: View, windowInsets: WindowInsetsCompat ->
ViewCompat.setOnApplyWindowInsetsListener(binding.frameContent) { view: View, windowInsets: WindowInsetsCompat ->
val insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
view.updatePadding(left = insets.left, right = insets.right)
InsetsHelper.insetAppBar(insets, appBar)
InsetsHelper.insetAppBar(insets, binding.appbarSettings)
windowInsets
}
}

@ -9,7 +9,6 @@ import android.icu.util.Calendar
import android.icu.util.TimeZone
import android.text.format.DateFormat
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.appcompat.app.AlertDialog
@ -20,6 +19,10 @@ import com.google.android.material.slider.Slider
import com.google.android.material.timepicker.MaterialTimePicker
import com.google.android.material.timepicker.TimeFormat
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.databinding.DialogSliderBinding
import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding
import org.yuzu.yuzu_emu.databinding.ListItemSettingSwitchBinding
import org.yuzu.yuzu_emu.databinding.ListItemSettingsHeaderBinding
import org.yuzu.yuzu_emu.features.settings.model.FloatSetting
import org.yuzu.yuzu_emu.features.settings.model.view.*
import org.yuzu.yuzu_emu.features.settings.ui.viewholder.*
@ -43,37 +46,29 @@ class SettingsAdapter(
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SettingViewHolder {
val view: View
val inflater = LayoutInflater.from(parent.context)
return when (viewType) {
SettingsItem.TYPE_HEADER -> {
view = inflater.inflate(R.layout.list_item_settings_header, parent, false)
HeaderViewHolder(view, this)
HeaderViewHolder(ListItemSettingsHeaderBinding.inflate(inflater), this)
}
SettingsItem.TYPE_CHECKBOX -> {
view = inflater.inflate(R.layout.list_item_setting_switch, parent, false)
SwitchSettingViewHolder(view, this)
SettingsItem.TYPE_SWITCH -> {
SwitchSettingViewHolder(ListItemSettingSwitchBinding.inflate(inflater), this)
}
SettingsItem.TYPE_SINGLE_CHOICE, SettingsItem.TYPE_STRING_SINGLE_CHOICE -> {
view = inflater.inflate(R.layout.list_item_setting, parent, false)
SingleChoiceViewHolder(view, this)
SingleChoiceViewHolder(ListItemSettingBinding.inflate(inflater), this)
}
SettingsItem.TYPE_SLIDER -> {
view = inflater.inflate(R.layout.list_item_setting, parent, false)
SliderViewHolder(view, this)
SliderViewHolder(ListItemSettingBinding.inflate(inflater), this)
}
SettingsItem.TYPE_SUBMENU -> {
view = inflater.inflate(R.layout.list_item_setting, parent, false)
SubmenuViewHolder(view, this)
SubmenuViewHolder(ListItemSettingBinding.inflate(inflater), this)
}
SettingsItem.TYPE_DATETIME_SETTING -> {
view = inflater.inflate(R.layout.list_item_setting, parent, false)
DateTimeViewHolder(view, this)
DateTimeViewHolder(ListItemSettingBinding.inflate(inflater), this)
}
else -> {
// TODO: Create an error view since we can't return null now
view = inflater.inflate(R.layout.list_item_settings_header, parent, false)
HeaderViewHolder(view, this)
HeaderViewHolder(ListItemSettingsHeaderBinding.inflate(inflater), this)
}
}
}
@ -191,15 +186,13 @@ class SettingsAdapter(
sliderProgress = item.selectedValue
val inflater = LayoutInflater.from(context)
val sliderLayout = inflater.inflate(R.layout.dialog_slider, null)
val sliderView = sliderLayout.findViewById<Slider>(R.id.slider)
val sliderBinding = DialogSliderBinding.inflate(inflater)
textSliderValue = sliderLayout.findViewById(R.id.text_value)
textSliderValue = sliderBinding.textValue
textSliderValue!!.text = sliderProgress.toString()
val units = sliderLayout.findViewById<TextView>(R.id.text_units)
units.text = item.units
sliderBinding.textUnits.text = item.units
sliderView.apply {
sliderBinding.slider.apply {
valueFrom = item.min.toFloat()
valueTo = item.max.toFloat()
value = sliderProgress.toFloat()
@ -211,11 +204,11 @@ class SettingsAdapter(
dialog = MaterialAlertDialogBuilder(context)
.setTitle(item.nameId)
.setView(sliderLayout)
.setView(sliderBinding.root)
.setPositiveButton(android.R.string.ok, this)
.setNegativeButton(android.R.string.cancel, defaultCancelListener)
.setNeutralButton(R.string.slider_default) { dialog: DialogInterface, which: Int ->
sliderView.value = item.defaultValue.toFloat()
sliderBinding.slider.value = item.defaultValue.toFloat()
onClick(dialog, which)
}
.show()

@ -14,9 +14,8 @@ import androidx.core.view.updatePadding
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.divider.MaterialDividerItemDecoration
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.databinding.FragmentSettingsBinding
import org.yuzu.yuzu_emu.features.settings.model.Setting
import org.yuzu.yuzu_emu.features.settings.model.Settings
import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
@ -26,9 +25,10 @@ class SettingsFragment : Fragment(), SettingsFragmentView {
private val presenter = SettingsFragmentPresenter(this)
private var activityView: SettingsActivityView? = null
private var adapter: SettingsAdapter? = null
private var settingsAdapter: SettingsAdapter? = null
private lateinit var recyclerView: RecyclerView
private var _binding: FragmentSettingsBinding? = null
private val binding get() = _binding!!
override fun onAttach(context: Context) {
super.onAttach(context)
@ -41,7 +41,6 @@ class SettingsFragment : Fragment(), SettingsFragmentView {
super.onCreate(savedInstanceState)
val menuTag = requireArguments().getString(ARGUMENT_MENU_TAG)
val gameId = requireArguments().getString(ARGUMENT_GAME_ID)
adapter = SettingsAdapter(this, requireActivity())
presenter.onCreate(menuTag!!, gameId!!)
}
@ -49,18 +48,20 @@ class SettingsFragment : Fragment(), SettingsFragmentView {
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_settings, container, false)
): View {
_binding = FragmentSettingsBinding.inflate(layoutInflater)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
val manager = LinearLayoutManager(activity)
recyclerView = view.findViewById(R.id.list_settings)
recyclerView.adapter = adapter
recyclerView.layoutManager = manager
settingsAdapter = SettingsAdapter(this, requireActivity())
val dividerDecoration = MaterialDividerItemDecoration(requireContext(), LinearLayoutManager.VERTICAL)
dividerDecoration.isLastItemDecorated = false
recyclerView.addItemDecoration(dividerDecoration)
binding.listSettings.apply {
adapter = settingsAdapter
layoutManager = LinearLayoutManager(activity)
addItemDecoration(dividerDecoration)
}
val activity = activity as SettingsActivityView?
presenter.onViewCreated(activity!!.settings)
@ -70,8 +71,8 @@ class SettingsFragment : Fragment(), SettingsFragmentView {
override fun onDetach() {
super.onDetach()
activityView = null
if (adapter != null) {
adapter!!.closeDialog()
if (settingsAdapter != null) {
settingsAdapter!!.closeDialog()
}
}
@ -86,7 +87,7 @@ class SettingsFragment : Fragment(), SettingsFragmentView {
}
override fun showSettingsList(settingsList: ArrayList<SettingsItem>) {
adapter!!.setSettings(settingsList)
settingsAdapter!!.setSettings(settingsList)
}
override fun loadDefaultSettings() {
@ -114,7 +115,7 @@ class SettingsFragment : Fragment(), SettingsFragmentView {
}
private fun setInsets() {
ViewCompat.setOnApplyWindowInsetsListener(recyclerView) { view: View, windowInsets: WindowInsetsCompat ->
ViewCompat.setOnApplyWindowInsetsListener(binding.listSettings) { view: View, windowInsets: WindowInsetsCompat ->
val insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
view.updatePadding(bottom = insets.bottom)
windowInsets

@ -4,31 +4,23 @@
package org.yuzu.yuzu_emu.features.settings.ui.viewholder
import android.view.View
import android.widget.TextView
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding
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
class DateTimeViewHolder(itemView: View, adapter: SettingsAdapter) :
SettingViewHolder(itemView, adapter) {
class DateTimeViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAdapter) :
SettingViewHolder(binding.root, adapter) {
private lateinit var item: DateTimeSetting
private lateinit var textSettingName: TextView
private lateinit var textSettingDescription: TextView
override fun findViews(root: View) {
textSettingName = root.findViewById(R.id.text_setting_name)
textSettingDescription = root.findViewById(R.id.text_setting_description)
}
override fun bind(item: SettingsItem) {
this.item = item as DateTimeSetting
textSettingName.setText(item.nameId)
binding.textSettingName.setText(item.nameId)
if (item.descriptionId!! > 0) {
textSettingDescription.setText(item.descriptionId)
textSettingDescription.visibility = View.VISIBLE
binding.textSettingDescription.setText(item.descriptionId)
binding.textSettingDescription.visibility = View.VISIBLE
} else {
textSettingDescription.visibility = View.GONE
binding.textSettingDescription.visibility = View.GONE
}
}

@ -4,25 +4,19 @@
package org.yuzu.yuzu_emu.features.settings.ui.viewholder
import android.view.View
import android.widget.TextView
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.databinding.ListItemSettingsHeaderBinding
import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter
class HeaderViewHolder(itemView: View, adapter: SettingsAdapter) :
SettingViewHolder(itemView, adapter) {
private lateinit var headerName: TextView
class HeaderViewHolder(val binding: ListItemSettingsHeaderBinding, adapter: SettingsAdapter) :
SettingViewHolder(binding.root, adapter) {
init {
itemView.setOnClickListener(null)
}
override fun findViews(root: View) {
headerName = root.findViewById(R.id.text_header_name)
}
override fun bind(item: SettingsItem) {
headerName.setText(item.nameId)
binding.textHeaderName.setText(item.nameId)
}
override fun onClick(clicked: View) {

@ -13,16 +13,8 @@ abstract class SettingViewHolder(itemView: View, protected val adapter: Settings
init {
itemView.setOnClickListener(this)
findViews(itemView)
}
/**
* Gets handles to all this ViewHolder's child views using their XML-defined identifiers.
*
* @param root The newly inflated top-level view.
*/
protected abstract fun findViews(root: View)
/**
* Called by the adapter to set this ViewHolder's child views to display the list item
* it must now represent.

@ -4,40 +4,32 @@
package org.yuzu.yuzu_emu.features.settings.ui.viewholder
import android.view.View
import android.widget.TextView
import org.yuzu.yuzu_emu.R
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.SingleChoiceSetting
import org.yuzu.yuzu_emu.features.settings.model.view.StringSingleChoiceSetting
import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter
class SingleChoiceViewHolder(itemView: View, adapter: SettingsAdapter) :
SettingViewHolder(itemView, adapter) {
class SingleChoiceViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAdapter) :
SettingViewHolder(binding.root, adapter) {
private lateinit var item: SettingsItem
private lateinit var textSettingName: TextView
private lateinit var textSettingDescription: TextView
override fun findViews(root: View) {
textSettingName = root.findViewById(R.id.text_setting_name)
textSettingDescription = root.findViewById(R.id.text_setting_description)
}
override fun bind(item: SettingsItem) {
this.item = item
textSettingName.setText(item.nameId)
textSettingDescription.visibility = View.VISIBLE
binding.textSettingName.setText(item.nameId)
binding.textSettingDescription.visibility = View.VISIBLE
if (item.descriptionId!! > 0) {
textSettingDescription.setText(item.descriptionId)
binding.textSettingDescription.setText(item.descriptionId)
} else if (item is SingleChoiceSetting) {
val resMgr = textSettingDescription.context.resources
val resMgr = binding.textSettingDescription.context.resources
val values = resMgr.getIntArray(item.valuesId)
for (i in values.indices) {
if (values[i] == item.selectedValue) {
textSettingDescription.text = resMgr.getStringArray(item.choicesId)[i]
binding.textSettingDescription.text = resMgr.getStringArray(item.choicesId)[i]
}
}
} else {
textSettingDescription.visibility = View.GONE
binding.textSettingDescription.visibility = View.GONE
}
}

@ -4,30 +4,23 @@
package org.yuzu.yuzu_emu.features.settings.ui.viewholder
import android.view.View
import android.widget.TextView
import org.yuzu.yuzu_emu.R
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.SliderSetting
import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter
class SliderViewHolder(itemView: View, adapter: SettingsAdapter) : SettingViewHolder(itemView, adapter) {
class SliderViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAdapter) :
SettingViewHolder(binding.root, adapter) {
private lateinit var item: SliderSetting
private lateinit var textSettingName: TextView
private lateinit var textSettingDescription: TextView
override fun findViews(root: View) {
textSettingName = root.findViewById(R.id.text_setting_name)
textSettingDescription = root.findViewById(R.id.text_setting_description)
}
override fun bind(item: SettingsItem) {
this.item = item as SliderSetting
textSettingName.setText(item.nameId)
binding.textSettingName.setText(item.nameId)
if (item.descriptionId!! > 0) {
textSettingDescription.setText(item.descriptionId)
textSettingDescription.visibility = View.VISIBLE
binding.textSettingDescription.setText(item.descriptionId)
binding.textSettingDescription.visibility = View.VISIBLE
} else {
textSettingDescription.visibility = View.GONE
binding.textSettingDescription.visibility = View.GONE
}
}

@ -4,31 +4,23 @@
package org.yuzu.yuzu_emu.features.settings.ui.viewholder
import android.view.View
import android.widget.TextView
import org.yuzu.yuzu_emu.R
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
class SubmenuViewHolder(itemView: View, adapter: SettingsAdapter) :
SettingViewHolder(itemView, adapter) {
class SubmenuViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAdapter) :
SettingViewHolder(binding.root, adapter) {
private lateinit var item: SubmenuSetting
private lateinit var textSettingName: TextView
private lateinit var textSettingDescription: TextView
override fun findViews(root: View) {
textSettingName = root.findViewById(R.id.text_setting_name)
textSettingDescription = root.findViewById(R.id.text_setting_description)
}
override fun bind(item: SettingsItem) {
this.item = item as SubmenuSetting
textSettingName.setText(item.nameId)
binding.textSettingName.setText(item.nameId)
if (item.descriptionId!! > 0) {
textSettingDescription.setText(item.descriptionId)
textSettingDescription.visibility = View.VISIBLE
binding.textSettingDescription.setText(item.descriptionId)
binding.textSettingDescription.visibility = View.VISIBLE
} else {
textSettingDescription.visibility = View.GONE
binding.textSettingDescription.visibility = View.GONE
}
}

@ -5,43 +5,31 @@ package org.yuzu.yuzu_emu.features.settings.ui.viewholder
import android.view.View
import android.widget.CompoundButton
import android.widget.TextView
import com.google.android.material.materialswitch.MaterialSwitch
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.databinding.ListItemSettingSwitchBinding
import org.yuzu.yuzu_emu.features.settings.model.view.SwitchSetting
import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter
class SwitchSettingViewHolder(itemView: View, adapter: SettingsAdapter) :
SettingViewHolder(itemView, adapter) {
private lateinit var item: SwitchSetting
private lateinit var textSettingName: TextView
private lateinit var textSettingDescription: TextView
private lateinit var switch: MaterialSwitch
override fun findViews(root: View) {
textSettingName = root.findViewById(R.id.text_setting_name)
textSettingDescription = root.findViewById(R.id.text_setting_description)
switch = root.findViewById(R.id.switch_widget)
}
class SwitchSettingViewHolder(val binding: ListItemSettingSwitchBinding, adapter: SettingsAdapter) :
SettingViewHolder(binding.root, adapter) {
override fun bind(item: SettingsItem) {
this.item = item as SwitchSetting
textSettingName.setText(item.nameId)
val setting = item as SwitchSetting
binding.textSettingName.setText(item.nameId)
if (item.descriptionId!! > 0) {
textSettingDescription.setText(item.descriptionId)
textSettingDescription.visibility = View.VISIBLE
binding.textSettingDescription.setText(item.descriptionId)
binding.textSettingDescription.visibility = View.VISIBLE
} else {
textSettingDescription.text = ""
textSettingDescription.visibility = View.GONE
binding.textSettingDescription.text = ""
binding.textSettingDescription.visibility = View.GONE
}
switch.isChecked = this.item.isChecked
switch.setOnCheckedChangeListener { _: CompoundButton, _: Boolean ->
adapter.onBooleanClick(item, bindingAdapterPosition, switch.isChecked)
binding.switchWidget.isChecked = setting.isChecked
binding.switchWidget.setOnCheckedChangeListener { _: CompoundButton, _: Boolean ->
adapter.onBooleanClick(item, bindingAdapterPosition, binding.switchWidget.isChecked)
}
}
override fun onClick(clicked: View) {
switch.toggle()
binding.switchWidget.toggle()
}
}

@ -10,8 +10,6 @@ import android.graphics.Color
import android.os.Bundle
import android.os.Handler
import android.view.*
import android.widget.Button
import android.widget.TextView
import android.widget.Toast
import androidx.fragment.app.Fragment
import androidx.localbroadcastmanager.content.LocalBroadcastManager
@ -20,8 +18,8 @@ import org.yuzu.yuzu_emu.NativeLibrary
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.YuzuApplication
import org.yuzu.yuzu_emu.activities.EmulationActivity
import org.yuzu.yuzu_emu.databinding.FragmentEmulationBinding
import org.yuzu.yuzu_emu.features.settings.model.Settings
import org.yuzu.yuzu_emu.overlay.InputOverlay
import org.yuzu.yuzu_emu.utils.DirectoryInitialization
import org.yuzu.yuzu_emu.utils.DirectoryInitialization.DirectoryInitializationState
import org.yuzu.yuzu_emu.utils.DirectoryStateReceiver
@ -29,13 +27,14 @@ import org.yuzu.yuzu_emu.utils.Log
class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.FrameCallback {
private lateinit var preferences: SharedPreferences
private var inputOverlay: InputOverlay? = null
private lateinit var emulationState: EmulationState
private var directoryStateReceiver: DirectoryStateReceiver? = null
private var emulationActivity: EmulationActivity? = null
private lateinit var perfStats: TextView
private var perfStatsUpdater: (() -> Unit)? = null
private var _binding: FragmentEmulationBinding? = null
private val binding get() = _binding!!
override fun onAttach(context: Context) {
super.onAttach(context)
if (context is EmulationActivity) {
@ -66,22 +65,19 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val contents = inflater.inflate(R.layout.fragment_emulation, container, false)
val surfaceView = contents.findViewById<SurfaceView>(R.id.surface_emulation)
surfaceView.holder.addCallback(this)
inputOverlay = contents.findViewById(R.id.surface_input_overlay)
perfStats = contents.findViewById(R.id.show_fps_text)
perfStats.setTextColor(Color.YELLOW)
val doneButton = contents.findViewById<Button>(R.id.done_control_config)
doneButton?.setOnClickListener { stopConfiguringControls() }
): View {
_binding = FragmentEmulationBinding.inflate(layoutInflater)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
binding.surfaceEmulation.holder.addCallback(this)
binding.showFpsText.setTextColor(Color.YELLOW)
binding.doneControlConfig.setOnClickListener { stopConfiguringControls() }
// Setup overlay.
resetInputOverlay()
updateShowFpsOverlay()
// The new Surface created here will get passed to the native code via onSurfaceChanged.
return contents
}
override fun onResume() {
@ -108,6 +104,11 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram
super.onPause()
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
override fun onDetach() {
NativeLibrary.clearEmulationActivity()
super.onDetach()
@ -144,7 +145,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram
}
fun refreshInputOverlay() {
inputOverlay!!.refreshControls()
binding.surfaceInputOverlay.refreshControls()
}
fun resetInputOverlay() {
@ -152,7 +153,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram
preferences.edit()
.putInt(Settings.PREF_CONTROL_SCALE, 50)
.apply()
inputOverlay!!.resetButtonPlacement()
binding.surfaceInputOverlay.resetButtonPlacement()
}
private fun updateShowFpsOverlay() {
@ -165,17 +166,17 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram
perfStatsUpdater = {
val perfStats = NativeLibrary.GetPerfStats()
if (perfStats[FPS] > 0) {
this.perfStats.text = String.format("FPS: %.1f", perfStats[FPS])
binding.showFpsText.text = String.format("FPS: %.1f", perfStats[FPS])
}
perfStatsUpdateHandler.postDelayed(perfStatsUpdater!!, 100)
}
perfStatsUpdateHandler.post(perfStatsUpdater!!)
perfStats.visibility = View.VISIBLE
binding.showFpsText.visibility = View.VISIBLE
} else {
if (perfStatsUpdater != null) {
perfStatsUpdateHandler.removeCallbacks(perfStatsUpdater!!)
}
perfStats.visibility = View.GONE
binding.showFpsText.visibility = View.GONE
}
}
@ -203,18 +204,17 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram
}
fun startConfiguringControls() {
requireView().findViewById<View>(R.id.done_control_config).visibility =
View.VISIBLE
inputOverlay!!.setIsInEditMode(true)
binding.doneControlConfig.visibility = View.VISIBLE
binding.surfaceInputOverlay.setIsInEditMode(true)
}
fun stopConfiguringControls() {
requireView().findViewById<View>(R.id.done_control_config).visibility = View.GONE
inputOverlay!!.setIsInEditMode(false)
binding.doneControlConfig.visibility = View.GONE
binding.surfaceInputOverlay.setIsInEditMode(false)
}
val isConfiguringControls: Boolean
get() = inputOverlay!!.isInEditMode
get() = binding.surfaceInputOverlay.isInEditMode
private class EmulationState(private val mGamePath: String?) {
private var state: State

@ -10,30 +10,29 @@ import android.os.Bundle
import android.view.Menu
import android.view.MenuItem
import android.view.View
import android.widget.FrameLayout
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.Toolbar
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
import androidx.core.view.ViewCompat
import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.updatePadding
import com.google.android.material.appbar.AppBarLayout
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import org.yuzu.yuzu_emu.NativeLibrary
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.activities.EmulationActivity
import org.yuzu.yuzu_emu.databinding.ActivityMainBinding
import org.yuzu.yuzu_emu.features.settings.ui.SettingsActivity
import org.yuzu.yuzu_emu.model.GameProvider
import org.yuzu.yuzu_emu.ui.platform.PlatformGamesFragment
import org.yuzu.yuzu_emu.utils.*
class MainActivity : AppCompatActivity(), MainView {
private lateinit var toolbar: Toolbar
private var platformGamesFragment: PlatformGamesFragment? = null
private val presenter = MainPresenter(this)
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
val splashScreen = installSplashScreen()
splashScreen.setKeepOnScreenCondition { !DirectoryInitialization.areDirectoriesReady() }
@ -41,12 +40,13 @@ class MainActivity : AppCompatActivity(), MainView {
ThemeHelper.setTheme(this)
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
WindowCompat.setDecorFitsSystemWindows(window, false)
findViews()
setSupportActionBar(toolbar)
setSupportActionBar(binding.toolbarMain)
presenter.onCreate()
if (savedInstanceState == null) {
StartupHandler.handleInit(this)
@ -81,11 +81,6 @@ class MainActivity : AppCompatActivity(), MainView {
presenter.addDirIfNeeded(AddDirectoryHelper(this))
}
// TODO: Replace with view binding
private fun findViews() {
toolbar = findViewById(R.id.toolbar_main)
}
override fun onCreateOptionsMenu(menu: Menu): Boolean {
menuInflater.inflate(R.menu.menu_game_grid, menu)
return true
@ -95,7 +90,7 @@ class MainActivity : AppCompatActivity(), MainView {
* MainView
*/
override fun setVersionString(version: String) {
toolbar.subtitle = version
binding.toolbarMain.subtitle = version
}
override fun refresh() {
@ -247,12 +242,10 @@ class MainActivity : AppCompatActivity(), MainView {
}
private fun setInsets() {
val appBar = findViewById<AppBarLayout>(R.id.appbar_main)
val frame = findViewById<FrameLayout>(R.id.games_platform_frame)
ViewCompat.setOnApplyWindowInsetsListener(frame) { view: View, windowInsets: WindowInsetsCompat ->
ViewCompat.setOnApplyWindowInsetsListener(binding.gamesPlatformFrame) { view: View, windowInsets: WindowInsetsCompat ->
val insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
view.updatePadding(left = insets.left, right = insets.right)
InsetsHelper.insetAppBar(insets, appBar)
InsetsHelper.insetAppBar(insets, binding.appbarMain)
windowInsets
}
}

@ -9,35 +9,33 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.ViewTreeObserver.OnGlobalLayoutListener
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.updatePadding
import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
import com.google.android.material.color.MaterialColors
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.YuzuApplication
import org.yuzu.yuzu_emu.adapters.GameAdapter
import org.yuzu.yuzu_emu.databinding.FragmentGridBinding
class PlatformGamesFragment : Fragment(), PlatformGamesView {
private val presenter = PlatformGamesPresenter(this)
private var adapter: GameAdapter? = null
private lateinit var recyclerView: RecyclerView
private lateinit var textView: TextView
private var _binding: FragmentGridBinding? = null
private val binding get() = _binding!!
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val rootView = inflater.inflate(R.layout.fragment_grid, container, false)
findViews(rootView)
): View {
presenter.onCreateView()
return rootView
_binding = FragmentGridBinding.inflate(inflater)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
@ -58,30 +56,34 @@ class PlatformGamesFragment : Fragment(), PlatformGamesView {
}
view.viewTreeObserver.removeOnGlobalLayoutListener(this)
val layoutManager = GridLayoutManager(activity, columns)
recyclerView.layoutManager = layoutManager
recyclerView.adapter = adapter
binding.gridGames.layoutManager = layoutManager
binding.gridGames.adapter = adapter
}
})
}
// Add swipe down to refresh gesture
val pullToRefresh = view.findViewById<SwipeRefreshLayout>(R.id.swipe_refresh)
pullToRefresh.setOnRefreshListener {
binding.swipeRefresh.setOnRefreshListener {
refresh()
pullToRefresh.isRefreshing = false
binding.swipeRefresh.isRefreshing = false
}
// Set theme color to the refresh animation's background
pullToRefresh.setProgressBackgroundColorSchemeColor(
MaterialColors.getColor(pullToRefresh, R.attr.colorPrimary)
binding.swipeRefresh.setProgressBackgroundColorSchemeColor(
MaterialColors.getColor(binding.swipeRefresh, R.attr.colorPrimary)
)
pullToRefresh.setColorSchemeColors(
MaterialColors.getColor(pullToRefresh, R.attr.colorOnPrimary)
binding.swipeRefresh.setColorSchemeColors(
MaterialColors.getColor(binding.swipeRefresh, R.attr.colorOnPrimary)
)
setInsets()
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
override fun refresh() {
val databaseHelper = YuzuApplication.databaseHelper
databaseHelper!!.scanLibrary(databaseHelper.writableDatabase)
@ -97,17 +99,12 @@ class PlatformGamesFragment : Fragment(), PlatformGamesView {
}
private fun updateTextView() {
textView.visibility =
binding.gamelistEmptyText.visibility =
if (adapter!!.itemCount == 0) View.VISIBLE else View.GONE
}
private fun findViews(root: View) {
recyclerView = root.findViewById(R.id.grid_games)
textView = root.findViewById(R.id.gamelist_empty_text)
}
private fun setInsets() {
ViewCompat.setOnApplyWindowInsetsListener(recyclerView) { view: View, windowInsets: WindowInsetsCompat ->
ViewCompat.setOnApplyWindowInsetsListener(binding.gridGames) { view: View, windowInsets: WindowInsetsCompat ->
val insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
view.updatePadding(bottom = insets.bottom)
windowInsets

@ -3,33 +3,14 @@
package org.yuzu.yuzu_emu.viewholders
import android.view.View
import android.widget.ImageView
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.databinding.CardGameBinding
import org.yuzu.yuzu_emu.model.Game
/**
* A simple class that stores references to views so that the GameAdapter doesn't need to
* keep calling findViewById(), which is expensive.
*/
class GameViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
var imageIcon: ImageView
var textGameTitle: TextView
var textGameCaption: TextView
var gameId: String? = null
// TODO Not need any of this stuff. Currently only the properties dialog needs it.
var path: String? = null
var title: String? = null
var description: String? = null
var regions: String? = null
var company: String? = null
class GameViewHolder(val binding: CardGameBinding) : RecyclerView.ViewHolder(binding.root) {
lateinit var game: Game
init {
itemView.tag = this
imageIcon = itemView.findViewById(R.id.image_game_screen)
textGameTitle = itemView.findViewById(R.id.text_game_title)
textGameCaption = itemView.findViewById(R.id.text_game_caption)
}
}