Add Play/Pause Icon & Background Animations
Fixes: 220876706
Test: Manual using video in chrome
Change-Id: I3100ba553554e2c6fdd4c3a8ed6a4d8bebb18d0a
diff --git a/packages/SystemUI/res/drawable/ic_media_pause.xml b/packages/SystemUI/res/drawable/ic_media_pause.xml
index 1f4b2cf..0009b6c 100644
--- a/packages/SystemUI/res/drawable/ic_media_pause.xml
+++ b/packages/SystemUI/res/drawable/ic_media_pause.xml
@@ -1,26 +1,91 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2022 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
- -->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="14dp"
- android:height="16dp"
- android:viewportWidth="14"
- android:viewportHeight="16">
- <path
- android:pathData="M9.1818,15.6363H13.5455V0.3635H9.1818V15.6363ZM0.4546,15.6363H4.8182V0.3635H0.4546V15.6363Z"
- android:fillColor="#FFFFFF"
- android:fillType="evenOdd"/>
-</vector>
\ No newline at end of file
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2022 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
+-->
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:aapt="http://schemas.android.com/aapt">
+ <aapt:attr name="android:drawable">
+ <vector android:height="24dp"
+ android:width="24dp"
+ android:viewportHeight="24"
+ android:viewportWidth="24">
+ <group android:name="_R_G">
+ <group android:name="_R_G_L_1_G"
+ android:translateX="12"
+ android:translateY="12.125">
+ <path android:name="_R_G_L_1_G_D_0_P_0"
+ android:fillColor="#ffffff"
+ android:fillAlpha="1"
+ android:fillType="nonZero"
+ android:pathData=" M-6 7 C-6,7 -2,7 -2,7 C-2,7 -2,-7 -2,-7 C-2,-7 -6,-7 -6,-7 C-6,-7 -6,7 -6,7c "/>
+ </group>
+ <group android:name="_R_G_L_0_G"
+ android:translateX="12"
+ android:translateY="12.125">
+ <path android:name="_R_G_L_0_G_D_0_P_0"
+ android:fillColor="#ffffff"
+ android:fillAlpha="1"
+ android:fillType="nonZero"
+ android:pathData=" M2 7 C2,7 6,7 6,7 C6,7 6,-0.12 6,-0.12 C6,-0.12 6,-7 6,-7 C6,-7 2,-7 2,-7 C2,-7 2,7 2,7c "/>
+ </group>
+ </group>
+ <group android:name="time_group"/>
+ </vector>
+ </aapt:attr>
+ <target android:name="_R_G_L_1_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="pathData"
+ android:duration="333"
+ android:startOffset="0"
+ android:valueFrom="M-6 7 C-6,7 -2,7 -2,7 C-2,7 -2,-7 -2,-7 C-2,-7 -6,-7 -6,-7 C-6,-7 -6,7 -6,7c "
+ android:valueTo="M-6 7 C-6,7 -2,3.94 -2,3.94 C-2,3.94 -2,-4.37 -2,-4.37 C-2,-4.37 -6,-7 -6,-7 C-6,-7 -6,7 -6,7c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.2,0 0,1 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="pathData"
+ android:duration="333"
+ android:startOffset="0"
+ android:valueFrom="M2 7 C2,7 6,7 6,7 C6,7 6,-0.12 6,-0.12 C6,-0.12 6,-7 6,-7 C6,-7 2,-7 2,-7 C2,-7 2,7 2,7c "
+ android:valueTo="M-5.62 7 C-5.62,7 -4.75,7 -4.75,7 C-4.75,7 6,-0.12 6,-0.12 C6,-0.12 -4.44,-7 -4.44,-7 C-4.44,-7 -5.62,-7 -5.62,-7 C-5.62,-7 -5.62,7 -5.62,7c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.2,0 0,1 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="time_group">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="translateX"
+ android:duration="517"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:valueType="floatType"/>
+ </set>
+ </aapt:attr>
+ </target>
+</animated-vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_media_pause_container.xml b/packages/SystemUI/res/drawable/ic_media_pause_container.xml
new file mode 100644
index 0000000..b92e635
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_media_pause_container.xml
@@ -0,0 +1,73 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2022 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
+-->
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:aapt="http://schemas.android.com/aapt">
+ <aapt:attr name="android:drawable">
+ <vector android:height="48dp"
+ android:width="48dp"
+ android:viewportHeight="48"
+ android:viewportWidth="48">
+ <group android:name="_R_G">
+ <group android:name="_R_G_L_1_G"
+ android:translateX="24"
+ android:translateY="24"
+ android:scaleX="0.5"
+ android:scaleY="0.5"/>
+ <group android:name="_R_G_L_0_G"
+ android:translateX="24"
+ android:translateY="24"
+ android:scaleX="0.5"
+ android:scaleY="0.5">
+ <path android:name="_R_G_L_0_G_D_0_P_0"
+ android:fillColor="#ffddb3"
+ android:fillAlpha="1"
+ android:fillType="nonZero"
+ android:pathData=" M48 -16 C48,-16 48,16 48,16 C48,33.67 33.67,48 16,48 C16,48 -16,48 -16,48 C-33.67,48 -48,33.67 -48,16 C-48,16 -48,-16 -48,-16 C-48,-33.67 -33.67,-48 -16,-48 C-16,-48 16,-48 16,-48 C33.67,-48 48,-33.67 48,-16c "/>
+ </group>
+ </group>
+ <group android:name="time_group"/>
+ </vector>
+ </aapt:attr>
+ <target android:name="_R_G_L_0_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="pathData"
+ android:duration="500"
+ android:startOffset="0"
+ android:valueFrom="M48 -16 C48,-16 48,16 48,16 C48,33.67 33.67,48 16,48 C16,48 -16,48 -16,48 C-33.67,48 -48,33.67 -48,16 C-48,16 -48,-16 -48,-16 C-48,-33.67 -33.67,-48 -16,-48 C-16,-48 16,-48 16,-48 C33.67,-48 48,-33.67 48,-16c "
+ android:valueTo="M48 0.25 C48,0.25 48,0 48,0 C47.75,26 31.25,48 0,48 C0,48 0,48 0,48 C-30,48 -48,25.75 -48,-0.25 C-48,-0.25 -48,-0.25 -48,-0.25 C-47.75,-23.5 -32.25,-47.75 0.5,-48 C0.5,-48 0.5,-48 0.5,-48 C28,-47.75 47.75,-29.75 48,0.25c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.526,0 0,1 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="time_group">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="translateX"
+ android:duration="517"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:valueType="floatType"/>
+ </set>
+ </aapt:attr>
+ </target>
+</animated-vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_media_play.xml b/packages/SystemUI/res/drawable/ic_media_play.xml
index 0eac1ad..eb32470 100644
--- a/packages/SystemUI/res/drawable/ic_media_play.xml
+++ b/packages/SystemUI/res/drawable/ic_media_play.xml
@@ -1,26 +1,91 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2022 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
- -->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
- android:viewportWidth="24"
- android:viewportHeight="24">
- <path
- android:pathData="M20,12L6,21V3L20,12ZM15.26,12L8.55,7.68V16.32L15.26,12Z"
- android:fillColor="#FFFFFF"
- android:fillType="evenOdd"/>
-</vector>
\ No newline at end of file
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2022 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
+-->
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:aapt="http://schemas.android.com/aapt">
+ <aapt:attr name="android:drawable">
+ <vector android:height="24dp"
+ android:width="24dp"
+ android:viewportHeight="24"
+ android:viewportWidth="24">
+ <group android:name="_R_G">
+ <group android:name="_R_G_L_1_G"
+ android:translateX="12"
+ android:translateY="12.125">
+ <path android:name="_R_G_L_1_G_D_0_P_0"
+ android:fillColor="#ffffff"
+ android:fillAlpha="1"
+ android:fillType="nonZero"
+ android:pathData=" M-6 7 C-6,7 -2,3.94 -2,3.94 C-2,3.94 -2,-4.37 -2,-4.37 C-2,-4.37 -6,-7 -6,-7 C-6,-7 -6,7 -6,7c "/>
+ </group>
+ <group android:name="_R_G_L_0_G"
+ android:translateX="12"
+ android:translateY="12.125">
+ <path android:name="_R_G_L_0_G_D_0_P_0"
+ android:fillColor="#ffffff"
+ android:fillAlpha="1"
+ android:fillType="nonZero"
+ android:pathData=" M-5.62 7 C-5.62,7 -4.75,7 -4.75,7 C-4.75,7 6,-0.12 6,-0.12 C6,-0.12 -4.44,-7 -4.44,-7 C-4.44,-7 -5.62,-7 -5.62,-7 C-5.62,-7 -5.62,7 -5.62,7c "/>
+ </group>
+ </group>
+ <group android:name="time_group"/>
+ </vector>
+ </aapt:attr>
+ <target android:name="_R_G_L_1_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="pathData"
+ android:duration="333"
+ android:startOffset="0"
+ android:valueFrom="M-6 7 C-6,7 -2,3.94 -2,3.94 C-2,3.94 -2,-4.37 -2,-4.37 C-2,-4.37 -6,-7 -6,-7 C-6,-7 -6,7 -6,7c "
+ android:valueTo="M-6 7 C-6,7 -2,7 -2,7 C-2,7 -2,-7 -2,-7 C-2,-7 -6,-7 -6,-7 C-6,-7 -6,7 -6,7c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.2,0 0,1 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="pathData"
+ android:duration="333"
+ android:startOffset="0"
+ android:valueFrom="M-5.62 7 C-5.62,7 -4.75,7 -4.75,7 C-4.75,7 6,-0.12 6,-0.12 C6,-0.12 -4.44,-7 -4.44,-7 C-4.44,-7 -5.62,-7 -5.62,-7 C-5.62,-7 -5.62,7 -5.62,7c "
+ android:valueTo="M2 7 C2,7 6,7 6,7 C6,7 6,-0.12 6,-0.12 C6,-0.12 6,-7 6,-7 C6,-7 2,-7 2,-7 C2,-7 2,7 2,7c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.2,0 0,1 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="time_group">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="translateX"
+ android:duration="517"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:valueType="floatType"/>
+ </set>
+ </aapt:attr>
+ </target>
+</animated-vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_media_play_container.xml b/packages/SystemUI/res/drawable/ic_media_play_container.xml
new file mode 100644
index 0000000..2fc9fc8
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_media_play_container.xml
@@ -0,0 +1,68 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2022 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
+-->
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:aapt="http://schemas.android.com/aapt">
+ <aapt:attr name="android:drawable">
+ <vector android:height="48dp"
+ android:width="48dp"
+ android:viewportHeight="48"
+ android:viewportWidth="48">
+ <group android:name="_R_G">
+ <group android:name="_R_G_L_0_G"
+ android:translateX="24"
+ android:translateY="24"
+ android:scaleX="0.5"
+ android:scaleY="0.5">
+ <path android:name="_R_G_L_0_G_D_0_P_0"
+ android:fillColor="#ffddb3"
+ android:fillAlpha="1"
+ android:fillType="nonZero"
+ android:pathData=" M48 0.25 C48,0.25 48,0 48,0 C47.75,26 31.25,48 0,48 C0,48 0,48 0,48 C-30,48 -48,25.75 -48,-0.25 C-48,-0.25 -48,-0.25 -48,-0.25 C-47.75,-23.5 -32.25,-47.75 0.5,-48 C0.5,-48 0.5,-48 0.5,-48 C28,-47.75 47.75,-29.75 48,0.25c "/>
+ </group>
+ </group>
+ <group android:name="time_group"/>
+ </vector>
+ </aapt:attr>
+ <target android:name="_R_G_L_0_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="pathData"
+ android:duration="500"
+ android:startOffset="0"
+ android:valueFrom="M48 0.25 C48,0.25 48,0 48,0 C47.75,26 31.25,48 0,48 C0,48 0,48 0,48 C-30,48 -48,25.75 -48,-0.25 C-48,-0.25 -48,-0.25 -48,-0.25 C-47.75,-23.5 -32.25,-47.75 0.5,-48 C0.5,-48 0.5,-48 0.5,-48 C28,-47.75 47.75,-29.75 48,0.25c "
+ android:valueTo="M48 -16 C48,-16 48,16 48,16 C48,33.67 33.67,48 16,48 C16,48 -16,48 -16,48 C-33.67,48 -48,33.67 -48,16 C-48,16 -48,-16 -48,-16 C-48,-33.67 -33.67,-48 -16,-48 C-16,-48 16,-48 16,-48 C33.67,-48 48,-33.67 48,-16c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.518,0 0,1 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="time_group">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="translateX"
+ android:duration="517"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:valueType="floatType"/>
+ </set>
+ </aapt:attr>
+ </target>
+</animated-vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/media_session_view.xml b/packages/SystemUI/res/layout/media_session_view.xml
index 1117356..e1f3eca 100644
--- a/packages/SystemUI/res/layout/media_session_view.xml
+++ b/packages/SystemUI/res/layout/media_session_view.xml
@@ -145,8 +145,7 @@
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_marginStart="@dimen/qs_media_padding"
- android:layout_marginEnd="@dimen/qs_media_padding"
- />
+ android:layout_marginEnd="@dimen/qs_media_padding" />
<!-- See comment in media_session_collapsed.xml for how these barriers are used -->
<androidx.constraintlayout.widget.Barrier
@@ -184,7 +183,7 @@
<!-- Button visibility will be controlled in code -->
<ImageButton
android:id="@+id/actionPrev"
- style="@style/MediaPlayer.SessionAction"
+ style="@style/MediaPlayer.SessionAction.Secondary"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_marginStart="4dp"
@@ -211,7 +210,7 @@
<ImageButton
android:id="@+id/actionNext"
- style="@style/MediaPlayer.SessionAction"
+ style="@style/MediaPlayer.SessionAction.Secondary"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_marginStart="0dp"
@@ -221,7 +220,7 @@
<ImageButton
android:id="@+id/action0"
- style="@style/MediaPlayer.SessionAction"
+ style="@style/MediaPlayer.SessionAction.Secondary"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_marginStart="@dimen/qs_media_action_spacing"
@@ -231,7 +230,7 @@
<ImageButton
android:id="@+id/action1"
- style="@style/MediaPlayer.SessionAction"
+ style="@style/MediaPlayer.SessionAction.Secondary"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_marginStart="@dimen/qs_media_action_spacing"
@@ -241,7 +240,7 @@
<ImageButton
android:id="@+id/action2"
- style="@style/MediaPlayer.SessionAction"
+ style="@style/MediaPlayer.SessionAction.Secondary"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_marginStart="@dimen/qs_media_action_spacing"
@@ -251,7 +250,7 @@
<ImageButton
android:id="@+id/action3"
- style="@style/MediaPlayer.SessionAction"
+ style="@style/MediaPlayer.SessionAction.Secondary"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_marginStart="@dimen/qs_media_action_spacing"
@@ -261,7 +260,7 @@
<ImageButton
android:id="@+id/action4"
- style="@style/MediaPlayer.SessionAction"
+ style="@style/MediaPlayer.SessionAction.Secondary"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_marginStart="@dimen/qs_media_action_spacing"
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index a61eda8..4776587 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -616,7 +616,6 @@
parent="@android:style/Widget.Material.Button.Borderless.Small">
<item name="android:background">@drawable/qs_media_light_source</item>
<item name="android:tint">?android:attr/textColorPrimary</item>
- <item name="android:stateListAnimator">@anim/media_button_state_list_animator</item>
<item name="android:paddingTop">12dp</item>
<item name="android:paddingStart">12dp</item>
<item name="android:paddingEnd">12dp</item>
@@ -629,6 +628,10 @@
<item name="android:backgroundTint">@color/media_player_solid_button_bg</item>
</style>
+ <style name="MediaPlayer.SessionAction.Secondary" parent="MediaPlayer.SessionAction">
+ <item name="android:stateListAnimator">@anim/media_button_state_list_animator</item>
+ </style>
+
<style name="MediaPlayer.OutlineButton">
<item name="android:background">@drawable/qs_media_outline_button</item>
<item name="android:textColor">?android:attr/textColorPrimary</item>
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
index d2f953f..b8da46e 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
@@ -30,13 +30,14 @@
import android.graphics.ColorMatrix;
import android.graphics.ColorMatrixColorFilter;
import android.graphics.Rect;
+import android.graphics.drawable.Animatable;
+import android.graphics.drawable.Animatable2;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
import android.media.session.MediaController;
import android.media.session.MediaSession;
import android.media.session.PlaybackState;
import android.os.Process;
-import android.text.Layout;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
@@ -67,6 +68,7 @@
import com.android.systemui.util.time.SystemClock;
import java.net.URISyntaxException;
+import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executor;
@@ -637,9 +639,34 @@
private void setSemanticButton(final ImageButton button, MediaAction mediaAction,
ConstraintSet collapsedSet, ConstraintSet expandedSet, boolean showInCompact) {
+ AnimationBindHandler animHandler;
+ if (button.getTag() == null) {
+ animHandler = new AnimationBindHandler();
+ button.setTag(animHandler);
+ } else {
+ animHandler = (AnimationBindHandler) button.getTag();
+ }
+
+ animHandler.tryExecute(() -> {
+ bindSemanticButton(animHandler, button, mediaAction,
+ collapsedSet, expandedSet, showInCompact);
+ });
+ }
+
+ private void bindSemanticButton(final AnimationBindHandler animHandler,
+ final ImageButton button, MediaAction mediaAction, ConstraintSet collapsedSet,
+ ConstraintSet expandedSet, boolean showInCompact) {
+
+ animHandler.unregisterAll();
if (mediaAction != null) {
- button.setImageIcon(mediaAction.getIcon());
+ final Drawable icon = mediaAction.getIcon();
+ button.setImageDrawable(icon);
button.setContentDescription(mediaAction.getContentDescription());
+ final Drawable bgDrawable = mediaAction.getBackground();
+ button.setBackground(bgDrawable);
+
+ animHandler.tryRegister(icon);
+ animHandler.tryRegister(bgDrawable);
Runnable action = mediaAction.getAction();
if (action == null) {
@@ -651,19 +678,75 @@
logSmartspaceCardReported(SMARTSPACE_CARD_CLICK_EVENT,
/* isRecommendationCard */ false);
action.run();
+
+ if (icon instanceof Animatable) {
+ ((Animatable) icon).start();
+ }
+ if (bgDrawable instanceof Animatable) {
+ ((Animatable) bgDrawable).start();
+ }
}
});
}
} else {
- button.setImageIcon(null);
+ button.setImageDrawable(null);
button.setContentDescription(null);
button.setEnabled(false);
+ button.setBackground(mContext.getDrawable(R.drawable.qs_media_round_button_background));
}
setVisibleAndAlpha(collapsedSet, button.getId(), mediaAction != null && showInCompact);
setVisibleAndAlpha(expandedSet, button.getId(), mediaAction != null);
}
+ private static class AnimationBindHandler extends Animatable2.AnimationCallback {
+ private ArrayList<Runnable> mOnAnimationsComplete = new ArrayList<>();
+ private ArrayList<Animatable2> mRegistrations = new ArrayList<>();
+
+ public void tryRegister(Drawable drawable) {
+ if (drawable instanceof Animatable2) {
+ Animatable2 anim = (Animatable2) drawable;
+ anim.registerAnimationCallback(this);
+ mRegistrations.add(anim);
+ }
+ }
+
+ public void unregisterAll() {
+ for (Animatable2 anim : mRegistrations) {
+ anim.unregisterAnimationCallback(this);
+ }
+ mRegistrations.clear();
+ }
+
+ public boolean isAnimationRunning() {
+ for (Animatable2 anim : mRegistrations) {
+ if (anim.isRunning()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public void tryExecute(Runnable action) {
+ if (isAnimationRunning()) {
+ mOnAnimationsComplete.add(action);
+ } else {
+ action.run();
+ }
+ }
+
+ @Override
+ public void onAnimationEnd(Drawable drawable) {
+ super.onAnimationEnd(drawable);
+ if (!isAnimationRunning()) {
+ for (Runnable action : mOnAnimationsComplete) {
+ action.run();
+ }
+ mOnAnimationsComplete.clear();
+ }
+ }
+ }
+
@Nullable
private ActivityLaunchAnimator.Controller buildLaunchAnimatorController(
TransitionLayout player) {
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaData.kt b/packages/SystemUI/src/com/android/systemui/media/MediaData.kt
index f1712db..47a0991 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaData.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaData.kt
@@ -170,9 +170,10 @@
/** State of a media action. */
data class MediaAction(
- val icon: Icon?,
+ val icon: Drawable?,
val action: Runnable?,
- val contentDescription: CharSequence?
+ val contentDescription: CharSequence?,
+ val background: Drawable?
)
/** State of the media device. */
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
index f457ae7..5c36cab 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
@@ -695,11 +695,12 @@
Icon.createWithResource(sbn.packageName, action.getIcon()!!.getResId())
} else {
action.getIcon()
- }.setTint(themeText)
+ }.setTint(themeText).loadDrawable(context)
val mediaAction = MediaAction(
mediaActionIcon,
runnable,
- action.title)
+ action.title,
+ context.getDrawable(R.drawable.qs_media_round_button_background))
actionIcons.add(mediaAction)
}
}
@@ -789,30 +790,34 @@
return when (action) {
PlaybackState.ACTION_PLAY -> {
MediaAction(
- Icon.createWithResource(context, R.drawable.ic_media_play),
+ context.getDrawable(R.drawable.ic_media_play),
{ controller.transportControls.play() },
- context.getString(R.string.controls_media_button_play)
+ context.getString(R.string.controls_media_button_play),
+ context.getDrawable(R.drawable.ic_media_play_container)
)
}
PlaybackState.ACTION_PAUSE -> {
MediaAction(
- Icon.createWithResource(context, R.drawable.ic_media_pause),
+ context.getDrawable(R.drawable.ic_media_pause),
{ controller.transportControls.pause() },
- context.getString(R.string.controls_media_button_pause)
+ context.getString(R.string.controls_media_button_pause),
+ context.getDrawable(R.drawable.ic_media_pause_container)
)
}
PlaybackState.ACTION_SKIP_TO_PREVIOUS -> {
MediaAction(
- Icon.createWithResource(context, R.drawable.ic_media_prev),
+ context.getDrawable(R.drawable.ic_media_prev),
{ controller.transportControls.skipToPrevious() },
- context.getString(R.string.controls_media_button_prev)
+ context.getString(R.string.controls_media_button_prev),
+ context.getDrawable(R.drawable.qs_media_round_button_background)
)
}
PlaybackState.ACTION_SKIP_TO_NEXT -> {
MediaAction(
- Icon.createWithResource(context, R.drawable.ic_media_next),
+ context.getDrawable(R.drawable.ic_media_next),
{ controller.transportControls.skipToNext() },
- context.getString(R.string.controls_media_button_next)
+ context.getString(R.string.controls_media_button_next),
+ context.getDrawable(R.drawable.qs_media_round_button_background)
)
}
else -> null
@@ -835,9 +840,10 @@
val it = state.customActions[index]
return MediaAction(
- Icon.createWithResource(packageName, it.icon),
+ Icon.createWithResource(packageName, it.icon).loadDrawable(context),
{ controller.transportControls.sendCustomAction(it, it.extras) },
- it.name
+ it.name,
+ context.getDrawable(R.drawable.ic_media_pause_container)
)
}
@@ -900,9 +906,11 @@
private fun getResumeMediaAction(action: Runnable): MediaAction {
return MediaAction(
- Icon.createWithResource(context, R.drawable.ic_media_play).setTint(themeText),
+ Icon.createWithResource(context, R.drawable.ic_media_play)
+ .setTint(themeText).loadDrawable(context),
action,
- context.getString(R.string.controls_media_resume)
+ context.getString(R.string.controls_media_resume),
+ context.getDrawable(R.drawable.ic_media_play_container)
)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
index d0f2816..04609ad 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
@@ -19,8 +19,9 @@
import org.mockito.Mockito.`when` as whenever
import android.content.Intent
import android.graphics.Color
+import android.graphics.drawable.Animatable2
+import android.graphics.drawable.AnimatedVectorDrawable
import android.graphics.drawable.GradientDrawable
-import android.graphics.drawable.Icon
import android.graphics.drawable.RippleDrawable
import android.media.MediaMetadata
import android.media.session.MediaSession
@@ -60,6 +61,7 @@
import org.mockito.ArgumentCaptor
import org.mockito.ArgumentMatchers.anyLong
import org.mockito.Mock
+import org.mockito.Mockito.any
import org.mockito.Mockito.mock
import org.mockito.Mockito.never
import org.mockito.Mockito.times
@@ -290,15 +292,15 @@
@Test
fun bindSemanticActions() {
- val icon = Icon.createWithResource(context, android.R.drawable.ic_media_play)
+ val icon = context.getDrawable(android.R.drawable.ic_media_play)
+ val bg = context.getDrawable(R.drawable.qs_media_round_button_background)
val semanticActions = MediaButton(
- playOrPause = MediaAction(icon, Runnable {}, "play"),
- nextOrCustom = MediaAction(icon, Runnable {}, "next"),
- custom0 = MediaAction(icon, null, "custom 0"),
- custom1 = MediaAction(icon, null, "custom 1")
+ playOrPause = MediaAction(icon, Runnable {}, "play", bg),
+ nextOrCustom = MediaAction(icon, Runnable {}, "next", bg),
+ custom0 = MediaAction(icon, null, "custom 0", bg),
+ custom1 = MediaAction(icon, null, "custom 1", bg)
)
val state = mediaData.copy(semanticActions = semanticActions)
-
player.attachPlayer(viewHolder)
player.bindPlayer(state, PACKAGE)
@@ -338,10 +340,10 @@
fun bind_seekBarDisabled_seekBarVisibilityIsSetToInvisible() {
whenever(seekBarViewModel.getEnabled()).thenReturn(false)
- val icon = Icon.createWithResource(context, android.R.drawable.ic_media_play)
+ val icon = context.getDrawable(android.R.drawable.ic_media_play)
val semanticActions = MediaButton(
- playOrPause = MediaAction(icon, Runnable {}, "play"),
- nextOrCustom = MediaAction(icon, Runnable {}, "next")
+ playOrPause = MediaAction(icon, Runnable {}, "play", null),
+ nextOrCustom = MediaAction(icon, Runnable {}, "next", null)
)
val state = mediaData.copy(semanticActions = semanticActions)
@@ -365,13 +367,14 @@
@Test
fun bindNotificationActions() {
- val icon = Icon.createWithResource(context, android.R.drawable.ic_media_play)
+ val icon = context.getDrawable(android.R.drawable.ic_media_play)
+ val bg = context.getDrawable(R.drawable.qs_media_round_button_background)
val actions = listOf(
- MediaAction(icon, Runnable {}, "previous"),
- MediaAction(icon, Runnable {}, "play"),
- MediaAction(icon, null, "next"),
- MediaAction(icon, null, "custom 0"),
- MediaAction(icon, Runnable {}, "custom 1")
+ MediaAction(icon, Runnable {}, "previous", bg),
+ MediaAction(icon, Runnable {}, "play", bg),
+ MediaAction(icon, null, "next", bg),
+ MediaAction(icon, null, "custom 0", bg),
+ MediaAction(icon, Runnable {}, "custom 1", bg)
)
val state = mediaData.copy(actions = actions,
actionsToShowInCompact = listOf(1, 2),
@@ -413,6 +416,72 @@
}
@Test
+ fun bindAnimatedSemanticActions() {
+ val mockAvd0 = mock(AnimatedVectorDrawable::class.java)
+ val mockAvd1 = mock(AnimatedVectorDrawable::class.java)
+ val mockAvd2 = mock(AnimatedVectorDrawable::class.java)
+ whenever(mockAvd0.mutate()).thenReturn(mockAvd0)
+ whenever(mockAvd1.mutate()).thenReturn(mockAvd1)
+ whenever(mockAvd2.mutate()).thenReturn(mockAvd2)
+
+ val icon = context.getDrawable(R.drawable.ic_media_play)
+ val bg = context.getDrawable(R.drawable.ic_media_play_container)
+ val semanticActions0 = MediaButton(
+ playOrPause = MediaAction(mockAvd0, Runnable {}, "play", null))
+ val semanticActions1 = MediaButton(
+ playOrPause = MediaAction(mockAvd1, Runnable {}, "pause", null))
+ val semanticActions2 = MediaButton(
+ playOrPause = MediaAction(mockAvd2, Runnable {}, "loading", null))
+ val state0 = mediaData.copy(semanticActions = semanticActions0)
+ val state1 = mediaData.copy(semanticActions = semanticActions1)
+ val state2 = mediaData.copy(semanticActions = semanticActions2)
+
+ player.attachPlayer(viewHolder)
+ player.bindPlayer(state0, PACKAGE)
+
+ // Validate first binding
+ assertThat(actionPlayPause.isEnabled()).isTrue()
+ assertThat(actionPlayPause.contentDescription).isEqualTo("play")
+ verify(collapsedSet).setVisibility(R.id.actionPlayPause, ConstraintSet.VISIBLE)
+ assertThat(actionPlayPause.hasOnClickListeners()).isTrue()
+
+ // Trigger animation & update mock
+ actionPlayPause.performClick()
+ verify(mockAvd0, times(1)).start()
+ whenever(mockAvd0.isRunning()).thenReturn(true)
+
+ // Validate states no longer bind
+ player.bindPlayer(state1, PACKAGE)
+ player.bindPlayer(state2, PACKAGE)
+ assertThat(actionPlayPause.contentDescription).isEqualTo("play")
+
+ // Complete animation and run callbacks
+ whenever(mockAvd0.isRunning()).thenReturn(false)
+ val captor = ArgumentCaptor.forClass(Animatable2.AnimationCallback::class.java)
+ verify(mockAvd0, times(1)).registerAnimationCallback(captor.capture())
+ verify(mockAvd1, never())
+ .registerAnimationCallback(any(Animatable2.AnimationCallback::class.java))
+ verify(mockAvd2, never())
+ .registerAnimationCallback(any(Animatable2.AnimationCallback::class.java))
+ captor.getValue().onAnimationEnd(mockAvd0)
+
+ // Validate correct state was bound
+ assertThat(actionPlayPause.contentDescription).isEqualTo("loading")
+ verify(mockAvd0, times(1))
+ .registerAnimationCallback(any(Animatable2.AnimationCallback::class.java))
+ verify(mockAvd1, times(1)
+ ).registerAnimationCallback(any(Animatable2.AnimationCallback::class.java))
+ verify(mockAvd2, times(1))
+ .registerAnimationCallback(any(Animatable2.AnimationCallback::class.java))
+ verify(mockAvd0, times(1))
+ .unregisterAnimationCallback(any(Animatable2.AnimationCallback::class.java))
+ verify(mockAvd1, times(1))
+ .unregisterAnimationCallback(any(Animatable2.AnimationCallback::class.java))
+ verify(mockAvd2, never())
+ .unregisterAnimationCallback(any(Animatable2.AnimationCallback::class.java))
+ }
+
+ @Test
fun bindText() {
player.attachPlayer(viewHolder)
player.bindPlayer(mediaData, PACKAGE)