diff options
| author | 2024-05-07 17:39:14 +0000 | |
|---|---|---|
| committer | 2024-05-07 17:39:14 +0000 | |
| commit | d24e1edfc6863008beeb550460dec86c2a2cd9f3 (patch) | |
| tree | c5fc4c91c3c91b83f05aa9451046b0587e8d2fdd | |
| parent | 99c0de24b429ac45cddd9a3b031fc5c625119474 (diff) | |
| parent | 6610b19405e912e759e2fe154ef51bbb9aa6ff52 (diff) | |
Merge "Animate changes between action icons." into 24D1-dev am: 6610b19405
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/27196797
Change-Id: I95faa93346c7e7c3541ad65090aa6cbacf9263a8
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
| -rw-r--r-- | packages/SystemUI/src/com/android/systemui/screenshot/ui/TransitioningIconDrawable.kt | 134 | ||||
| -rw-r--r-- | packages/SystemUI/src/com/android/systemui/screenshot/ui/binder/ActionButtonViewBinder.kt | 9 |
2 files changed, 142 insertions, 1 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ui/TransitioningIconDrawable.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ui/TransitioningIconDrawable.kt new file mode 100644 index 000000000000..0bc280c6c1e5 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ui/TransitioningIconDrawable.kt @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2024 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.screenshot.ui + +import android.animation.ValueAnimator +import android.content.res.ColorStateList +import android.graphics.Canvas +import android.graphics.ColorFilter +import android.graphics.drawable.Drawable +import androidx.core.animation.doOnEnd +import java.util.Objects + +/** */ +class TransitioningIconDrawable : Drawable() { + // The drawable for the current icon of this view. During icon transitions, this is the one + // being animated out. + private var drawable: Drawable? = null + + // The incoming new icon. Only populated during transition animations (when drawable is also + // non-null). + private var enteringDrawable: Drawable? = null + private var colorFilter: ColorFilter? = null + private var tint: ColorStateList? = null + private var alpha = 255 + + private var transitionAnimator = + ValueAnimator.ofFloat(0f, 1f).also { it.doOnEnd { onTransitionComplete() } } + + /** + * Set the drawable to be displayed, potentially animating the transition from one icon to the + * next. + */ + fun setIcon(incomingDrawable: Drawable?) { + if (Objects.equals(drawable, incomingDrawable) && !transitionAnimator.isRunning) { + return + } + + incomingDrawable?.colorFilter = colorFilter + incomingDrawable?.setTintList(tint) + + if (drawable == null) { + // No existing icon drawn, just show the new one without a transition + drawable = incomingDrawable + invalidateSelf() + return + } + + if (enteringDrawable != null) { + // There's already an entrance animation happening, just update the entering icon, not + // maintaining a queue or anything. + enteringDrawable = incomingDrawable + return + } + + // There was already an icon, need to animate between icons. + enteringDrawable = incomingDrawable + transitionAnimator.setCurrentFraction(0f) + transitionAnimator.start() + invalidateSelf() + } + + override fun draw(canvas: Canvas) { + // Scale the old one down, scale the new one up. + drawable?.let { + val scale = + if (transitionAnimator.isRunning) { + 1f - transitionAnimator.animatedFraction + } else { + 1f + } + drawScaledDrawable(it, canvas, scale) + } + enteringDrawable?.let { + val scale = transitionAnimator.animatedFraction + drawScaledDrawable(it, canvas, scale) + } + + if (transitionAnimator.isRunning) { + invalidateSelf() + } + } + + private fun drawScaledDrawable(drawable: Drawable, canvas: Canvas, scale: Float) { + drawable.bounds = getBounds() + canvas.save() + canvas.scale( + scale, + scale, + (drawable.intrinsicWidth / 2).toFloat(), + (drawable.intrinsicHeight / 2).toFloat() + ) + drawable.draw(canvas) + canvas.restore() + } + + private fun onTransitionComplete() { + drawable = enteringDrawable + enteringDrawable = null + invalidateSelf() + } + + override fun setTintList(tint: ColorStateList?) { + super.setTintList(tint) + drawable?.setTintList(tint) + enteringDrawable?.setTintList(tint) + this.tint = tint + } + + override fun setAlpha(alpha: Int) { + this.alpha = alpha + } + + override fun setColorFilter(colorFilter: ColorFilter?) { + this.colorFilter = colorFilter + drawable?.colorFilter = colorFilter + enteringDrawable?.colorFilter = colorFilter + } + + override fun getOpacity(): Int = alpha +} diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ui/binder/ActionButtonViewBinder.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ui/binder/ActionButtonViewBinder.kt index 3c5a0ec107f8..750bd530d9b2 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ui/binder/ActionButtonViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ui/binder/ActionButtonViewBinder.kt @@ -21,6 +21,7 @@ import android.widget.ImageView import android.widget.LinearLayout import android.widget.TextView import com.android.systemui.res.R +import com.android.systemui.screenshot.ui.TransitioningIconDrawable import com.android.systemui.screenshot.ui.viewmodel.ActionButtonViewModel object ActionButtonViewBinder { @@ -28,7 +29,13 @@ object ActionButtonViewBinder { fun bind(view: View, viewModel: ActionButtonViewModel) { val iconView = view.requireViewById<ImageView>(R.id.overlay_action_chip_icon) val textView = view.requireViewById<TextView>(R.id.overlay_action_chip_text) - iconView.setImageDrawable(viewModel.appearance.icon) + if (iconView.drawable == null) { + iconView.setImageDrawable(TransitioningIconDrawable()) + } + val drawable = iconView.drawable as? TransitioningIconDrawable + // Note we never re-bind a view to a different ActionButtonViewModel, different view + // models would remove/create separate views. + drawable?.setIcon(viewModel.appearance.icon) textView.text = viewModel.appearance.label setMargins(iconView, textView, viewModel.appearance.label?.isNotEmpty() ?: false) if (viewModel.onClicked != null) { |