Commit d7df8932 authored by Joey's avatar Joey

glucose: SuggestionView: simplify animation and logic

Signed-off-by: Joey's avatarJoey <bevilacquajoey@gmail.com>
Change-Id: I6d88685a34959eb9a2c43e95ee3ff84e4723ad20
parent 524ea163
......@@ -95,7 +95,7 @@ class PluginManager(context: Context) {
}
suspend fun fetchSuggestion(glucose: Glucose, onExecuted: (Float) -> Unit) {
delay(1000)
delay(300)
val value = glucose.value / 10 * 10
if (value <= LOWEST_SUGGESTION) {
......
......@@ -17,15 +17,15 @@ import it.diab.glucose.workers.CheckAgainWorker
import java.util.concurrent.TimeUnit
class CheckAgainSuggestion(
targetTimeFrame: TimeFrame,
private val targetTimeFrame: TimeFrame,
private val lowThreshold: Int
) : SuggestionConfig, SuggestionCallback<Int> {
) : SuggestionModel<Int> {
override val shouldAnimate = false
override val isValid by lazy {
val suggestionsTimeFrames = arrayOf(TimeFrame.MORNING, TimeFrame.LUNCH, TimeFrame.DINNER)
!suggestionsTimeFrames.contains(targetTimeFrame)
override fun isValid() = when (targetTimeFrame) {
TimeFrame.MORNING,
TimeFrame.DINNER,
TimeFrame.LUNCH -> false
else -> true
}
override val icon = R.drawable.ic_suggestion_remind
......
......@@ -20,13 +20,16 @@ class InsulinSuggestion(
private val glucose: Glucose,
private val proposedInsulin: Insulin,
private val onSuggestionApplied: (value: Float, insulin: Insulin) -> Unit
) : SuggestionCallback<Float>, SuggestionConfig {
) : SuggestionModel<Float> {
override val shouldAnimate = true
override val isValid by lazy {
val allowedTimeFrames = arrayOf(TimeFrame.MORNING, TimeFrame.LUNCH, TimeFrame.DINNER)
allowedTimeFrames.contains(glucose.timeFrame) && glucose.insulinId0 == -1L
override fun isValid(): Boolean {
val validTF = when (glucose.timeFrame) {
TimeFrame.MORNING,
TimeFrame.DINNER,
TimeFrame.LUNCH -> true
else -> false
}
return validTF && glucose.insulinId0 == -1L
}
override val icon = R.drawable.ic_suggestion_ml
......
/*
* Copyright (c) 2019 Bevilacqua Joey
*
* Licensed under the GNU GPLv3 license
*
* The text of the license can be found in the LICENSE file
* or at https://www.gnu.org/licenses/gpl.txt
*/
package it.diab.glucose.suggestion
interface SuggestionConfig {
val shouldAnimate: Boolean
val isValid: Boolean
val icon: Int
}
\ No newline at end of file
......@@ -9,8 +9,9 @@
package it.diab.glucose.suggestion
import android.content.res.Resources
import androidx.annotation.DrawableRes
interface SuggestionCallback <T> {
interface SuggestionModel <T> {
/**
* Check whether the suggestion has errors.
......@@ -48,4 +49,16 @@ interface SuggestionCallback <T> {
* @param value the suggestion
*/
fun onSuggestionApply(value: T)
/**
* Whether this suggestion is valid.
* If it's not valid the suggestion won't appear in the UI
*/
fun isValid(): Boolean
/**
* Get an icon that represents the suggestion
*/
@get:DrawableRes
val icon: Int
}
\ No newline at end of file
......@@ -8,49 +8,13 @@
*/
package it.diab.glucose.util.extensions
import android.animation.Animator
import android.animation.ValueAnimator
import android.content.Context
import android.graphics.Color
import android.graphics.PorterDuff
import android.text.Spannable
import android.text.SpannableString
import android.text.style.ForegroundColorSpan
import android.widget.TextView
import androidx.core.content.ContextCompat
import it.diab.glucose.R
fun TextView.animateThreeDots(): Animator {
val textLength = text.length
val spannable = SpannableString("$text...")
val animator = ValueAnimator.ofInt(0, 4).apply {
repeatCount = ValueAnimator.INFINITE
duration = 1000
addUpdateListener {
val count = it.animatedValue as Int
if (count < 4) {
spannable.run {
getSpans(
textLength,
textLength + 3,
ForegroundColorSpan::class.java
).forEach(this::removeSpan)
setSpan(
ForegroundColorSpan(Color.TRANSPARENT),
textLength + count,
textLength + 3,
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
)
}
setText(spannable, TextView.BufferType.SPANNABLE)
}
}
}
animator.start()
return animator
}
fun TextView.setErrorStatus(context: Context, toError: Boolean) {
val originalColor = ContextCompat.getColor(context, R.color.colorAccent)
val errorColor = ContextCompat.getColor(context, R.color.action_dangerous)
......
......@@ -8,25 +8,19 @@
*/
package it.diab.glucose.widget
import android.animation.Animator
import android.content.Context
import android.os.Handler
import android.util.AttributeSet
import android.view.View
import android.widget.LinearLayout
import android.widget.TextView
import androidx.annotation.AttrRes
import it.diab.glucose.R
import it.diab.glucose.suggestion.SuggestionCallback
import it.diab.glucose.suggestion.SuggestionConfig
import it.diab.glucose.suggestion.SuggestionModel
import it.diab.glucose.util.VibrationUtil
import it.diab.glucose.util.extensions.animateThreeDots
class SuggestionView : LinearLayout {
private val textView: TextView
private lateinit var animator: Animator
constructor(context: Context) : super(context)
constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
......@@ -37,67 +31,68 @@ class SuggestionView : LinearLayout {
init {
View.inflate(context, R.layout.component_suggestion, this)
textView = findViewById(R.id.insulin_suggestion_text)
textView.visibility = View.GONE
}
/**
* Try to apply a [SuggestionConfig] to the view.
* A config may or may not apply depending on the [SuggestionConfig.isValid]
* Try to apply a [SuggestionModel] to the view.
* A config may or may not apply depending on the [SuggestionModel.isValid]
* value.
*
* @param config the applied configuration
* @return whether the config has been enabled
* @param model the applied model
* @return whether the model has been enabled
*/
fun applyConfig(config: SuggestionConfig): Boolean {
if (!config.isValid) {
visibility = View.GONE
return false
}
textView.setCompoundDrawablesWithIntrinsicBounds(config.icon, 0, 0, 0)
if (config.shouldAnimate) {
showLoad()
fun <T> applyConfig(model: SuggestionModel<T>): Boolean {
if (model.isValid()) {
textView.setCompoundDrawablesWithIntrinsicBounds(model.icon, 0, 0, 0)
return true
}
return true
textView.visibility = View.GONE
return false
}
fun <T> onSuggestionLoaded(value: T, callback: SuggestionCallback<T>) {
if (::animator.isInitialized) {
animator.cancel()
}
fun <T> onSuggestionLoaded(value: T, model: SuggestionModel<T>) {
val isValid = model.validate(value)
textView.text = if (isValid) model.getSuccessMessage(value, resources)
else model.getFailMessage(value, resources)
if (!callback.validate(value)) {
val message = callback.getFailMessage(value, resources)
if (message == null) {
visibility = View.GONE
return
}
textView.apply {
text = message
isEnabled = false
}
if (textView.text.isEmpty()) {
return
}
visibility = View.VISIBLE
textView.text = callback.getSuccessMessage(value, resources)
textView.isEnabled = isValid
animateIn()
if (!isValid) {
return
}
textView.setOnClickListener {
VibrationUtil.vibrateForImportantClick(it)
callback.onSuggestionApply(value)
Handler().postDelayed(this::onSuggestionApplied, 350)
animate()
.alpha(0f)
.translationYBy(10f)
.withEndAction {
visibility = View.GONE
model.onSuggestionApply(value)
}
.start()
}
}
private fun showLoad() {
textView.text = resources.getString(R.string.glucose_editor_suggestion_loading)
animator = textView.animateThreeDots()
}
private fun animateIn() {
textView.apply {
translationY = 10f
alpha = 0f
visibility = View.VISIBLE
private fun onSuggestionApplied() {
animate().alpha(0f)
.withEndAction { visibility = View.GONE }
.start()
animate().alpha(1f)
.translationY(0f)
.start()
}
}
}
......@@ -25,7 +25,6 @@
android:paddingBottom="8dp"
android:stateListAnimator="@animator/lift_on_touch"
android:drawablePadding="8dp"
android:text="@string/glucose_editor_suggestion_loading"
android:textColor="@color/component_suggestion_fg"
android:textSize="16sp"
tools:drawableStart="@drawable/ic_suggestion_ml"
......
......@@ -19,7 +19,6 @@
<string name="glucose_editor_date_pick">एक और तारीख चुनें\u2026</string>
<string name="glucose_editor_saved">संरक्षित किया</string>
<string name="glucose_editor_close">Close</string>
<string name="glucose_editor_suggestion_loading">लोड हो रहा है</string>
<string name="glucose_editor_eat_level">Food intake</string>
<!-- Glucose suggestions -->
......
......@@ -19,7 +19,6 @@
<string name="glucose_editor_date_pick">Seleziona un\'altra data\u2026</string>
<string name="glucose_editor_saved">Salvato</string>
<string name="glucose_editor_close">Chiudi</string>
<string name="glucose_editor_suggestion_loading">Calcolo</string>
<string name="glucose_editor_eat_level">Cibo assunto</string>
<!-- Glucose suggestions -->
......
......@@ -19,7 +19,6 @@
<string name="glucose_editor_date_pick">Select another date\u2026</string>
<string name="glucose_editor_saved">Saved</string>
<string name="glucose_editor_close">Close</string>
<string name="glucose_editor_suggestion_loading">Loading</string>
<string name="glucose_editor_eat_level">Food intake</string>
<!-- Glucose suggestions -->
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment