aboutsummaryrefslogtreecommitdiff
path: root/library/src/main/kotlin
diff options
context:
space:
mode:
authorAllan Wang <me@allanwang.ca>2017-06-10 22:04:03 -0700
committerAllan Wang <me@allanwang.ca>2017-06-10 22:04:03 -0700
commitc0550e9892ae454e1f244dd2980c633d85803a25 (patch)
tree0adf61391859f3a71c50a9d499135248916d691c /library/src/main/kotlin
parent6dc743c0ba91904d27fba42a4e8e2de6a72c719a (diff)
downloadkau-c0550e9892ae454e1f244dd2980c633d85803a25.tar.gz
kau-c0550e9892ae454e1f244dd2980c633d85803a25.tar.bz2
kau-c0550e9892ae454e1f244dd2980c633d85803a25.zip
Support transparent backgrounds in the ripple canvas
Diffstat (limited to 'library/src/main/kotlin')
-rw-r--r--library/src/main/kotlin/ca/allanwang/kau/dialogs/color/ColorPickerDialog.kt32
-rw-r--r--library/src/main/kotlin/ca/allanwang/kau/kpref/KPrefBinder.kt13
-rw-r--r--library/src/main/kotlin/ca/allanwang/kau/kpref/items/KPrefCheckbox.kt15
-rw-r--r--library/src/main/kotlin/ca/allanwang/kau/kpref/items/KPrefColorPicker.kt9
-rw-r--r--library/src/main/kotlin/ca/allanwang/kau/kpref/items/KPrefHeader.kt3
-rw-r--r--library/src/main/kotlin/ca/allanwang/kau/kpref/items/KPrefItemBase.kt6
-rw-r--r--library/src/main/kotlin/ca/allanwang/kau/kpref/items/KPrefItemCore.kt20
-rw-r--r--library/src/main/kotlin/ca/allanwang/kau/utils/ColorUtils.kt20
-rw-r--r--library/src/main/kotlin/ca/allanwang/kau/views/RippleCanvas.kt59
9 files changed, 135 insertions, 42 deletions
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 4961211..4931913 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
@@ -29,6 +29,9 @@ internal class ColorPickerView @JvmOverloads constructor(
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
@@ -89,7 +92,7 @@ internal class ColorPickerView @JvmOverloads constructor(
hexInput.filters = arrayOf(InputFilter.LengthFilter(8))
}
}
- if (findColor(builder.defaultColor)) isInCustom = true //when toggled this will be false
+ if (findColor(builder.defaultColor) || !builder.allowCustom) isInCustom = true //when toggled this will be false
toggleCustom()
}
@@ -109,7 +112,7 @@ internal class ColorPickerView @JvmOverloads constructor(
isInCustom = !isInCustom
if (isInCustom) {
isInSub = false
- dialog.setActionButton(DialogAction.NEUTRAL, builder.presetText)
+ 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) {}
@@ -174,7 +177,7 @@ internal class ColorPickerView @JvmOverloads constructor(
customFrame.fadeIn()
} else {
findColor(selectedColor)
- dialog.setActionButton(DialogAction.NEUTRAL, builder.customText)
+ 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() })
@@ -190,19 +193,14 @@ internal class ColorPickerView @JvmOverloads constructor(
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(selectedColor)
- dialog.getActionButton(DialogAction.NEGATIVE).setTextColor(selectedColor)
- dialog.getActionButton(DialogAction.NEUTRAL).setTextColor(selectedColor)
+ dialog.getActionButton(DialogAction.POSITIVE).setTextColor(visibleColor)
+ dialog.getActionButton(DialogAction.NEGATIVE).setTextColor(visibleColor)
+ dialog.getActionButton(DialogAction.NEUTRAL).setTextColor(visibleColor)
}
if (!builder.allowCustom || !isInCustom) return
- // Once we get close to white or transparent,
- // the action buttons and seekbars will be a very light gray.
- // TODO change by dialog theme
- val visibleColor = if (Color.alpha(selectedColor) < 64 ||
- Color.red(selectedColor) > 247 && Color.green(selectedColor) > 247 && Color.blue(selectedColor) > 247)
- "#DEDEDE".toColor() else selectedColor
-
if (builder.allowCustomAlpha)
alphaSeekbar.visible().tint(visibleColor)
redSeekbar.tint(visibleColor)
@@ -253,7 +251,10 @@ internal class ColorPickerView @JvmOverloads constructor(
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()) return
+ 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()
@@ -328,6 +329,7 @@ class Builder {
var theme: Theme? = null
+ fun applyNestedBuilder(action: Builder.() -> Unit) = this.action()
}
@@ -341,7 +343,7 @@ fun Context.colorPickerDialog(action: Builder.() -> Unit): MaterialDialog {
autoDismiss(false)
positiveText(b.doneText)
negativeText(b.cancelText)
- neutralText(b.presetText)
+ if (b.allowCustom) neutralText(b.presetText)
onPositive { dialog, _ -> b.colorCallbacks.forEach { it.invoke(view.selectedColor) }; dialog.dismiss() }
onNegative { dialog, _ -> view.backOrCancel() }
if (b.allowCustom) onNeutral { dialog, _ -> view.toggleCustom() }
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 e346e77..acbb9a9 100644
--- a/library/src/main/kotlin/ca/allanwang/kau/kpref/KPrefBinder.kt
+++ b/library/src/main/kotlin/ca/allanwang/kau/kpref/KPrefBinder.kt
@@ -1,8 +1,10 @@
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
+import ca.allanwang.kau.dialogs.color.Builder
import ca.allanwang.kau.kpref.items.KPrefCheckbox
import ca.allanwang.kau.kpref.items.KPrefColorPicker
import ca.allanwang.kau.kpref.items.KPrefHeader
@@ -26,21 +28,26 @@ fun RecyclerView.setKPrefAdapter(builder: KPrefAdapterBuilder.() -> Unit): FastI
class KPrefAdapterBuilder {
- fun header(@StringRes title: Int) = list.add(KPrefHeader(title))
+ var textColor: Int? = null
+ var accentColor: Int? = null
+
+ fun header(@StringRes title: Int) = list.add(KPrefHeader(this, title))
fun checkbox(@StringRes title: Int,
@StringRes description: Int = -1,
iicon: IIcon? = null,
enabled: Boolean = true,
getter: () -> Boolean,
- setter: (value: Boolean) -> Unit) = list.add(KPrefCheckbox(title, description, iicon, enabled, getter, setter))
+ setter: (value: Boolean) -> Unit) = list.add(KPrefCheckbox(this, title, description, iicon, enabled, getter, setter))
fun colorPicker(@StringRes title: Int,
@StringRes description: Int = -1,
iicon: IIcon? = null,
enabled: Boolean = true,
getter: () -> Int,
- setter: (value: Int) -> Unit)= list.add(KPrefColorPicker(title, description, iicon, enabled, getter, setter))
+ setter: (value: Int) -> Unit,
+ configs: Builder.() -> Unit = {}) = list.add(KPrefColorPicker(this, title, description, iicon, enabled, getter, setter, configs))
internal val list: MutableList<KPrefItemCore> = mutableListOf()
+
} \ No newline at end of file
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 3c87571..9961df9 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
@@ -4,17 +4,20 @@ import android.support.annotation.StringRes
import android.view.View
import android.widget.CheckBox
import ca.allanwang.kau.R
+import ca.allanwang.kau.kpref.KPrefAdapterBuilder
+import ca.allanwang.kau.utils.tint
import com.mikepenz.iconics.typeface.IIcon
/**
* Created by Allan Wang on 2017-06-07.
*/
-class KPrefCheckbox(@StringRes title: Int,
+class KPrefCheckbox(builder: KPrefAdapterBuilder,
+ @StringRes title: Int,
@StringRes description: Int = -1,
iicon: IIcon? = null,
enabled: Boolean = true,
getter: () -> Boolean,
- setter: (value: Boolean) -> Unit) : KPrefItemBase<Boolean>(title, description, iicon, enabled, getter, setter) {
+ setter: (value: Boolean) -> Unit) : KPrefItemBase<Boolean>(builder, title, description, iicon, enabled, getter, setter) {
override fun onPostBindView(viewHolder: KPrefItemCore.ViewHolder) {
super.onPostBindView(viewHolder)
@@ -29,6 +32,14 @@ class KPrefCheckbox(@StringRes title: Int,
return true
}
+ override fun setColors(viewHolder: ViewHolder, builder: KPrefAdapterBuilder) {
+ super.setColors(viewHolder, builder)
+ if (builder.accentColor != null) {
+ val checkbox = viewHolder.itemView.findViewById(R.id.kau_pref_checkbox) as CheckBox
+ checkbox.tint(builder.accentColor!!)
+ }
+ }
+
override fun getType(): Int = R.id.kau_item_pref_checkbox
} \ No newline at end of file
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 cca35b0..38a12e2 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
@@ -3,18 +3,22 @@ package ca.allanwang.kau.kpref.items
import android.support.annotation.StringRes
import android.view.View
import ca.allanwang.kau.R
+import ca.allanwang.kau.dialogs.color.Builder
import ca.allanwang.kau.dialogs.color.colorPickerDialog
+import ca.allanwang.kau.kpref.KPrefAdapterBuilder
import com.mikepenz.iconics.typeface.IIcon
/**
* Created by Allan Wang on 2017-06-07.
*/
-class KPrefColorPicker(@StringRes title: Int,
+class KPrefColorPicker(builder: KPrefAdapterBuilder,
+ @StringRes title: Int,
@StringRes description: Int = -1,
iicon: IIcon? = null,
enabled: Boolean = true,
getter: () -> Int,
- setter: (value: Int) -> Unit) : KPrefItemBase<Int>(title, description, iicon, enabled, getter, setter) {
+ setter: (value: Int) -> Unit,
+ val configs: Builder.() -> Unit = {}) : KPrefItemBase<Int>(builder, title, description, iicon, enabled, getter, setter) {
override fun onPostBindView(viewHolder: KPrefItemCore.ViewHolder) {
super.onPostBindView(viewHolder)
@@ -26,6 +30,7 @@ class KPrefColorPicker(@StringRes title: Int,
titleRes = this@KPrefColorPicker.title
defaultColor = pref
colorCallbacks.add { pref = it }
+ applyNestedBuilder(configs)
}.show()
return true
}
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 9c22469..4d3952e 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
@@ -3,11 +3,12 @@ package ca.allanwang.kau.kpref.items
import android.support.annotation.StringRes
import android.view.View
import ca.allanwang.kau.R
+import ca.allanwang.kau.kpref.KPrefAdapterBuilder
/**
* Created by Allan Wang on 2017-06-07.
*/
-class KPrefHeader(@StringRes title: Int) : KPrefItemCore(title = title) {
+class KPrefHeader(builder:KPrefAdapterBuilder, @StringRes title: Int) : KPrefItemCore(builder, title = title) {
override fun getLayoutRes(): Int = R.layout.kau_preference_header
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 c86f3b6..b5e0254 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
@@ -4,6 +4,7 @@ import android.support.annotation.CallSuper
import android.support.annotation.StringRes
import android.util.Log
import ca.allanwang.kau.R
+import ca.allanwang.kau.kpref.KPrefAdapterBuilder
import com.mikepenz.iconics.typeface.IIcon
/**
@@ -12,12 +13,13 @@ import com.mikepenz.iconics.typeface.IIcon
* Base class for pref setters that include the Shared Preference hooks
*/
-abstract class KPrefItemBase<T>(@StringRes title: Int,
+abstract class KPrefItemBase<T>(builder: KPrefAdapterBuilder,
+ @StringRes title: Int,
@StringRes description: Int = -1,
iicon: IIcon? = null,
val enabled: Boolean = true,
private val getter: () -> T,
- private val setter: (value: T) -> Unit) : KPrefItemCore(title, description, iicon) {
+ private val setter: (value: T) -> Unit) : KPrefItemCore(builder, title, description, iicon) {
var pref: T
get() = getter.invoke()
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 8766234..112365a 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
@@ -12,7 +12,7 @@ import android.widget.LinearLayout
import android.widget.TextView
import butterknife.ButterKnife
import ca.allanwang.kau.R
-import ca.allanwang.kau.logging.SL
+import ca.allanwang.kau.kpref.KPrefAdapterBuilder
import ca.allanwang.kau.utils.*
import com.mikepenz.fastadapter.items.AbstractItem
import com.mikepenz.iconics.typeface.IIcon
@@ -23,7 +23,8 @@ import com.mikepenz.iconics.typeface.IIcon
* Core class containing nothing but the view items
*/
-abstract class KPrefItemCore(@StringRes val title: Int,
+abstract class KPrefItemCore(val builder: KPrefAdapterBuilder,
+ @StringRes val title: Int,
@StringRes val description: Int = -1,
val iicon: IIcon? = null) : AbstractItem<KPrefItemCore, KPrefItemCore.ViewHolder>() {
@@ -43,7 +44,22 @@ abstract class KPrefItemCore(@StringRes val title: Int,
iconFrame?.visible()
icon?.setIcon(iicon, 48)
} else iconFrame?.gone()
+ innerFrame?.removeAllViews()
onPostBindView(this)
+ setColors(this, builder)
+ }
+ }
+
+ @CallSuper
+ open fun setColors(viewHolder: ViewHolder, builder: KPrefAdapterBuilder) {
+ with(viewHolder) {
+ if (builder.textColor != null) {
+ title.setTextColor(builder.textColor!!)
+ desc?.setTextColor(builder.textColor!!)
+ }
+ if (builder.accentColor != null) {
+ icon?.drawable?.setTint(builder.accentColor!!)
+ }
}
}
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 c8fc90d..325a8af 100644
--- a/library/src/main/kotlin/ca/allanwang/kau/utils/ColorUtils.kt
+++ b/library/src/main/kotlin/ca/allanwang/kau/utils/ColorUtils.kt
@@ -33,12 +33,12 @@ fun Int.toHSV(): FloatArray {
return hsv
}
-fun FloatArray.toColor():Int=Color.HSVToColor(this)
+fun FloatArray.toColor(): Int = Color.HSVToColor(this)
-fun Int.isColorVisibleOn(@ColorInt color: Int, @IntRange(from = 0L, to = 255L) delta: Int = 8,
+fun Int.isColorVisibleOn(@ColorInt color: Int, @IntRange(from = 0L, to = 255L) delta: Int = 25,
@IntRange(from = 0L, to = 255L) minAlpha: Int = 50): Boolean =
if (Color.alpha(this) < minAlpha) false
- else (Math.abs(Color.red(this) - Color.red(color)) < delta
+ else !(Math.abs(Color.red(this) - Color.red(color)) < delta
&& Math.abs(Color.green(this) - Color.green(color)) < delta
&& Math.abs(Color.blue(this) - Color.blue(color)) < delta)
@@ -56,6 +56,20 @@ fun Int.adjustAlpha(factor: Float): Int {
return Color.argb(alpha, Color.red(this), Color.green(this), Color.blue(this))
}
+@ColorInt
+fun Int.lighten(@FloatRange(from = 0.0, to = 1.0) factor: Float = 0.2f): Int {
+ val (red, green, blue) = intArrayOf(Color.red(this), Color.green(this), Color.blue(this))
+ .map { Math.min(it * (1 + factor), 255.0f).toInt() }
+ return Color.argb(Color.alpha(this), red, green, blue)
+}
+
+@ColorInt
+fun Int.darken(@FloatRange(from = 0.0, to = 1.0) factor: Float = 0.2f): Int {
+ val (red, green, blue) = intArrayOf(Color.red(this), Color.green(this), Color.blue(this))
+ .map { Math.max(it * (1 - factor), 0f).toInt() }
+ return Color.argb(Color.alpha(this), red, green, blue)
+}
+
@Throws(IllegalArgumentException::class)
fun String.toColor(): Int {
val toParse: String
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 2c44197..9444ef9 100644
--- a/library/src/main/kotlin/ca/allanwang/kau/views/RippleCanvas.kt
+++ b/library/src/main/kotlin/ca/allanwang/kau/views/RippleCanvas.kt
@@ -7,6 +7,7 @@ import android.graphics.Color
import android.graphics.Paint
import android.util.AttributeSet
import android.view.View
+import ca.allanwang.kau.utils.adjustAlpha
/**
* Created by Allan Wang on 2016-11-17.
@@ -28,21 +29,49 @@ class RippleCanvas @JvmOverloads constructor(
paint.style = Paint.Style.FILL
}
+ /**
+ * Drawing the ripples involves having access to the next layer if it exists,
+ * and using its values to decide on the current color.
+ * If the next layer requests a fade, we will adjust the alpha of our current layer before drawing.
+ * Otherwise we will just draw the color as intended
+ */
override fun onDraw(canvas: Canvas) {
- canvas.drawColor(baseColor)
- val itr = ripples.iterator()
- while (itr.hasNext()) {
- val r = itr.next()
- paint.color = r.color
- canvas.drawCircle(r.x, r.y, r.radius, paint)
- if (r.radius == r.maxRadius) {
- itr.remove()
- baseColor = r.color
+ val itr = ripples.listIterator()
+ if (!itr.hasNext()) return canvas.drawColor(baseColor)
+ var next = itr.next()
+ canvas.drawColor(colorToDraw(baseColor, next.fade, next.radius, next.maxRadius))
+ var last = false
+ while (!last) {
+ val current = next
+ if (itr.hasNext()) next = itr.next()
+ else last = true
+ //We may fade any layer except for the last one
+ paint.color = colorToDraw(current.color, next.fade && !last, next.radius, next.maxRadius)
+ canvas.drawCircle(current.x, current.y, current.radius, paint)
+ if (current.radius == current.maxRadius) {
+ if (!last) {
+ itr.previous()
+ itr.remove()
+ itr.next()
+ } else {
+ itr.remove()
+ }
+ baseColor = current.color
}
}
}
- fun ripple(color: Int, startX: Float = 0f, startY: Float = 0f, duration: Int = 1000) {
+ /**
+ * Given our current color and next layer's radius & max,
+ * we will decide on the alpha of our current layer
+ */
+ internal fun colorToDraw(color: Int, fade: Boolean, current: Float, goal: Float): Int {
+ if (!fade || (current / goal <= FADE_PIVOT)) return color
+ val factor = (goal - current) / (goal - FADE_PIVOT * goal)
+ return color.adjustAlpha(factor)
+ }
+
+ 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()
@@ -54,7 +83,7 @@ class RippleCanvas @JvmOverloads constructor(
y = h / 2
else if (y > h) y = 0f
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)
+ val ripple = Ripple(color, x, y, 0f, maxRadius, fade)
ripples.add(ripple)
val animator = ValueAnimator.ofFloat(0f, maxRadius)
animator.duration = duration.toLong()
@@ -71,9 +100,15 @@ class RippleCanvas @JvmOverloads constructor(
invalidate()
}
- internal class Ripple(val color: Int, val x: Float, val y: Float, var radius: Float, val maxRadius: Float)
+ internal class Ripple(val color: Int,
+ val x: Float,
+ val y: Float,
+ var radius: Float,
+ val maxRadius: Float,
+ val fade: Boolean)
companion object {
const val MIDDLE = -1.0f
+ const val FADE_PIVOT = 0.5f
}
}