Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit fc7c53ce authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Automerger Merge Worker
Browse files

Merge "Controls UI - Loading animations" into rvc-dev am: 31db4f45

Change-Id: I6e5772fe009b59023ae2b9f8849000214da07e8b
parents 40e406c1 31db4f45
Loading
Loading
Loading
Loading
+124 −24
Original line number Diff line number Diff line
@@ -18,10 +18,15 @@ package com.android.systemui.controls.ui

import android.animation.Animator
import android.animation.AnimatorListenerAdapter
import android.animation.AnimatorSet
import android.animation.ObjectAnimator
import android.animation.ValueAnimator
import android.annotation.ColorRes
import android.app.Dialog
import android.content.Context
import android.content.res.ColorStateList
import android.graphics.drawable.ClipDrawable
import android.graphics.drawable.Drawable
import android.graphics.drawable.GradientDrawable
import android.graphics.drawable.LayerDrawable
import android.service.controls.Control
@@ -34,6 +39,7 @@ import android.service.controls.templates.TemperatureControlTemplate
import android.service.controls.templates.ToggleRangeTemplate
import android.service.controls.templates.ToggleTemplate
import android.util.MathUtils
import android.util.TypedValue
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
@@ -63,6 +69,8 @@ class ControlViewHolder(
        private const val UPDATE_DELAY_IN_MILLIS = 3000L
        private const val ALPHA_ENABLED = 255
        private const val ALPHA_DISABLED = 0
        private const val STATUS_ALPHA_ENABLED = 1f
        private const val STATUS_ALPHA_DIMMED = 0.45f
        private val FORCE_PANEL_DEVICES = setOf(
            DeviceTypes.TYPE_THERMOSTAT,
            DeviceTypes.TYPE_CAMERA
@@ -94,9 +102,11 @@ class ControlViewHolder(
    private val toggleBackgroundIntensity: Float = layout.context.resources
            .getFraction(R.fraction.controls_toggle_bg_intensity, 1, 1)
    private var stateAnimator: ValueAnimator? = null
    private var statusAnimator: Animator? = null
    private val baseLayer: GradientDrawable
    val icon: ImageView = layout.requireViewById(R.id.icon)
    val status: TextView = layout.requireViewById(R.id.status)
    private val status: TextView = layout.requireViewById(R.id.status)
    private var nextStatusText: CharSequence = ""
    val title: TextView = layout.requireViewById(R.id.title)
    val subtitle: TextView = layout.requireViewById(R.id.subtitle)
    val context: Context = layout.getContext()
@@ -105,6 +115,7 @@ class ControlViewHolder(
    var cancelUpdate: Runnable? = null
    var behavior: Behavior? = null
    var lastAction: ControlAction? = null
    var isLoading = false
    private var lastChallengeDialog: Dialog? = null
    private val onDialogCancel: () -> Unit = { lastChallengeDialog = null }

@@ -144,6 +155,7 @@ class ControlViewHolder(
            })
        }

        isLoading = false
        behavior = bindBehavior(behavior, findBehaviorClass(controlStatus, template, deviceType))
        updateContentDescription()
    }
@@ -189,11 +201,11 @@ class ControlViewHolder(
        val previousText = status.getText()

        cancelUpdate = uiExecutor.executeDelayed({
                status.setText(previousText)
            setStatusText(previousText)
            updateContentDescription()
        }, UPDATE_DELAY_IN_MILLIS)

        status.setText(tempStatus)
        setStatusText(tempStatus)
        updateContentDescription()
    }

@@ -231,18 +243,50 @@ class ControlViewHolder(
    }

    internal fun applyRenderInfo(enabled: Boolean, offset: Int, animated: Boolean = true) {
        setEnabled(enabled)

        val ri = RenderInfo.lookup(context, cws.componentName, deviceType, enabled, offset)

        val fg = context.resources.getColorStateList(ri.foreground, context.theme)
        val newText = nextStatusText
        nextStatusText = ""
        val control = cws.control

        var shouldAnimate = animated
        if (newText == status.text) {
            shouldAnimate = false
        }
        animateStatusChange(shouldAnimate) {
            updateStatusRow(enabled, newText, ri.icon, fg, control)
        }

        animateBackgroundChange(shouldAnimate, enabled, ri.enabledBackground)
    }

    fun getStatusText() = status.text

    fun setStatusTextSize(textSize: Float) =
        status.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize)

    fun setStatusText(text: CharSequence, immediately: Boolean = false) {
        if (immediately) {
            status.alpha = STATUS_ALPHA_ENABLED
            status.text = text
            nextStatusText = ""
        } else {
            nextStatusText = text
        }
    }

    private fun animateBackgroundChange(
        animated: Boolean,
        enabled: Boolean,
        @ColorRes bgColor: Int
    ) {
        val bg = context.resources.getColor(R.color.control_default_background, context.theme)
        var (newClipColor, newAlpha) = if (enabled) {
            // allow color overrides for the enabled state only
            val color = cws.control?.getCustomColor()?.let {
                val state = intArrayOf(android.R.attr.state_enabled)
                it.getColorForState(state, it.getDefaultColor())
            } ?: context.resources.getColor(ri.enabledBackground, context.theme)
            } ?: context.resources.getColor(bgColor, context.theme)
            listOf(color, ALPHA_ENABLED)
        } else {
            listOf(
@@ -251,21 +295,6 @@ class ControlViewHolder(
            )
        }

        status.setTextColor(fg)

        cws.control?.getCustomIcon()?.let {
            // do not tint custom icons, assume the intended icon color is correct
            icon.imageTintList = null
            icon.setImageIcon(it)
        } ?: run {
            icon.setImageDrawable(ri.icon)

            // do not color app icons
            if (deviceType != DeviceTypes.TYPE_ROUTINE) {
                icon.imageTintList = fg
            }
        }

        (clipLayer.getDrawable() as GradientDrawable).apply {
            val newBaseColor = if (behavior is ToggleRangeBehavior) {
                ColorUtils.blendARGB(bg, newClipColor, toggleBackgroundIntensity)
@@ -303,6 +332,77 @@ class ControlViewHolder(
        }
    }

    private fun animateStatusChange(animated: Boolean, statusRowUpdater: () -> Unit) {
        statusAnimator?.cancel()

        if (!animated) {
            statusRowUpdater.invoke()
            return
        }

        if (isLoading) {
            statusRowUpdater.invoke()
            statusAnimator = ObjectAnimator.ofFloat(status, "alpha", STATUS_ALPHA_DIMMED).apply {
                repeatMode = ValueAnimator.REVERSE
                repeatCount = ValueAnimator.INFINITE
                duration = 500L
                interpolator = Interpolators.LINEAR
                startDelay = 900L
                start()
            }
        } else {
            val fadeOut = ObjectAnimator.ofFloat(status, "alpha", 0f).apply {
                duration = 200L
                interpolator = Interpolators.LINEAR
                addListener(object : AnimatorListenerAdapter() {
                    override fun onAnimationEnd(animation: Animator?) {
                        statusRowUpdater.invoke()
                    }
                })
            }
            val fadeIn = ObjectAnimator.ofFloat(status, "alpha", STATUS_ALPHA_ENABLED).apply {
                duration = 200L
                interpolator = Interpolators.LINEAR
            }
            statusAnimator = AnimatorSet().apply {
                playSequentially(fadeOut, fadeIn)
                addListener(object : AnimatorListenerAdapter() {
                    override fun onAnimationEnd(animation: Animator?) {
                        status.alpha = STATUS_ALPHA_ENABLED
                        statusAnimator = null
                    }
                })
                start()
            }
        }
    }

    private fun updateStatusRow(
        enabled: Boolean,
        text: CharSequence,
        drawable: Drawable,
        color: ColorStateList,
        control: Control?
    ) {
        setEnabled(enabled)

        status.text = text
        status.setTextColor(color)

        control?.getCustomIcon()?.let {
            // do not tint custom icons, assume the intended icon color is correct
            icon.imageTintList = null
            icon.setImageIcon(it)
        } ?: run {
            icon.setImageDrawable(drawable)

            // do not color app icons
            if (deviceType != DeviceTypes.TYPE_ROUTINE) {
                icon.imageTintList = color
            }
        }
    }

    private fun setEnabled(enabled: Boolean) {
        status.setEnabled(enabled)
        icon.setEnabled(enabled)
+1 −1
Original line number Diff line number Diff line
@@ -24,7 +24,7 @@ class DefaultBehavior : Behavior {
    }

    override fun bind(cws: ControlWithState, colorOffset: Int) {
        cvh.status.setText(cws.control?.getStatusText() ?: "")
        cvh.setStatusText(cws.control?.getStatusText() ?: "")
        cvh.applyRenderInfo(false, colorOffset)
    }
}
+4 −6
Original line number Diff line number Diff line
@@ -97,6 +97,8 @@ private const val BUCKET_SIZE = 1000
private const val THERMOSTAT_RANGE = DeviceTypes.TYPE_THERMOSTAT * BUCKET_SIZE

private val deviceColorMap = mapOf<Int, Pair<Int, Int>>(
    (THERMOSTAT_RANGE + TemperatureControlTemplate.MODE_OFF) to
        Pair(R.color.control_default_foreground, R.color.control_default_background),
    (THERMOSTAT_RANGE + TemperatureControlTemplate.MODE_HEAT) to
        Pair(R.color.thermo_heat_foreground, R.color.control_enabled_thermo_heat_background),
    (THERMOSTAT_RANGE + TemperatureControlTemplate.MODE_COOL) to
@@ -108,13 +110,9 @@ private val deviceColorMap = mapOf<Int, Pair<Int, Int>>(
}

private val deviceIconMap = mapOf<Int, IconState>(
    THERMOSTAT_RANGE to IconState(
        R.drawable.ic_device_thermostat_off,
        R.drawable.ic_device_thermostat_on
    ),
    (THERMOSTAT_RANGE + TemperatureControlTemplate.MODE_OFF) to IconState(
        R.drawable.ic_device_thermostat_off,
        R.drawable.ic_device_thermostat_on
        R.drawable.ic_device_thermostat_off
    ),
    (THERMOSTAT_RANGE + TemperatureControlTemplate.MODE_HEAT) to IconState(
        R.drawable.ic_device_thermostat_off,
@@ -130,7 +128,7 @@ private val deviceIconMap = mapOf<Int, IconState>(
    ),
    (THERMOSTAT_RANGE + TemperatureControlTemplate.MODE_ECO) to IconState(
        R.drawable.ic_device_thermostat_off,
        R.drawable.ic_device_thermostat_on
        R.drawable.ic_device_thermostat_off
    ),
    DeviceTypes.TYPE_THERMOSTAT to IconState(
        R.drawable.ic_device_thermostat_off,
+5 −2
Original line number Diff line number Diff line
@@ -32,9 +32,12 @@ class StatusBehavior : Behavior {
        val msg = when (status) {
            Control.STATUS_ERROR -> R.string.controls_error_generic
            Control.STATUS_NOT_FOUND -> R.string.controls_error_removed
            else -> com.android.internal.R.string.loading
            else -> {
                cvh.isLoading = true
                com.android.internal.R.string.loading
            }
        cvh.status.setText(cvh.context.getString(msg))
        }
        cvh.setStatusText(cvh.context.getString(msg))
        cvh.applyRenderInfo(false, colorOffset)
    }
}
+1 −1
Original line number Diff line number Diff line
@@ -39,7 +39,7 @@ class TemperatureControlBehavior : Behavior {
    override fun bind(cws: ControlWithState, colorOffset: Int) {
        this.control = cws.control!!

        cvh.status.setText(control.getStatusText())
        cvh.setStatusText(control.getStatusText())

        val ld = cvh.layout.getBackground() as LayerDrawable
        clipLayer = ld.findDrawableByLayerId(R.id.clip_layer)
Loading