package im.angry.openeuicc.ui import android.content.Intent import android.content.pm.PackageManager import android.net.Uri import android.os.Build import android.os.Bundle import android.provider.Settings import android.widget.Toast import androidx.lifecycle.lifecycleScope import androidx.preference.CheckBoxPreference import androidx.preference.Preference import androidx.preference.PreferenceCategory import androidx.preference.PreferenceFragmentCompat import im.angry.openeuicc.common.R import im.angry.openeuicc.util.* import kotlinx.coroutines.flow.collect import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking open class SettingsFragment: PreferenceFragmentCompat() { private lateinit var developerPref: PreferenceCategory // Hidden developer options switch private var numClicks = 0 private var lastClickTimestamp = -1L private var lastToast: Toast? = null override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { setPreferencesFromResource(R.xml.pref_settings, rootKey) developerPref = requirePreference("pref_developer") // Show / hide developer preference based on whether it is enabled lifecycleScope.launch { preferenceRepository.developerOptionsEnabledFlow .onEach { developerPref.isVisible = it } .collect() } requirePreference("pref_info_app_version").apply { summary = requireContext().selfAppVersion // Enable developer options when this is clicked for 7 times setOnPreferenceClickListener(::onAppVersionClicked) } requirePreference("pref_advanced_language").apply { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) return@apply isVisible = true intent = Intent(Settings.ACTION_APP_LOCALE_SETTINGS).apply { data = Uri.fromParts("package", requireContext().packageName, null) } } requirePreference("pref_advanced_logs").apply { intent = Intent(requireContext(), LogsActivity::class.java) } requirePreference("pref_notifications_download") .bindBooleanFlow(preferenceRepository.notificationDownloadFlow) requirePreference("pref_notifications_delete") .bindBooleanFlow(preferenceRepository.notificationDeleteFlow) requirePreference("pref_notifications_switch") .bindBooleanFlow(preferenceRepository.notificationSwitchFlow) requirePreference("pref_advanced_disable_safeguard_removable_esim") .bindBooleanFlow(preferenceRepository.disableSafeguardFlow) requirePreference("pref_advanced_verbose_logging") .bindBooleanFlow(preferenceRepository.verboseLoggingFlow) requirePreference("pref_developer_unfiltered_profile_list") .bindBooleanFlow(preferenceRepository.unfilteredProfileListFlow) requirePreference("pref_developer_ignore_tls_certificate") .bindBooleanFlow(preferenceRepository.ignoreTLSCertificateFlow) requirePreference("pref_developer_euicc_memory_reset").apply { isVisible = context.packageManager.hasSystemFeature(PackageManager.FEATURE_USB_HOST) bindBooleanFlow(preferenceRepository.euiccMemoryResetFlow) } } protected fun requirePreference(key: CharSequence) = findPreference(key)!! override fun onStart() { super.onStart() setupRootViewInsets(requireView().requireViewById(R.id.recycler_view)) } @Suppress("UNUSED_PARAMETER") private fun onAppVersionClicked(pref: Preference): Boolean { if (developerPref.isVisible) return false val now = System.currentTimeMillis() if (now - lastClickTimestamp >= 1000) { numClicks = 1 } else { numClicks++ } lastClickTimestamp = now if (numClicks == 7) { lifecycleScope.launch { preferenceRepository.developerOptionsEnabledFlow.updatePreference(true) lastToast?.cancel() Toast.makeText( requireContext(), R.string.developer_options_enabled, Toast.LENGTH_SHORT ).show() } } else if (numClicks > 1) { lastToast?.cancel() lastToast = Toast.makeText( requireContext(), getString(R.string.developer_options_steps, 7 - numClicks), Toast.LENGTH_SHORT ) lastToast!!.show() } return true } protected fun CheckBoxPreference.bindBooleanFlow(flow: PreferenceFlowWrapper) { lifecycleScope.launch { flow.collect { isChecked = it } } setOnPreferenceChangeListener { _, newValue -> runBlocking { flow.updatePreference(newValue as Boolean) } true } } protected fun mergePreferenceOverlay(overlayKey: String, targetKey: String) { val overlayCat = requirePreference(overlayKey) val targetCat = requirePreference(targetKey) val prefs = buildList { for (i in 0..