From 87f4c3f105424ef084be1801bca97ff1620b5869 Mon Sep 17 00:00:00 2001 From: Charles Lombardo Date: Tue, 7 Mar 2023 13:40:24 -0500 Subject: [PATCH] android: Convert SettingsAdapter to Kotlin Update SettingsAdapter.kt --- .../features/settings/ui/SettingsAdapter.java | 366 ------------------ .../features/settings/ui/SettingsAdapter.kt | 313 +++++++++++++++ .../app/src/main/res/values/strings.xml | 2 + 3 files changed, 315 insertions(+), 366 deletions(-) delete mode 100644 src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsAdapter.java create mode 100644 src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsAdapter.kt diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsAdapter.java b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsAdapter.java deleted file mode 100644 index 47e73bfe2..000000000 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsAdapter.java +++ /dev/null @@ -1,366 +0,0 @@ -package org.yuzu.yuzu_emu.features.settings.ui; - -import android.content.Context; -import android.content.DialogInterface; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.DatePicker; -import android.widget.SeekBar; -import android.widget.TextView; -import android.widget.TimePicker; - -import androidx.appcompat.app.AlertDialog; -import androidx.recyclerview.widget.RecyclerView; - -import org.yuzu.yuzu_emu.R; -import org.yuzu.yuzu_emu.features.settings.model.FloatSetting; -import org.yuzu.yuzu_emu.features.settings.model.IntSetting; -import org.yuzu.yuzu_emu.features.settings.model.StringSetting; -import org.yuzu.yuzu_emu.features.settings.model.view.CheckBoxSetting; -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.model.view.SingleChoiceSetting; -import org.yuzu.yuzu_emu.features.settings.model.view.SliderSetting; -import org.yuzu.yuzu_emu.features.settings.model.view.StringSingleChoiceSetting; -import org.yuzu.yuzu_emu.features.settings.model.view.SubmenuSetting; -import org.yuzu.yuzu_emu.features.settings.ui.viewholder.CheckBoxSettingViewHolder; -import org.yuzu.yuzu_emu.features.settings.ui.viewholder.DateTimeViewHolder; -import org.yuzu.yuzu_emu.features.settings.ui.viewholder.HeaderViewHolder; -import org.yuzu.yuzu_emu.features.settings.ui.viewholder.SettingViewHolder; -import org.yuzu.yuzu_emu.features.settings.ui.viewholder.SingleChoiceViewHolder; -import org.yuzu.yuzu_emu.features.settings.ui.viewholder.SliderViewHolder; -import org.yuzu.yuzu_emu.features.settings.ui.viewholder.SubmenuViewHolder; -import org.yuzu.yuzu_emu.utils.Log; - -import java.util.ArrayList; - -public final class SettingsAdapter extends RecyclerView.Adapter - implements DialogInterface.OnClickListener, SeekBar.OnSeekBarChangeListener { - private SettingsFragmentView mView; - private Context mContext; - private ArrayList mSettings; - - private SettingsItem mClickedItem; - private int mClickedPosition; - private int mSeekbarProgress; - - private AlertDialog mDialog; - private TextView mTextSliderValue; - - public SettingsAdapter(SettingsFragmentView view, Context context) { - mView = view; - mContext = context; - mClickedPosition = -1; - } - - @Override - public SettingViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { - View view; - LayoutInflater inflater = LayoutInflater.from(parent.getContext()); - - switch (viewType) { - case SettingsItem.TYPE_HEADER: - view = inflater.inflate(R.layout.list_item_settings_header, parent, false); - return new HeaderViewHolder(view, this); - - case SettingsItem.TYPE_CHECKBOX: - view = inflater.inflate(R.layout.list_item_setting_checkbox, parent, false); - return new CheckBoxSettingViewHolder(view, this); - - case SettingsItem.TYPE_SINGLE_CHOICE: - case SettingsItem.TYPE_STRING_SINGLE_CHOICE: - view = inflater.inflate(R.layout.list_item_setting, parent, false); - return new SingleChoiceViewHolder(view, this); - - case SettingsItem.TYPE_SLIDER: - view = inflater.inflate(R.layout.list_item_setting, parent, false); - return new SliderViewHolder(view, this); - - case SettingsItem.TYPE_SUBMENU: - view = inflater.inflate(R.layout.list_item_setting, parent, false); - return new SubmenuViewHolder(view, this); - - case SettingsItem.TYPE_DATETIME_SETTING: - view = inflater.inflate(R.layout.list_item_setting, parent, false); - return new DateTimeViewHolder(view, this); - - default: - Log.error("[SettingsAdapter] Invalid view type: " + viewType); - return null; - } - } - - @Override - public void onBindViewHolder(SettingViewHolder holder, int position) { - holder.bind(getItem(position)); - } - - private SettingsItem getItem(int position) { - return mSettings.get(position); - } - - @Override - public int getItemCount() { - if (mSettings != null) { - return mSettings.size(); - } else { - return 0; - } - } - - @Override - public int getItemViewType(int position) { - return getItem(position).getType(); - } - - public void setSettings(ArrayList settings) { - mSettings = settings; - notifyDataSetChanged(); - } - - public void onBooleanClick(CheckBoxSetting item, int position, boolean checked) { - IntSetting setting = item.setChecked(checked); - notifyItemChanged(position); - - if (setting != null) { - mView.putSetting(setting); - } - - mView.onSettingChanged(); - } - - public void onSingleChoiceClick(SingleChoiceSetting item) { - mClickedItem = item; - - int value = getSelectionForSingleChoiceValue(item); - - AlertDialog.Builder builder = new AlertDialog.Builder(mView.getActivity()); - - builder.setTitle(item.getNameId()); - builder.setSingleChoiceItems(item.getChoicesId(), value, this); - - mDialog = builder.show(); - } - - public void onSingleChoiceClick(SingleChoiceSetting item, int position) { - mClickedPosition = position; - onSingleChoiceClick(item); - } - - public void onStringSingleChoiceClick(StringSingleChoiceSetting item) { - mClickedItem = item; - - AlertDialog.Builder builder = new AlertDialog.Builder(mView.getActivity()); - - builder.setTitle(item.getNameId()); - builder.setSingleChoiceItems(item.getChoicesId(), item.getSelectValueIndex(), this); - - mDialog = builder.show(); - } - - public void onStringSingleChoiceClick(StringSingleChoiceSetting item, int position) { - mClickedPosition = position; - onStringSingleChoiceClick(item); - } - - DialogInterface.OnClickListener defaultCancelListener = (dialog, which) -> closeDialog(); - - public void onDateTimeClick(DateTimeSetting item, int position) { - mClickedItem = item; - mClickedPosition = position; - - AlertDialog.Builder builder = new AlertDialog.Builder(mView.getActivity()); - - LayoutInflater inflater = LayoutInflater.from(mView.getActivity()); - View view = inflater.inflate(R.layout.sysclock_datetime_picker, null); - - DatePicker dp = view.findViewById(R.id.date_picker); - TimePicker tp = view.findViewById(R.id.time_picker); - - //set date and time to substrings of settingValue; format = 2018-12-24 04:20:69 (alright maybe not that 69) - String settingValue = item.getValue(); - dp.updateDate(Integer.parseInt(settingValue.substring(0, 4)), Integer.parseInt(settingValue.substring(5, 7)) - 1, Integer.parseInt(settingValue.substring(8, 10))); - - tp.setIs24HourView(true); - tp.setHour(Integer.parseInt(settingValue.substring(11, 13))); - tp.setMinute(Integer.parseInt(settingValue.substring(14, 16))); - - DialogInterface.OnClickListener ok = (dialog, which) -> { - //set it - int year = dp.getYear(); - if (year < 2000) { - year = 2000; - } - String month = ("00" + (dp.getMonth() + 1)).substring(String.valueOf(dp.getMonth() + 1).length()); - String day = ("00" + dp.getDayOfMonth()).substring(String.valueOf(dp.getDayOfMonth()).length()); - String hr = ("00" + tp.getHour()).substring(String.valueOf(tp.getHour()).length()); - String min = ("00" + tp.getMinute()).substring(String.valueOf(tp.getMinute()).length()); - String datetime = year + "-" + month + "-" + day + " " + hr + ":" + min + ":01"; - - StringSetting setting = item.setSelectedValue(datetime); - if (setting != null) { - mView.putSetting(setting); - } - - mView.onSettingChanged(); - - mClickedItem = null; - closeDialog(); - }; - - builder.setView(view); - builder.setPositiveButton(android.R.string.ok, ok); - builder.setNegativeButton(android.R.string.cancel, defaultCancelListener); - mDialog = builder.show(); - } - - public void onSliderClick(SliderSetting item, int position) { - mClickedItem = item; - mClickedPosition = position; - mSeekbarProgress = item.getSelectedValue(); - AlertDialog.Builder builder = new AlertDialog.Builder(mView.getActivity()); - - LayoutInflater inflater = LayoutInflater.from(mView.getActivity()); - View view = inflater.inflate(R.layout.dialog_seekbar, null); - - SeekBar seekbar = view.findViewById(R.id.seekbar); - - builder.setTitle(item.getNameId()); - builder.setView(view); - builder.setPositiveButton(android.R.string.ok, this); - builder.setNegativeButton(android.R.string.cancel, defaultCancelListener); - builder.setNeutralButton(R.string.slider_default, (DialogInterface dialog, int which) -> { - seekbar.setProgress(item.getDefaultValue()); - onClick(dialog, which); - }); - mDialog = builder.show(); - - mTextSliderValue = view.findViewById(R.id.text_value); - mTextSliderValue.setText(String.valueOf(mSeekbarProgress)); - - TextView units = view.findViewById(R.id.text_units); - units.setText(item.getUnits()); - - seekbar.setMin(item.getMin()); - seekbar.setMax(item.getMax()); - seekbar.setProgress(mSeekbarProgress); - - seekbar.setOnSeekBarChangeListener(this); - } - - public void onSubmenuClick(SubmenuSetting item) { - mView.loadSubMenu(item.getMenuKey()); - } - - @Override - public void onClick(DialogInterface dialog, int which) { - if (mClickedItem instanceof SingleChoiceSetting) { - SingleChoiceSetting scSetting = (SingleChoiceSetting) mClickedItem; - - int value = getValueForSingleChoiceSelection(scSetting, which); - if (scSetting.getSelectedValue() != value) { - mView.onSettingChanged(); - } - - // Get the backing Setting, which may be null (if for example it was missing from the file) - IntSetting setting = scSetting.setSelectedValue(value); - if (setting != null) { - mView.putSetting(setting); - } - - closeDialog(); - } else if (mClickedItem instanceof StringSingleChoiceSetting) { - StringSingleChoiceSetting scSetting = (StringSingleChoiceSetting) mClickedItem; - String value = scSetting.getValueAt(which); - if (!scSetting.getSelectedValue().equals(value)) - mView.onSettingChanged(); - - StringSetting setting = scSetting.setSelectedValue(value); - if (setting != null) { - mView.putSetting(setting); - } - - closeDialog(); - } else if (mClickedItem instanceof SliderSetting) { - SliderSetting sliderSetting = (SliderSetting) mClickedItem; - if (sliderSetting.getSelectedValue() != mSeekbarProgress) { - mView.onSettingChanged(); - } - - if (sliderSetting.getSetting() instanceof FloatSetting) { - float value = (float) mSeekbarProgress; - - FloatSetting setting = sliderSetting.setSelectedValue(value); - if (setting != null) { - mView.putSetting(setting); - } - } else { - IntSetting setting = sliderSetting.setSelectedValue(mSeekbarProgress); - if (setting != null) { - mView.putSetting(setting); - } - } - - closeDialog(); - } - - mClickedItem = null; - mSeekbarProgress = -1; - } - - public void closeDialog() { - if (mDialog != null) { - if (mClickedPosition != -1) { - notifyItemChanged(mClickedPosition); - mClickedPosition = -1; - } - mDialog.dismiss(); - mDialog = null; - } - } - - @Override - public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { - mSeekbarProgress = progress; - mTextSliderValue.setText(String.valueOf(mSeekbarProgress)); - } - - @Override - public void onStartTrackingTouch(SeekBar seekBar) { - } - - @Override - public void onStopTrackingTouch(SeekBar seekBar) { - } - - private int getValueForSingleChoiceSelection(SingleChoiceSetting item, int which) { - int valuesId = item.getValuesId(); - - if (valuesId > 0) { - int[] valuesArray = mContext.getResources().getIntArray(valuesId); - return valuesArray[which]; - } else { - return which; - } - } - - private int getSelectionForSingleChoiceValue(SingleChoiceSetting item) { - int value = item.getSelectedValue(); - int valuesId = item.getValuesId(); - - if (valuesId > 0) { - int[] valuesArray = mContext.getResources().getIntArray(valuesId); - for (int index = 0; index < valuesArray.length; index++) { - int current = valuesArray[index]; - if (current == value) { - return index; - } - } - } else { - return value; - } - - return -1; - } -} diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsAdapter.kt new file mode 100644 index 000000000..f35023b01 --- /dev/null +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsAdapter.kt @@ -0,0 +1,313 @@ +package org.yuzu.yuzu_emu.features.settings.ui + +import android.content.Context +import android.content.DialogInterface +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 +import androidx.recyclerview.widget.RecyclerView +import com.google.android.material.datepicker.MaterialDatePicker +import com.google.android.material.dialog.MaterialAlertDialogBuilder +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.features.settings.model.FloatSetting +import org.yuzu.yuzu_emu.features.settings.model.view.* +import org.yuzu.yuzu_emu.features.settings.ui.viewholder.* + +class SettingsAdapter( + private val fragmentView: SettingsFragmentView, + private val context: Context +) : RecyclerView.Adapter(), DialogInterface.OnClickListener { + private var settings: ArrayList? = null + private var clickedItem: SettingsItem? = null + private var clickedPosition: Int + private var dialog: AlertDialog? = null + private var sliderProgress = 0 + private var textSliderValue: TextView? = null + + private var defaultCancelListener = + DialogInterface.OnClickListener { _: DialogInterface?, _: Int -> closeDialog() } + + init { + clickedPosition = -1 + } + + 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) + } + SettingsItem.TYPE_CHECKBOX -> { + view = inflater.inflate(R.layout.list_item_setting_checkbox, parent, false) + CheckBoxSettingViewHolder(view, this) + } + SettingsItem.TYPE_SINGLE_CHOICE, SettingsItem.TYPE_STRING_SINGLE_CHOICE -> { + view = inflater.inflate(R.layout.list_item_setting, parent, false) + SingleChoiceViewHolder(view, this) + } + SettingsItem.TYPE_SLIDER -> { + view = inflater.inflate(R.layout.list_item_setting, parent, false) + SliderViewHolder(view, this) + } + SettingsItem.TYPE_SUBMENU -> { + view = inflater.inflate(R.layout.list_item_setting, parent, false) + SubmenuViewHolder(view, this) + } + SettingsItem.TYPE_DATETIME_SETTING -> { + view = inflater.inflate(R.layout.list_item_setting, parent, false) + DateTimeViewHolder(view, 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) + } + } + } + + override fun onBindViewHolder(holder: SettingViewHolder, position: Int) { + holder.bind(getItem(position)) + } + + private fun getItem(position: Int): SettingsItem { + return settings!![position] + } + + override fun getItemCount(): Int { + return if (settings != null) { + settings!!.size + } else { + 0 + } + } + + override fun getItemViewType(position: Int): Int { + return getItem(position).type + } + + fun setSettings(settings: ArrayList?) { + this.settings = settings + notifyDataSetChanged() + } + + fun onBooleanClick(item: CheckBoxSetting, position: Int, checked: Boolean) { + val setting = item.setChecked(checked) + notifyItemChanged(position) + if (setting != null) { + fragmentView.putSetting(setting) + } + fragmentView.onSettingChanged() + } + + private fun onSingleChoiceClick(item: SingleChoiceSetting) { + clickedItem = item + val value = getSelectionForSingleChoiceValue(item) + dialog = MaterialAlertDialogBuilder(context) + .setTitle(item.nameId) + .setSingleChoiceItems(item.choicesId, value, this) + .show() + } + + fun onSingleChoiceClick(item: SingleChoiceSetting, position: Int) { + clickedPosition = position + onSingleChoiceClick(item) + } + + private fun onStringSingleChoiceClick(item: StringSingleChoiceSetting) { + clickedItem = item + dialog = MaterialAlertDialogBuilder(context) + .setTitle(item.nameId) + .setSingleChoiceItems(item.choicesId, item.selectValueIndex, this) + .show() + } + + fun onStringSingleChoiceClick(item: StringSingleChoiceSetting, position: Int) { + clickedPosition = position + onStringSingleChoiceClick(item) + } + + fun onDateTimeClick(item: DateTimeSetting, position: Int) { + clickedItem = item + clickedPosition = position + val storedTime = java.lang.Long.decode(item.value) * 1000 + + // Helper to extract hour and minute from epoch time + val calendar: Calendar = Calendar.getInstance() + calendar.timeInMillis = storedTime + calendar.timeZone = TimeZone.getTimeZone("UTC") + + var timeFormat: Int = TimeFormat.CLOCK_12H + if (DateFormat.is24HourFormat(fragmentView.fragmentActivity)) { + timeFormat = TimeFormat.CLOCK_24H + } + + val datePicker: MaterialDatePicker = MaterialDatePicker.Builder.datePicker() + .setSelection(storedTime) + .setTitleText(R.string.select_rtc_date) + .build() + val timePicker: MaterialTimePicker = MaterialTimePicker.Builder() + .setTimeFormat(timeFormat) + .setHour(calendar.get(Calendar.HOUR_OF_DAY)) + .setMinute(calendar.get(Calendar.MINUTE)) + .setTitleText(R.string.select_rtc_time) + .build() + + datePicker.addOnPositiveButtonClickListener { + timePicker.show( + fragmentView.fragmentActivity.supportFragmentManager, + "TimePicker" + ) + } + timePicker.addOnPositiveButtonClickListener { + var epochTime: Long = datePicker.selection!! / 1000 + epochTime += timePicker.hour.toLong() * 60 * 60 + epochTime += timePicker.minute.toLong() * 60 + val rtcString = epochTime.toString() + if (item.value != rtcString) { + notifyItemChanged(clickedPosition) + fragmentView.onSettingChanged() + } + item.setSelectedValue(rtcString) + clickedItem = null + } + datePicker.show(fragmentView.fragmentActivity.supportFragmentManager, "DatePicker") + } + + fun onSliderClick(item: SliderSetting, position: Int) { + clickedItem = item + clickedPosition = position + sliderProgress = item.selectedValue + + val inflater = LayoutInflater.from(context) + val sliderLayout = inflater.inflate(R.layout.dialog_slider, null) + val sliderView = sliderLayout.findViewById(R.id.slider) + + textSliderValue = sliderLayout.findViewById(R.id.text_value) + textSliderValue!!.text = sliderProgress.toString() + val units = sliderLayout.findViewById(R.id.text_units) + units.text = item.units + + sliderView.apply { + valueFrom = item.min.toFloat() + valueTo = item.max.toFloat() + value = sliderProgress.toFloat() + addOnChangeListener { _: Slider, value: Float, _: Boolean -> + sliderProgress = value.toInt() + textSliderValue!!.text = sliderProgress.toString() + } + } + + dialog = MaterialAlertDialogBuilder(context) + .setTitle(item.nameId) + .setView(sliderLayout) + .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() + onClick(dialog, which) + } + .show() + } + + fun onSubmenuClick(item: SubmenuSetting) { + fragmentView.loadSubMenu(item.menuKey) + } + + override fun onClick(dialog: DialogInterface, which: Int) { + when (clickedItem) { + is SingleChoiceSetting -> { + val scSetting = clickedItem as SingleChoiceSetting + val value = getValueForSingleChoiceSelection(scSetting, which) + if (scSetting.selectedValue != value) { + fragmentView.onSettingChanged() + } + + // Get the backing Setting, which may be null (if for example it was missing from the file) + val setting = scSetting.setSelectedValue(value) + if (setting != null) { + fragmentView.putSetting(setting) + } + closeDialog() + } + is StringSingleChoiceSetting -> { + val scSetting = clickedItem as StringSingleChoiceSetting + val value = scSetting.getValueAt(which) + if (scSetting.selectedValue != value) fragmentView.onSettingChanged() + val setting = scSetting.setSelectedValue(value) + if (setting != null) { + fragmentView.putSetting(setting) + } + closeDialog() + } + is SliderSetting -> { + val sliderSetting = clickedItem as SliderSetting + if (sliderSetting.selectedValue != sliderProgress) { + fragmentView.onSettingChanged() + } + if (sliderSetting.setting is FloatSetting) { + val value = sliderProgress.toFloat() + val setting = sliderSetting.setSelectedValue(value) + if (setting != null) { + fragmentView.putSetting(setting) + } + } else { + val setting = sliderSetting.setSelectedValue(sliderProgress) + if (setting != null) { + fragmentView.putSetting(setting) + } + } + closeDialog() + } + } + clickedItem = null + sliderProgress = -1 + } + + fun closeDialog() { + if (dialog != null) { + if (clickedPosition != -1) { + notifyItemChanged(clickedPosition) + clickedPosition = -1 + } + dialog!!.dismiss() + dialog = null + } + } + + private fun getValueForSingleChoiceSelection(item: SingleChoiceSetting, which: Int): Int { + val valuesId = item.valuesId + return if (valuesId > 0) { + val valuesArray = context.resources.getIntArray(valuesId) + valuesArray[which] + } else { + which + } + } + + private fun getSelectionForSingleChoiceValue(item: SingleChoiceSetting): Int { + val value = item.selectedValue + val valuesId = item.valuesId + if (valuesId > 0) { + val valuesArray = context.resources.getIntArray(valuesId) + for (index in valuesArray.indices) { + val current = valuesArray[index] + if (current == value) { + return index + } + } + } else { + return value + } + return -1 + } +} diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml index aa0cef521..7ef6b803e 100644 --- a/src/android/app/src/main/res/values/strings.xml +++ b/src/android/app/src/main/res/values/strings.xml @@ -23,6 +23,8 @@ If the \"System clock type\" setting is set to \"Simulated clock\", this changes the fixed date and time to start at. Emulated region Emulated language + Select RTC Date + Select RTC Time API