Remove: Prefs Fragments

This commit is contained in:
machiav3lli 2022-09-18 16:11:08 +02:00
parent 68188d208c
commit 217c4e8997
6 changed files with 0 additions and 599 deletions

View File

@ -1,362 +0,0 @@
package com.machiav3lli.fdroid.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 android.widget.LinearLayout
import androidx.appcompat.app.AlertDialog
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.machiav3lli.fdroid.R
import com.machiav3lli.fdroid.content.Preferences
import com.machiav3lli.fdroid.databinding.FragmentPrefsBinding
import com.machiav3lli.fdroid.databinding.PreferenceItemBinding
import com.machiav3lli.fdroid.utility.Utils
import com.machiav3lli.fdroid.utility.extension.resources.TypefaceExtra
import com.machiav3lli.fdroid.utility.extension.resources.getColorFromAttr
import com.machiav3lli.fdroid.utility.extension.resources.inflate
import com.machiav3lli.fdroid.utility.extension.resources.setTextSizeScaled
import com.machiav3lli.fdroid.utility.extension.resources.sizeScaled
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 == null || key == Preferences.Key.RootPermission) {
preferences[Preferences.Key.RootPermission]?.setEnabled(
Shell.getCachedShell()?.isRoot
?: Shell.getShell().isRoot
)
preferences[Preferences.Key.RootSessionInstaller]?.setEnabled(Utils.rootInstallerEnabled)
}
if (key == Preferences.Key.Theme) {
requireActivity().recreate()
}
}
protected fun LinearLayout.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,
LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.WRAP_CONTENT
)
addView(
subText,
LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.WRAP_CONTENT
)
text.setOnClickListener { openURI(url.toUri()) }
subText.setOnClickListener { openURI(url.toUri()) }
}
protected inline fun LinearLayout.addCategory(
title: String,
callback: LinearLayout.() -> 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,
LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.WRAP_CONTENT
)
callback()
}
protected fun <T> LinearLayout.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 LinearLayout.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> LinearLayout.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 LinearLayout.addEditString(key: Preferences.Key<String>, title: String) {
addEdit(key, title, { it }, { it }, { })
}
protected fun LinearLayout.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>> LinearLayout.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> LinearLayout.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())
}
}
}

View File

@ -1,45 +0,0 @@
package com.machiav3lli.fdroid.ui.fragments
import android.view.ViewGroup
import android.widget.LinearLayout
import com.google.android.material.circularreveal.CircularRevealFrameLayout
import com.machiav3lli.fdroid.BuildConfig
import com.machiav3lli.fdroid.R
import com.machiav3lli.fdroid.content.Preferences
class PrefsOtherFragment : PrefsNavFragmentX() {
override fun setupPrefs(scrollLayout: CircularRevealFrameLayout) {
val preferences = LinearLayout(scrollLayout.context)
preferences.orientation = LinearLayout.VERTICAL
scrollLayout.addView(
preferences,
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
)
preferences.addCategory(getString(R.string.proxy)) {
addEnumeration(Preferences.Key.ProxyType, getString(R.string.proxy_type)) {
when (it) {
is Preferences.ProxyType.Direct -> getString(R.string.no_proxy)
is Preferences.ProxyType.Http -> getString(R.string.http_proxy)
is Preferences.ProxyType.Socks -> getString(R.string.socks_proxy)
}
}
addEditString(Preferences.Key.ProxyHost, getString(R.string.proxy_host))
addEditInt(Preferences.Key.ProxyPort, getString(R.string.proxy_port), 1..65535)
}
preferences.addCategory(getString(R.string.credits)) {
addText(
title = "Based on Foxy-Droid",
summary = "FoxyDroid",
url = "https://github.com/kitsunyan/foxy-droid/"
)
addText(
title = getString(R.string.application_name),
summary = "v ${BuildConfig.VERSION_NAME}",
url = "https://github.com/NeoApplications/Neo-Store/"
)
}
}
}

View File

@ -1,75 +0,0 @@
package com.machiav3lli.fdroid.ui.fragments
import android.view.ViewGroup
import android.widget.LinearLayout
import com.google.android.material.circularreveal.CircularRevealFrameLayout
import com.machiav3lli.fdroid.R
import com.machiav3lli.fdroid.content.Preferences
class PrefsUpdatesFragment : PrefsNavFragmentX() {
override fun setupPrefs(scrollLayout: CircularRevealFrameLayout) {
val preferences = LinearLayout(scrollLayout.context)
preferences.orientation = LinearLayout.VERTICAL
scrollLayout.addView(
preferences,
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
)
preferences.addCategory(getString(R.string.updates)) {
addEnumeration(
Preferences.Key.AutoSync,
getString(R.string.sync_repositories_automatically)
) {
when (it) {
Preferences.AutoSync.Never -> getString(R.string.never)
Preferences.AutoSync.Wifi -> getString(R.string.only_on_wifi)
Preferences.AutoSync.WifiBattery -> getString(R.string.only_on_wifi_and_battery)
Preferences.AutoSync.Always -> getString(R.string.always)
}
}
addEditInt(
Preferences.Key.ImagesCacheRetention,
getString(R.string.images_cache_retention),
1..365
)
addEditInt(
Preferences.Key.ReleasesCacheRetention,
getString(R.string.releases_cache_retention),
0..365
)
addEditInt(
Preferences.Key.AutoSyncInterval,
getString(R.string.auto_sync_interval),
1..1440
)
addSwitch(
Preferences.Key.InstallAfterSync, getString(R.string.install_after_sync),
getString(R.string.install_after_sync_summary)
)
addSwitch(
Preferences.Key.UpdateNotify, getString(R.string.notify_about_updates),
getString(R.string.notify_about_updates_summary)
)
addSwitch(
Preferences.Key.UpdateUnstable, getString(R.string.unstable_updates),
getString(R.string.unstable_updates_summary)
)
addSwitch(
Preferences.Key.IncompatibleVersions, getString(R.string.incompatible_versions),
getString(R.string.incompatible_versions_summary)
)
}
preferences.addCategory(getString(R.string.install_types)) {
addSwitch(
Preferences.Key.RootPermission, getString(R.string.root_permission),
getString(R.string.root_permission_description)
)
addSwitch(
Preferences.Key.RootSessionInstaller, getString(R.string.root_session_installer),
getString(R.string.root_session_installer_description)
)
}
}
}

View File

@ -1,56 +0,0 @@
package com.machiav3lli.fdroid.ui.fragments
import android.view.ViewGroup
import android.widget.LinearLayout
import com.google.android.material.circularreveal.CircularRevealFrameLayout
import com.machiav3lli.fdroid.R
import com.machiav3lli.fdroid.content.Preferences
import com.machiav3lli.fdroid.utility.Utils.getLocaleOfCode
import com.machiav3lli.fdroid.utility.Utils.languagesList
import com.machiav3lli.fdroid.utility.Utils.translateLocale
class PrefsUserFragment : PrefsNavFragmentX() {
override fun setupPrefs(scrollLayout: CircularRevealFrameLayout) {
val preferences = LinearLayout(scrollLayout.context)
preferences.orientation = LinearLayout.VERTICAL
scrollLayout.addView(
preferences,
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
)
preferences.addCategory(requireContext().getString(R.string.prefs_personalization)) {
addList(
Preferences.Key.Language,
context.getString(R.string.prefs_language_title),
languagesList
) { translateLocale(context.getLocaleOfCode(it)) }
addEnumeration(Preferences.Key.Theme, getString(R.string.theme)) {
when (it) {
is Preferences.Theme.System -> getString(R.string.system)
is Preferences.Theme.SystemBlack -> getString(R.string.system) + " " + getString(
R.string.amoled
)
is Preferences.Theme.Dynamic -> getString(R.string.dynamic)
is Preferences.Theme.Light -> getString(R.string.light)
is Preferences.Theme.Dark -> getString(R.string.dark)
is Preferences.Theme.Black -> getString(R.string.amoled)
}
}
addEnumeration(Preferences.Key.DefaultTab, getString(R.string.default_tab)) {
when (it) {
is Preferences.DefaultTab.Explore -> getString(R.string.explore)
is Preferences.DefaultTab.Latest -> getString(R.string.latest)
is Preferences.DefaultTab.Installed -> getString(R.string.installed)
}
}
addSwitch(
Preferences.Key.ShowScreenshots, getString(R.string.show_screenshots),
getString(R.string.show_screenshots_description)
)
addEditInt(Preferences.Key.UpdatedApps, getString(R.string.prefs_updated_apps), 1..200)
addEditInt(Preferences.Key.NewApps, getString(R.string.prefs_new_apps), 1..50)
}
}
}

View File

@ -1,16 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
</data>
<com.google.android.material.card.MaterialCardView
android:id="@+id/fragment_content"
style="?materialCardViewFilledStyle"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:cardBackgroundColor="?android:colorBackground"
app:shapeAppearanceOverlay="@style/Shape.BottomSheet" />
</layout>

View File

@ -1,45 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?android:attr/selectableItemBackground"
android:orientation="horizontal">
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical"
android:padding="16dp">
<com.google.android.material.textview.MaterialTextView
android:id="@+id/title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:singleLine="true"
android:textSize="16sp" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/summary"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="14sp" />
</LinearLayout>
<com.google.android.material.switchmaterial.SwitchMaterial
android:id="@+id/check"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_marginStart="-16dp"
android:clickable="false"
android:paddingHorizontal="12dp"
android:visibility="gone" />
</LinearLayout>
</layout>