aboutsummaryrefslogtreecommitdiff
path: root/kpref-activity/src/main/kotlin/ca/allanwang
diff options
context:
space:
mode:
Diffstat (limited to 'kpref-activity/src/main/kotlin/ca/allanwang')
-rw-r--r--kpref-activity/src/main/kotlin/ca/allanwang/kau/kpref/activity/KPrefActivity.kt137
-rw-r--r--kpref-activity/src/main/kotlin/ca/allanwang/kau/kpref/activity/KPrefBinder.kt128
-rw-r--r--kpref-activity/src/main/kotlin/ca/allanwang/kau/kpref/activity/items/KPrefCheckbox.kt32
-rw-r--r--kpref-activity/src/main/kotlin/ca/allanwang/kau/kpref/activity/items/KPrefColorPicker.kt71
-rw-r--r--kpref-activity/src/main/kotlin/ca/allanwang/kau/kpref/activity/items/KPrefHeader.kt24
-rw-r--r--kpref-activity/src/main/kotlin/ca/allanwang/kau/kpref/activity/items/KPrefItemBase.kt84
-rw-r--r--kpref-activity/src/main/kotlin/ca/allanwang/kau/kpref/activity/items/KPrefItemCore.kt143
-rw-r--r--kpref-activity/src/main/kotlin/ca/allanwang/kau/kpref/activity/items/KPrefPlainText.kt29
-rw-r--r--kpref-activity/src/main/kotlin/ca/allanwang/kau/kpref/activity/items/KPrefSeekbar.kt108
-rw-r--r--kpref-activity/src/main/kotlin/ca/allanwang/kau/kpref/activity/items/KPrefSubItems.kt43
-rw-r--r--kpref-activity/src/main/kotlin/ca/allanwang/kau/kpref/activity/items/KPrefText.kt62
11 files changed, 861 insertions, 0 deletions
diff --git a/kpref-activity/src/main/kotlin/ca/allanwang/kau/kpref/activity/KPrefActivity.kt b/kpref-activity/src/main/kotlin/ca/allanwang/kau/kpref/activity/KPrefActivity.kt
new file mode 100644
index 0000000..be8fad8
--- /dev/null
+++ b/kpref-activity/src/main/kotlin/ca/allanwang/kau/kpref/activity/KPrefActivity.kt
@@ -0,0 +1,137 @@
+package ca.allanwang.kau.kpref.activity
+
+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.activity.items.KPrefItemCore
+import ca.allanwang.kau.ui.views.RippleCanvas
+import ca.allanwang.kau.ui.widgets.TextSlider
+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 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/kpref-activity/src/main/kotlin/ca/allanwang/kau/kpref/activity/KPrefBinder.kt b/kpref-activity/src/main/kotlin/ca/allanwang/kau/kpref/activity/KPrefBinder.kt
new file mode 100644
index 0000000..6048c1a
--- /dev/null
+++ b/kpref-activity/src/main/kotlin/ca/allanwang/kau/kpref/activity/KPrefBinder.kt
@@ -0,0 +1,128 @@
+package ca.allanwang.kau.kpref.activity
+
+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.activity.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(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
+ fun seekbar(@StringRes title: Int,
+ getter: (() -> Int),
+ setter: ((value: Int) -> Unit),
+ builder: KPrefSeekbar.KPrefSeekbarContract.() -> Unit = {})
+ = list.add(KPrefSeekbar(KPrefSeekbar.KPrefSeekbarBuilder(globalOptions, title, getter, setter)
+ .apply { builder() }))
+
+ @KPrefMarker
+ val list: MutableList<KPrefItemCore> = mutableListOf()
+} \ No newline at end of file
diff --git a/kpref-activity/src/main/kotlin/ca/allanwang/kau/kpref/activity/items/KPrefCheckbox.kt b/kpref-activity/src/main/kotlin/ca/allanwang/kau/kpref/activity/items/KPrefCheckbox.kt
new file mode 100644
index 0000000..b7ccc69
--- /dev/null
+++ b/kpref-activity/src/main/kotlin/ca/allanwang/kau/kpref/activity/items/KPrefCheckbox.kt
@@ -0,0 +1,32 @@
+package ca.allanwang.kau.kpref.activity.items
+
+import android.view.View
+import android.widget.CheckBox
+import ca.allanwang.kau.R
+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
+ */
+open 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/kpref-activity/src/main/kotlin/ca/allanwang/kau/kpref/activity/items/KPrefColorPicker.kt b/kpref-activity/src/main/kotlin/ca/allanwang/kau/kpref/activity/items/KPrefColorPicker.kt
new file mode 100644
index 0000000..38008f3
--- /dev/null
+++ b/kpref-activity/src/main/kotlin/ca/allanwang/kau/kpref/activity/items/KPrefColorPicker.kt
@@ -0,0 +1,71 @@
+package ca.allanwang.kau.kpref.activity.items
+
+import android.view.View
+import ca.allanwang.kau.R
+import ca.allanwang.kau.colorpicker.CircleView
+import ca.allanwang.kau.colorpicker.ColorBuilder
+import ca.allanwang.kau.colorpicker.ColorContract
+import ca.allanwang.kau.colorpicker.colorPickerDialog
+import ca.allanwang.kau.kpref.activity.GlobalOptions
+
+/**
+ * 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
+ */
+open 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/kpref-activity/src/main/kotlin/ca/allanwang/kau/kpref/activity/items/KPrefHeader.kt b/kpref-activity/src/main/kotlin/ca/allanwang/kau/kpref/activity/items/KPrefHeader.kt
new file mode 100644
index 0000000..009caba
--- /dev/null
+++ b/kpref-activity/src/main/kotlin/ca/allanwang/kau/kpref/activity/items/KPrefHeader.kt
@@ -0,0 +1,24 @@
+package ca.allanwang.kau.kpref.activity.items
+
+import android.view.View
+import ca.allanwang.kau.R
+
+/**
+ * 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
+ */
+open 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/kpref-activity/src/main/kotlin/ca/allanwang/kau/kpref/activity/items/KPrefItemBase.kt b/kpref-activity/src/main/kotlin/ca/allanwang/kau/kpref/activity/items/KPrefItemBase.kt
new file mode 100644
index 0000000..d7133c1
--- /dev/null
+++ b/kpref-activity/src/main/kotlin/ca/allanwang/kau/kpref/activity/items/KPrefItemBase.kt
@@ -0,0 +1,84 @@
+package ca.allanwang.kau.kpref.activity.items
+
+import android.support.annotation.CallSuper
+import android.view.View
+import ca.allanwang.kau.R
+import ca.allanwang.kau.kpref.activity.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/kpref-activity/src/main/kotlin/ca/allanwang/kau/kpref/activity/items/KPrefItemCore.kt b/kpref-activity/src/main/kotlin/ca/allanwang/kau/kpref/activity/items/KPrefItemCore.kt
new file mode 100644
index 0000000..fdda464
--- /dev/null
+++ b/kpref-activity/src/main/kotlin/ca/allanwang/kau/kpref/activity/items/KPrefItemCore.kt
@@ -0,0 +1,143 @@
+package ca.allanwang.kau.kpref.activity.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.activity.GlobalOptions
+import ca.allanwang.kau.kpref.activity.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()
+ lowerFrame?.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 lowerFrame: LinearLayout? by bindOptionalView(R.id.kau_pref_lower_frame)
+ val innerContent: View?
+ get() = itemView.findViewById(R.id.kau_pref_inner_content)
+ val lowerContent: View?
+ get() = itemView.findViewById(R.id.kau_pref_lower_content)
+
+ inline fun <reified T : View> bindInnerView(@LayoutRes id: Int) = bindInnerView(id) { _: T -> }
+
+ inline fun <reified T : View> bindInnerView(@LayoutRes id: Int, onFirstBind: (T) -> Unit): 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)
+ onFirstBind(innerContent as T)
+ }
+ return innerContent as T
+ }
+
+ inline fun <reified T : View> bindLowerView(@LayoutRes id: Int) = bindLowerView(id) { _: T -> }
+
+ inline fun <reified T : View> bindLowerView(@LayoutRes id: Int, onFirstBind: (T) -> Unit): T {
+ if (lowerFrame == null) throw IllegalStateException("Cannot bind inner view when lowerContent does not exist")
+ if (lowerContent !is T) {
+ lowerFrame!!.removeAllViews()
+ LayoutInflater.from(lowerFrame!!.context).inflate(id, lowerFrame)
+ onFirstBind(lowerContent as T)
+ }
+ return lowerContent as T
+ }
+
+ operator fun get(@IdRes id: Int): View = itemView.findViewById(id)
+ }
+} \ No newline at end of file
diff --git a/kpref-activity/src/main/kotlin/ca/allanwang/kau/kpref/activity/items/KPrefPlainText.kt b/kpref-activity/src/main/kotlin/ca/allanwang/kau/kpref/activity/items/KPrefPlainText.kt
new file mode 100644
index 0000000..dd58cd5
--- /dev/null
+++ b/kpref-activity/src/main/kotlin/ca/allanwang/kau/kpref/activity/items/KPrefPlainText.kt
@@ -0,0 +1,29 @@
+package ca.allanwang.kau.kpref.activity.items
+
+import android.view.View
+import ca.allanwang.kau.R
+import ca.allanwang.kau.kpref.activity.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
+ *
+ */
+open 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/kpref-activity/src/main/kotlin/ca/allanwang/kau/kpref/activity/items/KPrefSeekbar.kt b/kpref-activity/src/main/kotlin/ca/allanwang/kau/kpref/activity/items/KPrefSeekbar.kt
new file mode 100644
index 0000000..39b5f12
--- /dev/null
+++ b/kpref-activity/src/main/kotlin/ca/allanwang/kau/kpref/activity/items/KPrefSeekbar.kt
@@ -0,0 +1,108 @@
+package ca.allanwang.kau.kpref.activity.items
+
+import android.view.View
+import android.widget.SeekBar
+import android.widget.TextView
+import ca.allanwang.kau.R
+import ca.allanwang.kau.kpref.activity.GlobalOptions
+import ca.allanwang.kau.kpref.KPrefException
+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
+ */
+open class KPrefSeekbar(val builder: KPrefSeekbarContract) : KPrefItemBase<Int>(builder) {
+
+
+ override fun defaultOnClick(itemView: View, innerContent: View?): Boolean = false
+
+ override fun onPostBindView(viewHolder: ViewHolder, textColor: Int?, accentColor: Int?) {
+ super.onPostBindView(viewHolder, textColor, accentColor)
+ val text = viewHolder.bindInnerView<TextView>(R.layout.kau_preference_seekbar_text)
+ if (textColor != null) text.setTextColor(textColor)
+
+ val tvc = builder.textViewConfigs
+
+ text.tvc()
+ val seekbar = viewHolder.bindLowerView<SeekBar>(R.layout.kau_preference_seekbar) {
+ it.max = builder.range
+ it.incrementProgressBy(builder.increments)
+ it.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {
+ override fun onProgressChanged(sb: SeekBar, progress: Int, fromUser: Boolean) {
+ text.text = builder.toText(progress.fromProgress)
+ }
+
+ override fun onStartTrackingTouch(sb: SeekBar) {}
+
+ override fun onStopTrackingTouch(sb: SeekBar) {
+ val trueProgress = sb.progress.fromProgress
+ pref = trueProgress
+ }
+ })
+ }
+ if (accentColor != null) seekbar.tint(accentColor)
+ text.text = builder.toText(seekbar.progress.fromProgress) //set initial text in case no change occurs
+ seekbar.progress = pref.toProgress
+ }
+
+ /**
+ * Extension of the base contract
+ */
+ interface KPrefSeekbarContract : BaseContract<Int> {
+ var min: Int
+ var max: Int
+ var increments: Int
+ var range: Int
+ /**
+ * Once a seekbar is let go, calculates what text to show in the text view
+ */
+ var toText: (Int) -> String
+ var textViewConfigs: TextView.() -> Unit
+ }
+
+ /**
+ * Default implementation of [KPrefSeekbarContract]
+ */
+ class KPrefSeekbarBuilder(
+ globalOptions: GlobalOptions,
+ titleRes: Int,
+ getter: () -> Int,
+ setter: (value: Int) -> Unit
+ ) : KPrefSeekbarContract, BaseContract<Int> by BaseBuilder(globalOptions, titleRes, getter, setter) {
+
+ override var min: Int = 0
+ set(value) {
+ field = value
+ range = -1
+ }
+ override var max: Int = 100
+ set(value) {
+ field = value
+ range = -1
+ }
+ override var increments: Int = 1
+
+ override var range: Int = max - min
+ //value doesn't matter; setting will prompt the check
+ set(value) {
+ if (max <= min) throw KPrefException("Range min ($min) must be smaller than max ($max)")
+ field = max - min
+ }
+
+ override var toText: (Int) -> String = { it.toString() }
+
+ override var textViewConfigs: TextView.() -> Unit = {}
+ }
+
+ val Int.toProgress: Int
+ get() = this - builder.min
+
+ val Int.fromProgress: Int
+ get() = this + builder.min
+
+ override fun getType(): Int = R.id.kau_item_pref_seekbar
+
+} \ No newline at end of file
diff --git a/kpref-activity/src/main/kotlin/ca/allanwang/kau/kpref/activity/items/KPrefSubItems.kt b/kpref-activity/src/main/kotlin/ca/allanwang/kau/kpref/activity/items/KPrefSubItems.kt
new file mode 100644
index 0000000..509394c
--- /dev/null
+++ b/kpref-activity/src/main/kotlin/ca/allanwang/kau/kpref/activity/items/KPrefSubItems.kt
@@ -0,0 +1,43 @@
+package ca.allanwang.kau.kpref.activity.items
+
+import android.view.View
+import ca.allanwang.kau.R
+import ca.allanwang.kau.kpref.activity.GlobalOptions
+import ca.allanwang.kau.kpref.activity.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
+ *
+ */
+open 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/kpref-activity/src/main/kotlin/ca/allanwang/kau/kpref/activity/items/KPrefText.kt b/kpref-activity/src/main/kotlin/ca/allanwang/kau/kpref/activity/items/KPrefText.kt
new file mode 100644
index 0000000..33510c3
--- /dev/null
+++ b/kpref-activity/src/main/kotlin/ca/allanwang/kau/kpref/activity/items/KPrefText.kt
@@ -0,0 +1,62 @@
+package ca.allanwang.kau.kpref.activity.items
+
+import android.view.View
+import android.widget.TextView
+import ca.allanwang.kau.R
+import ca.allanwang.kau.kpref.activity.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
+ *
+ */
+open 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