diff options
| author | 2024-02-13 18:54:36 +0000 | |
|---|---|---|
| committer | 2024-02-13 19:06:22 +0000 | |
| commit | b8ebdf763068369174d904680ffd56c0e5aac85c (patch) | |
| tree | 19eb677fee6709aa8a2188f8ccebc1311014ed87 | |
| parent | 4eac1b08d934f87d55910d3790f39b8bcd5573b5 (diff) | |
Use new LoadingEffect in UMO
LoadingEffectView is introduced which should replace TurbulenceNoiseView
later once the flag is fully ramped.
Bug: 325079325
Flag: ACONFIG shaderlib_loading_effect_refactor DEVELOPMENT
Test: MediaControlPanelTest, override adb shell device_config override systemui shaderlib_loading_effect_refactor true
Change-Id: Id460a3a7452010faa339fba7c8d49a42df3af6e2
8 files changed, 225 insertions, 18 deletions
diff --git a/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/loadingeffect/LoadingEffectView.kt b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/loadingeffect/LoadingEffectView.kt new file mode 100644 index 000000000000..aad593eb6a05 --- /dev/null +++ b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/loadingeffect/LoadingEffectView.kt @@ -0,0 +1,51 @@ +/* + * 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.surfaceeffects.loadingeffect + +import android.content.Context +import android.graphics.BlendMode +import android.graphics.Canvas +import android.graphics.Paint +import android.util.AttributeSet +import android.view.View + +/** Custom View for drawing the [LoadingEffect] with [Canvas.drawPaint]. */ +open class LoadingEffectView(context: Context?, attrs: AttributeSet?) : View(context, attrs) { + + private var paint: Paint? = null + private var blendMode: BlendMode = BlendMode.SRC_OVER + + override fun onDraw(canvas: Canvas) { + if (!canvas.isHardwareAccelerated) { + return + } + paint?.let { canvas.drawPaint(it) } + } + + /** Designed to be called on [LoadingEffect.PaintDrawCallback.onDraw]. */ + fun draw(paint: Paint) { + this.paint = paint + this.paint!!.blendMode = blendMode + + invalidate() + } + + /** Sets the blend mode of the [Paint]. */ + fun setBlendMode(blendMode: BlendMode) { + this.blendMode = blendMode + } +} diff --git a/packages/SystemUI/res/layout/media_session_view.xml b/packages/SystemUI/res/layout/media_session_view.xml index 5db9eee6a908..109e63c6167a 100644 --- a/packages/SystemUI/res/layout/media_session_view.xml +++ b/packages/SystemUI/res/layout/media_session_view.xml @@ -67,6 +67,18 @@ android:background="@drawable/qs_media_outline_layout_bg" /> + <com.android.systemui.surfaceeffects.loadingeffect.LoadingEffectView + android:id="@+id/loading_effect_view" + android:layout_width="match_parent" + android:layout_height="@dimen/qs_media_session_height_expanded" + app:layout_constraintStart_toStartOf="@id/album_art" + app:layout_constraintEnd_toEndOf="@id/album_art" + app:layout_constraintTop_toTopOf="@id/album_art" + app:layout_constraintBottom_toBottomOf="@id/album_art" + android:clipToOutline="true" + android:background="@drawable/qs_media_outline_layout_bg" + /> + <!-- Guideline for output switcher --> <androidx.constraintlayout.widget.Guideline android:id="@+id/center_vertical_guideline" diff --git a/packages/SystemUI/res/xml/media_session_collapsed.xml b/packages/SystemUI/res/xml/media_session_collapsed.xml index c053b33b4c63..2f2b10f8dc0c 100644 --- a/packages/SystemUI/res/xml/media_session_collapsed.xml +++ b/packages/SystemUI/res/xml/media_session_collapsed.xml @@ -55,6 +55,15 @@ app:layout_constraintBottom_toBottomOf="@+id/album_art" /> <Constraint + android:id="@+id/loading_effect_view" + android:layout_width="match_parent" + android:layout_height="@dimen/qs_media_session_height_collapsed" + app:layout_constraintStart_toStartOf="@+id/album_art" + app:layout_constraintEnd_toEndOf="@+id/album_art" + app:layout_constraintTop_toTopOf="@+id/album_art" + app:layout_constraintBottom_toBottomOf="@+id/album_art" /> + + <Constraint android:id="@+id/header_title" android:layout_width="wrap_content" android:layout_height="wrap_content" diff --git a/packages/SystemUI/res/xml/media_session_expanded.xml b/packages/SystemUI/res/xml/media_session_expanded.xml index 8bf7560d6ddb..0140d52bd175 100644 --- a/packages/SystemUI/res/xml/media_session_expanded.xml +++ b/packages/SystemUI/res/xml/media_session_expanded.xml @@ -48,6 +48,15 @@ app:layout_constraintBottom_toBottomOf="@+id/album_art" /> <Constraint + android:id="@+id/loading_effect_view" + android:layout_width="match_parent" + android:layout_height="@dimen/qs_media_session_height_expanded" + app:layout_constraintStart_toStartOf="@+id/album_art" + app:layout_constraintEnd_toEndOf="@+id/album_art" + app:layout_constraintTop_toTopOf="@+id/album_art" + app:layout_constraintBottom_toBottomOf="@+id/album_art" /> + + <Constraint android:id="@+id/header_title" android:layout_width="wrap_content" android:layout_height="wrap_content" diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/models/player/MediaViewHolder.kt b/packages/SystemUI/src/com/android/systemui/media/controls/models/player/MediaViewHolder.kt index 1b14f75d54ef..898eacff6246 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/models/player/MediaViewHolder.kt +++ b/packages/SystemUI/src/com/android/systemui/media/controls/models/player/MediaViewHolder.kt @@ -25,8 +25,9 @@ import android.widget.SeekBar import android.widget.TextView import androidx.constraintlayout.widget.Barrier import com.android.internal.widget.CachingIconView -import com.android.systemui.res.R import com.android.systemui.media.controls.models.GutsViewHolder +import com.android.systemui.res.R +import com.android.systemui.surfaceeffects.loadingeffect.LoadingEffectView import com.android.systemui.surfaceeffects.ripple.MultiRippleView import com.android.systemui.surfaceeffects.turbulencenoise.TurbulenceNoiseView import com.android.systemui.util.animation.TransitionLayout @@ -42,6 +43,7 @@ class MediaViewHolder constructor(itemView: View) { val multiRippleView = itemView.requireViewById<MultiRippleView>(R.id.touch_ripple_view) val turbulenceNoiseView = itemView.requireViewById<TurbulenceNoiseView>(R.id.turbulence_noise_view) + val loadingEffectView = itemView.requireViewById<LoadingEffectView>(R.id.loading_effect_view) val appIcon = itemView.requireViewById<ImageView>(R.id.icon) val titleText = itemView.requireViewById<TextView>(R.id.header_title) val artistText = itemView.requireViewById<TextView>(R.id.header_artist) @@ -171,6 +173,7 @@ class MediaViewHolder constructor(itemView: View) { setOf( R.id.album_art, R.id.turbulence_noise_view, + R.id.loading_effect_view, R.id.touch_ripple_view, ) } diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/ColorSchemeTransition.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/ColorSchemeTransition.kt index c87fd14a943d..952f9b8711f0 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/ColorSchemeTransition.kt +++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/ColorSchemeTransition.kt @@ -29,6 +29,7 @@ import com.android.internal.annotations.VisibleForTesting import com.android.settingslib.Utils import com.android.systemui.media.controls.models.player.MediaViewHolder import com.android.systemui.monet.ColorScheme +import com.android.systemui.surfaceeffects.loadingeffect.LoadingEffect import com.android.systemui.surfaceeffects.ripple.MultiRippleController import com.android.systemui.surfaceeffects.turbulencenoise.TurbulenceNoiseController @@ -118,6 +119,7 @@ internal constructor( turbulenceNoiseController, ::AnimatingColorTransition ) + var loadingEffect: LoadingEffect? = null val bgColor = context.getColor(com.google.android.material.R.color.material_dynamic_neutral20) val surfaceColor = @@ -128,7 +130,6 @@ internal constructor( mediaViewHolder.albumView.backgroundTintList = colorList mediaViewHolder.gutsViewHolder.setSurfaceColor(surfaceColor) } - val accentPrimary = animatingColorTransitionFactory( loadDefaultColor(R.attr.textColorPrimary), @@ -139,6 +140,7 @@ internal constructor( mediaViewHolder.gutsViewHolder.setAccentPrimaryColor(accentPrimary) multiRippleController.updateColor(accentPrimary) turbulenceNoiseController.updateNoiseColor(accentPrimary) + loadingEffect?.updateColor(accentPrimary) } val accentSecondary = diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java index aa92814a584d..e97c9d3d8c0b 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java +++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java @@ -41,6 +41,7 @@ import android.graphics.Color; import android.graphics.ColorMatrix; import android.graphics.ColorMatrixColorFilter; import android.graphics.Matrix; +import android.graphics.Paint; import android.graphics.Rect; import android.graphics.drawable.Animatable; import android.graphics.drawable.BitmapDrawable; @@ -81,6 +82,7 @@ import com.android.internal.logging.InstanceId; import com.android.internal.widget.CachingIconView; import com.android.settingslib.widget.AdaptiveIcon; import com.android.systemui.ActivityIntentHelper; +import com.android.systemui.Flags; import com.android.systemui.animation.ActivityTransitionAnimator; import com.android.systemui.animation.GhostedViewTransitionAnimatorController; import com.android.systemui.bluetooth.BroadcastDialogController; @@ -111,6 +113,9 @@ import com.android.systemui.res.R; import com.android.systemui.shared.system.SysUiStatsLog; import com.android.systemui.statusbar.NotificationLockscreenUserManager; import com.android.systemui.statusbar.policy.KeyguardStateController; +import com.android.systemui.surfaceeffects.loadingeffect.LoadingEffect; +import com.android.systemui.surfaceeffects.loadingeffect.LoadingEffect.Companion.AnimationState; +import com.android.systemui.surfaceeffects.loadingeffect.LoadingEffectView; import com.android.systemui.surfaceeffects.ripple.MultiRippleController; import com.android.systemui.surfaceeffects.ripple.MultiRippleView; import com.android.systemui.surfaceeffects.ripple.RippleAnimation; @@ -248,13 +253,34 @@ public class MediaControlPanel { private String mCurrentBroadcastApp; private MultiRippleController mMultiRippleController; private TurbulenceNoiseController mTurbulenceNoiseController; + private LoadingEffect mLoadingEffect; private final GlobalSettings mGlobalSettings; - + private final Random mRandom = new Random(); private TurbulenceNoiseAnimationConfig mTurbulenceNoiseAnimationConfig; private boolean mWasPlaying = false; private boolean mButtonClicked = false; - private final Random mRandom = new Random(); + private final LoadingEffect.Companion.PaintDrawCallback mNoiseDrawCallback = + new LoadingEffect.Companion.PaintDrawCallback() { + @Override + public void onDraw(@NonNull Paint loadingPaint) { + mMediaViewHolder.getLoadingEffectView().draw(loadingPaint); + } + }; + private final LoadingEffect.Companion.AnimationStateChangedCallback mStateChangedCallback = + new LoadingEffect.Companion.AnimationStateChangedCallback() { + @Override + public void onStateChanged(@NonNull AnimationState oldState, + @NonNull AnimationState newState) { + LoadingEffectView loadingEffectView = + mMediaViewHolder.getLoadingEffectView(); + if (newState == AnimationState.NOT_PLAYING) { + loadingEffectView.setVisibility(View.INVISIBLE); + } else { + loadingEffectView.setVisibility(View.VISIBLE); + } + } + }; /** * Initialize a new control panel @@ -456,6 +482,10 @@ public class MediaControlPanel { TurbulenceNoiseView turbulenceNoiseView = vh.getTurbulenceNoiseView(); turbulenceNoiseView.setBlendMode(BlendMode.SCREEN); + LoadingEffectView loadingEffectView = vh.getLoadingEffectView(); + loadingEffectView.setBlendMode(BlendMode.SCREEN); + loadingEffectView.setVisibility(View.INVISIBLE); + mTurbulenceNoiseController = new TurbulenceNoiseController(turbulenceNoiseView); mColorSchemeTransition = new ColorSchemeTransition( @@ -587,22 +617,41 @@ public class MediaControlPanel { } } - // Turbulence noise if (shouldPlayTurbulenceNoise()) { + // Need to create the config here to get the correct view size and color. if (mTurbulenceNoiseAnimationConfig == null) { mTurbulenceNoiseAnimationConfig = - createTurbulenceNoiseAnimation(); + createTurbulenceNoiseConfig(); + } + + if (Flags.shaderlibLoadingEffectRefactor()) { + if (mLoadingEffect == null) { + mLoadingEffect = new LoadingEffect( + Type.SIMPLEX_NOISE, + mTurbulenceNoiseAnimationConfig, + mNoiseDrawCallback, + mStateChangedCallback + ); + mColorSchemeTransition.setLoadingEffect(mLoadingEffect); + } + + mLoadingEffect.play(); + mMainExecutor.executeDelayed( + mLoadingEffect::finish, + TURBULENCE_NOISE_PLAY_DURATION + ); + } else { + mTurbulenceNoiseController.play( + Type.SIMPLEX_NOISE, + mTurbulenceNoiseAnimationConfig + ); + mMainExecutor.executeDelayed( + mTurbulenceNoiseController::finish, + TURBULENCE_NOISE_PLAY_DURATION + ); } - // Color will be correctly updated in ColorSchemeTransition. - mTurbulenceNoiseController.play( - Type.SIMPLEX_NOISE, - mTurbulenceNoiseAnimationConfig - ); - mMainExecutor.executeDelayed( - mTurbulenceNoiseController::finish, - TURBULENCE_NOISE_PLAY_DURATION - ); } + mButtonClicked = false; mWasPlaying = isPlaying(); @@ -1232,7 +1281,13 @@ public class MediaControlPanel { return mButtonClicked && !mWasPlaying && isPlaying(); } - private TurbulenceNoiseAnimationConfig createTurbulenceNoiseAnimation() { + private TurbulenceNoiseAnimationConfig createTurbulenceNoiseConfig() { + View targetView = Flags.shaderlibLoadingEffectRefactor() + ? mMediaViewHolder.getLoadingEffectView() : + mMediaViewHolder.getTurbulenceNoiseView(); + int width = targetView.getWidth(); + int height = targetView.getHeight(); + return new TurbulenceNoiseAnimationConfig( /* gridCount= */ 2.14f, TurbulenceNoiseAnimationConfig.DEFAULT_LUMINOSITY_MULTIPLIER, @@ -1242,10 +1297,11 @@ public class MediaControlPanel { /* noiseMoveSpeedX= */ 0.42f, /* noiseMoveSpeedY= */ 0f, TurbulenceNoiseAnimationConfig.DEFAULT_NOISE_SPEED_Z, + // Color will be correctly updated in ColorSchemeTransition. /* color= */ mColorSchemeTransition.getAccentPrimary().getCurrentColor(), /* backgroundColor= */ Color.BLACK, - /* width= */ mMediaViewHolder.getTurbulenceNoiseView().getWidth(), - /* height= */ mMediaViewHolder.getTurbulenceNoiseView().getHeight(), + width, + height, TurbulenceNoiseAnimationConfig.DEFAULT_MAX_DURATION_IN_MILLIS, /* easeInDuration= */ 1350f, /* easeOutDuration= */ 1350f, diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaControlPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaControlPanelTest.kt index 59965022d7cc..c896486339b9 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaControlPanelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaControlPanelTest.kt @@ -40,6 +40,7 @@ import android.media.MediaMetadata import android.media.session.MediaSession import android.media.session.PlaybackState import android.os.Bundle +import android.platform.test.annotations.EnableFlags import android.provider.Settings import android.provider.Settings.ACTION_MEDIA_CONTROLS_SETTINGS import android.testing.AndroidTestingRunner @@ -61,6 +62,7 @@ import androidx.test.filters.SmallTest import com.android.internal.logging.InstanceId import com.android.internal.widget.CachingIconView import com.android.systemui.ActivityIntentHelper +import com.android.systemui.Flags import com.android.systemui.SysuiTestCase import com.android.systemui.bluetooth.BroadcastDialogController import com.android.systemui.broadcast.BroadcastSender @@ -88,6 +90,7 @@ import com.android.systemui.plugins.FalsingManager import com.android.systemui.res.R import com.android.systemui.statusbar.NotificationLockscreenUserManager import com.android.systemui.statusbar.policy.KeyguardStateController +import com.android.systemui.surfaceeffects.loadingeffect.LoadingEffectView import com.android.systemui.surfaceeffects.ripple.MultiRippleView import com.android.systemui.surfaceeffects.turbulencenoise.TurbulenceNoiseAnimationConfig import com.android.systemui.surfaceeffects.turbulencenoise.TurbulenceNoiseView @@ -190,6 +193,7 @@ public class MediaControlPanelTest : SysuiTestCase() { private lateinit var dismissText: TextView private lateinit var multiRippleView: MultiRippleView private lateinit var turbulenceNoiseView: TurbulenceNoiseView + private lateinit var loadingEffectView: LoadingEffectView private lateinit var session: MediaSession private lateinit var device: MediaDeviceData @@ -402,6 +406,7 @@ public class MediaControlPanelTest : SysuiTestCase() { multiRippleView = MultiRippleView(context, null) turbulenceNoiseView = TurbulenceNoiseView(context, null) + loadingEffectView = LoadingEffectView(context, null) whenever(viewHolder.player).thenReturn(view) whenever(viewHolder.appIcon).thenReturn(appIcon) @@ -447,6 +452,7 @@ public class MediaControlPanelTest : SysuiTestCase() { whenever(viewHolder.multiRippleView).thenReturn(multiRippleView) whenever(viewHolder.turbulenceNoiseView).thenReturn(turbulenceNoiseView) + whenever(viewHolder.loadingEffectView).thenReturn(loadingEffectView) } /** Initialize elements for the recommendation view holder */ @@ -2429,6 +2435,7 @@ public class MediaControlPanelTest : SysuiTestCase() { mainExecutor.execute { assertThat(turbulenceNoiseView.visibility).isEqualTo(View.VISIBLE) + assertThat(loadingEffectView.visibility).isEqualTo(View.INVISIBLE) clock.advanceTime( MediaControlPanel.TURBULENCE_NOISE_PLAY_DURATION + @@ -2436,6 +2443,40 @@ public class MediaControlPanelTest : SysuiTestCase() { ) assertThat(turbulenceNoiseView.visibility).isEqualTo(View.INVISIBLE) + assertThat(loadingEffectView.visibility).isEqualTo(View.INVISIBLE) + } + } + + @Test + @EnableFlags(Flags.FLAG_SHADERLIB_LOADING_EFFECT_REFACTOR) + fun playTurbulenceNoise_newLoadingEffect_finishesAfterDuration() { + val semanticActions = + MediaButton( + playOrPause = + MediaAction( + icon = null, + action = {}, + contentDescription = "play", + background = null + ) + ) + val data = mediaData.copy(semanticActions = semanticActions) + player.attachPlayer(viewHolder) + player.bindPlayer(data, KEY) + + viewHolder.actionPlayPause.callOnClick() + + mainExecutor.execute { + assertThat(loadingEffectView.visibility).isEqualTo(View.VISIBLE) + assertThat(turbulenceNoiseView.visibility).isEqualTo(View.INVISIBLE) + + clock.advanceTime( + MediaControlPanel.TURBULENCE_NOISE_PLAY_DURATION + + TurbulenceNoiseAnimationConfig.DEFAULT_EASING_DURATION_IN_MILLIS.toLong() + ) + + assertThat(loadingEffectView.visibility).isEqualTo(View.INVISIBLE) + assertThat(turbulenceNoiseView.visibility).isEqualTo(View.INVISIBLE) } } @@ -2458,6 +2499,30 @@ public class MediaControlPanelTest : SysuiTestCase() { viewHolder.action0.callOnClick() assertThat(turbulenceNoiseView.visibility).isEqualTo(View.INVISIBLE) + assertThat(loadingEffectView.visibility).isEqualTo(View.INVISIBLE) + } + + @Test + @EnableFlags(Flags.FLAG_SHADERLIB_LOADING_EFFECT_REFACTOR) + fun playTurbulenceNoise_newLoadingEffect_whenPlaybackStateIsNotPlaying_doesNotPlayTurbulence() { + val semanticActions = + MediaButton( + custom0 = + MediaAction( + icon = null, + action = {}, + contentDescription = "custom0", + background = null + ), + ) + val data = mediaData.copy(semanticActions = semanticActions) + player.attachPlayer(viewHolder) + player.bindPlayer(data, KEY) + + viewHolder.action0.callOnClick() + + assertThat(loadingEffectView.visibility).isEqualTo(View.INVISIBLE) + assertThat(turbulenceNoiseView.visibility).isEqualTo(View.INVISIBLE) } @Test |