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

Commit 7fe71e59 authored by Hawkwood Glazier's avatar Hawkwood Glazier
Browse files

Update smartspace visibility to correct target during clock size transition

Sometimes ClockSizeTransition is given the wrong target visibility for
smartspace date/weather view. This causes it override the correct value
when it comes in later. To correct this, we ensure we're animating to
the correct target value every frame.

This may cause animation hiccups in some edge-cases, including the case
that's being fixed, but it corrected fast enough that it wasn't visible
in my testing.

Bug: 376075102
Flag: NONE Bugfix
Test: Manually checked reproduction
Change-Id: I5bfd754c82c15759348a2ecf8ba94a133f2d547b
parent 1dd200f9
Loading
Loading
Loading
Loading
+104 −86
Original line number Diff line number Diff line
@@ -86,15 +86,56 @@ class ClockSizeTransition(
            transition.values[SMARTSPACE_BOUNDS] = targetSSView.getRect()
        }

        open fun mutateBounds(
            view: View,
            fromIsVis: Boolean,
            toIsVis: Boolean,
            fromBounds: Rect,
            toBounds: Rect,
            fromSSBounds: Rect?,
            toSSBounds: Rect?,
        ) {}
        open fun initTargets(from: Target, to: Target) {}

        open fun mutateTargets(from: Target, to: Target) {}

        data class Target(
            var view: View,
            var visibility: Int,
            var isVisible: Boolean,
            var alpha: Float,
            var bounds: Rect,
            var ssBounds: Rect?,
        ) {
            companion object {
                fun fromStart(startValues: TransitionValues): Target {
                    var fromVis = startValues.values[PROP_VISIBILITY] as Int
                    var fromIsVis = fromVis == View.VISIBLE
                    var fromAlpha = startValues.values[PROP_ALPHA] as Float

                    // Align starting visibility and alpha
                    if (!fromIsVis) fromAlpha = 0f
                    else if (fromAlpha <= 0f) {
                        fromIsVis = false
                        fromVis = View.INVISIBLE
                    }

                    return Target(
                        view = startValues.view,
                        visibility = fromVis,
                        isVisible = fromIsVis,
                        alpha = fromAlpha,
                        bounds = startValues.values[PROP_BOUNDS] as Rect,
                        ssBounds = startValues.values[SMARTSPACE_BOUNDS] as Rect?,
                    )
                }

                fun fromEnd(endValues: TransitionValues): Target {
                    val toVis = endValues.values[PROP_VISIBILITY] as Int
                    val toIsVis = toVis == View.VISIBLE

                    return Target(
                        view = endValues.view,
                        visibility = toVis,
                        isVisible = toIsVis,
                        alpha = if (toIsVis) 1f else 0f,
                        bounds = endValues.values[PROP_BOUNDS] as Rect,
                        ssBounds = endValues.values[SMARTSPACE_BOUNDS] as Rect?,
                    )
                }
            }
        }

        override fun createAnimator(
            sceenRoot: ViewGroup,
@@ -109,72 +150,58 @@ class ClockSizeTransition(
                return null
            }

            var fromVis = startValues.values[PROP_VISIBILITY] as Int
            var fromIsVis = fromVis == View.VISIBLE
            var fromAlpha = startValues.values[PROP_ALPHA] as Float
            val fromBounds = startValues.values[PROP_BOUNDS] as Rect
            val fromSSBounds = startValues.values[SMARTSPACE_BOUNDS] as Rect?

            val toView = endValues.view
            val toVis = endValues.values[PROP_VISIBILITY] as Int
            val toBounds = endValues.values[PROP_BOUNDS] as Rect
            val toSSBounds = endValues.values[SMARTSPACE_BOUNDS] as Rect?
            val toIsVis = toVis == View.VISIBLE
            val toAlpha = if (toIsVis) 1f else 0f
            val from = Target.fromStart(startValues)
            val to = Target.fromEnd(endValues)
            initTargets(from, to)
            mutateTargets(from, to)

            // Align starting visibility and alpha
            if (!fromIsVis) fromAlpha = 0f
            else if (fromAlpha <= 0f) {
                fromIsVis = false
                fromVis = View.INVISIBLE
            }

            mutateBounds(toView, fromIsVis, toIsVis, fromBounds, toBounds, fromSSBounds, toSSBounds)
            if (fromIsVis == toIsVis && fromBounds.equals(toBounds)) {
            if (from.isVisible == to.isVisible && from.bounds.equals(to.bounds)) {
                if (DEBUG) {
                    Log.w(
                        TAG,
                        "Skipping no-op transition: $toView; " +
                            "vis: $fromVis -> $toVis; " +
                            "alpha: $fromAlpha -> $toAlpha; " +
                            "bounds: $fromBounds -> $toBounds; ",
                        "Skipping no-op transition: ${to.view}; " +
                            "vis: ${from.visibility} -> ${to.visibility}; " +
                            "alpha: ${from.alpha} -> ${to.alpha}; " +
                            "bounds: ${from.bounds} -> ${to.bounds}; ",
                    )
                }
                return null
            }

            val sendToBack = fromIsVis && !toIsVis
            val sendToBack = from.isVisible && !to.isVisible
            fun lerp(start: Int, end: Int, fract: Float): Int =
                MathUtils.lerp(start.toFloat(), end.toFloat(), fract).toInt()
            fun computeBounds(fract: Float): Rect =
                Rect(
                    lerp(fromBounds.left, toBounds.left, fract),
                    lerp(fromBounds.top, toBounds.top, fract),
                    lerp(fromBounds.right, toBounds.right, fract),
                    lerp(fromBounds.bottom, toBounds.bottom, fract),
                    lerp(from.bounds.left, to.bounds.left, fract),
                    lerp(from.bounds.top, to.bounds.top, fract),
                    lerp(from.bounds.right, to.bounds.right, fract),
                    lerp(from.bounds.bottom, to.bounds.bottom, fract),
                )

            fun assignAnimValues(src: String, fract: Float, vis: Int? = null) {
                mutateTargets(from, to)
                val bounds = computeBounds(fract)
                val alpha = MathUtils.lerp(fromAlpha, toAlpha, fract)
                val alpha = MathUtils.lerp(from.alpha, to.alpha, fract)
                if (DEBUG) {
                    Log.i(
                        TAG,
                        "$src: $toView; fract=$fract; alpha=$alpha; vis=$vis; bounds=$bounds;",
                        "$src: ${to.view}; fract=$fract; alpha=$alpha; vis=$vis; bounds=$bounds;",
                    )
                }
                toView.setVisibility(vis ?: View.VISIBLE)
                toView.setAlpha(alpha)
                toView.setRect(bounds)

                to.view.setVisibility(vis ?: View.VISIBLE)
                to.view.setAlpha(alpha)
                to.view.setRect(bounds)
            }

            if (DEBUG) {
                Log.i(
                    TAG,
                    "transitioning: $toView; " +
                        "vis: $fromVis -> $toVis; " +
                        "alpha: $fromAlpha -> $toAlpha; " +
                        "bounds: $fromBounds -> $toBounds; ",
                    "transitioning: ${to.view}; " +
                        "vis: ${from.visibility} -> ${to.visibility}; " +
                        "alpha: ${from.alpha} -> ${to.alpha}; " +
                        "bounds: ${from.bounds} -> ${to.bounds}; ",
                )
            }

@@ -190,11 +217,11 @@ class ClockSizeTransition(
                this@VisibilityBoundsTransition.addListener(
                    object : TransitionListenerAdapter() {
                        override fun onTransitionStart(t: Transition) {
                            toView.viewTreeObserver.addOnPreDrawListener(predrawCallback)
                            to.view.viewTreeObserver.addOnPreDrawListener(predrawCallback)
                        }

                        override fun onTransitionEnd(t: Transition) {
                            toView.viewTreeObserver.removeOnPreDrawListener(predrawCallback)
                            to.view.viewTreeObserver.removeOnPreDrawListener(predrawCallback)
                        }
                    }
                )
@@ -202,17 +229,17 @@ class ClockSizeTransition(
                val listener =
                    object : AnimatorListenerAdapter() {
                        override fun onAnimationStart(anim: Animator) {
                            assignAnimValues("start", 0f, fromVis)
                            assignAnimValues("start", 0f, from.visibility)
                        }

                        override fun onAnimationEnd(anim: Animator) {
                            assignAnimValues("end", 1f, toVis)
                            if (sendToBack) toView.translationZ = 0f
                            assignAnimValues("end", 1f, to.visibility)
                            if (sendToBack) to.view.translationZ = 0f
                        }
                    }

                anim.addListener(listener)
                assignAnimValues("init", 0f, fromVis)
                assignAnimValues("init", 0f, from.visibility)
            }
        }

@@ -251,31 +278,23 @@ class ClockSizeTransition(
            }
        }

        override fun mutateBounds(
            view: View,
            fromIsVis: Boolean,
            toIsVis: Boolean,
            fromBounds: Rect,
            toBounds: Rect,
            fromSSBounds: Rect?,
            toSSBounds: Rect?,
        ) {
        override fun initTargets(from: Target, to: Target) {
            // Move normally if clock is not changing visibility
            if (fromIsVis == toIsVis) return
            if (from.isVisible == to.isVisible) return

            fromBounds.set(toBounds)
            from.bounds.set(to.bounds)
            if (isLargeClock) {
                // Large clock shouldn't move; fromBounds already set
            } else if (toSSBounds != null && fromSSBounds != null) {
            } else if (to.ssBounds != null && from.ssBounds != null) {
                // Instead of moving the small clock the full distance, we compute the distance
                // smartspace will move. We then scale this to match the duration of this animation
                // so that the small clock moves at the same speed as smartspace.
                val ssTranslation =
                    abs((toSSBounds.top - fromSSBounds.top) * smallClockMoveScale).toInt()
                fromBounds.top = toBounds.top - ssTranslation
                fromBounds.bottom = toBounds.bottom - ssTranslation
                    abs((to.ssBounds!!.top - from.ssBounds!!.top) * smallClockMoveScale).toInt()
                from.bounds.top = to.bounds.top - ssTranslation
                from.bounds.bottom = to.bounds.bottom - ssTranslation
            } else {
                Log.e(TAG, "mutateBounds: smallClock received no smartspace bounds")
                Log.e(TAG, "initTargets: smallClock received no smartspace bounds")
            }
        }
    }
@@ -320,10 +339,9 @@ class ClockSizeTransition(
        }
    }

    // TODO: Might need a mechanism to update this one while in-progress
    class SmartspaceMoveTransition(
        val config: IntraBlueprintTransition.Config,
        viewModel: KeyguardClockViewModel,
        val viewModel: KeyguardClockViewModel,
    ) : VisibilityBoundsTransition() {
        private val isLargeClock = viewModel.isLargeClockVisible.value
        override val captureSmartspace = false
@@ -340,23 +358,23 @@ class ClockSizeTransition(
            addTarget(R.id.status_view_media_container)
        }

        override fun mutateBounds(
            view: View,
            fromIsVis: Boolean,
            toIsVis: Boolean,
            fromBounds: Rect,
            toBounds: Rect,
            fromSSBounds: Rect?,
            toSSBounds: Rect?,
        ) {
        override fun initTargets(from: Target, to: Target) {
            // If view is changing visibility, hold it in place
            if (fromIsVis == toIsVis) return
            if (DEBUG) Log.i(TAG, "Holding position of ${view.id}")
            if (from.isVisible == to.isVisible) return
            if (DEBUG) Log.i(TAG, "Holding position of ${to.view.id}")

            if (fromIsVis) {
                toBounds.set(fromBounds)
            if (from.isVisible) {
                to.bounds.set(from.bounds)
            } else {
                fromBounds.set(toBounds)
                from.bounds.set(to.bounds)
            }
        }

        override fun mutateTargets(from: Target, to: Target) {
            if (to.view.id == sharedR.id.date_smartspace_view) {
                to.isVisible = !viewModel.hasCustomWeatherDataDisplay.value
                to.visibility = if (to.isVisible) View.VISIBLE else View.GONE
                to.alpha = if (to.isVisible) 1f else 0f
            }
        }