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

Commit be26fbdf authored by Evan Laird's avatar Evan Laird Committed by Android (Google) Code Review
Browse files

Merge "System status event animation polish (1/n)" into tm-dev

parents 97bf21f8 519192a0
Loading
Loading
Loading
Loading
+6 −1
Original line number Diff line number Diff line
@@ -22,7 +22,10 @@
    android:layout_height="match_parent"
    android:layout_width="wrap_content"
    android:layout_gravity="center_vertical|end"
    android:focusable="true" >
    android:focusable="true"
    android:clipChildren="false"
    android:clipToPadding="false"
    >

        <LinearLayout
            android:id="@+id/icons_container"
@@ -34,5 +37,7 @@
            android:layout_gravity="center"
            android:minWidth="@dimen/ongoing_appops_chip_min_width"
            android:maxWidth="@dimen/ongoing_appops_chip_max_width"
            android:clipChildren="false"
            android:clipToPadding="false"
            />
</com.android.systemui.privacy.OngoingPrivacyChip>
 No newline at end of file
+2 −12
Original line number Diff line number Diff line
@@ -19,17 +19,7 @@
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_gravity="center_vertical|end"
    android:paddingTop="@dimen/status_bar_padding_top"
    android:paddingEnd="8dp"
    android:clipChildren="false"
    android:clipToPadding="false"
    >

    <ImageView
        android:id="@+id/dot_view"
        android:layout_width="10dp"
        android:layout_height="10dp"
        android:layout_gravity="center_vertical|end"
        android:src="@drawable/system_animation_ongoing_dot"
        android:visibility="invisible"
        />

</FrameLayout>
 No newline at end of file
+12 −1
Original line number Diff line number Diff line
@@ -22,13 +22,14 @@ import android.widget.ImageView
import android.widget.LinearLayout
import com.android.settingslib.Utils
import com.android.systemui.R
import com.android.systemui.statusbar.events.BackgroundAnimatableView

class OngoingPrivacyChip @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttrs: Int = 0,
    defStyleRes: Int = 0
) : FrameLayout(context, attrs, defStyleAttrs, defStyleRes) {
) : FrameLayout(context, attrs, defStyleAttrs, defStyleRes), BackgroundAnimatableView {

    private var iconMargin = 0
    private var iconSize = 0
@@ -50,6 +51,16 @@ class OngoingPrivacyChip @JvmOverloads constructor(
        updateResources()
    }

    /**
     * When animating as a chip in the status bar, we want to animate the width for the container
     * of the privacy items. We have to subtract our own top and left offset because the bounds
     * come to us as absolute on-screen bounds, and `iconsContainer` is laid out relative to the
     * frame layout's bounds.
     */
    override fun setBoundsForAnimation(l: Int, t: Int, r: Int, b: Int) {
        iconsContainer.setLeftTopRightBottom(l - left, t - top, r - left, b - top)
    }

    // Should only be called if the builder icons or app changed
    private fun updateView(builder: PrivacyChipBuilder) {
        fun setIcons(chipBuilder: PrivacyChipBuilder, iconsContainer: ViewGroup) {
+31 −5
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package com.android.systemui.statusbar.events

import android.annotation.SuppressLint
import android.content.Context
import android.graphics.Color
import android.graphics.drawable.ColorDrawable
@@ -27,13 +28,15 @@ import com.android.systemui.R
import com.android.systemui.privacy.OngoingPrivacyChip
import com.android.systemui.privacy.PrivacyItem

typealias ViewCreator = (context: Context) -> BackgroundAnimatableView

interface StatusEvent {
    val priority: Int
    // Whether or not to force the status bar open and show a dot
    val forceVisible: Boolean
    // Whether or not to show an animation for this event
    val showAnimation: Boolean
    val viewCreator: (context: Context) -> View
    val viewCreator: ViewCreator
    var contentDescription: String?

    // Update this event with values from another event.
@@ -47,14 +50,37 @@ interface StatusEvent {
    }
}

class BGView(
    context: Context
) : View(context), BackgroundAnimatableView {
    override val view: View
        get() = this

    override fun setBoundsForAnimation(l: Int, t: Int, r: Int, b: Int) {
        setLeftTopRightBottom(l, t, r, b)
    }
}

@SuppressLint("AppCompatCustomView")
class BGImageView(
    context: Context
) : ImageView(context), BackgroundAnimatableView {
    override val view: View
        get() = this

    override fun setBoundsForAnimation(l: Int, t: Int, r: Int, b: Int) {
        setLeftTopRightBottom(l, t, r, b)
    }
}

class BatteryEvent : StatusEvent {
    override val priority = 50
    override val forceVisible = false
    override val showAnimation = true
    override var contentDescription: String? = ""

    override val viewCreator: (context: Context) -> View = { context ->
        val iv = ImageView(context)
    override val viewCreator: (context: Context) -> BGImageView = { context ->
        val iv = BGImageView(context)
        iv.setImageDrawable(ThemedBatteryDrawable(context, Color.WHITE))
        iv.setBackgroundDrawable(ColorDrawable(Color.GREEN))
        iv
@@ -72,7 +98,7 @@ class PrivacyEvent(override val showAnimation: Boolean = true) : StatusEvent {
    var privacyItems: List<PrivacyItem> = listOf()
    private var privacyChip: OngoingPrivacyChip? = null

    override val viewCreator: (context: Context) -> View = { context ->
    override val viewCreator: ViewCreator = { context ->
        val v = LayoutInflater.from(context)
                .inflate(R.layout.ongoing_privacy_chip, null) as OngoingPrivacyChip
        v.privacyList = privacyItems
@@ -82,7 +108,7 @@ class PrivacyEvent(override val showAnimation: Boolean = true) : StatusEvent {
    }

    override fun toString(): String {
        return javaClass.simpleName
        return "${javaClass.simpleName}(forceVisible=$forceVisible, privacyItems=$privacyItems)"
    }

    override fun shouldUpdateFromEvent(other: StatusEvent?): Boolean {
+103 −48
Original line number Diff line number Diff line
@@ -18,14 +18,16 @@ package com.android.systemui.statusbar.events

import android.animation.ValueAnimator
import android.content.Context
import android.graphics.Point
import android.view.Gravity
import android.view.LayoutInflater
import android.view.View
import android.view.View.MeasureSpec.AT_MOST
import android.view.ViewGroup.LayoutParams.MATCH_PARENT
import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
import android.widget.FrameLayout
import com.android.systemui.R
import com.android.systemui.statusbar.phone.StatusBarLocationPublisher
import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider
import com.android.systemui.statusbar.window.StatusBarWindowController
import javax.inject.Inject

@@ -35,44 +37,73 @@ import javax.inject.Inject
class SystemEventChipAnimationController @Inject constructor(
    private val context: Context,
    private val statusBarWindowController: StatusBarWindowController,
    private val locationPublisher: StatusBarLocationPublisher
    private val contentInsetsProvider: StatusBarContentInsetsProvider
) : SystemStatusChipAnimationCallback {
    var showPersistentDot = false
        set(value) {
            field = value
            statusBarWindowController.setForceStatusBarVisible(value)
            maybeUpdateShowDot()
        }

    private lateinit var animationWindowView: FrameLayout
    private lateinit var animationDotView: View
    private var currentAnimatedView: View? = null

    private var currentAnimatedView: BackgroundAnimatableView? = null

    // Left for LTR, Right for RTL
    private var animationDirection = LEFT
    private var chipRight = 0
    private var chipLeft = 0
    private var chipWidth = 0
    private var dotCenter = Point(0, 0)
    private var dotSize = context.resources.getDimensionPixelSize(
            R.dimen.ongoing_appops_dot_diameter)
    // If the chip animates away to a persistent dot, then we modify the CHIP_OUT animation
    private var isAnimatingToDot = false

    // TODO: move to dagger
    private var initialized = false

    override fun onChipAnimationStart(
        viewCreator: (context: Context) -> View,
        viewCreator: ViewCreator,
        @SystemAnimationState state: Int
    ) {
        if (!initialized) init()

        if (state == ANIMATING_IN) {
            currentAnimatedView = viewCreator(context)
            animationWindowView.addView(currentAnimatedView, layoutParamsDefault())
            animationDirection = if (animationWindowView.isLayoutRtl) RIGHT else LEFT

            // Initialize the animated view
            val insets = contentInsetsProvider.getStatusBarContentInsetsForCurrentRotation()
            currentAnimatedView = viewCreator(context).also {
                animationWindowView.addView(
                        it.view,
                        layoutParamsDefault(
                                if (animationWindowView.isLayoutRtl) insets.first
                                else insets.second))
                it.view.alpha = 0f
                // For some reason, the window view's measured width is always 0 here, so use the
                // parent (status bar)
                it.view.measure(
                        View.MeasureSpec.makeMeasureSpec(
                                (animationWindowView.parent as View).width, AT_MOST),
                        View.MeasureSpec.makeMeasureSpec(animationWindowView.height, AT_MOST))
                chipWidth = it.chipWidth
            }

            // decide which direction we're animating from, and then set some screen coordinates
            val contentRect = contentInsetsProvider.getStatusBarContentAreaForCurrentRotation()
            when (animationDirection) {
                LEFT -> {
                    chipRight = contentRect.right
                    chipLeft = contentRect.right - chipWidth
                }
                else /* RIGHT */ -> {
                    chipLeft = contentRect.left
                    chipRight = contentRect.left + chipWidth
                }
            }

            // We are animating IN; chip comes in from View.END
            currentAnimatedView?.apply {
                val translation = width.toFloat()
                translationX = if (isLayoutRtl) -translation else translation
                alpha = 0f
                visibility = View.VISIBLE
                setPadding(locationPublisher.marginLeft, 0, locationPublisher.marginRight, 0)
                updateAnimatedViewBoundsForAmount(0.1f, this)
            }
        } else {
            // We are animating away
            currentAnimatedView?.apply {
                translationX = 0f
            currentAnimatedView!!.view.apply {
                alpha = 1f
            }
        }
@@ -82,15 +113,14 @@ class SystemEventChipAnimationController @Inject constructor(
        if (state == ANIMATING_IN) {
            // Finished animating in
            currentAnimatedView?.apply {
                translationX = 0f
                alpha = 1f
                updateAnimatedViewBoundsForAmount(1f, this)
            }
        } else {
            // Finished animating away
            currentAnimatedView?.apply {
            currentAnimatedView!!.view.apply {
                visibility = View.INVISIBLE
            }
            animationWindowView.removeView(currentAnimatedView)
            animationWindowView.removeView(currentAnimatedView!!.view)
        }
    }

@@ -98,22 +128,10 @@ class SystemEventChipAnimationController @Inject constructor(
        animator: ValueAnimator,
        @SystemAnimationState state: Int
    ) {
        // Alpha is parameterized 0,1, and translation from (width, 0)
        currentAnimatedView?.apply {
            val amt = animator.animatedValue as Float

            alpha = amt

            val w = width
            val translation = (1 - amt) * w
            translationX = if (isLayoutRtl) -translation else translation
        }
    }

    private fun maybeUpdateShowDot() {
        if (!initialized) return
        if (!showPersistentDot && currentAnimatedView == null) {
            animationDotView.visibility = View.INVISIBLE
            val amt = (animator.animatedValue as Float).amt()
            view.alpha = (animator.animatedValue as Float)
            updateAnimatedViewBoundsForAmount(amt, this)
        }
    }

@@ -121,19 +139,56 @@ class SystemEventChipAnimationController @Inject constructor(
        initialized = true
        animationWindowView = LayoutInflater.from(context)
                .inflate(R.layout.system_event_animation_window, null) as FrameLayout
        animationDotView = animationWindowView.findViewById(R.id.dot_view)
        val lp = FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT)
        lp.gravity = Gravity.END or Gravity.CENTER_VERTICAL
        statusBarWindowController.addViewToWindow(animationWindowView, lp)
        animationWindowView.clipToPadding = false
        animationWindowView.clipChildren = false
        animationWindowView.measureAllChildren = true
    }

    private fun start() = if (animationWindowView.isLayoutRtl) right() else left()
    private fun right() = locationPublisher.marginRight
    private fun left() = locationPublisher.marginLeft

    private fun layoutParamsDefault(): FrameLayout.LayoutParams =
    private fun layoutParamsDefault(marginEnd: Int): FrameLayout.LayoutParams =
            FrameLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT).also {
                it.gravity = Gravity.END or Gravity.CENTER_VERTICAL
            it.marginStart = start()
                it.marginEnd = marginEnd
            }

    private fun updateAnimatedViewBoundsForAmount(amt: Float, chip: BackgroundAnimatableView) {
        when (animationDirection) {
            LEFT -> {
                chip.setBoundsForAnimation(
                        (chipRight - (chipWidth * amt)).toInt(),
                        chip.view.top,
                        chipRight,
                        chip.view.bottom)
            }
            else /* RIGHT */ -> {
                chip.setBoundsForAnimation(
                        chipLeft,
                        chip.view.top,
                        (chipLeft + (chipWidth * amt)).toInt(),
                        chip.view.bottom)
            }
        }
    }

    private fun start() = if (animationWindowView.isLayoutRtl) right() else left()
    private fun right() = contentInsetsProvider.getStatusBarContentAreaForCurrentRotation().right
    private fun left() = contentInsetsProvider.getStatusBarContentAreaForCurrentRotation().left
    private fun Float.amt() = 0.01f.coerceAtLeast(this)
}

/**
 * Chips should provide a view that can be animated with something better than a fade-in
 */
interface BackgroundAnimatableView {
    val view: View // Since this can't extend View, add a view prop
        get() = this as View
    val chipWidth: Int
        get() = view.measuredWidth
    fun setBoundsForAnimation(l: Int, t: Int, r: Int, b: Int)
}

// Animation directions
private const val LEFT = 1
private const val RIGHT = 2
Loading