You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
288 lines
9.4 KiB
Kotlin
288 lines
9.4 KiB
Kotlin
package org.thoughtcrime.securesms.util
|
|
|
|
import android.animation.ArgbEvaluator
|
|
import android.animation.ValueAnimator
|
|
import android.content.Context
|
|
import android.graphics.Canvas
|
|
import android.graphics.Paint
|
|
import android.util.AttributeSet
|
|
import android.view.View
|
|
import android.view.animation.AccelerateDecelerateInterpolator
|
|
import android.widget.LinearLayout
|
|
import android.widget.RelativeLayout
|
|
import androidx.annotation.ColorInt
|
|
import androidx.annotation.ColorRes
|
|
import network.loki.messenger.R
|
|
import kotlin.math.roundToInt
|
|
|
|
interface GlowView {
|
|
var mainColor: Int
|
|
var sessionShadowColor: Int
|
|
}
|
|
|
|
object GlowViewUtilities {
|
|
|
|
fun animateColorIdChange(context: Context, view: GlowView, @ColorRes startColorID: Int, @ColorRes endColorID: Int) {
|
|
val startColor = context.resources.getColorWithID(startColorID, context.theme)
|
|
val endColor = context.resources.getColorWithID(endColorID, context.theme)
|
|
val animation = ValueAnimator.ofObject(ArgbEvaluator(), startColor, endColor)
|
|
animation.duration = 250
|
|
animation.addUpdateListener { animator ->
|
|
val color = animator.animatedValue as Int
|
|
view.mainColor = color
|
|
}
|
|
animation.start()
|
|
}
|
|
|
|
fun animateColorChange(view: GlowView, @ColorInt startColor: Int, @ColorInt endColor: Int) {
|
|
val animation = ValueAnimator.ofObject(ArgbEvaluator(), startColor, endColor)
|
|
animation.duration = 250
|
|
animation.addUpdateListener { animator ->
|
|
val color = animator.animatedValue as Int
|
|
view.mainColor = color
|
|
}
|
|
animation.start()
|
|
}
|
|
|
|
fun animateShadowColorIdChange(context: Context, view: GlowView, @ColorRes startColorID: Int, @ColorRes endColorID: Int) {
|
|
val startColor = context.resources.getColorWithID(startColorID, context.theme)
|
|
val endColor = context.resources.getColorWithID(endColorID, context.theme)
|
|
val animation = ValueAnimator.ofObject(ArgbEvaluator(), startColor, endColor)
|
|
animation.duration = 250
|
|
animation.addUpdateListener { animator ->
|
|
val color = animator.animatedValue as Int
|
|
view.sessionShadowColor = color
|
|
}
|
|
animation.start()
|
|
}
|
|
|
|
fun animateShadowColorChange(
|
|
view: GlowView,
|
|
@ColorInt startColor: Int,
|
|
@ColorInt endColor: Int,
|
|
duration: Long = 250
|
|
) {
|
|
val animation = ValueAnimator.ofObject(ArgbEvaluator(), startColor, endColor)
|
|
animation.duration = duration
|
|
animation.interpolator = AccelerateDecelerateInterpolator()
|
|
animation.addUpdateListener { animator ->
|
|
val color = animator.animatedValue as Int
|
|
view.sessionShadowColor = color
|
|
}
|
|
animation.start()
|
|
}
|
|
}
|
|
|
|
class PNModeView : LinearLayout, GlowView {
|
|
@ColorInt override var mainColor: Int = 0
|
|
set(newValue) { field = newValue; paint.color = newValue }
|
|
@ColorInt var strokeColor: Int = 0
|
|
set(newValue) { field = newValue; strokePaint.color = newValue }
|
|
@ColorInt override var sessionShadowColor: Int = 0
|
|
set(newValue) { field = newValue; paint.setShadowLayer(toPx(4, resources).toFloat(), 0.0f, 0.0f, newValue) }
|
|
|
|
private val paint: Paint by lazy {
|
|
val result = Paint()
|
|
result.style = Paint.Style.FILL
|
|
result.isAntiAlias = true
|
|
result
|
|
}
|
|
|
|
private val strokePaint: Paint by lazy {
|
|
val result = Paint()
|
|
result.style = Paint.Style.STROKE
|
|
result.isAntiAlias = true
|
|
result.strokeWidth = toPx(1, resources).toFloat()
|
|
result
|
|
}
|
|
|
|
// region Lifecycle
|
|
constructor(context: Context) : super(context) { }
|
|
constructor(context: Context, attrs: AttributeSet) : super(context, attrs) { }
|
|
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) { }
|
|
|
|
init {
|
|
setWillNotDraw(false)
|
|
}
|
|
// endregion
|
|
|
|
// region Updating
|
|
override fun onDraw(c: Canvas) {
|
|
val w = width.toFloat()
|
|
val h = height.toFloat()
|
|
val r = resources.getDimension(R.dimen.pn_option_corner_radius)
|
|
c.drawRoundRect(0.0f, 0.0f, w, h, r, r, paint)
|
|
c.drawRoundRect(0.0f, 0.0f, w, h, r, r, strokePaint)
|
|
super.onDraw(c)
|
|
}
|
|
// endregion
|
|
}
|
|
|
|
class NewConversationButtonImageView : androidx.appcompat.widget.AppCompatImageView, GlowView {
|
|
@ColorInt override var mainColor: Int = 0
|
|
set(newValue) { field = newValue; paint.color = newValue }
|
|
@ColorInt override var sessionShadowColor: Int = 0
|
|
set(newValue) { field = newValue; paint.setShadowLayer(toPx(6, resources).toFloat(), 0.0f, 0.0f, newValue) }
|
|
|
|
private val paint: Paint by lazy {
|
|
val result = Paint()
|
|
result.style = Paint.Style.FILL
|
|
result.isAntiAlias = true
|
|
result
|
|
}
|
|
|
|
// region Lifecycle
|
|
constructor(context: Context) : super(context) { }
|
|
constructor(context: Context, attrs: AttributeSet) : super(context, attrs) { }
|
|
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) { }
|
|
|
|
init {
|
|
setWillNotDraw(false)
|
|
}
|
|
// endregion
|
|
|
|
// region Updating
|
|
override fun onDraw(c: Canvas) {
|
|
val w = width.toFloat()
|
|
val h = height.toFloat()
|
|
c.drawCircle(w / 2, h / 2, w / 2, paint)
|
|
super.onDraw(c)
|
|
}
|
|
// endregion
|
|
}
|
|
|
|
class PathDotView : View, GlowView {
|
|
@ColorInt override var mainColor: Int = 0
|
|
set(newValue) { field = newValue; paint.color = newValue }
|
|
@ColorInt override var sessionShadowColor: Int = 0
|
|
set(newValue) { field = newValue; paint.setShadowLayer(toPx(4, resources).toFloat(), 0.0f, 0.0f, newValue) }
|
|
|
|
private val paint: Paint by lazy {
|
|
val result = Paint()
|
|
result.style = Paint.Style.FILL
|
|
result.isAntiAlias = true
|
|
result
|
|
}
|
|
|
|
// region Lifecycle
|
|
constructor(context: Context) : super(context) { }
|
|
constructor(context: Context, attrs: AttributeSet) : super(context, attrs) { }
|
|
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) { }
|
|
|
|
init {
|
|
setWillNotDraw(false)
|
|
}
|
|
// endregion
|
|
|
|
// region Updating
|
|
override fun onDraw(c: Canvas) {
|
|
val w = width.toFloat()
|
|
val h = height.toFloat()
|
|
c.drawCircle(w / 2, h / 2, w / 2, paint)
|
|
super.onDraw(c)
|
|
}
|
|
// endregion
|
|
}
|
|
|
|
class InputBarButtonImageViewContainer : RelativeLayout, GlowView {
|
|
@ColorInt override var mainColor: Int = 0
|
|
set(newValue) { field = newValue; fillPaint.color = newValue }
|
|
@ColorInt var strokeColor: Int = 0
|
|
set(newValue) { field = newValue; strokePaint.color = newValue }
|
|
@ColorInt override var sessionShadowColor: Int = 0 // Unused
|
|
|
|
private val fillPaint: Paint by lazy {
|
|
val result = Paint()
|
|
result.style = Paint.Style.FILL
|
|
result.isAntiAlias = true
|
|
result
|
|
}
|
|
|
|
private val strokePaint: Paint by lazy {
|
|
val result = Paint()
|
|
result.style = Paint.Style.STROKE
|
|
result.isAntiAlias = true
|
|
result.strokeWidth = 1.0f
|
|
result.alpha = (255 * 0.2f).roundToInt()
|
|
result
|
|
}
|
|
|
|
// region Lifecycle
|
|
constructor(context: Context) : super(context) { }
|
|
constructor(context: Context, attrs: AttributeSet) : super(context, attrs) { }
|
|
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) { }
|
|
|
|
init {
|
|
setWillNotDraw(false)
|
|
}
|
|
// endregion
|
|
|
|
// region Updating
|
|
override fun onDraw(c: Canvas) {
|
|
val w = width.toFloat()
|
|
val h = height.toFloat()
|
|
c.drawCircle(w / 2, h / 2, w / 2, fillPaint)
|
|
if (strokeColor != 0) {
|
|
c.drawCircle(w / 2, h / 2, w / 2, strokePaint)
|
|
}
|
|
super.onDraw(c)
|
|
}
|
|
// endregion
|
|
}
|
|
|
|
class MessageBubbleView : androidx.constraintlayout.widget.ConstraintLayout, GlowView {
|
|
@ColorInt override var mainColor: Int = 0
|
|
set(newValue) { field = newValue; paint.color = newValue }
|
|
@ColorInt override var sessionShadowColor: Int = 0
|
|
set(newValue) {
|
|
field = newValue
|
|
shadowPaint.setShadowLayer(toPx(10, resources).toFloat(), 0.0f, 0.0f, newValue)
|
|
|
|
if (numShadowRenders == 0) {
|
|
numShadowRenders = 1
|
|
}
|
|
|
|
invalidate()
|
|
}
|
|
var cornerRadius: Float = 0f
|
|
var numShadowRenders: Int = 0
|
|
|
|
private val paint: Paint by lazy {
|
|
val result = Paint()
|
|
result.style = Paint.Style.FILL
|
|
result.isAntiAlias = true
|
|
result
|
|
}
|
|
|
|
private val shadowPaint: Paint by lazy {
|
|
val result = Paint()
|
|
result.style = Paint.Style.FILL
|
|
result.isAntiAlias = true
|
|
result
|
|
}
|
|
|
|
// region Lifecycle
|
|
constructor(context: Context) : super(context) { }
|
|
constructor(context: Context, attrs: AttributeSet) : super(context, attrs) { }
|
|
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) { }
|
|
|
|
init {
|
|
setWillNotDraw(false)
|
|
}
|
|
// endregion
|
|
|
|
// region Updating
|
|
override fun onDraw(c: Canvas) {
|
|
val w = width.toFloat()
|
|
val h = height.toFloat()
|
|
|
|
(0 until numShadowRenders).forEach {
|
|
c.drawRoundRect(0f, 0f, w, h, cornerRadius, cornerRadius, shadowPaint)
|
|
}
|
|
|
|
c.drawRoundRect(0f, 0f, w, h, cornerRadius, cornerRadius, paint)
|
|
super.onDraw(c)
|
|
}
|
|
// endregion
|
|
}
|