From c31a37c828f483c62a5b46b473d636738fda6361 Mon Sep 17 00:00:00 2001
From: Charles Lombardo <clombardo169@gmail.com>
Date: Sat, 13 May 2023 18:43:35 -0400
Subject: [PATCH] android: Expose custom RTC setting

---
 .../features/settings/model/BooleanSetting.kt |  7 +--
 .../features/settings/model/StringSetting.kt  |  7 +--
 .../settings/model/view/DateTimeSetting.kt    |  2 +-
 .../settings/model/view/SwitchSetting.kt      |  6 +--
 .../features/settings/ui/SettingsAdapter.kt   |  5 +-
 .../settings/ui/SettingsFragmentPresenter.kt  | 54 +++++++++++++------
 .../ui/viewholder/DateTimeViewHolder.kt       | 12 ++++-
 .../features/settings/utils/SettingsFile.kt   |  7 +++
 .../app/src/main/res/values/strings.xml       |  3 ++
 9 files changed, 72 insertions(+), 31 deletions(-)

diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt
index 9bc0d41761..7c0ca08c74 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt
@@ -8,8 +8,7 @@ enum class BooleanSetting(
     override val section: String,
     override val defaultValue: Boolean
 ) : AbstractBooleanSetting {
-    // No boolean settings currently exist
-    EMPTY_SETTING("", "", false);
+    USE_CUSTOM_RTC("custom_rtc_enabled", Settings.SECTION_SYSTEM, false);
 
     override var boolean: Boolean = defaultValue
 
@@ -27,7 +26,9 @@ enum class BooleanSetting(
         }
 
     companion object {
-        private val NOT_RUNTIME_EDITABLE = emptyList<BooleanSetting>()
+        private val NOT_RUNTIME_EDITABLE = listOf(
+            USE_CUSTOM_RTC
+        )
 
         fun from(key: String): BooleanSetting? =
             BooleanSetting.values().firstOrNull { it.key == key }
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/StringSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/StringSetting.kt
index c41180b77c..d16ff17dfc 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/StringSetting.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/StringSetting.kt
@@ -8,8 +8,7 @@ enum class StringSetting(
     override val section: String,
     override val defaultValue: String
 ) : AbstractStringSetting {
-    // No string settings currently exist
-    EMPTY_SETTING("", "", "");
+    CUSTOM_RTC("custom_rtc", Settings.SECTION_SYSTEM, "0");
 
     override var string: String = defaultValue
 
@@ -27,7 +26,9 @@ enum class StringSetting(
         }
 
     companion object {
-        private val NOT_RUNTIME_EDITABLE = emptyList<StringSetting>()
+        private val NOT_RUNTIME_EDITABLE = listOf(
+            CUSTOM_RTC
+        )
 
         fun from(key: String): StringSetting? = StringSetting.values().firstOrNull { it.key == key }
     }
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/DateTimeSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/DateTimeSetting.kt
index f2038c5787..bc0bf77889 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/DateTimeSetting.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/DateTimeSetting.kt
@@ -7,10 +7,10 @@ import org.yuzu.yuzu_emu.features.settings.model.AbstractSetting
 import org.yuzu.yuzu_emu.features.settings.model.AbstractStringSetting
 
 class DateTimeSetting(
-    val key: String? = null,
     setting: AbstractSetting?,
     titleId: Int,
     descriptionId: Int,
+    val key: String? = null,
     private val defaultValue: String? = null
 ) : SettingsItem(setting, titleId, descriptionId) {
     override val type = TYPE_DATETIME_SETTING
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SwitchSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SwitchSetting.kt
index 6ed1aa8806..9f3c7e9d58 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SwitchSetting.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SwitchSetting.kt
@@ -15,14 +15,14 @@ class SwitchSetting(
     titleId: Int,
     descriptionId: Int,
     val key: String? = null,
-    val defaultValue: Boolean? = null
+    val defaultValue: Any? = null
 ) : SettingsItem(setting, titleId, descriptionId) {
     override val type = TYPE_SWITCH
 
     val isChecked: Boolean
         get() {
             if (setting == null) {
-                return defaultValue!!
+                return defaultValue as Boolean
             }
 
             // Try integer setting
@@ -38,7 +38,7 @@ class SwitchSetting(
                 return setting.boolean
             } catch (_: ClassCastException) {
             }
-            return defaultValue!!
+            return defaultValue as Boolean
         }
 
     /**
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
index f0b9083ab3..9439054827 100644
--- 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
@@ -182,10 +182,11 @@ class SettingsAdapter(
             epochTime += timePicker.minute.toLong() * 60
             val rtcString = epochTime.toString()
             if (item.value != rtcString) {
-                notifyItemChanged(clickedPosition)
                 fragmentView.onSettingChanged()
             }
-            item.setSelectedValue(rtcString)
+            notifyItemChanged(clickedPosition)
+            val setting = item.setSelectedValue(rtcString)
+            fragmentView.putSetting(setting)
             clickedItem = null
         }
         datePicker.show(
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt
index 9a7b605813..d36afa9323 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt
@@ -13,8 +13,10 @@ import org.yuzu.yuzu_emu.YuzuApplication
 import org.yuzu.yuzu_emu.features.settings.model.AbstractBooleanSetting
 import org.yuzu.yuzu_emu.features.settings.model.AbstractIntSetting
 import org.yuzu.yuzu_emu.features.settings.model.AbstractSetting
+import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting
 import org.yuzu.yuzu_emu.features.settings.model.IntSetting
 import org.yuzu.yuzu_emu.features.settings.model.Settings
+import org.yuzu.yuzu_emu.features.settings.model.StringSetting
 import org.yuzu.yuzu_emu.features.settings.model.view.*
 import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile
 import org.yuzu.yuzu_emu.utils.ThemeHelper
@@ -121,7 +123,7 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
                     R.string.frame_limit_enable,
                     R.string.frame_limit_enable_description,
                     IntSetting.RENDERER_USE_SPEED_LIMIT.key,
-                    true
+                    IntSetting.RENDERER_USE_SPEED_LIMIT.defaultValue
                 )
             )
             add(
@@ -133,7 +135,7 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
                     200,
                     "%",
                     IntSetting.RENDERER_SPEED_LIMIT.key,
-                    100
+                    IntSetting.RENDERER_SPEED_LIMIT.defaultValue
                 )
             )
             add(
@@ -144,7 +146,7 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
                     R.array.cpuAccuracyNames,
                     R.array.cpuAccuracyValues,
                     IntSetting.CPU_ACCURACY.key,
-                    0
+                    IntSetting.CPU_ACCURACY.defaultValue
                 )
             )
         }
@@ -159,7 +161,7 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
                     R.string.use_docked_mode,
                     R.string.use_docked_mode_description,
                     IntSetting.USE_DOCKED_MODE.key,
-                    false
+                    IntSetting.USE_DOCKED_MODE.defaultValue
                 )
             )
             add(
@@ -170,7 +172,7 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
                     R.array.regionNames,
                     R.array.regionValues,
                     IntSetting.REGION_INDEX.key,
-                    -1
+                    IntSetting.REGION_INDEX.defaultValue
                 )
             )
             add(
@@ -181,7 +183,25 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
                     R.array.languageNames,
                     R.array.languageValues,
                     IntSetting.LANGUAGE_INDEX.key,
-                    1
+                    IntSetting.LANGUAGE_INDEX.defaultValue
+                )
+            )
+            add(
+                SwitchSetting(
+                    BooleanSetting.USE_CUSTOM_RTC,
+                    R.string.use_custom_rtc,
+                    R.string.use_custom_rtc_description,
+                    BooleanSetting.USE_CUSTOM_RTC.key,
+                    BooleanSetting.USE_CUSTOM_RTC.defaultValue
+                )
+            )
+            add(
+                DateTimeSetting(
+                    StringSetting.CUSTOM_RTC,
+                    R.string.set_custom_rtc,
+                    0,
+                    StringSetting.CUSTOM_RTC.key,
+                    StringSetting.CUSTOM_RTC.defaultValue
                 )
             )
         }
@@ -198,7 +218,7 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
                     R.array.rendererApiNames,
                     R.array.rendererApiValues,
                     IntSetting.RENDERER_BACKEND.key,
-                    1
+                    IntSetting.RENDERER_BACKEND.defaultValue
                 )
             )
             add(
@@ -209,7 +229,7 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
                     R.array.rendererAccuracyNames,
                     R.array.rendererAccuracyValues,
                     IntSetting.RENDERER_ACCURACY.key,
-                    0
+                    IntSetting.RENDERER_ACCURACY.defaultValue
                 )
             )
             add(
@@ -220,7 +240,7 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
                     R.array.rendererResolutionNames,
                     R.array.rendererResolutionValues,
                     IntSetting.RENDERER_RESOLUTION.key,
-                    2
+                    IntSetting.RENDERER_RESOLUTION.defaultValue
                 )
             )
             add(
@@ -231,7 +251,7 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
                     R.array.rendererScalingFilterNames,
                     R.array.rendererScalingFilterValues,
                     IntSetting.RENDERER_SCALING_FILTER.key,
-                    1
+                    IntSetting.RENDERER_SCALING_FILTER.defaultValue
                 )
             )
             add(
@@ -242,7 +262,7 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
                     R.array.rendererAntiAliasingNames,
                     R.array.rendererAntiAliasingValues,
                     IntSetting.RENDERER_ANTI_ALIASING.key,
-                    0
+                    IntSetting.RENDERER_ANTI_ALIASING.defaultValue
                 )
             )
             add(
@@ -253,7 +273,7 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
                     R.array.rendererAspectRatioNames,
                     R.array.rendererAspectRatioValues,
                     IntSetting.RENDERER_ASPECT_RATIO.key,
-                    0
+                    IntSetting.RENDERER_ASPECT_RATIO.defaultValue
                 )
             )
             add(
@@ -262,7 +282,7 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
                     R.string.use_disk_shader_cache,
                     R.string.use_disk_shader_cache_description,
                     IntSetting.RENDERER_USE_DISK_SHADER_CACHE.key,
-                    true
+                    IntSetting.RENDERER_USE_DISK_SHADER_CACHE.defaultValue
                 )
             )
             add(
@@ -271,7 +291,7 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
                     R.string.renderer_force_max_clock,
                     R.string.renderer_force_max_clock_description,
                     IntSetting.RENDERER_FORCE_MAX_CLOCK.key,
-                    true
+                    IntSetting.RENDERER_FORCE_MAX_CLOCK.defaultValue
                 )
             )
             add(
@@ -280,7 +300,7 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
                     R.string.renderer_asynchronous_shaders,
                     R.string.renderer_asynchronous_shaders_description,
                     IntSetting.RENDERER_ASYNCHRONOUS_SHADERS.key,
-                    false
+                    IntSetting.RENDERER_ASYNCHRONOUS_SHADERS.defaultValue
                 )
             )
             add(
@@ -289,7 +309,7 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
                     R.string.renderer_debug,
                     R.string.renderer_debug_description,
                     IntSetting.RENDERER_DEBUG.key,
-                    false
+                    IntSetting.RENDERER_DEBUG.defaultValue
                 )
             )
         }
@@ -306,7 +326,7 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
                 100,
                 "%",
                 IntSetting.AUDIO_VOLUME.key,
-                100
+                IntSetting.AUDIO_VOLUME.defaultValue
             )
         )
     }
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/DateTimeViewHolder.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/DateTimeViewHolder.kt
index fb0c6c9e5a..b101a790d9 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/DateTimeViewHolder.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/DateTimeViewHolder.kt
@@ -5,10 +5,14 @@ package org.yuzu.yuzu_emu.features.settings.ui.viewholder
 
 import android.view.View
 import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding
-import org.yuzu.yuzu_emu.features.settings.model.AbstractStringSetting
 import org.yuzu.yuzu_emu.features.settings.model.view.DateTimeSetting
 import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
 import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter
+import java.time.Instant
+import java.time.ZoneId
+import java.time.ZonedDateTime
+import java.time.format.DateTimeFormatter
+import java.time.format.FormatStyle
 
 class DateTimeViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAdapter) :
     SettingViewHolder(binding.root, adapter) {
@@ -21,7 +25,11 @@ class DateTimeViewHolder(val binding: ListItemSettingBinding, adapter: SettingsA
             binding.textSettingDescription.setText(item.descriptionId)
             binding.textSettingDescription.visibility = View.VISIBLE
         } else {
-            binding.textSettingDescription.visibility = View.GONE
+            val epochTime = setting.value.toLong()
+            val instant = Instant.ofEpochMilli(epochTime * 1000)
+            val zonedTime = ZonedDateTime.ofInstant(instant, ZoneId.of("UTC"))
+            val dateFormatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM)
+            binding.textSettingDescription.text = dateFormatter.format(zonedTime)
         }
     }
 
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/utils/SettingsFile.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/utils/SettingsFile.kt
index 9c6515a547..4027df8380 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/utils/SettingsFile.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/utils/SettingsFile.kt
@@ -209,6 +209,13 @@ object SettingsFile {
             floatSetting.float = value.toFloat()
             return floatSetting
         }
+
+        val stringSetting = StringSetting.from(key)
+        if (stringSetting != null) {
+            stringSetting.string = value
+            return stringSetting
+        }
+
         return StringSetting.from(key)
     }
 
diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml
index f6d7422065..cd93bfc389 100644
--- a/src/android/app/src/main/res/values/strings.xml
+++ b/src/android/app/src/main/res/values/strings.xml
@@ -114,6 +114,9 @@
     <string name="emulated_language">Emulated language</string>
     <string name="select_rtc_date">Select RTC Date</string>
     <string name="select_rtc_time">Select RTC Time</string>
+    <string name="use_custom_rtc">Enable Custom RTC</string>
+    <string name="use_custom_rtc_description">This setting allows you to set a custom real time clock separate from your current system time</string>
+    <string name="set_custom_rtc">Set Custom RTC</string>
 
     <!-- Graphics settings strings -->
     <string name="renderer_api">API</string>