aboutsummaryrefslogtreecommitdiff
path: root/core/src/main/kotlin/ca/allanwang/kau/kpref
diff options
context:
space:
mode:
Diffstat (limited to 'core/src/main/kotlin/ca/allanwang/kau/kpref')
-rw-r--r--core/src/main/kotlin/ca/allanwang/kau/kpref/KPref.kt35
-rw-r--r--core/src/main/kotlin/ca/allanwang/kau/kpref/KPrefActivity.kt137
-rw-r--r--core/src/main/kotlin/ca/allanwang/kau/kpref/KPrefBinder.kt120
-rw-r--r--core/src/main/kotlin/ca/allanwang/kau/kpref/KPrefDelegate.kt89
-rw-r--r--core/src/main/kotlin/ca/allanwang/kau/kpref/items/KPrefCheckbox.kt33
-rw-r--r--core/src/main/kotlin/ca/allanwang/kau/kpref/items/KPrefColorPicker.kt73
-rw-r--r--core/src/main/kotlin/ca/allanwang/kau/kpref/items/KPrefHeader.kt25
-rw-r--r--core/src/main/kotlin/ca/allanwang/kau/kpref/items/KPrefItemBase.kt85
-rw-r--r--core/src/main/kotlin/ca/allanwang/kau/kpref/items/KPrefItemCore.kt126
-rw-r--r--core/src/main/kotlin/ca/allanwang/kau/kpref/items/KPrefPlainText.kt29
-rw-r--r--core/src/main/kotlin/ca/allanwang/kau/kpref/items/KPrefSubItems.kt43
-rw-r--r--core/src/main/kotlin/ca/allanwang/kau/kpref/items/KPrefText.kt62
12 files changed, 857 insertions, 0 deletions
diff --git a/core/src/main/kotlin/ca/allanwang/kau/kpref/KPref.kt b/core/src/main/kotlin/ca/allanwang/kau/kpref/KPref.kt
new file mode 100644
index 0000000..7fd8955
--- /dev/null
+++ b/core/src/main/kotlin/ca/allanwang/kau/kpref/KPref.kt
@@ -0,0 +1,35 @@
+package ca.allanwang.kau.kpref
+
+import android.content.Context
+import android.content.SharedPreferences
+
+/**
+ * Created by Allan Wang on 2017-06-07.
+ */
+open class KPref {
+
+ lateinit private var c: Context
+ lateinit internal var PREFERENCE_NAME: String
+ private var initialized = false
+
+ fun initialize(c: Context, preferenceName: String) {
+ if (initialized) throw KPrefException("KPref object $preferenceName has already been initialized; please only do so once")
+ initialized = true
+ this.c = c.applicationContext
+ PREFERENCE_NAME = preferenceName
+ }
+
+ internal val sp: SharedPreferences by lazy {
+ if (!initialized) throw KPrefException("KPref object has not yet been initialized; please initialize it with a context and preference name")
+ c.getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE)
+ }
+
+ internal val prefMap: MutableMap<String, KPrefDelegate<*>> = mutableMapOf()
+
+ fun reset() {
+ prefMap.values.forEach { it.invalidate() }
+ }
+
+ operator fun get(key: String): KPrefDelegate<*>? = prefMap[key]
+
+} \ No newline at end of file
diff --git a/core/src/main/kotlin/ca/allanwang/kau/kpref/KPrefActivity.kt b/core/src/main/kotlin/ca/allanwang/kau/kpref/KPrefActivity.kt
new file mode 100644
index 0000000..9a9f7d4
--- /dev/null
+++ b/core/src/main/kotlin/ca/allanwang/kau/kpref/KPrefActivity.kt
@@ -0,0 +1,137 @@
+package ca.allanwang.kau.kpref
+
+import android.os.Bundle
+import android.support.annotation.StringRes
+import android.support.constraint.ConstraintLayout
+import android.support.v7.app.AppCompatActivity
+import android.support.v7.widget.RecyclerView
+import android.support.v7.widget.Toolbar
+import android.view.View
+import android.view.animation.Animation
+import android.view.animation.AnimationUtils
+import android.widget.FrameLayout
+import android.widget.ViewAnimator
+import ca.allanwang.kau.R
+import ca.allanwang.kau.kpref.items.KPrefItemCore
+import ca.allanwang.kau.utils.bindView
+import ca.allanwang.kau.utils.resolveColor
+import ca.allanwang.kau.utils.statusBarColor
+import ca.allanwang.kau.utils.string
+import ca.allanwang.kau.widgets.TextSlider
+import ca.allanwang.kau.views.RippleCanvas
+import com.mikepenz.fastadapter.commons.adapters.FastItemAdapter
+
+abstract class KPrefActivity : AppCompatActivity(), KPrefActivityContract {
+
+ val adapter: FastItemAdapter<KPrefItemCore>
+ @Suppress("UNCHECKED_CAST")
+ get() = recycler.adapter as FastItemAdapter<KPrefItemCore>
+ val recycler: RecyclerView
+ get() = prefHolder.currentView as RecyclerView
+ val container: ConstraintLayout by bindView(R.id.kau_container)
+ val bgCanvas: RippleCanvas by bindView(R.id.kau_ripple)
+ val toolbarCanvas: RippleCanvas by bindView(R.id.kau_toolbar_ripple)
+ val toolbar: Toolbar by bindView(R.id.kau_toolbar)
+ val toolbarTitle: TextSlider by bindView(R.id.kau_toolbar_text)
+ val prefHolder: ViewAnimator by bindView(R.id.kau_holder)
+ private lateinit var globalOptions: GlobalOptions
+ var animate: Boolean = true
+ set(value) {
+ field = value
+ toolbarTitle.animationType = if (value) TextSlider.ANIMATION_SLIDE_HORIZONTAL else TextSlider.ANIMATION_NONE
+ }
+
+ private val SLIDE_IN_LEFT_ITEMS: Animation by lazy { AnimationUtils.loadAnimation(this, R.anim.kau_slide_in_left) }
+ private val SLIDE_IN_RIGHT_ITEMS: Animation by lazy { AnimationUtils.loadAnimation(this, R.anim.kau_slide_in_right) }
+ private val SLIDE_OUT_LEFT_ITEMS: Animation by lazy { AnimationUtils.loadAnimation(this, R.anim.kau_slide_out_left) }
+ private val SLIDE_OUT_RIGHT_ITEMS: Animation by lazy { AnimationUtils.loadAnimation(this, R.anim.kau_slide_out_right) }
+
+ /**
+ * Core attribute builder that is consistent throughout all items
+ * Leave blank to use defaults
+ */
+ abstract fun kPrefCoreAttributes(): CoreAttributeContract.() -> Unit
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ //setup layout
+ setContentView(R.layout.kau_activity_kpref)
+ setSupportActionBar(toolbar)
+ if (supportActionBar != null)
+ with(supportActionBar!!) {
+ setDisplayHomeAsUpEnabled(true)
+ setDisplayShowHomeEnabled(true)
+ toolbar.setNavigationOnClickListener { onBackPressed() }
+ setDisplayShowTitleEnabled(false)
+ }
+ window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_STABLE or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
+ statusBarColor = 0x30000000
+ toolbarCanvas.set(resolveColor(R.attr.colorPrimary))
+ bgCanvas.set(resolveColor(android.R.attr.colorBackground))
+ prefHolder.animateFirstView = false
+ //setup prefs
+ val core = CoreAttributeBuilder()
+ val builder = kPrefCoreAttributes()
+ core.builder()
+ globalOptions = GlobalOptions(core, this)
+ showNextPrefs(R.string.kau_settings, onCreateKPrefs(savedInstanceState))
+ }
+
+ override fun onPostCreate(savedInstanceState: Bundle?) {
+ super.onPostCreate(savedInstanceState)
+ }
+
+ override fun showNextPrefs(@StringRes toolbarTitleRes: Int, builder: KPrefAdapterBuilder.() -> Unit) {
+ val rv = RecyclerView(this).apply {
+ layoutParams = FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT)
+ setKPrefAdapter(globalOptions, builder)
+ }
+ with(prefHolder) {
+ inAnimation = if (animate) SLIDE_IN_RIGHT_ITEMS else null
+ outAnimation = if (animate) SLIDE_OUT_LEFT_ITEMS else null
+ addView(rv)
+ showNext()
+ }
+ toolbarTitle.setNextText(string(toolbarTitleRes))
+ }
+
+ override fun showPrevPrefs() {
+ val current = prefHolder.currentView
+ with(prefHolder) {
+ inAnimation = if (animate) SLIDE_IN_LEFT_ITEMS else null
+ outAnimation = if (animate) SLIDE_OUT_RIGHT_ITEMS else null
+ showPrevious()
+ removeView(current)
+ adapter.notifyAdapterDataSetChanged()
+ }
+ toolbarTitle.setPrevText()
+ }
+
+ fun reload(vararg index: Int) {
+ if (index.isEmpty()) adapter.notifyAdapterDataSetChanged()
+ else index.forEach { adapter.notifyItemChanged(it) }
+ }
+
+ override fun reloadByTitle(@StringRes vararg title: Int) {
+ if (title.isEmpty()) return
+ adapter.adapterItems.forEachIndexed { index, item ->
+ if (title.any { item.core.titleRes == it })
+ adapter.notifyItemChanged(index)
+ }
+ }
+
+ abstract fun onCreateKPrefs(savedInstanceState: Bundle?): KPrefAdapterBuilder.() -> Unit
+
+ override fun onBackPressed() {
+ if (!backPress()) super.onBackPressed()
+ }
+
+ fun backPress(): Boolean {
+ if (!toolbarTitle.isRoot) {
+ showPrevPrefs()
+ return true
+ }
+ return false
+ }
+}
+
diff --git a/core/src/main/kotlin/ca/allanwang/kau/kpref/KPrefBinder.kt b/core/src/main/kotlin/ca/allanwang/kau/kpref/KPrefBinder.kt
new file mode 100644
index 0000000..7f42d2a
--- /dev/null
+++ b/core/src/main/kotlin/ca/allanwang/kau/kpref/KPrefBinder.kt
@@ -0,0 +1,120 @@
+package ca.allanwang.kau.kpref
+
+import android.support.annotation.StringRes
+import android.support.v7.widget.LinearLayoutManager
+import android.support.v7.widget.RecyclerView
+import ca.allanwang.kau.R
+import ca.allanwang.kau.kpref.items.*
+import com.mikepenz.fastadapter.commons.adapters.FastItemAdapter
+import org.jetbrains.anko.doAsync
+import org.jetbrains.anko.uiThread
+
+/**
+ * Created by Allan Wang on 2017-06-08.
+ *
+ * Houses all the components that can be called externally to setup the kpref mainAdapter
+ */
+
+/**
+ * Base extension that will register the layout manager and mainAdapter with the given items
+ * Returns FastAdapter
+ */
+fun RecyclerView.setKPrefAdapter(globalOptions: GlobalOptions, builder: KPrefAdapterBuilder.() -> Unit): FastItemAdapter<KPrefItemCore> {
+ layoutManager = LinearLayoutManager(context)
+ val adapter = FastItemAdapter<KPrefItemCore>()
+ adapter.withOnClickListener { v, _, item, _ -> item.onClick(v, v.findViewById(R.id.kau_pref_inner_content)) }
+ this.adapter = adapter
+ doAsync {
+ val items = KPrefAdapterBuilder(globalOptions)
+ builder.invoke(items)
+ uiThread {
+ adapter.add(items.list)
+ }
+ }
+ return adapter
+}
+
+@DslMarker
+annotation class KPrefMarker
+
+/**
+ * Contains attributes shared amongst all kpref items
+ */
+@KPrefMarker
+interface CoreAttributeContract {
+ var textColor: (() -> Int)?
+ var accentColor: (() -> Int)?
+}
+
+/**
+ * Implementation of [CoreAttributeContract]
+ */
+class CoreAttributeBuilder : CoreAttributeContract {
+ override var textColor: (() -> Int)? = null
+ override var accentColor: (() -> Int)? = textColor
+}
+
+interface KPrefActivityContract {
+ fun showNextPrefs(@StringRes toolbarTitleRes: Int, builder: KPrefAdapterBuilder.() -> Unit)
+ fun showPrevPrefs()
+ fun reloadByTitle(@StringRes vararg title: Int)
+}
+
+
+class GlobalOptions(core: CoreAttributeContract, activity: KPrefActivityContract
+) : CoreAttributeContract by core, KPrefActivityContract by activity
+
+
+/**
+ * Builder for kpref items
+ * Contains DSLs for every possible item
+ * The arguments are all the mandatory values plus an optional builder housing all the possible configurations
+ * The mandatory values are final so they cannot be edited in the builder
+ */
+@KPrefMarker
+class KPrefAdapterBuilder(internal val globalOptions: GlobalOptions) {
+
+ @KPrefMarker
+ fun header(@StringRes title: Int)
+ = list.add(KPrefHeader(KPrefItemCore.CoreBuilder(globalOptions, title)))
+
+ @KPrefMarker
+ fun checkbox(@StringRes title: Int,
+ getter: (() -> Boolean),
+ setter: ((value: Boolean) -> Unit),
+ builder: KPrefItemBase.BaseContract<Boolean>.() -> Unit = {})
+ = list.add(KPrefCheckbox(KPrefItemBase.BaseBuilder(globalOptions, title, getter, setter)
+ .apply { builder() }))
+
+ @KPrefMarker
+ fun colorPicker(@StringRes title: Int,
+ getter: (() -> Int),
+ setter: ((value: Int) -> Unit),
+ builder: KPrefColorPicker.KPrefColorContract.() -> Unit = {})
+ = list.add(KPrefColorPicker(KPrefColorPicker.KPrefColorBuilder(globalOptions, title, getter, setter)
+ .apply { builder() }))
+
+ @KPrefMarker
+ fun <T> text(@StringRes title: Int,
+ getter: (() -> T),
+ setter: ((value: T) -> Unit),
+ builder: KPrefText.KPrefTextContract<T>.() -> Unit = {})
+ = list.add(KPrefText<T>(KPrefText.KPrefTextBuilder<T>(globalOptions, title, getter, setter)
+ .apply { builder() }))
+
+ @KPrefMarker
+ fun subItems(@StringRes title: Int,
+ itemBuilder: KPrefAdapterBuilder.() -> Unit,
+ builder: KPrefSubItems.KPrefSubItemsContract.() -> Unit)
+ = list.add(KPrefSubItems(KPrefSubItems.KPrefSubItemsBuilder(globalOptions, title, itemBuilder)
+ .apply { builder() }))
+
+ @KPrefMarker
+ fun plainText(@StringRes title: Int,
+ builder: KPrefItemBase.BaseContract<Unit>.() -> Unit = {})
+ = list.add(KPrefPlainText(KPrefPlainText.KPrefPlainTextBuilder(globalOptions, title)
+ .apply { builder() }))
+
+ @KPrefMarker
+ internal val list: MutableList<KPrefItemCore> = mutableListOf()
+} \ No newline at end of file
diff --git a/core/src/main/kotlin/ca/allanwang/kau/kpref/KPrefDelegate.kt b/core/src/main/kotlin/ca/allanwang/kau/kpref/KPrefDelegate.kt
new file mode 100644
index 0000000..4d57ff1
--- /dev/null
+++ b/core/src/main/kotlin/ca/allanwang/kau/kpref/KPrefDelegate.kt
@@ -0,0 +1,89 @@
+package ca.allanwang.kau.kpref
+
+/**
+ * Created by Allan Wang on 2017-06-07.
+ */
+private object UNINITIALIZED
+
+fun KPref.kpref(key: String, fallback: Boolean, postSetter: (value: Boolean) -> Unit = {}): KPrefDelegate<Boolean> = KPrefDelegate(key, fallback, this, postSetter)
+fun KPref.kpref(key: String, fallback: Double, postSetter: (value: Float) -> Unit = {}): KPrefDelegate<Float> = KPrefDelegate(key, fallback.toFloat(), this, postSetter)
+fun KPref.kpref(key: String, fallback: Float, postSetter: (value: Float) -> Unit = {}): KPrefDelegate<Float> = KPrefDelegate(key, fallback, this, postSetter)
+fun KPref.kpref(key: String, fallback: Int, postSetter: (value: Int) -> Unit = {}): KPrefDelegate<Int> = KPrefDelegate(key, fallback, this, postSetter)
+fun KPref.kpref(key: String, fallback: Long, postSetter: (value: Long) -> Unit = {}): KPrefDelegate<Long> = KPrefDelegate(key, fallback, this, postSetter)
+fun KPref.kpref(key: String, fallback: Set<String>, postSetter: (value: Set<String>) -> Unit = {}): KPrefDelegate<StringSet> = KPrefDelegate(key, StringSet(fallback), this, postSetter)
+fun KPref.kpref(key: String, fallback: String, postSetter: (value: String) -> Unit = {}): KPrefDelegate<String> = KPrefDelegate(key, fallback, this, postSetter)
+
+class StringSet(set: Collection<String>) : LinkedHashSet<String>(set)
+
+/**
+ * Implementation of a kpref data item
+ * Contains a unique key for the shared preference as well as a nonnull fallback item
+ * Also contains an optional mutable postSetter that will be called every time a new value is given
+ */
+class KPrefDelegate<T : Any> internal constructor(private val key: String, private val fallback: T, private val pref: KPref, var postSetter: (value: T) -> Unit = {}, lock: Any? = null) : Lazy<T>, java.io.Serializable {
+
+ @Volatile private var _value: Any = UNINITIALIZED
+ private val lock = lock ?: this
+
+ init {
+ if (pref.prefMap.containsKey(key))
+ throw KPrefException("$key is already used elsewhere in preference ${pref.PREFERENCE_NAME}")
+ pref.prefMap.put(key, this@KPrefDelegate)
+ }
+
+ fun invalidate() {
+ _value = UNINITIALIZED
+ }
+
+ override val value: T
+ get() {
+ val _v1 = _value
+ if (_v1 !== UNINITIALIZED)
+ @Suppress("UNCHECKED_CAST")
+ return _v1 as T
+
+ return synchronized(lock) {
+ val _v2 = _value
+ if (_v2 !== UNINITIALIZED) {
+ @Suppress("UNCHECKED_CAST")
+ _v2 as T
+ } else {
+ _value = when (fallback) {
+ is Boolean -> pref.sp.getBoolean(key, fallback)
+ is Float -> pref.sp.getFloat(key, fallback)
+ is Int -> pref.sp.getInt(key, fallback)
+ is Long -> pref.sp.getLong(key, fallback)
+ is StringSet -> StringSet(pref.sp.getStringSet(key, fallback))
+ is String -> pref.sp.getString(key, fallback)
+ else -> throw KPrefException(fallback)
+ }
+ @Suppress("UNCHECKED_CAST")
+ _value as T
+ }
+ }
+ }
+
+ override fun isInitialized(): Boolean = _value !== UNINITIALIZED
+
+ override fun toString(): String = if (isInitialized()) value.toString() else "Lazy kPref $key not initialized yet."
+
+ operator fun setValue(any: Any, property: kotlin.reflect.KProperty<*>, t: T) {
+ _value = t
+ val editor = pref.sp.edit()
+ when (t) {
+ is Boolean -> editor.putBoolean(key, t)
+ is Float -> editor.putFloat(key, t)
+ is Int -> editor.putInt(key, t)
+ is Long -> editor.putLong(key, t)
+ is StringSet -> editor.putStringSet(key, t)
+ is String -> editor.putString(key, t)
+ else -> throw KPrefException(t)
+ }
+ editor.apply()
+ postSetter.invoke(t)
+ }
+}
+
+class KPrefException(message: String) : IllegalAccessException(message) {
+ constructor(element: Any?) : this("Invalid type in pref cache: ${element?.javaClass?.simpleName ?: "null"}")
+} \ No newline at end of file
diff --git a/core/src/main/kotlin/ca/allanwang/kau/kpref/items/KPrefCheckbox.kt b/core/src/main/kotlin/ca/allanwang/kau/kpref/items/KPrefCheckbox.kt
new file mode 100644
index 0000000..22cc927
--- /dev/null
+++ b/core/src/main/kotlin/ca/allanwang/kau/kpref/items/KPrefCheckbox.kt
@@ -0,0 +1,33 @@
+package ca.allanwang.kau.kpref.items
+
+import android.view.View
+import android.widget.CheckBox
+import ca.allanwang.kau.R
+import ca.allanwang.kau.kpref.KPrefMarker
+import ca.allanwang.kau.utils.tint
+
+/**
+ * Created by Allan Wang on 2017-06-07.
+ *
+ * Checkbox preference
+ * When clicked, will toggle the preference and the apply the result to the checkbox
+ */
+class KPrefCheckbox(builder: BaseContract<Boolean>) : KPrefItemBase<Boolean>(builder) {
+
+ override fun defaultOnClick(itemView: View, innerContent: View?): Boolean {
+ pref = !pref
+ (innerContent as CheckBox).isChecked = pref
+ return true
+ }
+
+ override fun onPostBindView(viewHolder: ViewHolder, textColor: Int?, accentColor: Int?) {
+ super.onPostBindView(viewHolder, textColor, accentColor)
+ val checkbox = viewHolder.bindInnerView<CheckBox>(R.layout.kau_preference_checkbox)
+ if (accentColor != null) checkbox.tint(accentColor)
+ checkbox.isChecked = pref
+ checkbox.jumpDrawablesToCurrentState() //Cancel the animation
+ }
+
+ override fun getType(): Int = R.id.kau_item_pref_checkbox
+
+} \ No newline at end of file
diff --git a/core/src/main/kotlin/ca/allanwang/kau/kpref/items/KPrefColorPicker.kt b/core/src/main/kotlin/ca/allanwang/kau/kpref/items/KPrefColorPicker.kt
new file mode 100644
index 0000000..c573939
--- /dev/null
+++ b/core/src/main/kotlin/ca/allanwang/kau/kpref/items/KPrefColorPicker.kt
@@ -0,0 +1,73 @@
+package ca.allanwang.kau.kpref.items
+
+import android.view.View
+import ca.allanwang.kau.R
+import ca.allanwang.kau.dialogs.color.CircleView
+import ca.allanwang.kau.dialogs.color.ColorBuilder
+import ca.allanwang.kau.dialogs.color.ColorContract
+import ca.allanwang.kau.dialogs.color.colorPickerDialog
+import ca.allanwang.kau.kpref.CoreAttributeContract
+import ca.allanwang.kau.kpref.GlobalOptions
+import ca.allanwang.kau.kpref.KPrefMarker
+
+/**
+ * Created by Allan Wang on 2017-06-07.
+ *
+ * ColorPicker preference
+ * When a color is successfully selected in the dialog, it will be saved as an int
+ */
+class KPrefColorPicker(val builder: KPrefColorContract) : KPrefItemBase<Int>(builder) {
+
+ override fun onPostBindView(viewHolder: ViewHolder, textColor: Int?, accentColor: Int?) {
+ super.onPostBindView(viewHolder, textColor, accentColor)
+ builder.apply {
+ titleRes = core.titleRes
+ colorCallback = {
+ pref = it
+ }
+ }
+ if (builder.showPreview) {
+ val preview = viewHolder.bindInnerView<CircleView>(R.layout.kau_preference_color_preview)
+ preview.setBackgroundColor(pref)
+ preview.withBorder = true
+ builder.apply {
+ colorCallback = {
+ pref = it
+ if (builder.showPreview)
+ preview.setBackgroundColor(it)
+ }
+ }
+ }
+ }
+
+
+ override fun defaultOnClick(itemView: View, innerContent: View?): Boolean {
+ builder.apply {
+ defaultColor = pref //update color
+ }
+ itemView.context.colorPickerDialog(builder).show()
+ return true
+ }
+
+ /**
+ * Extension of the base contract and [ColorContract] along with a showPreview option
+ */
+ interface KPrefColorContract : BaseContract<Int>, ColorContract {
+ var showPreview: Boolean
+ }
+
+ /**
+ * Default implementation of [KPrefColorContract]
+ */
+ class KPrefColorBuilder(globalOptions: GlobalOptions,
+ override var titleRes: Int,
+ getter: () -> Int,
+ setter: (value: Int) -> Unit
+ ) : KPrefColorContract, BaseContract<Int> by BaseBuilder<Int>(globalOptions, titleRes, getter, setter),
+ ColorContract by ColorBuilder() {
+ override var showPreview: Boolean = true
+ }
+
+ override fun getType(): Int = R.id.kau_item_pref_color_picker
+
+} \ No newline at end of file
diff --git a/core/src/main/kotlin/ca/allanwang/kau/kpref/items/KPrefHeader.kt b/core/src/main/kotlin/ca/allanwang/kau/kpref/items/KPrefHeader.kt
new file mode 100644
index 0000000..fa8efff
--- /dev/null
+++ b/core/src/main/kotlin/ca/allanwang/kau/kpref/items/KPrefHeader.kt
@@ -0,0 +1,25 @@
+package ca.allanwang.kau.kpref.items
+
+import android.view.View
+import ca.allanwang.kau.R
+import ca.allanwang.kau.kpref.KPrefMarker
+
+/**
+ * Created by Allan Wang on 2017-06-07.
+ *
+ * Header preference
+ * This view just holds a title and is not clickable. It is styled using the accent color
+ */
+class KPrefHeader(builder: CoreContract) : KPrefItemCore(builder) {
+
+ override fun getLayoutRes(): Int = R.layout.kau_preference_header
+
+ override fun onPostBindView(viewHolder: ViewHolder, textColor: Int?, accentColor: Int?) {
+ if (accentColor != null) viewHolder.title.setTextColor(accentColor)
+ }
+
+ override fun onClick(itemView: View, innerContent: View?): Boolean = true
+
+ override fun getType() = R.id.kau_item_pref_header
+
+} \ No newline at end of file
diff --git a/core/src/main/kotlin/ca/allanwang/kau/kpref/items/KPrefItemBase.kt b/core/src/main/kotlin/ca/allanwang/kau/kpref/items/KPrefItemBase.kt
new file mode 100644
index 0000000..bb0f0a3
--- /dev/null
+++ b/core/src/main/kotlin/ca/allanwang/kau/kpref/items/KPrefItemBase.kt
@@ -0,0 +1,85 @@
+package ca.allanwang.kau.kpref.items
+
+import android.support.annotation.CallSuper
+import android.view.View
+import ca.allanwang.kau.R
+import ca.allanwang.kau.kpref.CoreAttributeContract
+import ca.allanwang.kau.kpref.GlobalOptions
+import ca.allanwang.kau.utils.resolveDrawable
+
+/**
+ * Created by Allan Wang on 2017-06-05.
+ *
+ * Base class for pref setters that include the Shared Preference hooks
+ */
+abstract class KPrefItemBase<T>(val base: BaseContract<T>) : KPrefItemCore(base) {
+
+ open var pref: T
+ get() = base.getter.invoke()
+ set(value) {
+ base.setter.invoke(value)
+ }
+
+ var enabled: Boolean = true
+
+ init {
+ if (base.onClick == null) base.onClick = {
+ itemView, innerContent, _ ->
+ defaultOnClick(itemView, innerContent)
+ }
+ }
+
+ abstract fun defaultOnClick(itemView: View, innerContent: View?): Boolean
+
+ @CallSuper
+ override fun onPostBindView(viewHolder: ViewHolder, textColor: Int?, accentColor: Int?) {
+ enabled = base.enabler.invoke()
+ with(viewHolder) {
+ if (!enabled) container?.background = null
+ container?.alpha = if (enabled) 1.0f else 0.3f
+ }
+ }
+
+ override final fun onClick(itemView: View, innerContent: View?): Boolean {
+ return if (enabled) base.onClick?.invoke(itemView, innerContent, this) ?: false
+ else base.onDisabledClick?.invoke(itemView, innerContent, this) ?: false
+ }
+
+ override fun unbindView(holder: ViewHolder) {
+ super.unbindView(holder)
+ with(holder) {
+ container?.isEnabled = true
+ container?.background = itemView.context.resolveDrawable(android.R.attr.selectableItemBackground)
+ container?.alpha = 1.0f
+ }
+ }
+
+ override final fun getLayoutRes(): Int = R.layout.kau_preference
+
+ /**
+ * Extension of the core contract
+ * Since everything that extends the base is an actual preference, there must be a getter and setter
+ * The rest are optional and will have their defaults
+ */
+ interface BaseContract<T> : CoreContract {
+ var enabler: () -> Boolean
+ var onClick: ((itemView: View, innerContent: View?, item: KPrefItemBase<T>) -> Boolean)?
+ var onDisabledClick: ((itemView: View, innerContent: View?, item: KPrefItemBase<T>) -> Boolean)?
+ val getter: () -> T
+ val setter: (value: T) -> Unit
+ }
+
+ /**
+ * Default implementation of [BaseContract]
+ */
+ class BaseBuilder<T>(globalOptions: GlobalOptions,
+ titleRes: Int,
+ override val getter: () -> T,
+ override val setter: (value: T) -> Unit
+ ) : CoreContract by CoreBuilder(globalOptions, titleRes), BaseContract<T> {
+ override var enabler: () -> Boolean = { true }
+ override var onClick: ((itemView: View, innerContent: View?, item: KPrefItemBase<T>) -> Boolean)? = null
+ override var onDisabledClick: ((itemView: View, innerContent: View?, item: KPrefItemBase<T>) -> Boolean)? = null
+ }
+
+} \ No newline at end of file
diff --git a/core/src/main/kotlin/ca/allanwang/kau/kpref/items/KPrefItemCore.kt b/core/src/main/kotlin/ca/allanwang/kau/kpref/items/KPrefItemCore.kt
new file mode 100644
index 0000000..5f684ba
--- /dev/null
+++ b/core/src/main/kotlin/ca/allanwang/kau/kpref/items/KPrefItemCore.kt
@@ -0,0 +1,126 @@
+package ca.allanwang.kau.kpref.items
+
+import android.support.annotation.CallSuper
+import android.support.annotation.IdRes
+import android.support.annotation.LayoutRes
+import android.support.annotation.StringRes
+import android.support.v7.widget.RecyclerView
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.ImageView
+import android.widget.LinearLayout
+import android.widget.TextView
+import ca.allanwang.kau.R
+import ca.allanwang.kau.adapters.ThemableIItem
+import ca.allanwang.kau.adapters.ThemableIItemDelegate
+import ca.allanwang.kau.kpref.GlobalOptions
+import ca.allanwang.kau.kpref.KPrefMarker
+import ca.allanwang.kau.utils.*
+import com.mikepenz.fastadapter.items.AbstractItem
+import com.mikepenz.iconics.typeface.IIcon
+
+/**
+ * Created by Allan Wang on 2017-06-05.
+ *
+ * Core class containing nothing but the view items
+ */
+
+abstract class KPrefItemCore(val core: CoreContract) : AbstractItem<KPrefItemCore, KPrefItemCore.ViewHolder>(),
+ ThemableIItem by ThemableIItemDelegate() {
+
+ override final fun getViewHolder(v: View) = ViewHolder(v)
+
+ @CallSuper
+ override fun bindView(viewHolder: ViewHolder, payloads: List<Any>) {
+ super.bindView(viewHolder, payloads)
+ with(viewHolder) {
+ val context = itemView.context
+ title.text = context.string(core.titleRes)
+ if (core.descRes > 0)
+ desc?.visible()?.setText(core.descRes)
+ else
+ desc?.gone()
+ if (core.iicon != null) icon?.visible()?.setIcon(core.iicon, 24)
+ else icon?.gone()
+ innerFrame?.removeAllViews()
+ val textColor = core.globalOptions.textColor?.invoke()
+ if (textColor != null) {
+ title.setTextColor(textColor)
+ desc?.setTextColor(textColor)
+ }
+ val accentColor = core.globalOptions.accentColor?.invoke()
+ if (accentColor != null) {
+ icon?.drawable?.setTint(accentColor)
+ }
+ onPostBindView(this, textColor, accentColor)
+ }
+ }
+
+ abstract fun onPostBindView(viewHolder: ViewHolder, textColor: Int?, accentColor: Int?)
+
+ abstract fun onClick(itemView: View, innerContent: View?): Boolean
+
+ override fun unbindView(holder: ViewHolder) {
+ super.unbindView(holder)
+ with(holder) {
+ title.text = null
+ desc?.text = null
+ icon?.setImageDrawable(null)
+// innerFrame?.removeAllViews()
+ }
+ }
+
+ /**
+ * Core values for all kpref items
+ */
+ @KPrefMarker
+ interface CoreContract {
+ val globalOptions: GlobalOptions
+ @get:StringRes val titleRes: Int
+ var descRes: Int
+ @StringRes get
+ var iicon: IIcon?
+
+ /**
+ * Attempts to reload current item by identifying it with its [titleRes]
+ */
+ fun reloadSelf()
+ }
+
+ /**
+ * Default implementation of [CoreContract]
+ */
+ class CoreBuilder(override val globalOptions: GlobalOptions,
+ override @param:StringRes val titleRes: Int) : CoreContract {
+ override var descRes: Int = -1
+ override var iicon: IIcon? = null
+
+ override fun reloadSelf() {
+ globalOptions.reloadByTitle(titleRes)
+ }
+ }
+
+ class ViewHolder(v: View) : RecyclerView.ViewHolder(v) {
+ val title: TextView by bindView(R.id.kau_pref_title)
+ val container: ViewGroup? by bindOptionalView(R.id.kau_pref_container)
+ val desc: TextView? by bindOptionalView(R.id.kau_pref_desc)
+ val icon: ImageView? by bindOptionalView(R.id.kau_pref_icon)
+ val innerFrame: LinearLayout? by bindOptionalView(R.id.kau_pref_inner_frame)
+ val innerContent: View?
+ get() = itemView.findViewById(R.id.kau_pref_inner_content)
+
+ inline fun <reified T : View> bindInnerView(@LayoutRes id: Int): T {
+ if (innerFrame == null) throw IllegalStateException("Cannot bind inner view when innerFrame does not exist")
+ if (innerContent !is T) {
+ innerFrame!!.removeAllViews()
+ LayoutInflater.from(innerFrame!!.context).inflate(id, innerFrame)
+ }
+ return innerContent as T
+ }
+
+ inline fun <reified T : View> getInnerView() = innerContent as T
+
+ operator fun get(@IdRes id: Int): View = itemView.findViewById(id)
+ }
+} \ No newline at end of file
diff --git a/core/src/main/kotlin/ca/allanwang/kau/kpref/items/KPrefPlainText.kt b/core/src/main/kotlin/ca/allanwang/kau/kpref/items/KPrefPlainText.kt
new file mode 100644
index 0000000..a782430
--- /dev/null
+++ b/core/src/main/kotlin/ca/allanwang/kau/kpref/items/KPrefPlainText.kt
@@ -0,0 +1,29 @@
+package ca.allanwang.kau.kpref.items
+
+import android.view.View
+import ca.allanwang.kau.R
+import ca.allanwang.kau.kpref.GlobalOptions
+
+/**
+ * Created by Allan Wang on 2017-06-14.
+ *
+ * Just text with the core options. Extends base preference but has an empty getter and setter
+ * Useful replacement of [KPrefText] when nothing is displayed on the right side,
+ * and when the preference is completely handled by the click
+ *
+ */
+class KPrefPlainText(val builder: KPrefPlainTextBuilder) : KPrefItemBase<Unit>(builder) {
+
+ override fun defaultOnClick(itemView: View, innerContent: View?): Boolean {
+ //nothing
+ return true
+ }
+
+ class KPrefPlainTextBuilder(
+ globalOptions: GlobalOptions,
+ titleRes: Int
+ ) : BaseContract<Unit> by BaseBuilder(globalOptions, titleRes, {}, {})
+
+ override fun getType(): Int = R.id.kau_item_pref_plain_text
+
+} \ No newline at end of file
diff --git a/core/src/main/kotlin/ca/allanwang/kau/kpref/items/KPrefSubItems.kt b/core/src/main/kotlin/ca/allanwang/kau/kpref/items/KPrefSubItems.kt
new file mode 100644
index 0000000..51625ab
--- /dev/null
+++ b/core/src/main/kotlin/ca/allanwang/kau/kpref/items/KPrefSubItems.kt
@@ -0,0 +1,43 @@
+package ca.allanwang.kau.kpref.items
+
+import android.view.View
+import ca.allanwang.kau.R
+import ca.allanwang.kau.kpref.GlobalOptions
+import ca.allanwang.kau.kpref.KPrefAdapterBuilder
+
+/**
+ * Created by Allan Wang on 2017-06-14.
+ *
+ * Sub item preference
+ * When clicked, will navigate to a new set of preferences and add the old list to a stack
+ *
+ */
+class KPrefSubItems(val builder: KPrefSubItemsContract) : KPrefItemCore(builder) {
+
+ override fun onClick(itemView: View, innerContent: View?): Boolean {
+ builder.globalOptions.showNextPrefs(builder.titleRes, builder.itemBuilder)
+ return true
+ }
+
+ override fun getLayoutRes(): Int = R.layout.kau_preference
+
+ override fun onPostBindView(viewHolder: ViewHolder, textColor: Int?, accentColor: Int?) {}
+ /**
+ * Extension of the base contract with an optional text getter
+ */
+ interface KPrefSubItemsContract : CoreContract {
+ val itemBuilder: KPrefAdapterBuilder.() -> Unit
+ }
+
+ /**
+ * Default implementation of [KPrefTextContract]
+ */
+ class KPrefSubItemsBuilder(
+ globalOptions: GlobalOptions,
+ titleRes: Int,
+ override val itemBuilder: KPrefAdapterBuilder.() -> Unit
+ ) : KPrefSubItemsContract, CoreContract by CoreBuilder(globalOptions, titleRes)
+
+ override fun getType(): Int = R.id.kau_item_pref_sub_item
+
+} \ No newline at end of file
diff --git a/core/src/main/kotlin/ca/allanwang/kau/kpref/items/KPrefText.kt b/core/src/main/kotlin/ca/allanwang/kau/kpref/items/KPrefText.kt
new file mode 100644
index 0000000..8662b6a
--- /dev/null
+++ b/core/src/main/kotlin/ca/allanwang/kau/kpref/items/KPrefText.kt
@@ -0,0 +1,62 @@
+package ca.allanwang.kau.kpref.items
+
+import android.view.View
+import android.widget.TextView
+import ca.allanwang.kau.R
+import ca.allanwang.kau.kpref.GlobalOptions
+import ca.allanwang.kau.utils.toast
+
+/**
+ * Created by Allan Wang on 2017-06-14.
+ *
+ * Text preference
+ * Holds a textview to display data on the right
+ * This is still a generic preference
+ *
+ */
+class KPrefText<T>(val builder: KPrefTextContract<T>) : KPrefItemBase<T>(builder) {
+
+ /**
+ * Automatically reload on set
+ */
+ override var pref: T
+ get() = base.getter.invoke()
+ set(value) {
+ base.setter.invoke(value)
+ builder.reloadSelf()
+ }
+
+ override fun defaultOnClick(itemView: View, innerContent: View?): Boolean {
+ itemView.context.toast("No click function set")
+ return true
+ }
+
+ override fun onPostBindView(viewHolder: ViewHolder, textColor: Int?, accentColor: Int?) {
+ super.onPostBindView(viewHolder, textColor, accentColor)
+ val textview = viewHolder.bindInnerView<TextView>(R.layout.kau_preference_text)
+ if (textColor != null) textview.setTextColor(textColor)
+ textview.text = builder.textGetter.invoke(pref)
+ }
+
+ /**
+ * Extension of the base contract with an optional text getter
+ */
+ interface KPrefTextContract<T> : BaseContract<T> {
+ var textGetter: (T) -> String?
+ }
+
+ /**
+ * Default implementation of [KPrefTextContract]
+ */
+ class KPrefTextBuilder<T>(
+ globalOptions: GlobalOptions,
+ titleRes: Int,
+ getter: () -> T,
+ setter: (value: T) -> Unit
+ ) : KPrefTextContract<T>, BaseContract<T> by BaseBuilder<T>(globalOptions, titleRes, getter, setter) {
+ override var textGetter: (T) -> String? = { it?.toString() }
+ }
+
+ override fun getType(): Int = R.id.kau_item_pref_text
+
+} \ No newline at end of file