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

Commit 26e7ca51 authored by Treehugger Robot's avatar Treehugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Modify layout & rendering translation for ClockView" into main

parents 9cf56c8a f85044e5
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -34,6 +34,7 @@
    <dimen name="small_clock_padding_top">28dp</dimen>
    <dimen name="clock_padding_start">28dp</dimen>
    <dimen name="weather_date_icon_padding">28dp</dimen>
    <dimen name="clock_vertical_digit_buffer">8dp</dimen>

    <!-- When large clock is showing, offset the smartspace by this amount -->
    <dimen name="keyguard_smartspace_top_offset">12dp</dimen>
+12 −15
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 The Android Open Source Project
 * Copyright (C) 2025 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
@@ -16,20 +16,17 @@

package com.android.systemui.shared.clocks

import android.graphics.Rect
import android.view.View
import android.graphics.Canvas

fun computeLayoutDiff(
    view: View,
    targetRegion: Rect,
    isLargeClock: Boolean,
): Pair<Float, Float> {
    val parent = view.parent
    if (parent is View && parent.isLaidOut() && isLargeClock) {
        return Pair(
            targetRegion.centerX() - parent.width / 2f,
            targetRegion.centerY() - parent.height / 2f
        )
object CanvasUtil {
    fun Canvas.translate(pt: VPointF) = this.translate(pt.x, pt.y)

    fun Canvas.translate(pt: VPoint) = this.translate(pt.x.toFloat(), pt.y.toFloat())

    fun <T> Canvas.use(func: (Canvas) -> T): T {
        val saveNum = save()
        val result = func(this)
        restoreToCount(saveNum)
        return result
    }
    return Pair(0f, 0f)
}
+18 −36
Original line number Diff line number Diff line
@@ -20,57 +20,42 @@ import android.animation.Animator
import android.animation.AnimatorListenerAdapter
import android.animation.TimeInterpolator
import android.animation.ValueAnimator
import android.graphics.Point
import com.android.systemui.shared.clocks.VPointF.Companion.times

class DigitTranslateAnimator(val updateCallback: () -> Unit) {
    val DEFAULT_ANIMATION_DURATION = 500L
    val updatedTranslate = Point(0, 0)
class DigitTranslateAnimator(private val updateCallback: (VPointF) -> Unit) {
    var currentTranslation = VPointF.ZERO
    var baseTranslation = VPointF.ZERO
    var targetTranslation = VPointF.ZERO

    val baseTranslation = Point(0, 0)
    var targetTranslation: Point? = null
    val bounceAnimator: ValueAnimator =
    private val bounceAnimator: ValueAnimator =
        ValueAnimator.ofFloat(1f).apply {
            duration = DEFAULT_ANIMATION_DURATION
            addUpdateListener {
                updateTranslation(it.animatedFraction, updatedTranslate)
                updateCallback()
            }
            addUpdateListener { updateCallback(getInterpolatedTranslation(it.animatedFraction)) }
            addListener(
                object : AnimatorListenerAdapter() {
                    override fun onAnimationEnd(animation: Animator) {
                        rebase()
                        baseTranslation = currentTranslation
                    }

                    override fun onAnimationCancel(animation: Animator) {
                        rebase()
                        baseTranslation = currentTranslation
                    }
                }
            )
        }

    fun rebase() {
        baseTranslation.x = updatedTranslate.x
        baseTranslation.y = updatedTranslate.y
    }

    fun animatePosition(
        animate: Boolean = true,
        delay: Long = 0,
        duration: Long = -1L,
        duration: Long,
        interpolator: TimeInterpolator? = null,
        targetTranslation: Point? = null,
        targetTranslation: VPointF,
        onAnimationEnd: Runnable? = null,
    ) {
        this.targetTranslation = targetTranslation ?: Point(0, 0)
        this.targetTranslation = targetTranslation
        if (animate) {
            bounceAnimator.cancel()
            bounceAnimator.startDelay = delay
            bounceAnimator.duration =
                if (duration == -1L) {
                    DEFAULT_ANIMATION_DURATION
                } else {
                    duration
                }
            bounceAnimator.duration = duration
            interpolator?.let { bounceAnimator.interpolator = it }
            if (onAnimationEnd != null) {
                val listener =
@@ -89,16 +74,13 @@ class DigitTranslateAnimator(val updateCallback: () -> Unit) {
            bounceAnimator.start()
        } else {
            // No animation is requested, thus set base and target state to the same state.
            updateTranslation(1F, updatedTranslate)
            rebase()
            updateCallback()
            currentTranslation = targetTranslation
            baseTranslation = targetTranslation
            updateCallback(targetTranslation)
        }
    }

    fun updateTranslation(progress: Float, outPoint: Point) {
        outPoint.x =
            (baseTranslation.x + progress * (targetTranslation!!.x - baseTranslation.x)).toInt()
        outPoint.y =
            (baseTranslation.y + progress * (targetTranslation!!.y - baseTranslation.y)).toInt()
    fun getInterpolatedTranslation(progress: Float): VPointF {
        return baseTranslation + progress * (targetTranslation - baseTranslation)
    }
}
+7 −5
Original line number Diff line number Diff line
@@ -35,12 +35,14 @@ import com.android.systemui.plugins.clocks.DefaultClockFaceLayout
import com.android.systemui.plugins.clocks.ThemeConfig
import com.android.systemui.plugins.clocks.WeatherData
import com.android.systemui.plugins.clocks.ZenData
import com.android.systemui.shared.clocks.ViewUtils.computeLayoutDiff
import com.android.systemui.shared.clocks.view.FlexClockView
import com.android.systemui.shared.clocks.view.HorizontalAlignment
import com.android.systemui.shared.clocks.view.VerticalAlignment
import java.util.Locale
import java.util.TimeZone
import kotlin.math.max
import kotlin.math.roundToInt

// TODO(b/364680879): Merge w/ ComposedDigitalLayerController
class FlexClockFaceController(clockCtx: ClockContext, private val isLargeClock: Boolean) :
@@ -168,17 +170,17 @@ class FlexClockFaceController(clockCtx: ClockContext, private val isLargeClock:
                        else targetRegion.height() / maxHeight

                    FrameLayout.LayoutParams(
                        (maxWidth * scale).toInt(),
                        (maxHeight * scale).toInt(),
                        (maxWidth * scale).roundToInt(),
                        (maxHeight * scale).roundToInt(),
                    )
                }

            lp.gravity = Gravity.CENTER
            view.layoutParams = lp
            targetRegion?.let {
                val (xDiff, yDiff) = computeLayoutDiff(view, it, isLargeClock)
                view.translationX = xDiff
                view.translationY = yDiff
                val diff = view.computeLayoutDiff(it, isLargeClock)
                view.translationX = diff.x
                view.translationY = diff.y
            }
        }

+224 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2025 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.systemui.shared.clocks

import android.graphics.Point
import android.graphics.PointF
import android.graphics.Rect
import android.graphics.RectF
import kotlin.math.abs
import kotlin.math.max
import kotlin.math.min
import kotlin.math.sqrt

private val X_MASK: ULong = 0xFFFFFFFF00000000U
private val Y_MASK: ULong = 0x00000000FFFFFFFFU

private fun unpackX(data: ULong): Int = ((data and X_MASK) shr 32).toInt()

private fun unpackY(data: ULong): Int = (data and Y_MASK).toInt()

private fun pack(x: Int, y: Int): ULong {
    return ((x.toULong() shl 32) and X_MASK) or (y.toULong() and Y_MASK)
}

@JvmInline
value class VPointF(private val data: ULong) {
    val x: Float
        get() = Float.fromBits(unpackX(data))

    val y: Float
        get() = Float.fromBits(unpackY(data))

    constructor() : this(0f, 0f)

    constructor(pt: PointF) : this(pt.x, pt.y)

    constructor(x: Int, y: Int) : this(x.toFloat(), y.toFloat())

    constructor(x: Int, y: Float) : this(x.toFloat(), y)

    constructor(x: Float, y: Int) : this(x, y.toFloat())

    constructor(x: Float, y: Float) : this(pack(x.toBits(), y.toBits()))

    fun toPointF() = PointF(x, y)

    fun lengthSq(): Float = x * x + y * y

    fun length(): Float = sqrt(lengthSq())

    fun abs() = VPointF(abs(x), abs(y))

    fun dot(pt: VPointF): Float = x * pt.x + y * pt.y

    fun normalize(): VPointF {
        val length = this.length()
        return VPointF(x / length, y / length)
    }

    operator fun component1(): Float = x

    operator fun component2(): Float = y

    override fun toString() = "($x, $y)"

    operator fun plus(pt: VPoint) = VPointF(x + pt.x, y + pt.y)

    operator fun plus(pt: VPointF) = VPointF(x + pt.x, y + pt.y)

    operator fun plus(value: Int) = VPointF(x + value, y + value)

    operator fun plus(value: Float) = VPointF(x + value, y + value)

    operator fun minus(pt: VPoint) = VPointF(x - pt.x, y - pt.y)

    operator fun minus(pt: VPointF) = VPointF(x - pt.x, y - pt.y)

    operator fun minus(value: Int) = VPointF(x - value, y - value)

    operator fun minus(value: Float) = VPointF(x - value, y - value)

    operator fun times(pt: VPoint) = VPointF(x * pt.x, y * pt.y)

    operator fun times(pt: VPointF) = VPointF(x * pt.x, y * pt.y)

    operator fun times(value: Int) = VPointF(x * value, y * value)

    operator fun times(value: Float) = VPointF(x * value, y * value)

    operator fun div(pt: VPoint) = VPointF(x / pt.x, y / pt.y)

    operator fun div(pt: VPointF) = VPointF(x / pt.x, y / pt.y)

    operator fun div(value: Int) = VPointF(x / value, y / value)

    operator fun div(value: Float) = VPointF(x / value, y / value)

    companion object {
        val ZERO = VPointF(0, 0)

        fun max(lhs: VPointF, rhs: VPointF) = VPointF(max(lhs.x, rhs.x), max(lhs.y, rhs.y))

        fun min(lhs: VPointF, rhs: VPointF) = VPointF(min(lhs.x, rhs.x), min(lhs.y, rhs.y))

        operator fun Float.plus(value: VPointF) = VPointF(this + value.x, this + value.y)

        operator fun Int.minus(value: VPointF) = VPointF(this - value.x, this - value.y)

        operator fun Float.minus(value: VPointF) = VPointF(this - value.x, this - value.y)

        operator fun Int.times(value: VPointF) = VPointF(this * value.x, this * value.y)

        operator fun Float.times(value: VPointF) = VPointF(this * value.x, this * value.y)

        operator fun Int.div(value: VPointF) = VPointF(this / value.x, this / value.y)

        operator fun Float.div(value: VPointF) = VPointF(this / value.x, this / value.y)

        val RectF.center: VPointF
            get() = VPointF(centerX(), centerY())

        val RectF.size: VPointF
            get() = VPointF(width(), height())
    }
}

@JvmInline
value class VPoint(private val data: ULong) {
    val x: Int
        get() = unpackX(data)

    val y: Int
        get() = unpackY(data)

    constructor() : this(0, 0)

    constructor(x: Int, y: Int) : this(pack(x, y))

    fun toPoint() = Point(x, y)

    fun abs() = VPoint(abs(x), abs(y))

    operator fun component1(): Int = x

    operator fun component2(): Int = y

    override fun toString() = "($x, $y)"

    operator fun plus(pt: VPoint) = VPoint(x + pt.x, y + pt.y)

    operator fun plus(pt: VPointF) = VPointF(x + pt.x, y + pt.y)

    operator fun plus(value: Int) = VPoint(x + value, y + value)

    operator fun plus(value: Float) = VPointF(x + value, y + value)

    operator fun minus(pt: VPoint) = VPoint(x - pt.x, y - pt.y)

    operator fun minus(pt: VPointF) = VPointF(x - pt.x, y - pt.y)

    operator fun minus(value: Int) = VPoint(x - value, y - value)

    operator fun minus(value: Float) = VPointF(x - value, y - value)

    operator fun times(pt: VPoint) = VPoint(x * pt.x, y * pt.y)

    operator fun times(pt: VPointF) = VPointF(x * pt.x, y * pt.y)

    operator fun times(value: Int) = VPoint(x * value, y * value)

    operator fun times(value: Float) = VPointF(x * value, y * value)

    operator fun div(pt: VPoint) = VPoint(x / pt.x, y / pt.y)

    operator fun div(pt: VPointF) = VPointF(x / pt.x, y / pt.y)

    operator fun div(value: Int) = VPoint(x / value, y / value)

    operator fun div(value: Float) = VPointF(x / value, y / value)

    companion object {
        val ZERO = VPoint(0, 0)

        fun max(lhs: VPoint, rhs: VPoint) = VPoint(max(lhs.x, rhs.x), max(lhs.y, rhs.y))

        fun min(lhs: VPoint, rhs: VPoint) = VPoint(min(lhs.x, rhs.x), min(lhs.y, rhs.y))

        operator fun Int.plus(value: VPoint) = VPoint(this + value.x, this + value.y)

        operator fun Float.plus(value: VPoint) = VPointF(this + value.x, this + value.y)

        operator fun Int.minus(value: VPoint) = VPoint(this - value.x, this - value.y)

        operator fun Float.minus(value: VPoint) = VPointF(this - value.x, this - value.y)

        operator fun Int.times(value: VPoint) = VPoint(this * value.x, this * value.y)

        operator fun Float.times(value: VPoint) = VPointF(this * value.x, this * value.y)

        operator fun Int.div(value: VPoint) = VPoint(this / value.x, this / value.y)

        operator fun Float.div(value: VPoint) = VPointF(this / value.x, this / value.y)

        val Rect.center: VPoint
            get() = VPoint(centerX(), centerY())

        val Rect.size: VPoint
            get() = VPoint(width(), height())
    }
}
Loading