mirror of
https://github.com/Aviortheking/Neo-Store.git
synced 2025-06-08 16:59:55 +00:00
Add: Prefs activity and base fragment
This commit is contained in:
parent
51a84d342b
commit
08badc9327
@ -0,0 +1,197 @@
|
|||||||
|
package com.looker.droidify.ui.activities
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.*
|
||||||
|
import android.view.inputmethod.InputMethodManager
|
||||||
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
import androidx.navigation.NavController
|
||||||
|
import androidx.navigation.fragment.NavHostFragment
|
||||||
|
import androidx.navigation.ui.AppBarConfiguration
|
||||||
|
import androidx.navigation.ui.navigateUp
|
||||||
|
import androidx.navigation.ui.setupActionBarWithNavController
|
||||||
|
import androidx.navigation.ui.setupWithNavController
|
||||||
|
import com.google.android.material.appbar.MaterialToolbar
|
||||||
|
import com.looker.droidify.BuildConfig
|
||||||
|
import com.looker.droidify.ContextWrapperX
|
||||||
|
import com.looker.droidify.MainApplication
|
||||||
|
import com.looker.droidify.R
|
||||||
|
import com.looker.droidify.content.Preferences
|
||||||
|
import com.looker.droidify.database.CursorOwner
|
||||||
|
import com.looker.droidify.databinding.ActivityPrefsXBinding
|
||||||
|
import com.looker.droidify.installer.AppInstaller
|
||||||
|
import com.looker.droidify.service.Connection
|
||||||
|
import com.looker.droidify.service.SyncService
|
||||||
|
import com.looker.droidify.ui.fragments.MainNavFragmentX
|
||||||
|
import com.looker.droidify.ui.fragments.Source
|
||||||
|
import com.looker.droidify.utility.extension.text.nullIfEmpty
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
// TODO clean up the bloat
|
||||||
|
class PrefsActivityX : AppCompatActivity() {
|
||||||
|
companion object {
|
||||||
|
const val ACTION_UPDATES = "${BuildConfig.APPLICATION_ID}.intent.action.UPDATES"
|
||||||
|
const val ACTION_INSTALL = "${BuildConfig.APPLICATION_ID}.intent.action.INSTALL"
|
||||||
|
const val EXTRA_CACHE_FILE_NAME =
|
||||||
|
"${BuildConfig.APPLICATION_ID}.intent.extra.CACHE_FILE_NAME"
|
||||||
|
}
|
||||||
|
|
||||||
|
sealed class SpecialIntent {
|
||||||
|
object Updates : SpecialIntent()
|
||||||
|
class Install(val packageName: String?, val cacheFileName: String?) : SpecialIntent()
|
||||||
|
}
|
||||||
|
|
||||||
|
lateinit var binding: ActivityPrefsXBinding
|
||||||
|
lateinit var toolbar: MaterialToolbar
|
||||||
|
lateinit var appBarConfiguration: AppBarConfiguration
|
||||||
|
private lateinit var navController: NavController
|
||||||
|
|
||||||
|
private val syncConnection = Connection(SyncService::class.java, onBind = { _, _ ->
|
||||||
|
navController.currentDestination?.let {
|
||||||
|
val source = Source.values()[when (it.id) {
|
||||||
|
R.id.updateTab -> 1
|
||||||
|
R.id.otherTab -> 2
|
||||||
|
else -> 0 // R.id.userTab
|
||||||
|
}]
|
||||||
|
updateUpdateNotificationBlocker(source)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
val db
|
||||||
|
get() = (application as MainApplication).db
|
||||||
|
|
||||||
|
lateinit var cursorOwner: CursorOwner
|
||||||
|
private set
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
setTheme(Preferences[Preferences.Key.Theme].getResId(resources.configuration))
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
|
binding = ActivityPrefsXBinding.inflate(layoutInflater)
|
||||||
|
binding.lifecycleOwner = this
|
||||||
|
toolbar = binding.toolbar
|
||||||
|
|
||||||
|
if (savedInstanceState == null && (intent.flags and Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY) == 0) {
|
||||||
|
handleIntent(intent)
|
||||||
|
}
|
||||||
|
setContentView(binding.root)
|
||||||
|
|
||||||
|
if (savedInstanceState == null) {
|
||||||
|
cursorOwner = CursorOwner()
|
||||||
|
supportFragmentManager.beginTransaction()
|
||||||
|
.add(cursorOwner, CursorOwner::class.java.name)
|
||||||
|
.commit()
|
||||||
|
} else {
|
||||||
|
cursorOwner = supportFragmentManager
|
||||||
|
.findFragmentByTag(CursorOwner::class.java.name) as CursorOwner
|
||||||
|
}
|
||||||
|
|
||||||
|
setSupportActionBar(toolbar)
|
||||||
|
|
||||||
|
toolbar.isFocusableInTouchMode = true
|
||||||
|
binding.collapsingToolbar.title = getString(R.string.settings)
|
||||||
|
|
||||||
|
val navHostFragment =
|
||||||
|
supportFragmentManager.findFragmentById(R.id.fragment_content) as NavHostFragment
|
||||||
|
navController = navHostFragment.navController
|
||||||
|
binding.bottomNavigation.setupWithNavController(navController)
|
||||||
|
|
||||||
|
appBarConfiguration = AppBarConfiguration(
|
||||||
|
setOf(R.id.exploreTab, R.id.latestTab, R.id.installedTab)
|
||||||
|
)
|
||||||
|
setupActionBarWithNavController(navController, appBarConfiguration)
|
||||||
|
|
||||||
|
supportFragmentManager.addFragmentOnAttachListener { _, _ ->
|
||||||
|
hideKeyboard()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onStart() {
|
||||||
|
super.onStart()
|
||||||
|
syncConnection.bind(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onSupportNavigateUp(): Boolean {
|
||||||
|
return navController.navigateUp(appBarConfiguration)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun hideKeyboard() {
|
||||||
|
(getSystemService(INPUT_METHOD_SERVICE) as? InputMethodManager)
|
||||||
|
?.hideSoftInputFromWindow((currentFocus ?: window.decorView).windowToken, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onNewIntent(intent: Intent?) {
|
||||||
|
super.onNewIntent(intent)
|
||||||
|
handleIntent(intent)
|
||||||
|
}
|
||||||
|
|
||||||
|
private val Intent.packageName: String?
|
||||||
|
get() {
|
||||||
|
val uri = data
|
||||||
|
return when {
|
||||||
|
uri?.scheme == "package" || uri?.scheme == "fdroid.app" -> {
|
||||||
|
uri.schemeSpecificPart?.nullIfEmpty()
|
||||||
|
}
|
||||||
|
uri?.scheme == "market" && uri.host == "details" -> {
|
||||||
|
uri.getQueryParameter("id")?.nullIfEmpty()
|
||||||
|
}
|
||||||
|
uri != null && uri.scheme in setOf("http", "https") -> {
|
||||||
|
val host = uri.host.orEmpty()
|
||||||
|
if (host == "f-droid.org" || host.endsWith(".f-droid.org")) {
|
||||||
|
uri.lastPathSegment?.nullIfEmpty()
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handleSpecialIntent(specialIntent: SpecialIntent) {
|
||||||
|
when (specialIntent) {
|
||||||
|
is SpecialIntent.Updates -> navController.navigate(R.id.installedTab)
|
||||||
|
is SpecialIntent.Install -> {
|
||||||
|
val packageName = specialIntent.packageName
|
||||||
|
if (!packageName.isNullOrEmpty()) {
|
||||||
|
lifecycleScope.launch {
|
||||||
|
specialIntent.cacheFileName?.let {
|
||||||
|
AppInstaller.getInstance(this@PrefsActivityX)
|
||||||
|
?.defaultInstaller?.install(packageName, it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Unit
|
||||||
|
}
|
||||||
|
}::class
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handleIntent(intent: Intent?) {
|
||||||
|
when (intent?.action) {
|
||||||
|
ACTION_UPDATES -> handleSpecialIntent(SpecialIntent.Updates)
|
||||||
|
ACTION_INSTALL -> handleSpecialIntent(
|
||||||
|
SpecialIntent.Install(
|
||||||
|
intent.packageName,
|
||||||
|
intent.getStringExtra(EXTRA_CACHE_FILE_NAME)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun attachBaseContext(newBase: Context) {
|
||||||
|
super.attachBaseContext(ContextWrapperX.wrap(newBase))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun updateUpdateNotificationBlocker(activeSource: Source) {
|
||||||
|
val blockerFragment = if (activeSource == Source.UPDATES) {
|
||||||
|
supportFragmentManager.fragments.asSequence().mapNotNull { it as? MainNavFragmentX }
|
||||||
|
.find { it.source == activeSource }
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
syncConnection.binder?.setUpdateNotificationBlocker(blockerFragment)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,356 @@
|
|||||||
|
package com.looker.droidify.ui.fragments
|
||||||
|
|
||||||
|
import android.app.Dialog
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.net.Uri
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.text.InputFilter
|
||||||
|
import android.text.InputType
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import android.view.WindowManager
|
||||||
|
import androidx.appcompat.app.AlertDialog
|
||||||
|
import androidx.appcompat.widget.LinearLayoutCompat
|
||||||
|
import androidx.core.net.toUri
|
||||||
|
import androidx.core.widget.NestedScrollView
|
||||||
|
import androidx.fragment.app.DialogFragment
|
||||||
|
import androidx.fragment.app.Fragment
|
||||||
|
import androidx.lifecycle.Lifecycle
|
||||||
|
import androidx.lifecycle.flowWithLifecycle
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
import com.google.android.material.circularreveal.CircularRevealFrameLayout
|
||||||
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
|
import com.google.android.material.switchmaterial.SwitchMaterial
|
||||||
|
import com.google.android.material.textfield.TextInputEditText
|
||||||
|
import com.google.android.material.textview.MaterialTextView
|
||||||
|
import com.looker.droidify.R
|
||||||
|
import com.looker.droidify.content.Preferences
|
||||||
|
import com.looker.droidify.databinding.FragmentPrefsBinding
|
||||||
|
import com.looker.droidify.databinding.PreferenceItemBinding
|
||||||
|
import com.looker.droidify.utility.extension.resources.*
|
||||||
|
import com.topjohnwu.superuser.Shell
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
abstract class PrefsNavFragmentX : Fragment() {
|
||||||
|
private lateinit var binding: FragmentPrefsBinding
|
||||||
|
private var preferenceBinding: PreferenceItemBinding? = null
|
||||||
|
private val preferences = mutableMapOf<Preferences.Key<*>, Preference<*>>()
|
||||||
|
|
||||||
|
override fun onResume() {
|
||||||
|
super.onResume()
|
||||||
|
preferences.forEach { (_, preference) -> preference.update() }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateView(
|
||||||
|
inflater: LayoutInflater,
|
||||||
|
container: ViewGroup?,
|
||||||
|
savedInstanceState: Bundle?,
|
||||||
|
): View {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
binding = FragmentPrefsBinding.inflate(inflater, container, false)
|
||||||
|
binding.lifecycleOwner = this
|
||||||
|
return binding.root
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
preferenceBinding = PreferenceItemBinding.inflate(layoutInflater)
|
||||||
|
|
||||||
|
val content = binding.fragmentContent
|
||||||
|
val scroll = NestedScrollView(content.context)
|
||||||
|
scroll.id = R.id.preferences_list
|
||||||
|
scroll.isFillViewport = true
|
||||||
|
content.addView(
|
||||||
|
scroll,
|
||||||
|
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||||
|
ViewGroup.LayoutParams.MATCH_PARENT
|
||||||
|
)
|
||||||
|
val scrollLayout = CircularRevealFrameLayout(content.context)
|
||||||
|
scroll.addView(
|
||||||
|
scrollLayout,
|
||||||
|
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||||
|
ViewGroup.LayoutParams.WRAP_CONTENT
|
||||||
|
)
|
||||||
|
|
||||||
|
setupPrefs(scrollLayout)
|
||||||
|
|
||||||
|
lifecycleScope.launch {
|
||||||
|
Preferences.subject
|
||||||
|
.flowWithLifecycle(lifecycle, Lifecycle.State.STARTED)
|
||||||
|
.collect { updatePreference(it) }
|
||||||
|
}
|
||||||
|
updatePreference(null)
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract fun setupPrefs(scrollLayout: CircularRevealFrameLayout)
|
||||||
|
|
||||||
|
private fun openURI(url: Uri) {
|
||||||
|
startActivity(Intent(Intent.ACTION_VIEW, url))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroyView() {
|
||||||
|
super.onDestroyView()
|
||||||
|
preferences.clear()
|
||||||
|
preferenceBinding = null
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun updatePreference(key: Preferences.Key<*>?) {
|
||||||
|
if (key != null) {
|
||||||
|
preferences[key]?.update()
|
||||||
|
}
|
||||||
|
if (key == null || key == Preferences.Key.ProxyType) {
|
||||||
|
val enabled = when (Preferences[Preferences.Key.ProxyType]) {
|
||||||
|
is Preferences.ProxyType.Direct -> false
|
||||||
|
is Preferences.ProxyType.Http, is Preferences.ProxyType.Socks -> true
|
||||||
|
}
|
||||||
|
preferences[Preferences.Key.ProxyHost]?.setEnabled(enabled)
|
||||||
|
preferences[Preferences.Key.ProxyPort]?.setEnabled(enabled)
|
||||||
|
}
|
||||||
|
if (key == Preferences.Key.RootPermission) {
|
||||||
|
preferences[Preferences.Key.RootPermission]?.setEnabled(
|
||||||
|
Shell.getCachedShell()?.isRoot
|
||||||
|
?: Shell.getShell().isRoot
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (key == Preferences.Key.Theme) {
|
||||||
|
requireActivity().recreate()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected fun LinearLayoutCompat.addText(title: String, summary: String, url: String) {
|
||||||
|
val text = MaterialTextView(context)
|
||||||
|
val subText = MaterialTextView(context)
|
||||||
|
text.text = title
|
||||||
|
subText.text = summary
|
||||||
|
text.setTextSizeScaled(16)
|
||||||
|
subText.setTextSizeScaled(14)
|
||||||
|
resources.sizeScaled(16).let {
|
||||||
|
text.setPadding(it, it, 5, 5)
|
||||||
|
subText.setPadding(it, 5, 5, 25)
|
||||||
|
}
|
||||||
|
addView(
|
||||||
|
text,
|
||||||
|
LinearLayoutCompat.LayoutParams.MATCH_PARENT,
|
||||||
|
LinearLayoutCompat.LayoutParams.WRAP_CONTENT
|
||||||
|
)
|
||||||
|
addView(
|
||||||
|
subText,
|
||||||
|
LinearLayoutCompat.LayoutParams.MATCH_PARENT,
|
||||||
|
LinearLayoutCompat.LayoutParams.WRAP_CONTENT
|
||||||
|
)
|
||||||
|
text.setOnClickListener { openURI(url.toUri()) }
|
||||||
|
subText.setOnClickListener { openURI(url.toUri()) }
|
||||||
|
}
|
||||||
|
|
||||||
|
protected inline fun LinearLayoutCompat.addCategory(
|
||||||
|
title: String,
|
||||||
|
callback: LinearLayoutCompat.() -> Unit,
|
||||||
|
) {
|
||||||
|
val text = MaterialTextView(context)
|
||||||
|
text.typeface = TypefaceExtra.medium
|
||||||
|
text.setTextSizeScaled(14)
|
||||||
|
text.setTextColor(text.context.getColorFromAttr(R.attr.colorPrimary))
|
||||||
|
text.text = title
|
||||||
|
resources.sizeScaled(16).let { text.setPadding(it, it, it, 0) }
|
||||||
|
addView(
|
||||||
|
text,
|
||||||
|
LinearLayoutCompat.LayoutParams.MATCH_PARENT,
|
||||||
|
LinearLayoutCompat.LayoutParams.WRAP_CONTENT
|
||||||
|
)
|
||||||
|
callback()
|
||||||
|
}
|
||||||
|
|
||||||
|
protected fun <T> LinearLayoutCompat.addPreference(
|
||||||
|
key: Preferences.Key<T>, title: String,
|
||||||
|
summaryProvider: () -> String, dialogProvider: ((Context) -> AlertDialog)?,
|
||||||
|
): Preference<T> {
|
||||||
|
val preference =
|
||||||
|
Preference(key, this@PrefsNavFragmentX, this, title, summaryProvider, dialogProvider)
|
||||||
|
preferences[key] = preference
|
||||||
|
return preference
|
||||||
|
}
|
||||||
|
|
||||||
|
protected fun LinearLayoutCompat.addSwitch(
|
||||||
|
key: Preferences.Key<Boolean>,
|
||||||
|
title: String,
|
||||||
|
summary: String,
|
||||||
|
) {
|
||||||
|
val preference = addPreference(key, title, { summary }, null)
|
||||||
|
preference.check.visibility = View.VISIBLE
|
||||||
|
preference.view.setOnClickListener { Preferences[key] = !Preferences[key] }
|
||||||
|
preference.setCallback { preference.check.isChecked = Preferences[key] }
|
||||||
|
}
|
||||||
|
|
||||||
|
protected fun <T> LinearLayoutCompat.addEdit(
|
||||||
|
key: Preferences.Key<T>, title: String, valueToString: (T) -> String,
|
||||||
|
stringToValue: (String) -> T?, configureEdit: (TextInputEditText) -> Unit,
|
||||||
|
) {
|
||||||
|
addPreference(key, title, { valueToString(Preferences[key]) }) { it ->
|
||||||
|
val scroll = NestedScrollView(it)
|
||||||
|
scroll.resources.sizeScaled(20).let { scroll.setPadding(it, 0, it, 0) }
|
||||||
|
val edit = TextInputEditText(it)
|
||||||
|
configureEdit(edit)
|
||||||
|
edit.id = android.R.id.edit
|
||||||
|
edit.resources.sizeScaled(16)
|
||||||
|
.let { edit.setPadding(edit.paddingLeft, it, edit.paddingRight, it) }
|
||||||
|
edit.setText(valueToString(Preferences[key]))
|
||||||
|
edit.hint = edit.text.toString()
|
||||||
|
edit.text?.let { editable -> edit.setSelection(editable.length) }
|
||||||
|
edit.requestFocus()
|
||||||
|
scroll.addView(
|
||||||
|
edit,
|
||||||
|
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||||
|
ViewGroup.LayoutParams.WRAP_CONTENT
|
||||||
|
)
|
||||||
|
MaterialAlertDialogBuilder(it)
|
||||||
|
.setTitle(title)
|
||||||
|
.setView(scroll)
|
||||||
|
.setPositiveButton(R.string.ok) { _, _ ->
|
||||||
|
val value = stringToValue(edit.text.toString()) ?: key.default.value
|
||||||
|
post { Preferences[key] = value }
|
||||||
|
}
|
||||||
|
.setNegativeButton(R.string.cancel, null)
|
||||||
|
.create()
|
||||||
|
.apply {
|
||||||
|
window!!.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected fun LinearLayoutCompat.addEditString(key: Preferences.Key<String>, title: String) {
|
||||||
|
addEdit(key, title, { it }, { it }, { })
|
||||||
|
}
|
||||||
|
|
||||||
|
protected fun LinearLayoutCompat.addEditInt(
|
||||||
|
key: Preferences.Key<Int>,
|
||||||
|
title: String,
|
||||||
|
range: IntRange?,
|
||||||
|
) {
|
||||||
|
addEdit(key, title, { it.toString() }, { it.toIntOrNull() }) {
|
||||||
|
it.inputType = InputType.TYPE_CLASS_NUMBER or InputType.TYPE_NUMBER_FLAG_DECIMAL
|
||||||
|
if (range != null) it.filters =
|
||||||
|
arrayOf(InputFilter { source, start, end, dest, dstart, dend ->
|
||||||
|
val value = (dest.substring(0, dstart) + source.substring(start, end) +
|
||||||
|
dest.substring(dend, dest.length)).toIntOrNull()
|
||||||
|
if (value != null && value in range) null else ""
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected fun <T : Preferences.Enumeration<T>> LinearLayoutCompat.addEnumeration(
|
||||||
|
key: Preferences.Key<T>,
|
||||||
|
title: String,
|
||||||
|
valueToString: (T) -> String,
|
||||||
|
) {
|
||||||
|
addPreference(key, title, { valueToString(Preferences[key]) }) {
|
||||||
|
val values = key.default.value.values
|
||||||
|
MaterialAlertDialogBuilder(it)
|
||||||
|
.setTitle(title)
|
||||||
|
.setSingleChoiceItems(
|
||||||
|
values.map(valueToString).toTypedArray(),
|
||||||
|
values.indexOf(Preferences[key])
|
||||||
|
) { dialog, which ->
|
||||||
|
dialog.dismiss()
|
||||||
|
post { Preferences[key] = values[which] }
|
||||||
|
}
|
||||||
|
.setNegativeButton(R.string.cancel, null)
|
||||||
|
.create()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected fun <T> LinearLayoutCompat.addList(
|
||||||
|
key: Preferences.Key<T>,
|
||||||
|
title: String,
|
||||||
|
values: List<T>,
|
||||||
|
valueToString: (T) -> String,
|
||||||
|
) {
|
||||||
|
addPreference(key, title, { valueToString(Preferences[key]) }) {
|
||||||
|
MaterialAlertDialogBuilder(it)
|
||||||
|
.setTitle(title)
|
||||||
|
.setSingleChoiceItems(
|
||||||
|
values.map(valueToString).toTypedArray(),
|
||||||
|
values.indexOf(Preferences[key])
|
||||||
|
) { dialog, which ->
|
||||||
|
dialog.dismiss()
|
||||||
|
post { Preferences[key] = values[which] }
|
||||||
|
}
|
||||||
|
.setNegativeButton(R.string.cancel, null)
|
||||||
|
.create()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected class Preference<T>(
|
||||||
|
private val key: Preferences.Key<T>,
|
||||||
|
fragment: Fragment,
|
||||||
|
parent: ViewGroup,
|
||||||
|
titleText: String,
|
||||||
|
private val summaryProvider: () -> String,
|
||||||
|
private val dialogProvider: ((Context) -> AlertDialog)?,
|
||||||
|
) {
|
||||||
|
val view = parent.inflate(R.layout.preference_item)
|
||||||
|
val title = view.findViewById<MaterialTextView>(R.id.title)!!
|
||||||
|
val summary = view.findViewById<MaterialTextView>(R.id.summary)!!
|
||||||
|
val check = view.findViewById<SwitchMaterial>(R.id.check)!!
|
||||||
|
|
||||||
|
private var callback: (() -> Unit)? = null
|
||||||
|
|
||||||
|
init {
|
||||||
|
title.text = titleText
|
||||||
|
parent.addView(view)
|
||||||
|
if (dialogProvider != null) {
|
||||||
|
view.setOnClickListener {
|
||||||
|
PreferenceDialog(key.name)
|
||||||
|
.show(
|
||||||
|
fragment.childFragmentManager,
|
||||||
|
"${PreferenceDialog::class.java.name}.${key.name}"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
update()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setCallback(callback: () -> Unit) {
|
||||||
|
this.callback = callback
|
||||||
|
update()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setEnabled(enabled: Boolean) {
|
||||||
|
view.isEnabled = enabled
|
||||||
|
title.isEnabled = enabled
|
||||||
|
summary.isEnabled = enabled
|
||||||
|
check.isEnabled = enabled
|
||||||
|
}
|
||||||
|
|
||||||
|
fun update() {
|
||||||
|
summary.text = summaryProvider()
|
||||||
|
summary.visibility = if (summary.text.isNotEmpty()) View.VISIBLE else View.GONE
|
||||||
|
callback?.invoke()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createDialog(context: Context): AlertDialog {
|
||||||
|
return dialogProvider!!(context)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class PreferenceDialog() : DialogFragment() {
|
||||||
|
companion object {
|
||||||
|
private const val EXTRA_KEY = "key"
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(key: String) : this() {
|
||||||
|
arguments = Bundle().apply {
|
||||||
|
putString(EXTRA_KEY, key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||||
|
val preferences = (parentFragment as PrefsNavFragmentX).preferences
|
||||||
|
val key = requireArguments().getString(EXTRA_KEY)!!
|
||||||
|
.let { name -> preferences.keys.find { it.name == name }!! }
|
||||||
|
val preference = preferences[key]!!
|
||||||
|
return preference.createDialog(requireContext())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user