SettingsFragment.kt 5.9 KB

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