From c00f9e2674f5109a64f1f50873ce08089a2fce33 Mon Sep 17 00:00:00 2001 From: Allan Wang Date: Sat, 10 Jun 2017 23:46:02 -0700 Subject: Create kpref activity base --- .../kau/dialogs/color/ColorPickerDialog.kt | 297 +------------------- .../allanwang/kau/dialogs/color/ColorPickerView.kt | 301 +++++++++++++++++++++ .../kotlin/ca/allanwang/kau/kpref/KPrefActivity.kt | 37 +++ .../kotlin/ca/allanwang/kau/kpref/KPrefBinder.kt | 6 +- .../kotlin/ca/allanwang/kau/kpref/KPrefDelegate.kt | 2 +- .../ca/allanwang/kau/kpref/items/KPrefCheckbox.kt | 16 +- .../allanwang/kau/kpref/items/KPrefColorPicker.kt | 7 +- .../ca/allanwang/kau/kpref/items/KPrefHeader.kt | 8 +- .../ca/allanwang/kau/kpref/items/KPrefItemBase.kt | 2 +- .../ca/allanwang/kau/kpref/items/KPrefItemCore.kt | 9 +- .../kotlin/ca/allanwang/kau/utils/ColorUtils.kt | 4 +- .../kotlin/ca/allanwang/kau/utils/ContextUtils.kt | 18 +- .../main/kotlin/ca/allanwang/kau/utils/Utils.kt | 13 +- .../kotlin/ca/allanwang/kau/views/RippleCanvas.kt | 19 +- library/src/main/res/layout/kau_activity_kpref.xml | 43 +++ library/src/main/res/values/dimens.xml | 1 + 16 files changed, 454 insertions(+), 329 deletions(-) create mode 100644 library/src/main/kotlin/ca/allanwang/kau/dialogs/color/ColorPickerView.kt create mode 100644 library/src/main/kotlin/ca/allanwang/kau/kpref/KPrefActivity.kt create mode 100644 library/src/main/res/layout/kau_activity_kpref.xml (limited to 'library/src') diff --git a/library/src/main/kotlin/ca/allanwang/kau/dialogs/color/ColorPickerDialog.kt b/library/src/main/kotlin/ca/allanwang/kau/dialogs/color/ColorPickerDialog.kt index 4931913..0ce236c 100644 --- a/library/src/main/kotlin/ca/allanwang/kau/dialogs/color/ColorPickerDialog.kt +++ b/library/src/main/kotlin/ca/allanwang/kau/dialogs/color/ColorPickerDialog.kt @@ -2,302 +2,10 @@ package ca.allanwang.kau.dialogs.color import android.content.Context import android.graphics.Color -import android.support.annotation.ColorInt -import android.support.v4.content.res.ResourcesCompat -import android.text.Editable -import android.text.InputFilter -import android.text.TextWatcher -import android.util.AttributeSet -import android.view.View -import android.view.ViewGroup -import android.widget.* import ca.allanwang.kau.R import ca.allanwang.kau.utils.* -import com.afollestad.materialdialogs.DialogAction import com.afollestad.materialdialogs.MaterialDialog import com.afollestad.materialdialogs.Theme -import com.afollestad.materialdialogs.color.FillGridView -import java.util.* - -/** - * Created by Allan Wang on 2017-06-08. - */ -internal class ColorPickerView @JvmOverloads constructor( - context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0 -) : ScrollView(context, attrs, defStyleAttr) { - var selectedColor: Int = -1 - var isInSub: Boolean = false - var isInCustom: Boolean = false - var circleSize: Int = context.dimen(R.dimen.kau_color_circle_size).toInt() - val backgroundColor = context.resolveColor(R.attr.md_background_color, - if (context.resolveColor(android.R.attr.textColorPrimary).isColorDark()) Color.WHITE else 0xff424242.toInt()) - val backgroundColorTint = if (backgroundColor.isColorDark()) backgroundColor.lighten(0.2f) else backgroundColor.darken(0.2f) - lateinit var dialog: MaterialDialog - lateinit var builder: Builder - lateinit var colorsTop: IntArray - var colorsSub: Array? = null - var topIndex: Int = -1 - var subIndex: Int = -1 - var colorIndex: Int - get() = if (isInSub) subIndex else topIndex - set(value) { - if (isInSub) subIndex = value - else { - topIndex = value - if (colorsSub != null && colorsSub!!.size > value) { - dialog.setActionButton(DialogAction.NEGATIVE, builder.backText) - isInSub = true - invalidateGrid() - } - } - } - - - val gridView: FillGridView by bindView(R.id.md_grid) - val customFrame: LinearLayout by bindView(R.id.md_colorChooserCustomFrame) - val customColorIndicator: View by bindView(R.id.md_colorIndicator) - val hexInput: EditText by bindView(R.id.md_hexInput) - val alphaLabel: TextView by bindView(R.id.md_colorALabel) - val alphaSeekbar: SeekBar by bindView(R.id.md_colorA) - val alphaValue: TextView by bindView(R.id.md_colorAValue) - val redSeekbar: SeekBar by bindView(R.id.md_colorR) - val redValue: TextView by bindView(R.id.md_colorRValue) - val greenSeekbar: SeekBar by bindView(R.id.md_colorG) - val greenValue: TextView by bindView(R.id.md_colorGValue) - val blueSeekbar: SeekBar by bindView(R.id.md_colorB) - val blueValue: TextView by bindView(R.id.md_colorBValue) - - var customHexTextWatcher: TextWatcher? = null - var customRgbListener: SeekBar.OnSeekBarChangeListener? = null - - init { - View.inflate(context, R.layout.md_dialog_colorchooser, this) - } - - fun bind(builder: Builder, dialog: MaterialDialog) { - this.builder = builder - this.dialog = dialog - this.colorsTop = builder.colorsTop() - this.colorsSub = builder.colorsSub() - this.selectedColor = builder.defaultColor - if (builder.allowCustom) { - if (!builder.allowCustomAlpha) { - alphaLabel.gone() - alphaSeekbar.gone() - alphaValue.gone() - hexInput.hint = String.format("%06X", selectedColor) - hexInput.filters = arrayOf(InputFilter.LengthFilter(6)) - } else { - hexInput.hint = String.format("%08X", selectedColor) - hexInput.filters = arrayOf(InputFilter.LengthFilter(8)) - } - } - if (findColor(builder.defaultColor) || !builder.allowCustom) isInCustom = true //when toggled this will be false - toggleCustom() - } - - fun backOrCancel() { - if (isInSub) { - dialog.setActionButton(DialogAction.NEGATIVE, builder.cancelText) - //to top - isInSub = false - subIndex = -1 - invalidateGrid() - } else { - dialog.cancel() - } - } - - fun toggleCustom() { - isInCustom = !isInCustom - if (isInCustom) { - isInSub = false - if (builder.allowCustom) dialog.setActionButton(DialogAction.NEUTRAL, builder.presetText) - dialog.setActionButton(DialogAction.NEGATIVE, builder.cancelText) - customHexTextWatcher = object : TextWatcher { - override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {} - - override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) { - try { - selectedColor = Color.parseColor("#" + s.toString()) - } catch (e: IllegalArgumentException) { - selectedColor = Color.BLACK - } - - customColorIndicator.setBackgroundColor(selectedColor) - if (alphaSeekbar.isVisible()) { - val alpha = Color.alpha(selectedColor) - alphaSeekbar.progress = alpha - alphaValue.text = String.format(Locale.CANADA, "%d", alpha) - } - redSeekbar.progress = Color.red(selectedColor) - greenSeekbar.progress = Color.green(selectedColor) - blueSeekbar.progress = Color.blue(selectedColor) - isInSub = false - topIndex = -1 - subIndex = -1 - refreshColors() - } - - override fun afterTextChanged(s: Editable?) {} - } - hexInput.setText(selectedColor.toHexString(builder.allowCustomAlpha, false)) - hexInput.addTextChangedListener(customHexTextWatcher) - customRgbListener = object : SeekBar.OnSeekBarChangeListener { - override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) { - if (fromUser) { - val color = if (builder.allowCustomAlpha) - Color.argb(alphaSeekbar.progress, - redSeekbar.progress, - greenSeekbar.progress, - blueSeekbar.progress) - else Color.rgb(redSeekbar.progress, - greenSeekbar.progress, - blueSeekbar.progress) - - hexInput.setText(color.toHexString(builder.allowCustomAlpha, false)) - } - if (builder.allowCustomAlpha) alphaValue.text = alphaSeekbar.progress.toString() - redValue.text = redSeekbar.progress.toString() - greenValue.text = greenSeekbar.progress.toString() - blueValue.text = blueSeekbar.progress.toString() - } - - override fun onStartTrackingTouch(seekBar: SeekBar) {} - - override fun onStopTrackingTouch(seekBar: SeekBar) {} - } - redSeekbar.setOnSeekBarChangeListener(customRgbListener) - greenSeekbar.setOnSeekBarChangeListener(customRgbListener) - blueSeekbar.setOnSeekBarChangeListener(customRgbListener) - if (alphaSeekbar.isVisible()) - alphaSeekbar.setOnSeekBarChangeListener(customRgbListener) - hexInput.setText(selectedColor.toHexString(alphaSeekbar.isVisible(), false)) - gridView.fadeOut(onFinish = { gridView.gone() }) - customFrame.fadeIn() - } else { - findColor(selectedColor) - if (builder.allowCustom) dialog.setActionButton(DialogAction.NEUTRAL, builder.customText) - dialog.setActionButton(DialogAction.NEGATIVE, if (isInSub) builder.backText else builder.cancelText) - gridView.fadeIn(onStart = { invalidateGrid() }) - customFrame.fadeOut(onFinish = { customFrame.gone() }) - hexInput.removeTextChangedListener(customHexTextWatcher) - customHexTextWatcher = null - alphaSeekbar.setOnSeekBarChangeListener(null) - redSeekbar.setOnSeekBarChangeListener(null) - greenSeekbar.setOnSeekBarChangeListener(null) - blueSeekbar.setOnSeekBarChangeListener(null) - customRgbListener = null - } - } - - fun refreshColors() { - if (!isInCustom) findColor(selectedColor) - //Ensure that our tinted color is still visible against the background - val visibleColor = if (selectedColor.isColorVisibleOn(backgroundColor)) selectedColor else backgroundColorTint - if (builder.dynamicButtonColors) { - dialog.getActionButton(DialogAction.POSITIVE).setTextColor(visibleColor) - dialog.getActionButton(DialogAction.NEGATIVE).setTextColor(visibleColor) - dialog.getActionButton(DialogAction.NEUTRAL).setTextColor(visibleColor) - } - if (!builder.allowCustom || !isInCustom) return - if (builder.allowCustomAlpha) - alphaSeekbar.visible().tint(visibleColor) - redSeekbar.tint(visibleColor) - greenSeekbar.tint(visibleColor) - blueSeekbar.tint(visibleColor) - hexInput.tint(visibleColor) - } - - fun findColor(@ColorInt color: Int): Boolean { - topIndex = -1 - subIndex = -1 - colorsTop.forEachIndexed { - index, topColor -> - if (findSubColor(color, index)) { - topIndex = index - return true - } - if (topColor == color) { // If no sub colors exists and top color matches - topIndex = index - return true - } - } - return false - } - - fun findSubColor(@ColorInt color: Int, topIndex: Int): Boolean { - if (colorsSub == null || colorsSub!!.size <= topIndex) return false - colorsSub!![topIndex].forEachIndexed { - index, subColor -> - if (subColor == color) { - subIndex = index - return true - } - } - return false - } - - fun invalidateGrid() { - if (gridView.adapter == null) { - gridView.adapter = ColorGridAdapter() - gridView.selector = ResourcesCompat.getDrawable(resources, R.drawable.kau_transparent, null) - } else { - (gridView.adapter as BaseAdapter).notifyDataSetChanged() - } - } - - inner class ColorGridAdapter : BaseAdapter(), OnClickListener, OnLongClickListener { - override fun onClick(v: View) { - if (v.tag != null && v.tag is String) { - val tags = (v.tag as String).split(":") - if (colorIndex == tags[0].toInt()) { - colorIndex = tags[0].toInt() //Go to sub list if exists - return - } - if (colorIndex != -1) (gridView.getChildAt(colorIndex) as CircleView).animateSelected(false) - selectedColor = tags[1].toInt() - refreshColors() - val currentSub = isInSub - colorIndex = tags[0].toInt() - if (currentSub == isInSub) (gridView.getChildAt(colorIndex) as CircleView).animateSelected(true) - //Otherwise we are invalidating our grid, so there is no point in animating - } - } - - override fun onLongClick(v: View): Boolean { - if (v.tag != null && v.tag is String) { - val tag = (v.tag as String).split(":") - val color = tag[1].toInt() - (v as CircleView).showHint(color) - return true - } - return false - } - - override fun getItem(position: Int): Any = if (isInSub) colorsSub!![topIndex][position] else colorsTop[position] - - override fun getCount(): Int = if (isInSub) colorsSub!![topIndex].size else colorsTop.size - - override fun getItemId(position: Int): Long = position.toLong() - - override fun getView(position: Int, convertView: View?, parent: ViewGroup): View { - val view: CircleView = if (convertView == null) - CircleView(context).apply { layoutParams = AbsListView.LayoutParams(circleSize, circleSize) } - else - convertView as CircleView - val color: Int = if (isInSub) colorsSub!![topIndex][position] else colorsTop[position] - return view.apply { - setBackgroundColor(color) - isSelected = colorIndex == position - tag = "$position:$color" - setOnClickListener(this@ColorGridAdapter) - setOnLongClickListener(this@ColorGridAdapter) - } - } - - } -} class Builder { var title: String? = null @@ -332,7 +40,10 @@ class Builder { fun applyNestedBuilder(action: Builder.() -> Unit) = this.action() } - +/** + * This is the extension that allows us to initialize the dialog + * Note that this returns just the dialog; you still need to call .show() to show it + */ fun Context.colorPickerDialog(action: Builder.() -> Unit): MaterialDialog { val b = Builder() b.action() diff --git a/library/src/main/kotlin/ca/allanwang/kau/dialogs/color/ColorPickerView.kt b/library/src/main/kotlin/ca/allanwang/kau/dialogs/color/ColorPickerView.kt new file mode 100644 index 0000000..9d0c9c1 --- /dev/null +++ b/library/src/main/kotlin/ca/allanwang/kau/dialogs/color/ColorPickerView.kt @@ -0,0 +1,301 @@ +package ca.allanwang.kau.dialogs.color + +import android.content.Context +import android.graphics.Color +import android.support.annotation.ColorInt +import android.support.v4.content.res.ResourcesCompat +import android.text.Editable +import android.text.InputFilter +import android.text.TextWatcher +import android.util.AttributeSet +import android.view.View +import android.view.ViewGroup +import android.widget.* +import ca.allanwang.kau.R +import ca.allanwang.kau.utils.* +import com.afollestad.materialdialogs.DialogAction +import com.afollestad.materialdialogs.MaterialDialog +import com.afollestad.materialdialogs.color.FillGridView +import java.util.* + +/** + * Created by Allan Wang on 2017-06-08. + * + * ColorPicker component of the ColorPickerDialog + */ +internal class ColorPickerView @JvmOverloads constructor( + context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0 +) : ScrollView(context, attrs, defStyleAttr) { + var selectedColor: Int = -1 + var isInSub: Boolean = false + var isInCustom: Boolean = false + var circleSize: Int = context.dimen(R.dimen.kau_color_circle_size).toInt() + val backgroundColor = context.resolveColor(R.attr.md_background_color, + if (context.resolveColor(android.R.attr.textColorPrimary).isColorDark()) Color.WHITE else 0xff424242.toInt()) + val backgroundColorTint = if (backgroundColor.isColorDark()) backgroundColor.lighten(0.2f) else backgroundColor.darken(0.2f) + lateinit var dialog: MaterialDialog + lateinit var builder: Builder + lateinit var colorsTop: IntArray + var colorsSub: Array? = null + var topIndex: Int = -1 + var subIndex: Int = -1 + var colorIndex: Int + get() = if (isInSub) subIndex else topIndex + set(value) { + if (isInSub) subIndex = value + else { + topIndex = value + if (colorsSub != null && colorsSub!!.size > value) { + dialog.setActionButton(DialogAction.NEGATIVE, builder.backText) + isInSub = true + invalidateGrid() + } + } + } + + + val gridView: FillGridView by bindView(R.id.md_grid) + val customFrame: LinearLayout by bindView(R.id.md_colorChooserCustomFrame) + val customColorIndicator: View by bindView(R.id.md_colorIndicator) + val hexInput: EditText by bindView(R.id.md_hexInput) + val alphaLabel: TextView by bindView(R.id.md_colorALabel) + val alphaSeekbar: SeekBar by bindView(R.id.md_colorA) + val alphaValue: TextView by bindView(R.id.md_colorAValue) + val redSeekbar: SeekBar by bindView(R.id.md_colorR) + val redValue: TextView by bindView(R.id.md_colorRValue) + val greenSeekbar: SeekBar by bindView(R.id.md_colorG) + val greenValue: TextView by bindView(R.id.md_colorGValue) + val blueSeekbar: SeekBar by bindView(R.id.md_colorB) + val blueValue: TextView by bindView(R.id.md_colorBValue) + + var customHexTextWatcher: TextWatcher? = null + var customRgbListener: SeekBar.OnSeekBarChangeListener? = null + + init { + View.inflate(context, R.layout.md_dialog_colorchooser, this) + } + + fun bind(builder: Builder, dialog: MaterialDialog) { + this.builder = builder + this.dialog = dialog + this.colorsTop = builder.colorsTop() + this.colorsSub = builder.colorsSub() + this.selectedColor = builder.defaultColor + if (builder.allowCustom) { + if (!builder.allowCustomAlpha) { + alphaLabel.gone() + alphaSeekbar.gone() + alphaValue.gone() + hexInput.hint = String.format("%06X", selectedColor) + hexInput.filters = arrayOf(InputFilter.LengthFilter(6)) + } else { + hexInput.hint = String.format("%08X", selectedColor) + hexInput.filters = arrayOf(InputFilter.LengthFilter(8)) + } + } + if (findColor(builder.defaultColor) || !builder.allowCustom) isInCustom = true //when toggled this will be false + toggleCustom() + } + + fun backOrCancel() { + if (isInSub) { + dialog.setActionButton(DialogAction.NEGATIVE, builder.cancelText) + //to top + isInSub = false + subIndex = -1 + invalidateGrid() + } else { + dialog.cancel() + } + } + + fun toggleCustom() { + isInCustom = !isInCustom + if (isInCustom) { + isInSub = false + if (builder.allowCustom) dialog.setActionButton(DialogAction.NEUTRAL, builder.presetText) + dialog.setActionButton(DialogAction.NEGATIVE, builder.cancelText) + customHexTextWatcher = object : TextWatcher { + override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {} + + override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) { + try { + selectedColor = Color.parseColor("#" + s.toString()) + } catch (e: IllegalArgumentException) { + selectedColor = Color.BLACK + } + + customColorIndicator.setBackgroundColor(selectedColor) + if (alphaSeekbar.isVisible()) { + val alpha = Color.alpha(selectedColor) + alphaSeekbar.progress = alpha + alphaValue.text = String.format(Locale.CANADA, "%d", alpha) + } + redSeekbar.progress = Color.red(selectedColor) + greenSeekbar.progress = Color.green(selectedColor) + blueSeekbar.progress = Color.blue(selectedColor) + isInSub = false + topIndex = -1 + subIndex = -1 + refreshColors() + } + + override fun afterTextChanged(s: Editable?) {} + } + hexInput.setText(selectedColor.toHexString(builder.allowCustomAlpha, false)) + hexInput.addTextChangedListener(customHexTextWatcher) + customRgbListener = object : SeekBar.OnSeekBarChangeListener { + override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) { + if (fromUser) { + val color = if (builder.allowCustomAlpha) + Color.argb(alphaSeekbar.progress, + redSeekbar.progress, + greenSeekbar.progress, + blueSeekbar.progress) + else Color.rgb(redSeekbar.progress, + greenSeekbar.progress, + blueSeekbar.progress) + + hexInput.setText(color.toHexString(builder.allowCustomAlpha, false)) + } + if (builder.allowCustomAlpha) alphaValue.text = alphaSeekbar.progress.toString() + redValue.text = redSeekbar.progress.toString() + greenValue.text = greenSeekbar.progress.toString() + blueValue.text = blueSeekbar.progress.toString() + } + + override fun onStartTrackingTouch(seekBar: SeekBar) {} + + override fun onStopTrackingTouch(seekBar: SeekBar) {} + } + redSeekbar.setOnSeekBarChangeListener(customRgbListener) + greenSeekbar.setOnSeekBarChangeListener(customRgbListener) + blueSeekbar.setOnSeekBarChangeListener(customRgbListener) + if (alphaSeekbar.isVisible()) + alphaSeekbar.setOnSeekBarChangeListener(customRgbListener) + hexInput.setText(selectedColor.toHexString(alphaSeekbar.isVisible(), false)) + gridView.fadeOut(onFinish = { gridView.gone() }) + customFrame.fadeIn() + } else { + findColor(selectedColor) + if (builder.allowCustom) dialog.setActionButton(DialogAction.NEUTRAL, builder.customText) + dialog.setActionButton(DialogAction.NEGATIVE, if (isInSub) builder.backText else builder.cancelText) + gridView.fadeIn(onStart = { invalidateGrid() }) + customFrame.fadeOut(onFinish = { customFrame.gone() }) + hexInput.removeTextChangedListener(customHexTextWatcher) + customHexTextWatcher = null + alphaSeekbar.setOnSeekBarChangeListener(null) + redSeekbar.setOnSeekBarChangeListener(null) + greenSeekbar.setOnSeekBarChangeListener(null) + blueSeekbar.setOnSeekBarChangeListener(null) + customRgbListener = null + } + } + + fun refreshColors() { + if (!isInCustom) findColor(selectedColor) + //Ensure that our tinted color is still visible against the background + val visibleColor = if (selectedColor.isColorVisibleOn(backgroundColor)) selectedColor else backgroundColorTint + if (builder.dynamicButtonColors) { + dialog.getActionButton(DialogAction.POSITIVE).setTextColor(visibleColor) + dialog.getActionButton(DialogAction.NEGATIVE).setTextColor(visibleColor) + dialog.getActionButton(DialogAction.NEUTRAL).setTextColor(visibleColor) + } + if (!builder.allowCustom || !isInCustom) return + if (builder.allowCustomAlpha) + alphaSeekbar.visible().tint(visibleColor) + redSeekbar.tint(visibleColor) + greenSeekbar.tint(visibleColor) + blueSeekbar.tint(visibleColor) + hexInput.tint(visibleColor) + } + + fun findColor(@ColorInt color: Int): Boolean { + topIndex = -1 + subIndex = -1 + colorsTop.forEachIndexed { + index, topColor -> + if (findSubColor(color, index)) { + topIndex = index + return true + } + if (topColor == color) { // If no sub colors exists and top color matches + topIndex = index + return true + } + } + return false + } + + fun findSubColor(@ColorInt color: Int, topIndex: Int): Boolean { + if (colorsSub == null || colorsSub!!.size <= topIndex) return false + colorsSub!![topIndex].forEachIndexed { + index, subColor -> + if (subColor == color) { + subIndex = index + return true + } + } + return false + } + + fun invalidateGrid() { + if (gridView.adapter == null) { + gridView.adapter = ColorGridAdapter() + gridView.selector = ResourcesCompat.getDrawable(resources, R.drawable.kau_transparent, null) + } else { + (gridView.adapter as BaseAdapter).notifyDataSetChanged() + } + } + + inner class ColorGridAdapter : BaseAdapter(), OnClickListener, OnLongClickListener { + override fun onClick(v: View) { + if (v.tag != null && v.tag is String) { + val tags = (v.tag as String).split(":") + if (colorIndex == tags[0].toInt()) { + colorIndex = tags[0].toInt() //Go to sub list if exists + return + } + if (colorIndex != -1) (gridView.getChildAt(colorIndex) as CircleView).animateSelected(false) + selectedColor = tags[1].toInt() + refreshColors() + val currentSub = isInSub + colorIndex = tags[0].toInt() + if (currentSub == isInSub) (gridView.getChildAt(colorIndex) as CircleView).animateSelected(true) + //Otherwise we are invalidating our grid, so there is no point in animating + } + } + + override fun onLongClick(v: View): Boolean { + if (v.tag != null && v.tag is String) { + val tag = (v.tag as String).split(":") + val color = tag[1].toInt() + (v as CircleView).showHint(color) + return true + } + return false + } + + override fun getItem(position: Int): Any = if (isInSub) colorsSub!![topIndex][position] else colorsTop[position] + + override fun getCount(): Int = if (isInSub) colorsSub!![topIndex].size else colorsTop.size + + override fun getItemId(position: Int): Long = position.toLong() + + override fun getView(position: Int, convertView: View?, parent: ViewGroup): View { + val view: CircleView = if (convertView == null) + CircleView(context).apply { layoutParams = AbsListView.LayoutParams(circleSize, circleSize) } + else + convertView as CircleView + val color: Int = if (isInSub) colorsSub!![topIndex][position] else colorsTop[position] + return view.apply { + setBackgroundColor(color) + isSelected = colorIndex == position + tag = "$position:$color" + setOnClickListener(this@ColorGridAdapter) + setOnLongClickListener(this@ColorGridAdapter) + } + } + + } +} \ No newline at end of file diff --git a/library/src/main/kotlin/ca/allanwang/kau/kpref/KPrefActivity.kt b/library/src/main/kotlin/ca/allanwang/kau/kpref/KPrefActivity.kt new file mode 100644 index 0000000..eb9202d --- /dev/null +++ b/library/src/main/kotlin/ca/allanwang/kau/kpref/KPrefActivity.kt @@ -0,0 +1,37 @@ +package ca.allanwang.kau.kpref + +import android.os.Bundle +import android.support.v7.app.AppCompatActivity +import android.support.v7.widget.RecyclerView +import android.support.v7.widget.Toolbar +import android.view.View +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.views.RippleCanvas +import com.mikepenz.fastadapter.commons.adapters.FastItemAdapter + + +abstract class KPrefActivity : AppCompatActivity() { + + lateinit var adapter: FastItemAdapter + val recycler: RecyclerView by bindView(R.id.kau_recycler) + 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) + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.kau_activity_kpref) + setSupportActionBar(toolbar) + window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_STABLE or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION + window.statusBarColor = 0x30000000 + toolbarCanvas.set(resolveColor(R.attr.colorPrimary)) + bgCanvas.set(resolveColor(android.R.attr.colorBackground)) + adapter = recycler.setKPrefAdapter(onCreateKPrefs(savedInstanceState)) + } + + abstract fun onCreateKPrefs(savedInstanceState: Bundle?): KPrefAdapterBuilder.() -> Unit + +} diff --git a/library/src/main/kotlin/ca/allanwang/kau/kpref/KPrefBinder.kt b/library/src/main/kotlin/ca/allanwang/kau/kpref/KPrefBinder.kt index acbb9a9..57f4cb9 100644 --- a/library/src/main/kotlin/ca/allanwang/kau/kpref/KPrefBinder.kt +++ b/library/src/main/kotlin/ca/allanwang/kau/kpref/KPrefBinder.kt @@ -1,6 +1,5 @@ package ca.allanwang.kau.kpref -import android.support.annotation.ColorInt import android.support.annotation.StringRes import android.support.v7.widget.LinearLayoutManager import android.support.v7.widget.RecyclerView @@ -29,7 +28,12 @@ fun RecyclerView.setKPrefAdapter(builder: KPrefAdapterBuilder.() -> Unit): FastI class KPrefAdapterBuilder { var textColor: Int? = null + get() = textColorGetter?.invoke() ?: field var accentColor: Int? = null + get() = accentColorGetter?.invoke() ?: field + + var textColorGetter: (() -> Int)? = null + var accentColorGetter: (() -> Int)? = null fun header(@StringRes title: Int) = list.add(KPrefHeader(this, title)) diff --git a/library/src/main/kotlin/ca/allanwang/kau/kpref/KPrefDelegate.kt b/library/src/main/kotlin/ca/allanwang/kau/kpref/KPrefDelegate.kt index 9638904..5c6caa9 100644 --- a/library/src/main/kotlin/ca/allanwang/kau/kpref/KPrefDelegate.kt +++ b/library/src/main/kotlin/ca/allanwang/kau/kpref/KPrefDelegate.kt @@ -60,7 +60,7 @@ class KPrefDelegate internal constructor(private val key: String, priva override fun isInitialized(): Boolean = _value !== UNINITIALIZED - override fun toString(): String = if (isInitialized()) value.toString() else "Lazy pref $key not initialized yet." + 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 diff --git a/library/src/main/kotlin/ca/allanwang/kau/kpref/items/KPrefCheckbox.kt b/library/src/main/kotlin/ca/allanwang/kau/kpref/items/KPrefCheckbox.kt index 9961df9..474c3e5 100644 --- a/library/src/main/kotlin/ca/allanwang/kau/kpref/items/KPrefCheckbox.kt +++ b/library/src/main/kotlin/ca/allanwang/kau/kpref/items/KPrefCheckbox.kt @@ -5,11 +5,15 @@ import android.view.View import android.widget.CheckBox import ca.allanwang.kau.R import ca.allanwang.kau.kpref.KPrefAdapterBuilder +import ca.allanwang.kau.logging.SL import ca.allanwang.kau.utils.tint import com.mikepenz.iconics.typeface.IIcon /** * 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: KPrefAdapterBuilder, @StringRes title: Int, @@ -19,11 +23,6 @@ class KPrefCheckbox(builder: KPrefAdapterBuilder, getter: () -> Boolean, setter: (value: Boolean) -> Unit) : KPrefItemBase(builder, title, description, iicon, enabled, getter, setter) { - override fun onPostBindView(viewHolder: KPrefItemCore.ViewHolder) { - super.onPostBindView(viewHolder) - viewHolder.addInnerView(R.layout.kau_preference_checkbox) - (viewHolder[R.id.kau_pref_checkbox] as CheckBox).isChecked = pref - } override fun onClick(itemView: View): Boolean { val checkbox = itemView.findViewById(R.id.kau_pref_checkbox) as CheckBox @@ -32,11 +31,14 @@ class KPrefCheckbox(builder: KPrefAdapterBuilder, return true } - override fun setColors(viewHolder: ViewHolder, builder: KPrefAdapterBuilder) { - super.setColors(viewHolder, builder) + override fun onPostBindView(viewHolder: ViewHolder, builder: KPrefAdapterBuilder) { + super.onPostBindView(viewHolder, builder) + viewHolder.addInnerView(R.layout.kau_preference_checkbox) if (builder.accentColor != null) { val checkbox = viewHolder.itemView.findViewById(R.id.kau_pref_checkbox) as CheckBox checkbox.tint(builder.accentColor!!) + checkbox.isChecked = pref //Checkbox tick needs to be delayed since notifyDataSetChanged will cancel the animation + //It seems to work well here } } diff --git a/library/src/main/kotlin/ca/allanwang/kau/kpref/items/KPrefColorPicker.kt b/library/src/main/kotlin/ca/allanwang/kau/kpref/items/KPrefColorPicker.kt index 38a12e2..a9926ef 100644 --- a/library/src/main/kotlin/ca/allanwang/kau/kpref/items/KPrefColorPicker.kt +++ b/library/src/main/kotlin/ca/allanwang/kau/kpref/items/KPrefColorPicker.kt @@ -10,6 +10,9 @@ import com.mikepenz.iconics.typeface.IIcon /** * 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(builder: KPrefAdapterBuilder, @StringRes title: Int, @@ -20,8 +23,8 @@ class KPrefColorPicker(builder: KPrefAdapterBuilder, setter: (value: Int) -> Unit, val configs: Builder.() -> Unit = {}) : KPrefItemBase(builder, title, description, iicon, enabled, getter, setter) { - override fun onPostBindView(viewHolder: KPrefItemCore.ViewHolder) { - super.onPostBindView(viewHolder) + override fun onPostBindView(viewHolder: KPrefItemCore.ViewHolder, builder: KPrefAdapterBuilder) { + super.onPostBindView(viewHolder, builder) //TODO add color circle view } diff --git a/library/src/main/kotlin/ca/allanwang/kau/kpref/items/KPrefHeader.kt b/library/src/main/kotlin/ca/allanwang/kau/kpref/items/KPrefHeader.kt index 4d3952e..1175fc2 100644 --- a/library/src/main/kotlin/ca/allanwang/kau/kpref/items/KPrefHeader.kt +++ b/library/src/main/kotlin/ca/allanwang/kau/kpref/items/KPrefHeader.kt @@ -7,13 +7,17 @@ import ca.allanwang.kau.kpref.KPrefAdapterBuilder /** * 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:KPrefAdapterBuilder, @StringRes title: Int) : KPrefItemCore(builder, title = title) { +class KPrefHeader(builder: KPrefAdapterBuilder, @StringRes title: Int) : KPrefItemCore(builder, title = title) { override fun getLayoutRes(): Int = R.layout.kau_preference_header - override fun onPostBindView(viewHolder: ViewHolder) { + override fun onPostBindView(viewHolder: ViewHolder, builder: KPrefAdapterBuilder) { viewHolder.itemView.isClickable = false + if (builder.accentColor != null) viewHolder.title.setTextColor(builder.accentColor!!) } override fun onClick(itemView: View): Boolean = true diff --git a/library/src/main/kotlin/ca/allanwang/kau/kpref/items/KPrefItemBase.kt b/library/src/main/kotlin/ca/allanwang/kau/kpref/items/KPrefItemBase.kt index b5e0254..adcbc8a 100644 --- a/library/src/main/kotlin/ca/allanwang/kau/kpref/items/KPrefItemBase.kt +++ b/library/src/main/kotlin/ca/allanwang/kau/kpref/items/KPrefItemBase.kt @@ -28,7 +28,7 @@ abstract class KPrefItemBase(builder: KPrefAdapterBuilder, } @CallSuper - override fun onPostBindView(viewHolder: ViewHolder) { + override fun onPostBindView(viewHolder: ViewHolder, builder: KPrefAdapterBuilder) { viewHolder.itemView.isEnabled = enabled viewHolder.itemView.alpha = if (enabled) 1.0f else 0.3f } diff --git a/library/src/main/kotlin/ca/allanwang/kau/kpref/items/KPrefItemCore.kt b/library/src/main/kotlin/ca/allanwang/kau/kpref/items/KPrefItemCore.kt index 112365a..98e977e 100644 --- a/library/src/main/kotlin/ca/allanwang/kau/kpref/items/KPrefItemCore.kt +++ b/library/src/main/kotlin/ca/allanwang/kau/kpref/items/KPrefItemCore.kt @@ -45,13 +45,12 @@ abstract class KPrefItemCore(val builder: KPrefAdapterBuilder, icon?.setIcon(iicon, 48) } else iconFrame?.gone() innerFrame?.removeAllViews() - onPostBindView(this) - setColors(this, builder) + setColors(this) + onPostBindView(this, builder) } } - @CallSuper - open fun setColors(viewHolder: ViewHolder, builder: KPrefAdapterBuilder) { + fun setColors(viewHolder: ViewHolder) { with(viewHolder) { if (builder.textColor != null) { title.setTextColor(builder.textColor!!) @@ -63,7 +62,7 @@ abstract class KPrefItemCore(val builder: KPrefAdapterBuilder, } } - abstract fun onPostBindView(viewHolder: ViewHolder) + abstract fun onPostBindView(viewHolder: ViewHolder, builder: KPrefAdapterBuilder) abstract fun onClick(itemView: View): Boolean diff --git a/library/src/main/kotlin/ca/allanwang/kau/utils/ColorUtils.kt b/library/src/main/kotlin/ca/allanwang/kau/utils/ColorUtils.kt index 325a8af..35681b0 100644 --- a/library/src/main/kotlin/ca/allanwang/kau/utils/ColorUtils.kt +++ b/library/src/main/kotlin/ca/allanwang/kau/utils/ColorUtils.kt @@ -82,12 +82,12 @@ fun String.toColor(): Int { //Get ColorStateList fun Context.colorStateList(@ColorInt color: Int): ColorStateList { - val disabledColor = getDisabledColor() + val disabledColor = color.adjustAlpha(0.3f) return ColorStateList(arrayOf(intArrayOf(android.R.attr.state_enabled, -android.R.attr.state_checked), intArrayOf(android.R.attr.state_enabled, android.R.attr.state_checked), intArrayOf(-android.R.attr.state_enabled, -android.R.attr.state_checked), intArrayOf(-android.R.attr.state_enabled, android.R.attr.state_checked)), - intArrayOf(this.resolveColor(R.attr.colorControlNormal), color, disabledColor, disabledColor)) + intArrayOf(color.adjustAlpha(0.8f), color, disabledColor, disabledColor)) } /* diff --git a/library/src/main/kotlin/ca/allanwang/kau/utils/ContextUtils.kt b/library/src/main/kotlin/ca/allanwang/kau/utils/ContextUtils.kt index 16804a5..5547fe1 100644 --- a/library/src/main/kotlin/ca/allanwang/kau/utils/ContextUtils.kt +++ b/library/src/main/kotlin/ca/allanwang/kau/utils/ContextUtils.kt @@ -3,6 +3,7 @@ package ca.allanwang.kau.utils import android.app.Activity import android.content.Context import android.content.Intent +import android.graphics.Color import android.graphics.drawable.Drawable import android.net.ConnectivityManager import android.os.Handler @@ -29,6 +30,12 @@ fun Activity.restart(extras: ((Intent) -> Unit)? = null) { overridePendingTransition(0, 0) } +var Activity.navigationBarColor: Int + get() = if (buildIsLollipopAndUp) window.navigationBarColor else Color.BLACK + set(value) { + if (buildIsLollipopAndUp) window.navigationBarColor = value + } + //Toast helpers fun Context.toast(@StringRes id: Int, duration: Int = Toast.LENGTH_LONG) = toast(this.string(id), duration) @@ -86,10 +93,11 @@ fun Context.showChangelog(@XmlRes xmlRes: Int) { }).start() } -fun Context.isNetworkAvailable(): Boolean { - val connectivityManager = getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager - val activeNetworkInfo = connectivityManager.activeNetworkInfo - return activeNetworkInfo != null && activeNetworkInfo.isConnectedOrConnecting -} +val Context.isNetworkAvailable: Boolean + get() { + val connectivityManager = getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager + val activeNetworkInfo = connectivityManager.activeNetworkInfo + return activeNetworkInfo != null && activeNetworkInfo.isConnectedOrConnecting + } fun Context.getDip(value: Float): Float = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, value, resources.displayMetrics) \ No newline at end of file diff --git a/library/src/main/kotlin/ca/allanwang/kau/utils/Utils.kt b/library/src/main/kotlin/ca/allanwang/kau/utils/Utils.kt index 79f80f3..e00891d 100644 --- a/library/src/main/kotlin/ca/allanwang/kau/utils/Utils.kt +++ b/library/src/main/kotlin/ca/allanwang/kau/utils/Utils.kt @@ -1,11 +1,20 @@ package ca.allanwang.kau.utils import android.content.res.Resources +import android.os.Build /** * Created by Allan Wang on 2017-05-28. */ -fun Int.dpToPx(): Int = (this * Resources.getSystem().displayMetrics.density).toInt() +val Int.dpToPx: Int + get() = (this * Resources.getSystem().displayMetrics.density).toInt() -fun Int.pxToDp(px: Int) = (px / android.content.res.Resources.getSystem().displayMetrics.density).toInt() \ No newline at end of file +val Int.pxToDp: Int + get() = (this / Resources.getSystem().displayMetrics.density).toInt() + +val buildIsLollipopAndUp: Boolean + get() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP + +val buildIsMarshmallowAndUp: Boolean + get() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.M \ No newline at end of file diff --git a/library/src/main/kotlin/ca/allanwang/kau/views/RippleCanvas.kt b/library/src/main/kotlin/ca/allanwang/kau/views/RippleCanvas.kt index 9444ef9..eaa9121 100644 --- a/library/src/main/kotlin/ca/allanwang/kau/views/RippleCanvas.kt +++ b/library/src/main/kotlin/ca/allanwang/kau/views/RippleCanvas.kt @@ -72,16 +72,18 @@ class RippleCanvas @JvmOverloads constructor( } fun ripple(color: Int, startX: Float = 0f, startY: Float = 0f, duration: Int = 1000, fade: Boolean = Color.alpha(color) != 255) { - var x = startX - var y = startY val w = width.toFloat() val h = height.toFloat() - if (x == MIDDLE) - x = w / 2 - else if (x > w) x = 0f - if (y == MIDDLE) - y = h / 2 - else if (y > h) y = 0f + val x = when (startX) { + MIDDLE -> w/2 + END -> w + else -> startX + } + val y = when (startY) { + MIDDLE -> h/2 + END -> h + else -> startY + } val maxRadius = Math.hypot(Math.max(x, w - x).toDouble(), Math.max(y, h - y).toDouble()).toFloat() val ripple = Ripple(color, x, y, 0f, maxRadius, fade) ripples.add(ripple) @@ -109,6 +111,7 @@ class RippleCanvas @JvmOverloads constructor( companion object { const val MIDDLE = -1.0f + const val END = -2.0f const val FADE_PIVOT = 0.5f } } diff --git a/library/src/main/res/layout/kau_activity_kpref.xml b/library/src/main/res/layout/kau_activity_kpref.xml new file mode 100644 index 0000000..35f16cd --- /dev/null +++ b/library/src/main/res/layout/kau_activity_kpref.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + diff --git a/library/src/main/res/values/dimens.xml b/library/src/main/res/values/dimens.xml index 85ef703..fddb6e3 100644 --- a/library/src/main/res/values/dimens.xml +++ b/library/src/main/res/values/dimens.xml @@ -12,4 +12,5 @@ 100dp 56dp + 24dp -- cgit v1.2.3