aboutsummaryrefslogtreecommitdiff
path: root/kpref-activity
diff options
context:
space:
mode:
Diffstat (limited to 'kpref-activity')
-rw-r--r--kpref-activity/.gitignore1
-rw-r--r--kpref-activity/build.gradle61
-rw-r--r--kpref-activity/progress-proguard.txt1
-rw-r--r--kpref-activity/src/androidTest/java/ca/allanwang/kau/ExampleInstrumentedTest.java26
-rw-r--r--kpref-activity/src/main/AndroidManifest.xml1
-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
-rw-r--r--kpref-activity/src/main/res/layout/kau_pref_activity.xml54
-rw-r--r--kpref-activity/src/main/res/layout/kau_preference.xml108
-rw-r--r--kpref-activity/src/main/res/layout/kau_preference_checkbox.xml7
-rw-r--r--kpref-activity/src/main/res/layout/kau_preference_color_preview.xml6
-rw-r--r--kpref-activity/src/main/res/layout/kau_preference_header.xml10
-rw-r--r--kpref-activity/src/main/res/layout/kau_preference_seekbar.xml8
-rw-r--r--kpref-activity/src/main/res/layout/kau_preference_seekbar_text.xml12
-rw-r--r--kpref-activity/src/main/res/layout/kau_preference_text.xml7
-rw-r--r--kpref-activity/src/main/res/values/ids.xml19
-rw-r--r--kpref-activity/src/main/res/values/strings.xml3
-rw-r--r--kpref-activity/src/test/java/ca/allanwang/kau/ExampleUnitTest.java17
27 files changed, 1202 insertions, 0 deletions
diff --git a/kpref-activity/.gitignore b/kpref-activity/.gitignore
new file mode 100644
index 0000000..796b96d
--- /dev/null
+++ b/kpref-activity/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/kpref-activity/build.gradle b/kpref-activity/build.gradle
new file mode 100644
index 0000000..31d0a70
--- /dev/null
+++ b/kpref-activity/build.gradle
@@ -0,0 +1,61 @@
+plugins {
+ id 'com.gladed.androidgitversion' version '0.3.4'
+}
+
+apply plugin: 'com.android.library'
+apply plugin: 'kotlin-android'
+apply plugin: 'com.github.dcendents.android-maven'
+
+group = project.APP_GROUP
+
+android {
+ compileSdkVersion Integer.parseInt(project.TARGET_SDK)
+ buildToolsVersion project.BUILD_TOOLS
+
+ androidGitVersion {
+ codeFormat = 'MMNNPPBB'
+ prefix 'v'
+ }
+
+ defaultConfig {
+ minSdkVersion Integer.parseInt(project.MIN_SDK)
+ targetSdkVersion Integer.parseInt(project.TARGET_SDK)
+ versionCode androidGitVersion.code()
+ versionName androidGitVersion.name()
+ consumerProguardFiles 'progress-proguard.txt'
+ testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+ }
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ }
+ }
+ lintOptions {
+ abortOnError false
+ checkReleaseBuilds false
+ }
+ resourcePrefix "kau_pref"
+ sourceSets {
+ main.java.srcDirs += 'src/main/kotlin'
+ test.java.srcDirs += 'src/test/kotlin'
+ }
+}
+
+dependencies {
+ compile fileTree(dir: 'libs', include: ['*.jar'])
+ androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
+ exclude group: 'com.android.support', module: 'support-annotations'
+ })
+ testCompile 'junit:junit:4.12'
+
+ compile project(':core-ui')
+ compile project(':adapter')
+ compile project(':colorpicker')
+
+ compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
+ testCompile "org.jetbrains.kotlin:kotlin-test-junit:$kotlin_version"
+
+}
+
+apply from: '../artifacts.gradle'
diff --git a/kpref-activity/progress-proguard.txt b/kpref-activity/progress-proguard.txt
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/kpref-activity/progress-proguard.txt
@@ -0,0 +1 @@
+
diff --git a/kpref-activity/src/androidTest/java/ca/allanwang/kau/ExampleInstrumentedTest.java b/kpref-activity/src/androidTest/java/ca/allanwang/kau/ExampleInstrumentedTest.java
new file mode 100644
index 0000000..7b079b2
--- /dev/null
+++ b/kpref-activity/src/androidTest/java/ca/allanwang/kau/ExampleInstrumentedTest.java
@@ -0,0 +1,26 @@
+package ca.allanwang.kau;
+
+import android.content.Context;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static org.junit.Assert.*;
+
+/**
+ * Instrumentation test, which will execute on an Android device.
+ *
+ * @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
+ */
+@RunWith(AndroidJUnit4.class)
+public class ExampleInstrumentedTest {
+ @Test
+ public void useAppContext() throws Exception {
+ // Context of the app under test.
+ Context appContext = InstrumentationRegistry.getTargetContext();
+
+ assertEquals("ca.allanwang.kau.test", appContext.getPackageName());
+ }
+}
diff --git a/kpref-activity/src/main/AndroidManifest.xml b/kpref-activity/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..6c86827
--- /dev/null
+++ b/kpref-activity/src/main/AndroidManifest.xml
@@ -0,0 +1 @@
+<manifest package="ca.allanwang.kau.kpref.activity" />
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
diff --git a/kpref-activity/src/main/res/layout/kau_pref_activity.xml b/kpref-activity/src/main/res/layout/kau_pref_activity.xml
new file mode 100644
index 0000000..d068618
--- /dev/null
+++ b/kpref-activity/src/main/res/layout/kau_pref_activity.xml
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="utf-8"?>
+<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/kau_container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <ca.allanwang.kau.ui.views.RippleCanvas
+ android:id="@+id/kau_toolbar_ripple"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ app:layout_constraintBottom_toBottomOf="@+id/kau_toolbar"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toTopOf="parent" />
+
+ <android.support.v7.widget.Toolbar
+ android:id="@id/kau_toolbar"
+ android:layout_width="0dp"
+ android:layout_height="?attr/actionBarSize"
+ android:layout_marginTop="@dimen/kau_status_bar_height"
+ android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toTopOf="parent">
+
+ <ca.allanwang.kau.widgets.TextSlider
+ android:id="@+id/kau_toolbar_text"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ app:animation_type="slide_horizontal" />
+
+ </android.support.v7.widget.Toolbar>
+
+ <ca.allanwang.kau.ui.views.RippleCanvas
+ android:id="@+id/kau_ripple"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toBottomOf="@id/kau_toolbar" />
+
+ <ViewAnimator
+ android:id="@+id/kau_holder"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toBottomOf="@id/kau_toolbar" />
+
+</android.support.constraint.ConstraintLayout>
diff --git a/kpref-activity/src/main/res/layout/kau_preference.xml b/kpref-activity/src/main/res/layout/kau_preference.xml
new file mode 100644
index 0000000..1a53726
--- /dev/null
+++ b/kpref-activity/src/main/res/layout/kau_preference.xml
@@ -0,0 +1,108 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--due to animations, we need a wrapper viewgroup so our changes will stick-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:baselineAligned="false"
+ android:clipToPadding="false"
+ android:minHeight="?android:attr/listPreferredItemHeightSmall"
+ android:orientation="horizontal">
+
+ <android.support.constraint.ConstraintLayout
+ android:id="@id/kau_pref_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:background="?android:attr/selectableItemBackground"
+ android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+ android:paddingStart="?android:attr/listPreferredItemPaddingStart">
+
+ <!--As per Android N, icons (24dp) are aligned to the left rather than centered-->
+
+ <ImageView
+ android:id="@id/kau_pref_icon"
+ android:layout_width="56dp"
+ android:layout_height="56dp"
+ android:layout_marginBottom="4dp"
+ android:layout_marginTop="4dp"
+ android:contentDescription="@string/kau_pref_icon"
+ android:paddingEnd="32dp"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintVertical_bias="0.5"
+ tools:layout_editor_absoluteX="0dp" />
+
+ <TextView
+ android:id="@id/kau_pref_title"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="@dimen/kau_padding_normal"
+ android:ellipsize="marquee"
+ android:textAppearance="?android:attr/textAppearanceListItem"
+ android:textColor="?android:attr/textColorPrimary"
+ app:layout_constraintBottom_toTopOf="@id/kau_pref_desc"
+ app:layout_constraintEnd_toStartOf="@id/kau_pref_inner_frame"
+ app:layout_constraintHorizontal_bias="0"
+ app:layout_constraintStart_toEndOf="@id/kau_pref_icon"
+ app:layout_constraintTop_toTopOf="parent"
+ tools:layout_editor_absoluteX="-175dp" />
+
+ <TextView
+ android:id="@id/kau_pref_desc"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:ellipsize="end"
+ android:maxLines="10"
+ android:textAppearance="?android:attr/textAppearanceListItemSecondary"
+ android:textColor="?android:attr/textColorSecondary"
+ app:layout_constraintBottom_toTopOf="@id/kau_pref_lower_frame"
+ app:layout_constraintEnd_toStartOf="@id/kau_pref_inner_frame"
+ app:layout_constraintHorizontal_bias="0"
+ app:layout_constraintStart_toEndOf="@id/kau_pref_icon"
+ app:layout_constraintTop_toBottomOf="@id/kau_pref_title"
+ tools:layout_editor_absoluteX="-175dp" />
+
+ <LinearLayout
+ android:id="@id/kau_pref_lower_frame"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="@dimen/kau_padding_normal"
+ android:orientation="vertical"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintEnd_toStartOf="@id/kau_pref_inner_frame"
+ app:layout_constraintHorizontal_bias="0"
+ app:layout_constraintStart_toEndOf="@id/kau_pref_icon"
+ app:layout_constraintTop_toBottomOf="@id/kau_pref_desc"
+ tools:layout_editor_absoluteX="-175dp" />
+
+ <android.support.constraint.Barrier
+ android:id="@id/kau_pref_barrier"
+ android:layout_width="1dp"
+ android:layout_height="wrap_content"
+ app:barrierDirection="end"
+ app:constraint_referenced_ids="kau_pref_title,kau_pref_desc,kau_pref_lower_frame"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintTop_toTopOf="parent" />
+
+ <LinearLayout
+ android:id="@id/kau_pref_inner_frame"
+ android:layout_width="wrap_content"
+ android:layout_height="0dp"
+ android:gravity="center_vertical|end"
+ android:orientation="horizontal"
+ android:paddingStart="@dimen/kau_padding_normal"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintHorizontal_bias="1.0"
+ app:layout_constraintStart_toEndOf="@id/kau_pref_barrier"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintVertical_bias="0.5"
+ tools:layout_editor_absoluteX="1dp" />
+
+ </android.support.constraint.ConstraintLayout>
+
+</LinearLayout> \ No newline at end of file
diff --git a/kpref-activity/src/main/res/layout/kau_preference_checkbox.xml b/kpref-activity/src/main/res/layout/kau_preference_checkbox.xml
new file mode 100644
index 0000000..016394f
--- /dev/null
+++ b/kpref-activity/src/main/res/layout/kau_preference_checkbox.xml
@@ -0,0 +1,7 @@
+<CheckBox xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@id/kau_pref_inner_content"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:focusable="false"
+ android:clickable="false"
+ android:background="@null" /> \ No newline at end of file
diff --git a/kpref-activity/src/main/res/layout/kau_preference_color_preview.xml b/kpref-activity/src/main/res/layout/kau_preference_color_preview.xml
new file mode 100644
index 0000000..fbb049b
--- /dev/null
+++ b/kpref-activity/src/main/res/layout/kau_preference_color_preview.xml
@@ -0,0 +1,6 @@
+<ca.allanwang.kau.colorpicker.CircleView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@id/kau_pref_inner_content"
+ android:layout_width="40dp"
+ android:layout_height="40dp"
+ android:focusable="false"
+ android:clickable="false" /> \ No newline at end of file
diff --git a/kpref-activity/src/main/res/layout/kau_preference_header.xml b/kpref-activity/src/main/res/layout/kau_preference_header.xml
new file mode 100644
index 0000000..5deece3
--- /dev/null
+++ b/kpref-activity/src/main/res/layout/kau_preference_header.xml
@@ -0,0 +1,10 @@
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/kau_pref_title"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="8dip"
+ android:paddingEnd="?android:attr/listPreferredItemPaddingRight"
+ android:paddingStart="?android:attr/listPreferredItemPaddingLeft"
+ android:paddingTop="16dip"
+ android:textColor="?android:attr/colorAccent"
+ android:textSize="14sp" /> \ No newline at end of file
diff --git a/kpref-activity/src/main/res/layout/kau_preference_seekbar.xml b/kpref-activity/src/main/res/layout/kau_preference_seekbar.xml
new file mode 100644
index 0000000..8da4d5d
--- /dev/null
+++ b/kpref-activity/src/main/res/layout/kau_preference_seekbar.xml
@@ -0,0 +1,8 @@
+<SeekBar xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@id/kau_pref_lower_content"
+ android:layout_width="match_parent"
+ android:paddingTop="@dimen/kau_padding_normal"
+ android:layout_height="wrap_content"
+ android:focusable="false"
+ android:clickable="false"
+ android:background="@null" /> \ No newline at end of file
diff --git a/kpref-activity/src/main/res/layout/kau_preference_seekbar_text.xml b/kpref-activity/src/main/res/layout/kau_preference_seekbar_text.xml
new file mode 100644
index 0000000..6ba2543
--- /dev/null
+++ b/kpref-activity/src/main/res/layout/kau_preference_seekbar_text.xml
@@ -0,0 +1,12 @@
+<!--TextView that aligns to the bottom-->
+
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@id/kau_pref_inner_content"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="bottom"
+ android:background="@null"
+ android:clickable="false"
+ android:focusable="false"
+ android:gravity="bottom|end"
+ android:paddingBottom="@dimen/kau_padding_normal" /> \ No newline at end of file
diff --git a/kpref-activity/src/main/res/layout/kau_preference_text.xml b/kpref-activity/src/main/res/layout/kau_preference_text.xml
new file mode 100644
index 0000000..a4d901e
--- /dev/null
+++ b/kpref-activity/src/main/res/layout/kau_preference_text.xml
@@ -0,0 +1,7 @@
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@id/kau_pref_inner_content"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:focusable="false"
+ android:clickable="false"
+ android:background="@null" /> \ No newline at end of file
diff --git a/kpref-activity/src/main/res/values/ids.xml b/kpref-activity/src/main/res/values/ids.xml
new file mode 100644
index 0000000..0972706
--- /dev/null
+++ b/kpref-activity/src/main/res/values/ids.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <item name="kau_pref_barrier" type="id" />
+ <item name="kau_pref_container" type="id" />
+ <item name="kau_pref_desc" type="id" />
+ <item name="kau_pref_icon" type="id" />
+ <item name="kau_pref_inner_content" type="id" />
+ <item name="kau_pref_inner_frame" type="id" />
+ <item name="kau_pref_item_checkbox" type="id" />
+ <item name="kau_pref_item_color_picker" type="id" />
+ <item name="kau_pref_item_header" type="id" />
+ <item name="kau_pref_item_plain_text" type="id" />
+ <item name="kau_pref_item_seekbar" type="id" />
+ <item name="kau_pref_item_sub_item" type="id" />
+ <item name="kau_pref_item_text" type="id" />
+ <item name="kau_pref_lower_content" type="id" />
+ <item name="kau_pref_lower_frame" type="id" />
+ <item name="kau_pref_title" type="id" />
+</resources> \ No newline at end of file
diff --git a/kpref-activity/src/main/res/values/strings.xml b/kpref-activity/src/main/res/values/strings.xml
new file mode 100644
index 0000000..0402364
--- /dev/null
+++ b/kpref-activity/src/main/res/values/strings.xml
@@ -0,0 +1,3 @@
+<resources>
+ <string name="app_name">kpref-activity</string>
+</resources>
diff --git a/kpref-activity/src/test/java/ca/allanwang/kau/ExampleUnitTest.java b/kpref-activity/src/test/java/ca/allanwang/kau/ExampleUnitTest.java
new file mode 100644
index 0000000..a29b447
--- /dev/null
+++ b/kpref-activity/src/test/java/ca/allanwang/kau/ExampleUnitTest.java
@@ -0,0 +1,17 @@
+package ca.allanwang.kau;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
+ */
+public class ExampleUnitTest {
+ @Test
+ public void addition_isCorrect() throws Exception {
+ assertEquals(4, 2 + 2);
+ }
+} \ No newline at end of file