SettingsFragment.kt 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  1. package im.angry.openeuicc.ui
  2. import android.content.Intent
  3. import android.net.Uri
  4. import android.os.Build
  5. import android.os.Bundle
  6. import android.provider.Settings
  7. import android.widget.Toast
  8. import androidx.lifecycle.lifecycleScope
  9. import androidx.preference.CheckBoxPreference
  10. import androidx.preference.ListPreference
  11. import androidx.preference.Preference
  12. import androidx.preference.PreferenceCategory
  13. import androidx.preference.PreferenceFragmentCompat
  14. import im.angry.openeuicc.common.R
  15. import im.angry.openeuicc.util.PreferenceFlowWrapper
  16. import im.angry.openeuicc.util.preferenceRepository
  17. import im.angry.openeuicc.util.selfAppVersion
  18. import im.angry.openeuicc.util.setupRootViewInsets
  19. import kotlinx.coroutines.flow.collect
  20. import kotlinx.coroutines.flow.onEach
  21. import kotlinx.coroutines.launch
  22. open class SettingsFragment : PreferenceFragmentCompat() {
  23. private lateinit var developerPref: PreferenceCategory
  24. // Hidden developer options switch
  25. private var numClicks = 0
  26. private var lastClickTimestamp = -1L
  27. private var lastToast: Toast? = null
  28. override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
  29. setPreferencesFromResource(R.xml.pref_settings, rootKey)
  30. developerPref = requirePreference("pref_developer")
  31. // Show / hide developer preference based on whether it is enabled
  32. lifecycleScope.launch {
  33. preferenceRepository.developerOptionsEnabledFlow
  34. .onEach(developerPref::setVisible)
  35. .collect()
  36. }
  37. requirePreference<Preference>("pref_info_app_version").apply {
  38. summary = requireContext().selfAppVersion
  39. // Enable developer options when this is clicked for 7 times
  40. setOnPreferenceClickListener(::onAppVersionClicked)
  41. }
  42. requirePreference<Preference>("pref_advanced_language").apply {
  43. if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) return@apply
  44. isVisible = true
  45. intent = Intent(Settings.ACTION_APP_LOCALE_SETTINGS).apply {
  46. data = Uri.fromParts("package", requireContext().packageName, null)
  47. }
  48. }
  49. requirePreference<Preference>("pref_advanced_logs").apply {
  50. intent = Intent(requireContext(), LogsActivity::class.java)
  51. }
  52. requirePreference<CheckBoxPreference>("pref_notifications_download")
  53. .bindBooleanFlow(preferenceRepository.notificationDownloadFlow)
  54. requirePreference<CheckBoxPreference>("pref_notifications_delete")
  55. .bindBooleanFlow(preferenceRepository.notificationDeleteFlow)
  56. requirePreference<CheckBoxPreference>("pref_notifications_switch")
  57. .bindBooleanFlow(preferenceRepository.notificationSwitchFlow)
  58. requirePreference<CheckBoxPreference>("pref_advanced_disable_safeguard_removable_esim")
  59. .bindBooleanFlow(preferenceRepository.disableSafeguardFlow)
  60. requirePreference<CheckBoxPreference>("pref_advanced_verbose_logging")
  61. .bindBooleanFlow(preferenceRepository.verboseLoggingFlow)
  62. requirePreference<CheckBoxPreference>("pref_developer_unfiltered_profile_list")
  63. .bindBooleanFlow(preferenceRepository.unfilteredProfileListFlow)
  64. requirePreference<CheckBoxPreference>("pref_developer_ignore_tls_certificate")
  65. .bindBooleanFlow(preferenceRepository.ignoreTLSCertificateFlow)
  66. requirePreference<CheckBoxPreference>("pref_developer_refresh_after_switch")
  67. .bindBooleanFlow(preferenceRepository.refreshAfterSwitchFlow)
  68. requirePreference<ListPreference>("pref_developer_es10x_mss")
  69. .bindIntFlow(preferenceRepository.es10xMssFlow, 63)
  70. requirePreference<Preference>("pref_developer_isdr_aid_list").apply {
  71. intent = Intent(requireContext(), IsdrAidListActivity::class.java)
  72. }
  73. }
  74. protected fun <T : Preference> requirePreference(key: CharSequence) =
  75. findPreference<T>(key)!!
  76. override fun onStart() {
  77. super.onStart()
  78. setupRootViewInsets(requireView().requireViewById(R.id.recycler_view))
  79. }
  80. @Suppress("UNUSED_PARAMETER")
  81. private fun onAppVersionClicked(pref: Preference): Boolean {
  82. if (developerPref.isVisible) return false
  83. val now = System.currentTimeMillis()
  84. numClicks = if (now - lastClickTimestamp >= 1000) 1 else numClicks + 1
  85. lastClickTimestamp = now
  86. lifecycleScope.launch {
  87. preferenceRepository.developerOptionsEnabledFlow.updatePreference(numClicks >= 7)
  88. }
  89. val toastText = when {
  90. numClicks == 7 -> getString(R.string.developer_options_enabled)
  91. numClicks > 1 -> getString(R.string.developer_options_steps, 7 - numClicks)
  92. else -> return true
  93. }
  94. lastToast?.cancel()
  95. lastToast = Toast.makeText(requireContext(), toastText, Toast.LENGTH_SHORT)
  96. lastToast!!.show()
  97. return true
  98. }
  99. protected fun CheckBoxPreference.bindBooleanFlow(flow: PreferenceFlowWrapper<Boolean>) {
  100. lifecycleScope.launch {
  101. flow.collect(::setChecked)
  102. }
  103. setOnPreferenceChangeListener { _, newValue ->
  104. lifecycleScope.launch {
  105. flow.updatePreference(newValue as Boolean)
  106. }
  107. true
  108. }
  109. }
  110. private fun ListPreference.bindIntFlow(flow: PreferenceFlowWrapper<Int>, defaultValue: Int) {
  111. lifecycleScope.launch {
  112. flow.collect { value = it.toString() }
  113. }
  114. setOnPreferenceChangeListener { _, newValue ->
  115. lifecycleScope.launch {
  116. flow.updatePreference((newValue as String).toIntOrNull() ?: defaultValue)
  117. }
  118. true
  119. }
  120. }
  121. protected fun mergePreferenceOverlay(overlayKey: String, targetKey: String) {
  122. val overlayCat = requirePreference<PreferenceCategory>(overlayKey)
  123. val targetCat = requirePreference<PreferenceCategory>(targetKey)
  124. val prefs = buildList {
  125. for (i in 0..<overlayCat.preferenceCount) {
  126. add(overlayCat.getPreference(i))
  127. }
  128. }
  129. prefs.forEach {
  130. overlayCat.removePreference(it)
  131. targetCat.addPreference(it)
  132. }
  133. overlayCat.parent?.removePreference(overlayCat)
  134. }
  135. }