Skip to content

Instantly share code, notes, and snippets.

@arunkumar9t2
Last active September 6, 2023 15:20
Show Gist options
  • Save arunkumar9t2/c41604b83ed56e6dc556c151fe3d8628 to your computer and use it in GitHub Desktop.
Save arunkumar9t2/c41604b83ed56e6dc556c151fe3d8628 to your computer and use it in GitHub Desktop.
Android PreferenceScreen DSL for using with androidx.preference framework.
@file:Suppress("NOTHING_TO_INLINE")
import android.content.Context
import androidx.annotation.StringRes
import androidx.fragment.app.Fragment
import androidx.preference.*
/**
* DSL marker for restricting access scope when [PreferencesBuilder.preference] is nested.
*/
@DslMarker
annotation class PreferenceDsl
/**
* Data structure that exposes methods required for preferences DSL.
*
* @param preferenceManager [PreferenceManager] instance received from [PreferenceFragmentCompat]
* @param preferenceGroup [PreferenceGroup] instance that will be used to add any newly created
* [Preference] instances
* @see preferences
*/
@PreferenceDsl
open class PreferencesBuilder<T : PreferenceGroup>(
val preferenceManager: PreferenceManager,
val preferenceGroup: T
) {
/**
* Managed [Context] instance from [preferenceManager] that has current preference theme and
* styles applied
*/
val preferenceContext: Context = preferenceManager.context
/**
* Creates an [Preference] instance of type [T] and adds to current [PreferenceScreen] in the
* hierarchy. [builder] can be used to access the created [Preference] and configure it.
*
* Note: This method uses reflection to instantiate [T]'s with single arg [Context] constructor
*/
inline fun <reified T : Preference> preference(crossinline builder: T.() -> Unit) {
T::class.java.getConstructor(Context::class.java)
.newInstance(preferenceContext)
.apply(builder)
.let(preferenceGroup::addPreference)
}
/**
* Same as [preference] but type defaulted to [Preference]
*/
inline fun simplePreference(crossinline builder: Preference.() -> Unit) {
preference(builder)
}
/**
* Adds a [PreferenceCategory] in the current [PreferenceScreen]. [builder] can be used to
* add additional [Preference] to this group.
*/
inline fun category(crossinline builder: CategoryPreferencesBuilder.() -> Unit) {
PreferenceCategory(preferenceContext).let { preferenceCategory ->
preferenceGroup.addPreference(preferenceCategory)
CategoryPreferencesBuilder(preferenceManager, preferenceCategory).apply(builder)
}
}
}
/**
* Data structure for constructing [Preference] hierarchy with given [PreferenceCategory] as the
* category header.
*/
@PreferenceDsl
class CategoryPreferencesBuilder(
preferenceManager: PreferenceManager,
preferenceScreen: PreferenceCategory
) : PreferencesBuilder<PreferenceCategory>(preferenceManager, preferenceScreen) {
fun properties(builder: PreferenceCategory.() -> Unit) {
preferenceGroup.apply(builder)
}
}
/**
* Constructs [PreferenceScreen] hierarchy and adds to this [PreferenceFragmentCompat] by calling
* [PreferenceFragmentCompat.setPreferenceScreen].
*
* Usage:
* ```
* override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
* preferences{
* simplePreference {
* titleRes(R.string.preference_search)
* keyRes(R.string.preference_key_search)
* summaryRes(R.string.preference_search_summary)
* targetFragment<SearchPreferencesFragment>()
* }
* }
* }
* ```
*/
inline fun PreferenceFragmentCompat.preferences(crossinline builder: PreferencesBuilder<PreferenceScreen>.() -> Unit) {
val context = preferenceManager.context
preferenceScreen = preferenceManager.createPreferenceScreen(context).also { screen ->
PreferencesBuilder(preferenceManager, screen).apply(builder)
}
}
inline fun <reified T : Fragment> Preference.targetFragment() {
fragment = T::class.java.name
}
inline fun Preference.keyRes(@StringRes keyRes: Int) {
key = context.getString(keyRes)
}
inline fun Preference.summaryRes(@StringRes summaryRes: Int) {
summary = context.getText(summaryRes)
}
inline fun Preference.titleRes(@StringRes tittleRes: Int) {
title = context.getText(tittleRes)
}
inline fun Preference.defaultValueRes(@StringRes defaultValueRes: Int) {
setDefaultValue(context.getString(defaultValueRes))
}
inline fun <reified T : Preference> Preference.summaryProvider(crossinline summaryProvider: T.() -> CharSequence) {
setSummaryProvider { (this as T).summaryProvider() }
}
fun Preference.defaultSummaryProvider() {
when {
this is ListPreference -> setSummaryProvider(ListPreference.SimpleSummaryProvider.getInstance())
this is EditTextPreference -> setSummaryProvider(EditTextPreference.SimpleSummaryProvider.getInstance())
}
}
@arunkumar9t2
Copy link
Author

arunkumar9t2 commented Mar 5, 2019

Sample usage

enter image description here

Note: Icons code not shown above

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment