diff options
Diffstat (limited to 'src/android')
3 files changed, 315 insertions, 366 deletions
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 @@ | |||
| 1 | package org.yuzu.yuzu_emu.features.settings.ui; | ||
| 2 | |||
| 3 | import android.content.Context; | ||
| 4 | import android.content.DialogInterface; | ||
| 5 | import android.view.LayoutInflater; | ||
| 6 | import android.view.View; | ||
| 7 | import android.view.ViewGroup; | ||
| 8 | import android.widget.DatePicker; | ||
| 9 | import android.widget.SeekBar; | ||
| 10 | import android.widget.TextView; | ||
| 11 | import android.widget.TimePicker; | ||
| 12 | |||
| 13 | import androidx.appcompat.app.AlertDialog; | ||
| 14 | import androidx.recyclerview.widget.RecyclerView; | ||
| 15 | |||
| 16 | import org.yuzu.yuzu_emu.R; | ||
| 17 | import org.yuzu.yuzu_emu.features.settings.model.FloatSetting; | ||
| 18 | import org.yuzu.yuzu_emu.features.settings.model.IntSetting; | ||
| 19 | import org.yuzu.yuzu_emu.features.settings.model.StringSetting; | ||
| 20 | import org.yuzu.yuzu_emu.features.settings.model.view.CheckBoxSetting; | ||
| 21 | import org.yuzu.yuzu_emu.features.settings.model.view.DateTimeSetting; | ||
| 22 | import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem; | ||
| 23 | import org.yuzu.yuzu_emu.features.settings.model.view.SingleChoiceSetting; | ||
| 24 | import org.yuzu.yuzu_emu.features.settings.model.view.SliderSetting; | ||
| 25 | import org.yuzu.yuzu_emu.features.settings.model.view.StringSingleChoiceSetting; | ||
| 26 | import org.yuzu.yuzu_emu.features.settings.model.view.SubmenuSetting; | ||
| 27 | import org.yuzu.yuzu_emu.features.settings.ui.viewholder.CheckBoxSettingViewHolder; | ||
| 28 | import org.yuzu.yuzu_emu.features.settings.ui.viewholder.DateTimeViewHolder; | ||
| 29 | import org.yuzu.yuzu_emu.features.settings.ui.viewholder.HeaderViewHolder; | ||
| 30 | import org.yuzu.yuzu_emu.features.settings.ui.viewholder.SettingViewHolder; | ||
| 31 | import org.yuzu.yuzu_emu.features.settings.ui.viewholder.SingleChoiceViewHolder; | ||
| 32 | import org.yuzu.yuzu_emu.features.settings.ui.viewholder.SliderViewHolder; | ||
| 33 | import org.yuzu.yuzu_emu.features.settings.ui.viewholder.SubmenuViewHolder; | ||
| 34 | import org.yuzu.yuzu_emu.utils.Log; | ||
| 35 | |||
| 36 | import java.util.ArrayList; | ||
| 37 | |||
| 38 | public final class SettingsAdapter extends RecyclerView.Adapter<SettingViewHolder> | ||
| 39 | implements DialogInterface.OnClickListener, SeekBar.OnSeekBarChangeListener { | ||
| 40 | private SettingsFragmentView mView; | ||
| 41 | private Context mContext; | ||
| 42 | private ArrayList<SettingsItem> mSettings; | ||
| 43 | |||
| 44 | private SettingsItem mClickedItem; | ||
| 45 | private int mClickedPosition; | ||
| 46 | private int mSeekbarProgress; | ||
| 47 | |||
| 48 | private AlertDialog mDialog; | ||
| 49 | private TextView mTextSliderValue; | ||
| 50 | |||
| 51 | public SettingsAdapter(SettingsFragmentView view, Context context) { | ||
| 52 | mView = view; | ||
| 53 | mContext = context; | ||
| 54 | mClickedPosition = -1; | ||
| 55 | } | ||
| 56 | |||
| 57 | @Override | ||
| 58 | public SettingViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { | ||
| 59 | View view; | ||
| 60 | LayoutInflater inflater = LayoutInflater.from(parent.getContext()); | ||
| 61 | |||
| 62 | switch (viewType) { | ||
| 63 | case SettingsItem.TYPE_HEADER: | ||
| 64 | view = inflater.inflate(R.layout.list_item_settings_header, parent, false); | ||
| 65 | return new HeaderViewHolder(view, this); | ||
| 66 | |||
| 67 | case SettingsItem.TYPE_CHECKBOX: | ||
| 68 | view = inflater.inflate(R.layout.list_item_setting_checkbox, parent, false); | ||
| 69 | return new CheckBoxSettingViewHolder(view, this); | ||
| 70 | |||
| 71 | case SettingsItem.TYPE_SINGLE_CHOICE: | ||
| 72 | case SettingsItem.TYPE_STRING_SINGLE_CHOICE: | ||
| 73 | view = inflater.inflate(R.layout.list_item_setting, parent, false); | ||
| 74 | return new SingleChoiceViewHolder(view, this); | ||
| 75 | |||
| 76 | case SettingsItem.TYPE_SLIDER: | ||
| 77 | view = inflater.inflate(R.layout.list_item_setting, parent, false); | ||
| 78 | return new SliderViewHolder(view, this); | ||
| 79 | |||
| 80 | case SettingsItem.TYPE_SUBMENU: | ||
| 81 | view = inflater.inflate(R.layout.list_item_setting, parent, false); | ||
| 82 | return new SubmenuViewHolder(view, this); | ||
| 83 | |||
| 84 | case SettingsItem.TYPE_DATETIME_SETTING: | ||
| 85 | view = inflater.inflate(R.layout.list_item_setting, parent, false); | ||
| 86 | return new DateTimeViewHolder(view, this); | ||
| 87 | |||
| 88 | default: | ||
| 89 | Log.error("[SettingsAdapter] Invalid view type: " + viewType); | ||
| 90 | return null; | ||
| 91 | } | ||
| 92 | } | ||
| 93 | |||
| 94 | @Override | ||
| 95 | public void onBindViewHolder(SettingViewHolder holder, int position) { | ||
| 96 | holder.bind(getItem(position)); | ||
| 97 | } | ||
| 98 | |||
| 99 | private SettingsItem getItem(int position) { | ||
| 100 | return mSettings.get(position); | ||
| 101 | } | ||
| 102 | |||
| 103 | @Override | ||
| 104 | public int getItemCount() { | ||
| 105 | if (mSettings != null) { | ||
| 106 | return mSettings.size(); | ||
| 107 | } else { | ||
| 108 | return 0; | ||
| 109 | } | ||
| 110 | } | ||
| 111 | |||
| 112 | @Override | ||
| 113 | public int getItemViewType(int position) { | ||
| 114 | return getItem(position).getType(); | ||
| 115 | } | ||
| 116 | |||
| 117 | public void setSettings(ArrayList<SettingsItem> settings) { | ||
| 118 | mSettings = settings; | ||
| 119 | notifyDataSetChanged(); | ||
| 120 | } | ||
| 121 | |||
| 122 | public void onBooleanClick(CheckBoxSetting item, int position, boolean checked) { | ||
| 123 | IntSetting setting = item.setChecked(checked); | ||
| 124 | notifyItemChanged(position); | ||
| 125 | |||
| 126 | if (setting != null) { | ||
| 127 | mView.putSetting(setting); | ||
| 128 | } | ||
| 129 | |||
| 130 | mView.onSettingChanged(); | ||
| 131 | } | ||
| 132 | |||
| 133 | public void onSingleChoiceClick(SingleChoiceSetting item) { | ||
| 134 | mClickedItem = item; | ||
| 135 | |||
| 136 | int value = getSelectionForSingleChoiceValue(item); | ||
| 137 | |||
| 138 | AlertDialog.Builder builder = new AlertDialog.Builder(mView.getActivity()); | ||
| 139 | |||
| 140 | builder.setTitle(item.getNameId()); | ||
| 141 | builder.setSingleChoiceItems(item.getChoicesId(), value, this); | ||
| 142 | |||
| 143 | mDialog = builder.show(); | ||
| 144 | } | ||
| 145 | |||
| 146 | public void onSingleChoiceClick(SingleChoiceSetting item, int position) { | ||
| 147 | mClickedPosition = position; | ||
| 148 | onSingleChoiceClick(item); | ||
| 149 | } | ||
| 150 | |||
| 151 | public void onStringSingleChoiceClick(StringSingleChoiceSetting item) { | ||
| 152 | mClickedItem = item; | ||
| 153 | |||
| 154 | AlertDialog.Builder builder = new AlertDialog.Builder(mView.getActivity()); | ||
| 155 | |||
| 156 | builder.setTitle(item.getNameId()); | ||
| 157 | builder.setSingleChoiceItems(item.getChoicesId(), item.getSelectValueIndex(), this); | ||
| 158 | |||
| 159 | mDialog = builder.show(); | ||
| 160 | } | ||
| 161 | |||
| 162 | public void onStringSingleChoiceClick(StringSingleChoiceSetting item, int position) { | ||
| 163 | mClickedPosition = position; | ||
| 164 | onStringSingleChoiceClick(item); | ||
| 165 | } | ||
| 166 | |||
| 167 | DialogInterface.OnClickListener defaultCancelListener = (dialog, which) -> closeDialog(); | ||
| 168 | |||
| 169 | public void onDateTimeClick(DateTimeSetting item, int position) { | ||
| 170 | mClickedItem = item; | ||
| 171 | mClickedPosition = position; | ||
| 172 | |||
| 173 | AlertDialog.Builder builder = new AlertDialog.Builder(mView.getActivity()); | ||
| 174 | |||
| 175 | LayoutInflater inflater = LayoutInflater.from(mView.getActivity()); | ||
| 176 | View view = inflater.inflate(R.layout.sysclock_datetime_picker, null); | ||
| 177 | |||
| 178 | DatePicker dp = view.findViewById(R.id.date_picker); | ||
| 179 | TimePicker tp = view.findViewById(R.id.time_picker); | ||
| 180 | |||
| 181 | //set date and time to substrings of settingValue; format = 2018-12-24 04:20:69 (alright maybe not that 69) | ||
| 182 | String settingValue = item.getValue(); | ||
| 183 | dp.updateDate(Integer.parseInt(settingValue.substring(0, 4)), Integer.parseInt(settingValue.substring(5, 7)) - 1, Integer.parseInt(settingValue.substring(8, 10))); | ||
| 184 | |||
| 185 | tp.setIs24HourView(true); | ||
| 186 | tp.setHour(Integer.parseInt(settingValue.substring(11, 13))); | ||
| 187 | tp.setMinute(Integer.parseInt(settingValue.substring(14, 16))); | ||
| 188 | |||
| 189 | DialogInterface.OnClickListener ok = (dialog, which) -> { | ||
| 190 | //set it | ||
| 191 | int year = dp.getYear(); | ||
| 192 | if (year < 2000) { | ||
| 193 | year = 2000; | ||
| 194 | } | ||
| 195 | String month = ("00" + (dp.getMonth() + 1)).substring(String.valueOf(dp.getMonth() + 1).length()); | ||
| 196 | String day = ("00" + dp.getDayOfMonth()).substring(String.valueOf(dp.getDayOfMonth()).length()); | ||
| 197 | String hr = ("00" + tp.getHour()).substring(String.valueOf(tp.getHour()).length()); | ||
| 198 | String min = ("00" + tp.getMinute()).substring(String.valueOf(tp.getMinute()).length()); | ||
| 199 | String datetime = year + "-" + month + "-" + day + " " + hr + ":" + min + ":01"; | ||
| 200 | |||
| 201 | StringSetting setting = item.setSelectedValue(datetime); | ||
| 202 | if (setting != null) { | ||
| 203 | mView.putSetting(setting); | ||
| 204 | } | ||
| 205 | |||
| 206 | mView.onSettingChanged(); | ||
| 207 | |||
| 208 | mClickedItem = null; | ||
| 209 | closeDialog(); | ||
| 210 | }; | ||
| 211 | |||
| 212 | builder.setView(view); | ||
| 213 | builder.setPositiveButton(android.R.string.ok, ok); | ||
| 214 | builder.setNegativeButton(android.R.string.cancel, defaultCancelListener); | ||
| 215 | mDialog = builder.show(); | ||
| 216 | } | ||
| 217 | |||
| 218 | public void onSliderClick(SliderSetting item, int position) { | ||
| 219 | mClickedItem = item; | ||
| 220 | mClickedPosition = position; | ||
| 221 | mSeekbarProgress = item.getSelectedValue(); | ||
| 222 | AlertDialog.Builder builder = new AlertDialog.Builder(mView.getActivity()); | ||
| 223 | |||
| 224 | LayoutInflater inflater = LayoutInflater.from(mView.getActivity()); | ||
| 225 | View view = inflater.inflate(R.layout.dialog_seekbar, null); | ||
| 226 | |||
| 227 | SeekBar seekbar = view.findViewById(R.id.seekbar); | ||
| 228 | |||
| 229 | builder.setTitle(item.getNameId()); | ||
| 230 | builder.setView(view); | ||
| 231 | builder.setPositiveButton(android.R.string.ok, this); | ||
| 232 | builder.setNegativeButton(android.R.string.cancel, defaultCancelListener); | ||
| 233 | builder.setNeutralButton(R.string.slider_default, (DialogInterface dialog, int which) -> { | ||
| 234 | seekbar.setProgress(item.getDefaultValue()); | ||
| 235 | onClick(dialog, which); | ||
| 236 | }); | ||
| 237 | mDialog = builder.show(); | ||
| 238 | |||
| 239 | mTextSliderValue = view.findViewById(R.id.text_value); | ||
| 240 | mTextSliderValue.setText(String.valueOf(mSeekbarProgress)); | ||
| 241 | |||
| 242 | TextView units = view.findViewById(R.id.text_units); | ||
| 243 | units.setText(item.getUnits()); | ||
| 244 | |||
| 245 | seekbar.setMin(item.getMin()); | ||
| 246 | seekbar.setMax(item.getMax()); | ||
| 247 | seekbar.setProgress(mSeekbarProgress); | ||
| 248 | |||
| 249 | seekbar.setOnSeekBarChangeListener(this); | ||
| 250 | } | ||
| 251 | |||
| 252 | public void onSubmenuClick(SubmenuSetting item) { | ||
| 253 | mView.loadSubMenu(item.getMenuKey()); | ||
| 254 | } | ||
| 255 | |||
| 256 | @Override | ||
| 257 | public void onClick(DialogInterface dialog, int which) { | ||
| 258 | if (mClickedItem instanceof SingleChoiceSetting) { | ||
| 259 | SingleChoiceSetting scSetting = (SingleChoiceSetting) mClickedItem; | ||
| 260 | |||
| 261 | int value = getValueForSingleChoiceSelection(scSetting, which); | ||
| 262 | if (scSetting.getSelectedValue() != value) { | ||
| 263 | mView.onSettingChanged(); | ||
| 264 | } | ||
| 265 | |||
| 266 | // Get the backing Setting, which may be null (if for example it was missing from the file) | ||
| 267 | IntSetting setting = scSetting.setSelectedValue(value); | ||
| 268 | if (setting != null) { | ||
| 269 | mView.putSetting(setting); | ||
| 270 | } | ||
| 271 | |||
| 272 | closeDialog(); | ||
| 273 | } else if (mClickedItem instanceof StringSingleChoiceSetting) { | ||
| 274 | StringSingleChoiceSetting scSetting = (StringSingleChoiceSetting) mClickedItem; | ||
| 275 | String value = scSetting.getValueAt(which); | ||
| 276 | if (!scSetting.getSelectedValue().equals(value)) | ||
| 277 | mView.onSettingChanged(); | ||
| 278 | |||
| 279 | StringSetting setting = scSetting.setSelectedValue(value); | ||
| 280 | if (setting != null) { | ||
| 281 | mView.putSetting(setting); | ||
| 282 | } | ||
| 283 | |||
| 284 | closeDialog(); | ||
| 285 | } else if (mClickedItem instanceof SliderSetting) { | ||
| 286 | SliderSetting sliderSetting = (SliderSetting) mClickedItem; | ||
| 287 | if (sliderSetting.getSelectedValue() != mSeekbarProgress) { | ||
| 288 | mView.onSettingChanged(); | ||
| 289 | } | ||
| 290 | |||
| 291 | if (sliderSetting.getSetting() instanceof FloatSetting) { | ||
| 292 | float value = (float) mSeekbarProgress; | ||
| 293 | |||
| 294 | FloatSetting setting = sliderSetting.setSelectedValue(value); | ||
| 295 | if (setting != null) { | ||
| 296 | mView.putSetting(setting); | ||
| 297 | } | ||
| 298 | } else { | ||
| 299 | IntSetting setting = sliderSetting.setSelectedValue(mSeekbarProgress); | ||
| 300 | if (setting != null) { | ||
| 301 | mView.putSetting(setting); | ||
| 302 | } | ||
| 303 | } | ||
| 304 | |||
| 305 | closeDialog(); | ||
| 306 | } | ||
| 307 | |||
| 308 | mClickedItem = null; | ||
| 309 | mSeekbarProgress = -1; | ||
| 310 | } | ||
| 311 | |||
| 312 | public void closeDialog() { | ||
| 313 | if (mDialog != null) { | ||
| 314 | if (mClickedPosition != -1) { | ||
| 315 | notifyItemChanged(mClickedPosition); | ||
| 316 | mClickedPosition = -1; | ||
| 317 | } | ||
| 318 | mDialog.dismiss(); | ||
| 319 | mDialog = null; | ||
| 320 | } | ||
| 321 | } | ||
| 322 | |||
| 323 | @Override | ||
| 324 | public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { | ||
| 325 | mSeekbarProgress = progress; | ||
| 326 | mTextSliderValue.setText(String.valueOf(mSeekbarProgress)); | ||
| 327 | } | ||
| 328 | |||
| 329 | @Override | ||
| 330 | public void onStartTrackingTouch(SeekBar seekBar) { | ||
| 331 | } | ||
| 332 | |||
| 333 | @Override | ||
| 334 | public void onStopTrackingTouch(SeekBar seekBar) { | ||
| 335 | } | ||
| 336 | |||
| 337 | private int getValueForSingleChoiceSelection(SingleChoiceSetting item, int which) { | ||
| 338 | int valuesId = item.getValuesId(); | ||
| 339 | |||
| 340 | if (valuesId > 0) { | ||
| 341 | int[] valuesArray = mContext.getResources().getIntArray(valuesId); | ||
| 342 | return valuesArray[which]; | ||
| 343 | } else { | ||
| 344 | return which; | ||
| 345 | } | ||
| 346 | } | ||
| 347 | |||
| 348 | private int getSelectionForSingleChoiceValue(SingleChoiceSetting item) { | ||
| 349 | int value = item.getSelectedValue(); | ||
| 350 | int valuesId = item.getValuesId(); | ||
| 351 | |||
| 352 | if (valuesId > 0) { | ||
| 353 | int[] valuesArray = mContext.getResources().getIntArray(valuesId); | ||
| 354 | for (int index = 0; index < valuesArray.length; index++) { | ||
| 355 | int current = valuesArray[index]; | ||
| 356 | if (current == value) { | ||
| 357 | return index; | ||
| 358 | } | ||
| 359 | } | ||
| 360 | } else { | ||
| 361 | return value; | ||
| 362 | } | ||
| 363 | |||
| 364 | return -1; | ||
| 365 | } | ||
| 366 | } | ||
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 @@ | |||
| 1 | package org.yuzu.yuzu_emu.features.settings.ui | ||
| 2 | |||
| 3 | import android.content.Context | ||
| 4 | import android.content.DialogInterface | ||
| 5 | import android.icu.util.Calendar | ||
| 6 | import android.icu.util.TimeZone | ||
| 7 | import android.text.format.DateFormat | ||
| 8 | import android.view.LayoutInflater | ||
| 9 | import android.view.View | ||
| 10 | import android.view.ViewGroup | ||
| 11 | import android.widget.TextView | ||
| 12 | import androidx.appcompat.app.AlertDialog | ||
| 13 | import androidx.recyclerview.widget.RecyclerView | ||
| 14 | import com.google.android.material.datepicker.MaterialDatePicker | ||
| 15 | import com.google.android.material.dialog.MaterialAlertDialogBuilder | ||
| 16 | import com.google.android.material.slider.Slider | ||
| 17 | import com.google.android.material.timepicker.MaterialTimePicker | ||
| 18 | import com.google.android.material.timepicker.TimeFormat | ||
| 19 | import org.yuzu.yuzu_emu.R | ||
| 20 | import org.yuzu.yuzu_emu.features.settings.model.FloatSetting | ||
| 21 | import org.yuzu.yuzu_emu.features.settings.model.view.* | ||
| 22 | import org.yuzu.yuzu_emu.features.settings.ui.viewholder.* | ||
| 23 | |||
| 24 | class SettingsAdapter( | ||
| 25 | private val fragmentView: SettingsFragmentView, | ||
| 26 | private val context: Context | ||
| 27 | ) : RecyclerView.Adapter<SettingViewHolder?>(), DialogInterface.OnClickListener { | ||
| 28 | private var settings: ArrayList<SettingsItem>? = null | ||
| 29 | private var clickedItem: SettingsItem? = null | ||
| 30 | private var clickedPosition: Int | ||
| 31 | private var dialog: AlertDialog? = null | ||
| 32 | private var sliderProgress = 0 | ||
| 33 | private var textSliderValue: TextView? = null | ||
| 34 | |||
| 35 | private var defaultCancelListener = | ||
| 36 | DialogInterface.OnClickListener { _: DialogInterface?, _: Int -> closeDialog() } | ||
| 37 | |||
| 38 | init { | ||
| 39 | clickedPosition = -1 | ||
| 40 | } | ||
| 41 | |||
| 42 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SettingViewHolder { | ||
| 43 | val view: View | ||
| 44 | val inflater = LayoutInflater.from(parent.context) | ||
| 45 | return when (viewType) { | ||
| 46 | SettingsItem.TYPE_HEADER -> { | ||
| 47 | view = inflater.inflate(R.layout.list_item_settings_header, parent, false) | ||
| 48 | HeaderViewHolder(view, this) | ||
| 49 | } | ||
| 50 | SettingsItem.TYPE_CHECKBOX -> { | ||
| 51 | view = inflater.inflate(R.layout.list_item_setting_checkbox, parent, false) | ||
| 52 | CheckBoxSettingViewHolder(view, this) | ||
| 53 | } | ||
| 54 | SettingsItem.TYPE_SINGLE_CHOICE, SettingsItem.TYPE_STRING_SINGLE_CHOICE -> { | ||
| 55 | view = inflater.inflate(R.layout.list_item_setting, parent, false) | ||
| 56 | SingleChoiceViewHolder(view, this) | ||
| 57 | } | ||
| 58 | SettingsItem.TYPE_SLIDER -> { | ||
| 59 | view = inflater.inflate(R.layout.list_item_setting, parent, false) | ||
| 60 | SliderViewHolder(view, this) | ||
| 61 | } | ||
| 62 | SettingsItem.TYPE_SUBMENU -> { | ||
| 63 | view = inflater.inflate(R.layout.list_item_setting, parent, false) | ||
| 64 | SubmenuViewHolder(view, this) | ||
| 65 | } | ||
| 66 | SettingsItem.TYPE_DATETIME_SETTING -> { | ||
| 67 | view = inflater.inflate(R.layout.list_item_setting, parent, false) | ||
| 68 | DateTimeViewHolder(view, this) | ||
| 69 | } | ||
| 70 | else -> { | ||
| 71 | // TODO: Create an error view since we can't return null now | ||
| 72 | view = inflater.inflate(R.layout.list_item_settings_header, parent, false) | ||
| 73 | HeaderViewHolder(view, this) | ||
| 74 | } | ||
| 75 | } | ||
| 76 | } | ||
| 77 | |||
| 78 | override fun onBindViewHolder(holder: SettingViewHolder, position: Int) { | ||
| 79 | holder.bind(getItem(position)) | ||
| 80 | } | ||
| 81 | |||
| 82 | private fun getItem(position: Int): SettingsItem { | ||
| 83 | return settings!![position] | ||
| 84 | } | ||
| 85 | |||
| 86 | override fun getItemCount(): Int { | ||
| 87 | return if (settings != null) { | ||
| 88 | settings!!.size | ||
| 89 | } else { | ||
| 90 | 0 | ||
| 91 | } | ||
| 92 | } | ||
| 93 | |||
| 94 | override fun getItemViewType(position: Int): Int { | ||
| 95 | return getItem(position).type | ||
| 96 | } | ||
| 97 | |||
| 98 | fun setSettings(settings: ArrayList<SettingsItem>?) { | ||
| 99 | this.settings = settings | ||
| 100 | notifyDataSetChanged() | ||
| 101 | } | ||
| 102 | |||
| 103 | fun onBooleanClick(item: CheckBoxSetting, position: Int, checked: Boolean) { | ||
| 104 | val setting = item.setChecked(checked) | ||
| 105 | notifyItemChanged(position) | ||
| 106 | if (setting != null) { | ||
| 107 | fragmentView.putSetting(setting) | ||
| 108 | } | ||
| 109 | fragmentView.onSettingChanged() | ||
| 110 | } | ||
| 111 | |||
| 112 | private fun onSingleChoiceClick(item: SingleChoiceSetting) { | ||
| 113 | clickedItem = item | ||
| 114 | val value = getSelectionForSingleChoiceValue(item) | ||
| 115 | dialog = MaterialAlertDialogBuilder(context) | ||
| 116 | .setTitle(item.nameId) | ||
| 117 | .setSingleChoiceItems(item.choicesId, value, this) | ||
| 118 | .show() | ||
| 119 | } | ||
| 120 | |||
| 121 | fun onSingleChoiceClick(item: SingleChoiceSetting, position: Int) { | ||
| 122 | clickedPosition = position | ||
| 123 | onSingleChoiceClick(item) | ||
| 124 | } | ||
| 125 | |||
| 126 | private fun onStringSingleChoiceClick(item: StringSingleChoiceSetting) { | ||
| 127 | clickedItem = item | ||
| 128 | dialog = MaterialAlertDialogBuilder(context) | ||
| 129 | .setTitle(item.nameId) | ||
| 130 | .setSingleChoiceItems(item.choicesId, item.selectValueIndex, this) | ||
| 131 | .show() | ||
| 132 | } | ||
| 133 | |||
| 134 | fun onStringSingleChoiceClick(item: StringSingleChoiceSetting, position: Int) { | ||
| 135 | clickedPosition = position | ||
| 136 | onStringSingleChoiceClick(item) | ||
| 137 | } | ||
| 138 | |||
| 139 | fun onDateTimeClick(item: DateTimeSetting, position: Int) { | ||
| 140 | clickedItem = item | ||
| 141 | clickedPosition = position | ||
| 142 | val storedTime = java.lang.Long.decode(item.value) * 1000 | ||
| 143 | |||
| 144 | // Helper to extract hour and minute from epoch time | ||
| 145 | val calendar: Calendar = Calendar.getInstance() | ||
| 146 | calendar.timeInMillis = storedTime | ||
| 147 | calendar.timeZone = TimeZone.getTimeZone("UTC") | ||
| 148 | |||
| 149 | var timeFormat: Int = TimeFormat.CLOCK_12H | ||
| 150 | if (DateFormat.is24HourFormat(fragmentView.fragmentActivity)) { | ||
| 151 | timeFormat = TimeFormat.CLOCK_24H | ||
| 152 | } | ||
| 153 | |||
| 154 | val datePicker: MaterialDatePicker<Long> = MaterialDatePicker.Builder.datePicker() | ||
| 155 | .setSelection(storedTime) | ||
| 156 | .setTitleText(R.string.select_rtc_date) | ||
| 157 | .build() | ||
| 158 | val timePicker: MaterialTimePicker = MaterialTimePicker.Builder() | ||
| 159 | .setTimeFormat(timeFormat) | ||
| 160 | .setHour(calendar.get(Calendar.HOUR_OF_DAY)) | ||
| 161 | .setMinute(calendar.get(Calendar.MINUTE)) | ||
| 162 | .setTitleText(R.string.select_rtc_time) | ||
| 163 | .build() | ||
| 164 | |||
| 165 | datePicker.addOnPositiveButtonClickListener { | ||
| 166 | timePicker.show( | ||
| 167 | fragmentView.fragmentActivity.supportFragmentManager, | ||
| 168 | "TimePicker" | ||
| 169 | ) | ||
| 170 | } | ||
| 171 | timePicker.addOnPositiveButtonClickListener { | ||
| 172 | var epochTime: Long = datePicker.selection!! / 1000 | ||
| 173 | epochTime += timePicker.hour.toLong() * 60 * 60 | ||
| 174 | epochTime += timePicker.minute.toLong() * 60 | ||
| 175 | val rtcString = epochTime.toString() | ||
| 176 | if (item.value != rtcString) { | ||
| 177 | notifyItemChanged(clickedPosition) | ||
| 178 | fragmentView.onSettingChanged() | ||
| 179 | } | ||
| 180 | item.setSelectedValue(rtcString) | ||
| 181 | clickedItem = null | ||
| 182 | } | ||
| 183 | datePicker.show(fragmentView.fragmentActivity.supportFragmentManager, "DatePicker") | ||
| 184 | } | ||
| 185 | |||
| 186 | fun onSliderClick(item: SliderSetting, position: Int) { | ||
| 187 | clickedItem = item | ||
| 188 | clickedPosition = position | ||
| 189 | sliderProgress = item.selectedValue | ||
| 190 | |||
| 191 | val inflater = LayoutInflater.from(context) | ||
| 192 | val sliderLayout = inflater.inflate(R.layout.dialog_slider, null) | ||
| 193 | val sliderView = sliderLayout.findViewById<Slider>(R.id.slider) | ||
| 194 | |||
| 195 | textSliderValue = sliderLayout.findViewById(R.id.text_value) | ||
| 196 | textSliderValue!!.text = sliderProgress.toString() | ||
| 197 | val units = sliderLayout.findViewById<TextView>(R.id.text_units) | ||
| 198 | units.text = item.units | ||
| 199 | |||
| 200 | sliderView.apply { | ||
| 201 | valueFrom = item.min.toFloat() | ||
| 202 | valueTo = item.max.toFloat() | ||
| 203 | value = sliderProgress.toFloat() | ||
| 204 | addOnChangeListener { _: Slider, value: Float, _: Boolean -> | ||
| 205 | sliderProgress = value.toInt() | ||
| 206 | textSliderValue!!.text = sliderProgress.toString() | ||
| 207 | } | ||
| 208 | } | ||
| 209 | |||
| 210 | dialog = MaterialAlertDialogBuilder(context) | ||
| 211 | .setTitle(item.nameId) | ||
| 212 | .setView(sliderLayout) | ||
| 213 | .setPositiveButton(android.R.string.ok, this) | ||
| 214 | .setNegativeButton(android.R.string.cancel, defaultCancelListener) | ||
| 215 | .setNeutralButton(R.string.slider_default) { dialog: DialogInterface, which: Int -> | ||
| 216 | sliderView.value = item.defaultValue.toFloat() | ||
| 217 | onClick(dialog, which) | ||
| 218 | } | ||
| 219 | .show() | ||
| 220 | } | ||
| 221 | |||
| 222 | fun onSubmenuClick(item: SubmenuSetting) { | ||
| 223 | fragmentView.loadSubMenu(item.menuKey) | ||
| 224 | } | ||
| 225 | |||
| 226 | override fun onClick(dialog: DialogInterface, which: Int) { | ||
| 227 | when (clickedItem) { | ||
| 228 | is SingleChoiceSetting -> { | ||
| 229 | val scSetting = clickedItem as SingleChoiceSetting | ||
| 230 | val value = getValueForSingleChoiceSelection(scSetting, which) | ||
| 231 | if (scSetting.selectedValue != value) { | ||
| 232 | fragmentView.onSettingChanged() | ||
| 233 | } | ||
| 234 | |||
| 235 | // Get the backing Setting, which may be null (if for example it was missing from the file) | ||
| 236 | val setting = scSetting.setSelectedValue(value) | ||
| 237 | if (setting != null) { | ||
| 238 | fragmentView.putSetting(setting) | ||
| 239 | } | ||
| 240 | closeDialog() | ||
| 241 | } | ||
| 242 | is StringSingleChoiceSetting -> { | ||
| 243 | val scSetting = clickedItem as StringSingleChoiceSetting | ||
| 244 | val value = scSetting.getValueAt(which) | ||
| 245 | if (scSetting.selectedValue != value) fragmentView.onSettingChanged() | ||
| 246 | val setting = scSetting.setSelectedValue(value) | ||
| 247 | if (setting != null) { | ||
| 248 | fragmentView.putSetting(setting) | ||
| 249 | } | ||
| 250 | closeDialog() | ||
| 251 | } | ||
| 252 | is SliderSetting -> { | ||
| 253 | val sliderSetting = clickedItem as SliderSetting | ||
| 254 | if (sliderSetting.selectedValue != sliderProgress) { | ||
| 255 | fragmentView.onSettingChanged() | ||
| 256 | } | ||
| 257 | if (sliderSetting.setting is FloatSetting) { | ||
| 258 | val value = sliderProgress.toFloat() | ||
| 259 | val setting = sliderSetting.setSelectedValue(value) | ||
| 260 | if (setting != null) { | ||
| 261 | fragmentView.putSetting(setting) | ||
| 262 | } | ||
| 263 | } else { | ||
| 264 | val setting = sliderSetting.setSelectedValue(sliderProgress) | ||
| 265 | if (setting != null) { | ||
| 266 | fragmentView.putSetting(setting) | ||
| 267 | } | ||
| 268 | } | ||
| 269 | closeDialog() | ||
| 270 | } | ||
| 271 | } | ||
| 272 | clickedItem = null | ||
| 273 | sliderProgress = -1 | ||
| 274 | } | ||
| 275 | |||
| 276 | fun closeDialog() { | ||
| 277 | if (dialog != null) { | ||
| 278 | if (clickedPosition != -1) { | ||
| 279 | notifyItemChanged(clickedPosition) | ||
| 280 | clickedPosition = -1 | ||
| 281 | } | ||
| 282 | dialog!!.dismiss() | ||
| 283 | dialog = null | ||
| 284 | } | ||
| 285 | } | ||
| 286 | |||
| 287 | private fun getValueForSingleChoiceSelection(item: SingleChoiceSetting, which: Int): Int { | ||
| 288 | val valuesId = item.valuesId | ||
| 289 | return if (valuesId > 0) { | ||
| 290 | val valuesArray = context.resources.getIntArray(valuesId) | ||
| 291 | valuesArray[which] | ||
| 292 | } else { | ||
| 293 | which | ||
| 294 | } | ||
| 295 | } | ||
| 296 | |||
| 297 | private fun getSelectionForSingleChoiceValue(item: SingleChoiceSetting): Int { | ||
| 298 | val value = item.selectedValue | ||
| 299 | val valuesId = item.valuesId | ||
| 300 | if (valuesId > 0) { | ||
| 301 | val valuesArray = context.resources.getIntArray(valuesId) | ||
| 302 | for (index in valuesArray.indices) { | ||
| 303 | val current = valuesArray[index] | ||
| 304 | if (current == value) { | ||
| 305 | return index | ||
| 306 | } | ||
| 307 | } | ||
| 308 | } else { | ||
| 309 | return value | ||
| 310 | } | ||
| 311 | return -1 | ||
| 312 | } | ||
| 313 | } | ||
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 @@ | |||
| 23 | <string name="init_time_description">If the \"System clock type\" setting is set to \"Simulated clock\", this changes the fixed date and time to start at.</string> | 23 | <string name="init_time_description">If the \"System clock type\" setting is set to \"Simulated clock\", this changes the fixed date and time to start at.</string> |
| 24 | <string name="emulated_region">Emulated region</string> | 24 | <string name="emulated_region">Emulated region</string> |
| 25 | <string name="emulated_language">Emulated language</string> | 25 | <string name="emulated_language">Emulated language</string> |
| 26 | <string name="select_rtc_date">Select RTC Date</string> | ||
| 27 | <string name="select_rtc_time">Select RTC Time</string> | ||
| 26 | 28 | ||
| 27 | <!-- Graphics settings strings --> | 29 | <!-- Graphics settings strings --> |
| 28 | <string name="renderer_api">API</string> | 30 | <string name="renderer_api">API</string> |