Remove old layout and flag
Since the new layout is complete and can handle notification action buttons, we can remove the old one now. - Deleted layout files (xml and PlayerViewHolder), and moved PlayerSessionViewHolder into MediaViewHolder - Removed the flag MEDIA_SESSION_LAYOUT - Refactored MediaControlPanel to be more readable Bug: 218875984 Test: atest com.android.systemui.media Test: atest QSPanelControllerTest QuickQSPanelControllerTest Change-Id: I37681542dec5cf10ec7346aa9f888f8f890d2c11
This commit is contained in:
@@ -41,7 +41,7 @@ Files under [`systemui/media/`](/packages/SystemUI/src/com/android/systemui/medi
|
||||
* SeekBarViewModel.kt
|
||||
* Implements its own `computePosition()` for the seekbar (to avoid continually polling the `PlaybackState`, which involves binder calls)
|
||||
* Does some touch falsing (ignore flings, require drags to start near the thumb - otherwise users would often accidentally trigger the seekbar when they meant to move the carousel or shade)
|
||||
* PlayerViewHolder.kt
|
||||
* MediaViewHolder.kt
|
||||
* Holds references to the UI elements in the panel
|
||||
* Animation support:
|
||||
* MediaHierarchyManager.kt
|
||||
|
||||
@@ -1,300 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (C) 2019 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
|
||||
-->
|
||||
|
||||
<!-- Layout for media controls inside QSPanel carousel -->
|
||||
<com.android.systemui.util.animation.TransitionLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/qs_media_controls"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:clipChildren="false"
|
||||
android:clipToPadding="false"
|
||||
android:gravity="center_horizontal|fill_vertical"
|
||||
android:forceHasOverlappingRendering="false"
|
||||
android:background="@drawable/qs_media_background"
|
||||
android:theme="@style/MediaPlayer">
|
||||
|
||||
<androidx.constraintlayout.widget.Guideline
|
||||
android:id="@+id/center_vertical_guideline"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
app:layout_constraintGuide_percent="0.6" />
|
||||
|
||||
<androidx.constraintlayout.widget.Guideline
|
||||
android:id="@+id/center_horizontal_guideline"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
app:layout_constraintGuide_begin="48dp" />
|
||||
|
||||
<!-- As per Material Design on Biderectionality, this is forced to LTR in code -->
|
||||
<FrameLayout
|
||||
android:id="@+id/notification_media_progress_time"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:forceHasOverlappingRendering="false">
|
||||
<!-- width is set to "match_parent" to avoid extra layout calls -->
|
||||
<TextView
|
||||
android:id="@+id/media_elapsed_time"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentStart="true"
|
||||
android:fontFamily="@*android:string/config_bodyFontFamily"
|
||||
android:gravity="start"
|
||||
android:textSize="12sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/media_total_time"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:fontFamily="@*android:string/config_bodyFontFamily"
|
||||
android:gravity="end"
|
||||
android:textSize="12sp" />
|
||||
</FrameLayout>
|
||||
|
||||
<!-- Actions must be ordered left-to-right even in RTL layout. However, they appear in a chain
|
||||
with the artist name, and must as a group appear at the end of that chain. This is
|
||||
accomplished by having all actions appear in a LTR chain within the parent, and then biasing it
|
||||
to the right side, then this barrier is used to bound the text views. -->
|
||||
<androidx.constraintlayout.widget.Barrier
|
||||
android:id="@+id/media_action_barrier"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:orientation="vertical"
|
||||
app:layout_constraintTop_toBottomOf="@id/header_title"
|
||||
app:barrierDirection="start"
|
||||
app:constraint_referenced_ids="action0,action1,action2,action3,action4"
|
||||
/>
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/action0"
|
||||
style="@style/MediaPlayer.Action"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/action1"
|
||||
style="@style/MediaPlayer.Action"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/action2"
|
||||
style="@style/MediaPlayer.Action"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/action3"
|
||||
style="@style/MediaPlayer.Action"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/action4"
|
||||
style="@style/MediaPlayer.Action"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp" />
|
||||
|
||||
<!-- Album Art -->
|
||||
<ImageView
|
||||
android:id="@+id/album_art"
|
||||
android:layout_width="@dimen/qs_media_album_size"
|
||||
android:layout_height="@dimen/qs_media_album_size"
|
||||
android:layout_gravity="center_vertical"
|
||||
style="@style/MediaPlayer.Album"
|
||||
android:background="@drawable/qs_media_art_background"
|
||||
android:clipToOutline="true" />
|
||||
|
||||
<!-- Seamless Output Switcher -->
|
||||
<LinearLayout
|
||||
android:id="@+id/media_seamless"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="48dp"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="top|end"
|
||||
android:paddingTop="@dimen/qs_media_padding"
|
||||
android:paddingEnd="@dimen/qs_media_padding"
|
||||
android:background="@drawable/qs_media_light_source"
|
||||
android:forceHasOverlappingRendering="false">
|
||||
<LinearLayout
|
||||
android:id="@+id/media_seamless_button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:minHeight="@dimen/qs_seamless_height"
|
||||
android:theme="@style/MediaPlayer.SolidButton"
|
||||
android:background="@drawable/qs_media_seamless_background"
|
||||
android:orientation="horizontal"
|
||||
android:contentDescription="@string/quick_settings_media_device_label">
|
||||
<ImageView
|
||||
android:id="@+id/media_seamless_image"
|
||||
android:layout_width="@dimen/qs_seamless_icon_size"
|
||||
android:layout_height="@dimen/qs_seamless_icon_size"
|
||||
android:layout_gravity="center"
|
||||
android:tint="?android:attr/textColorPrimary"
|
||||
android:src="@*android:drawable/ic_media_seamless" />
|
||||
<TextView
|
||||
android:id="@+id/media_seamless_text"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginStart="4dp"
|
||||
android:fontFamily="@*android:string/config_headlineFontFamilyMedium"
|
||||
android:singleLine="true"
|
||||
android:text="@*android:string/ext_media_seamless_action"
|
||||
android:textDirection="locale"
|
||||
android:textSize="12sp"
|
||||
android:lineHeight="16sp" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
<!-- Seek Bar -->
|
||||
<!-- As per Material Design on Biderectionality, this is forced to LTR in code -->
|
||||
<SeekBar
|
||||
android:id="@+id/media_progress_bar"
|
||||
style="@style/MediaPlayer.ProgressBar"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:maxHeight="@dimen/qs_media_enabled_seekbar_height"
|
||||
android:paddingTop="@dimen/qs_media_enabled_seekbar_vertical_padding"
|
||||
android:layout_marginTop="-22dp"
|
||||
android:paddingBottom="2dp"
|
||||
android:splitTrack="false" />
|
||||
|
||||
<!-- Song name -->
|
||||
<TextView
|
||||
android:id="@+id/header_title"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="@*android:string/config_headlineFontFamilyMedium"
|
||||
android:singleLine="true"
|
||||
android:textSize="16sp" />
|
||||
|
||||
<!-- Artist name -->
|
||||
<TextView
|
||||
android:id="@+id/header_artist"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="@*android:string/config_headlineFontFamily"
|
||||
android:singleLine="true"
|
||||
style="@style/MediaPlayer.Subtitle"
|
||||
android:textSize="14sp" />
|
||||
|
||||
<com.android.internal.widget.CachingIconView
|
||||
android:id="@+id/icon"
|
||||
style="@style/MediaPlayer.AppIcon"
|
||||
android:layout_width="@dimen/qs_media_icon_size"
|
||||
android:layout_height="@dimen/qs_media_icon_size" />
|
||||
|
||||
<!-- Long press menu -->
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/qs_media_padding"
|
||||
android:layout_marginStart="@dimen/qs_media_padding"
|
||||
android:layout_marginEnd="@dimen/qs_media_padding"
|
||||
android:id="@+id/remove_text"
|
||||
android:fontFamily="@*android:string/config_headlineFontFamily"
|
||||
android:singleLine="true"
|
||||
android:ellipsize="marquee"
|
||||
android:marqueeRepeatLimit="marquee_forever"
|
||||
android:text="@string/controls_media_close_session"
|
||||
android:gravity="center_horizontal|top"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintBottom_toTopOf="@id/cancel"/>
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/settings"
|
||||
android:background="@drawable/qs_media_light_source"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/qs_media_padding"
|
||||
android:layout_marginEnd="@dimen/qs_media_action_spacing"
|
||||
android:layout_marginBottom="@dimen/qs_media_padding"
|
||||
app:layout_constrainedWidth="true"
|
||||
app:layout_constraintWidth_min="48dp"
|
||||
app:layout_constraintHeight_min="48dp"
|
||||
app:layout_constraintHorizontal_chainStyle="spread_inside"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@id/cancel"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/remove_text">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/settings_text"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center|bottom"
|
||||
style="@style/MediaPlayer.OutlineButton"
|
||||
android:text="@string/controls_media_settings_button" />
|
||||
</FrameLayout>
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/cancel"
|
||||
android:background="@drawable/qs_media_light_source"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/qs_media_action_spacing"
|
||||
android:layout_marginEnd="@dimen/qs_media_action_spacing"
|
||||
android:layout_marginBottom="@dimen/qs_media_padding"
|
||||
app:layout_constrainedWidth="true"
|
||||
app:layout_constraintWidth_min="48dp"
|
||||
app:layout_constraintHeight_min="48dp"
|
||||
app:layout_constraintStart_toEndOf="@id/settings"
|
||||
app:layout_constraintEnd_toStartOf="@id/dismiss"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/remove_text">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/cancel_text"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center|bottom"
|
||||
style="@style/MediaPlayer.OutlineButton"
|
||||
android:text="@string/cancel" />
|
||||
</FrameLayout>
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/dismiss"
|
||||
android:background="@drawable/qs_media_light_source"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/qs_media_action_spacing"
|
||||
android:layout_marginEnd="@dimen/qs_media_padding"
|
||||
android:layout_marginBottom="@dimen/qs_media_padding"
|
||||
app:layout_constrainedWidth="true"
|
||||
app:layout_constraintWidth_min="48dp"
|
||||
app:layout_constraintHeight_min="48dp"
|
||||
app:layout_constraintStart_toEndOf="@id/cancel"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/remove_text">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/dismiss_text"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center|bottom"
|
||||
style="@style/MediaPlayer.OutlineButton"
|
||||
android:text="@string/controls_media_dismiss_button"
|
||||
/>
|
||||
</FrameLayout>
|
||||
</com.android.systemui.util.animation.TransitionLayout>
|
||||
@@ -959,10 +959,7 @@
|
||||
|
||||
<!-- Size of media cards in the QSPanel carousel -->
|
||||
<dimen name="qs_media_padding">16dp</dimen>
|
||||
<dimen name="qs_media_album_size_small">72dp</dimen>
|
||||
<dimen name="qs_media_album_size">84dp</dimen>
|
||||
<dimen name="qs_media_album_radius">14dp</dimen>
|
||||
<dimen name="qs_media_album_device_padding">26dp</dimen>
|
||||
<dimen name="qs_media_info_margin">12dp</dimen>
|
||||
<dimen name="qs_media_info_spacing">8dp</dimen>
|
||||
<dimen name="qs_media_icon_size">20dp</dimen>
|
||||
@@ -974,10 +971,7 @@
|
||||
<dimen name="qs_seamless_icon_size">12dp</dimen>
|
||||
<dimen name="qs_media_disabled_seekbar_height">1dp</dimen>
|
||||
<dimen name="qs_media_enabled_seekbar_height">2dp</dimen>
|
||||
<dimen name="qs_media_enabled_seekbar_vertical_padding">28dp</dimen>
|
||||
<dimen name="qs_media_disabled_seekbar_vertical_padding">29dp</dimen>
|
||||
|
||||
<!-- Sizes for alternate session-based layout -->
|
||||
<dimen name="qs_media_session_enabled_seekbar_vertical_padding">15dp</dimen>
|
||||
<dimen name="qs_media_session_disabled_seekbar_vertical_padding">16dp</dimen>
|
||||
<dimen name="qs_media_session_height_expanded">184dp</dimen>
|
||||
|
||||
@@ -1,176 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (C) 2020 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
|
||||
-->
|
||||
<ConstraintSet
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
<Constraint
|
||||
android:id="@+id/icon"
|
||||
android:layout_width="@dimen/qs_media_icon_size"
|
||||
android:layout_height="@dimen/qs_media_icon_size"
|
||||
android:translationY="@dimen/qs_media_icon_offset"
|
||||
android:translationX="@dimen/qs_media_icon_offset"
|
||||
app:layout_constraintEnd_toEndOf="@id/album_art"
|
||||
app:layout_constraintBottom_toBottomOf="@id/album_art"
|
||||
/>
|
||||
|
||||
<Constraint
|
||||
android:id="@+id/media_seamless"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="48dp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/center_vertical_guideline"
|
||||
app:layout_constraintBottom_toTopOf="@id/center_horizontal_guideline"
|
||||
app:layout_constraintHorizontal_chainStyle="spread_inside"
|
||||
app:layout_constraintHorizontal_bias="1"
|
||||
app:layout_constrainedWidth="true"
|
||||
app:layout_constraintWidth_min="48dp"
|
||||
app:layout_constraintHeight_min="48dp"
|
||||
android:layout_marginStart="@dimen/qs_center_guideline_padding"
|
||||
/>
|
||||
|
||||
<Constraint
|
||||
android:id="@+id/album_art"
|
||||
android:layout_width="@dimen/qs_media_album_size_small"
|
||||
android:layout_height="@dimen/qs_media_album_size_small"
|
||||
android:layout_marginTop="@dimen/qs_media_padding"
|
||||
android:layout_marginStart="@dimen/qs_media_padding"
|
||||
android:layout_marginBottom="@dimen/qs_media_padding"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
/>
|
||||
|
||||
<!-- Song name -->
|
||||
<Constraint
|
||||
android:id="@+id/header_title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/qs_media_info_margin"
|
||||
android:layout_marginEnd="@dimen/qs_center_guideline_padding"
|
||||
app:layout_constrainedWidth="true"
|
||||
app:layout_constraintBottom_toTopOf="@id/center_horizontal_guideline"
|
||||
app:layout_constraintStart_toEndOf="@id/album_art"
|
||||
app:layout_constraintEnd_toStartOf="@id/center_vertical_guideline"
|
||||
app:layout_constraintHorizontal_bias="0"/>
|
||||
|
||||
<!-- Artist name -->
|
||||
<Constraint
|
||||
android:id="@+id/header_artist"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constrainedWidth="true"
|
||||
android:layout_marginTop="@dimen/qs_media_info_spacing"
|
||||
app:layout_constraintTop_toBottomOf="@id/center_horizontal_guideline"
|
||||
app:layout_constraintStart_toStartOf="@id/header_title"
|
||||
app:layout_constraintEnd_toStartOf="@id/media_action_barrier"
|
||||
app:layout_constraintHorizontal_bias="0"/>
|
||||
|
||||
<!-- Seek Bar -->
|
||||
<Constraint
|
||||
android:id="@+id/media_progress_bar"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:alpha="0.0"
|
||||
app:layout_constraintTop_toBottomOf="@id/album_art"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
android:visibility="gone"
|
||||
/>
|
||||
|
||||
<Constraint
|
||||
android:id="@+id/notification_media_progress_time"
|
||||
android:alpha="0.0"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintTop_toBottomOf="@id/album_art"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
android:visibility="gone"
|
||||
/>
|
||||
|
||||
<Constraint
|
||||
android:id="@+id/action0"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:layout_marginStart="@dimen/qs_media_padding"
|
||||
android:layout_marginEnd="@dimen/qs_media_action_spacing"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintHorizontal_chainStyle="packed"
|
||||
app:layout_constraintTop_toBottomOf="@id/center_horizontal_guideline"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintRight_toLeftOf="@id/action1"
|
||||
app:layout_constraintHorizontal_bias="1"
|
||||
>
|
||||
</Constraint>
|
||||
|
||||
<Constraint
|
||||
android:id="@+id/action1"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:layout_marginStart="@dimen/qs_media_action_spacing"
|
||||
android:layout_marginEnd="@dimen/qs_media_action_spacing"
|
||||
app:layout_constraintTop_toBottomOf="@id/center_horizontal_guideline"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintLeft_toRightOf="@id/action0"
|
||||
app:layout_constraintRight_toLeftOf="@id/action2"
|
||||
>
|
||||
</Constraint>
|
||||
|
||||
<Constraint
|
||||
android:id="@+id/action2"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:layout_marginStart="@dimen/qs_media_action_spacing"
|
||||
android:layout_marginEnd="@dimen/qs_media_action_spacing"
|
||||
app:layout_constraintTop_toBottomOf="@id/center_horizontal_guideline"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintLeft_toRightOf="@id/action1"
|
||||
app:layout_constraintRight_toLeftOf="@id/action3"
|
||||
>
|
||||
</Constraint>
|
||||
|
||||
<Constraint
|
||||
android:id="@+id/action3"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:layout_marginStart="@dimen/qs_media_action_spacing"
|
||||
android:layout_marginEnd="@dimen/qs_media_action_spacing"
|
||||
app:layout_constraintTop_toBottomOf="@id/center_horizontal_guideline"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintLeft_toRightOf="@id/action2"
|
||||
app:layout_constraintRight_toLeftOf="@id/action4"
|
||||
>
|
||||
</Constraint>
|
||||
|
||||
<Constraint
|
||||
android:id="@+id/action4"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:layout_marginStart="@dimen/qs_media_action_spacing"
|
||||
android:layout_marginEnd="@dimen/qs_media_padding"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintHorizontal_chainStyle="packed"
|
||||
app:layout_constraintTop_toBottomOf="@id/center_horizontal_guideline"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintLeft_toRightOf="@id/action3"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0"
|
||||
>
|
||||
</Constraint>
|
||||
</ConstraintSet>
|
||||
@@ -1,174 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (C) 2020 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
|
||||
-->
|
||||
<ConstraintSet
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
<Constraint
|
||||
android:id="@+id/icon"
|
||||
android:layout_width="@dimen/qs_media_icon_size"
|
||||
android:layout_height="@dimen/qs_media_icon_size"
|
||||
android:translationY="@dimen/qs_media_icon_offset"
|
||||
android:translationX="@dimen/qs_media_icon_offset"
|
||||
app:layout_constraintEnd_toEndOf="@id/album_art"
|
||||
app:layout_constraintBottom_toBottomOf="@id/album_art"
|
||||
/>
|
||||
|
||||
<Constraint
|
||||
android:id="@+id/media_seamless"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="48dp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/center_vertical_guideline"
|
||||
app:layout_constraintHorizontal_chainStyle="spread_inside"
|
||||
app:layout_constraintHorizontal_bias="1"
|
||||
app:layout_constrainedWidth="true"
|
||||
app:layout_constraintWidth_min="48dp"
|
||||
app:layout_constraintHeight_min="48dp"
|
||||
android:paddingTop="@dimen/qs_media_padding"
|
||||
android:paddingEnd="@dimen/qs_media_padding"
|
||||
android:layout_marginStart="@dimen/qs_center_guideline_padding"
|
||||
android:layout_marginBottom="4dp" />
|
||||
|
||||
<Constraint
|
||||
android:id="@+id/album_art"
|
||||
android:layout_width="@dimen/qs_media_album_size"
|
||||
android:layout_height="@dimen/qs_media_album_size"
|
||||
android:layout_marginTop="@dimen/qs_media_padding"
|
||||
android:layout_marginStart="@dimen/qs_media_padding"
|
||||
android:layout_marginBottom="0dp"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
/>
|
||||
|
||||
<!-- Song name -->
|
||||
<Constraint
|
||||
android:id="@+id/header_title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="26dp"
|
||||
android:layout_marginStart="@dimen/qs_media_info_margin"
|
||||
android:layout_marginEnd="@dimen/qs_media_padding"
|
||||
app:layout_constrainedWidth="true"
|
||||
app:layout_constraintTop_toTopOf="@+id/album_art"
|
||||
app:layout_constraintStart_toEndOf="@id/album_art"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0"/>
|
||||
|
||||
<!-- Artist name -->
|
||||
<Constraint
|
||||
android:id="@+id/header_artist"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="@dimen/qs_media_padding"
|
||||
android:layout_marginBottom="@dimen/qs_media_info_margin"
|
||||
app:layout_constrainedWidth="true"
|
||||
android:layout_marginTop="1dp"
|
||||
app:layout_constraintTop_toBottomOf="@id/header_title"
|
||||
app:layout_constraintStart_toStartOf="@id/header_title"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0"/>
|
||||
|
||||
<!-- Seek Bar -->
|
||||
<Constraint
|
||||
android:id="@+id/media_progress_bar"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="34dp"
|
||||
app:layout_constraintTop_toBottomOf="@id/center_horizontal_guideline"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
/>
|
||||
|
||||
<Constraint
|
||||
android:id="@+id/notification_media_progress_time"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="@dimen/qs_media_padding"
|
||||
android:layout_marginStart="@dimen/qs_media_padding"
|
||||
app:layout_constraintTop_toBottomOf="@id/media_progress_bar"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
/>
|
||||
|
||||
<Constraint
|
||||
android:id="@+id/action0"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:layout_marginStart="@dimen/qs_media_padding"
|
||||
android:layout_marginEnd="@dimen/qs_media_action_spacing"
|
||||
android:layout_marginBottom="@dimen/qs_media_action_margin"
|
||||
app:layout_constraintHorizontal_chainStyle="packed"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintRight_toLeftOf="@id/action1"
|
||||
app:layout_constraintTop_toBottomOf="@id/media_progress_bar"
|
||||
app:layout_constraintBottom_toBottomOf="parent">
|
||||
</Constraint>
|
||||
|
||||
<Constraint
|
||||
android:id="@+id/action1"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:layout_marginStart="@dimen/qs_media_action_spacing"
|
||||
android:layout_marginEnd="@dimen/qs_media_action_spacing"
|
||||
android:layout_marginBottom="@dimen/qs_media_action_margin"
|
||||
app:layout_constraintLeft_toRightOf="@id/action0"
|
||||
app:layout_constraintRight_toLeftOf="@id/action2"
|
||||
app:layout_constraintTop_toBottomOf="@id/media_progress_bar"
|
||||
app:layout_constraintBottom_toBottomOf="parent">
|
||||
</Constraint>
|
||||
|
||||
<Constraint
|
||||
android:id="@+id/action2"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:layout_marginStart="@dimen/qs_media_action_spacing"
|
||||
android:layout_marginEnd="@dimen/qs_media_action_spacing"
|
||||
android:layout_marginBottom="@dimen/qs_media_action_margin"
|
||||
app:layout_constraintLeft_toRightOf="@id/action1"
|
||||
app:layout_constraintRight_toLeftOf="@id/action3"
|
||||
app:layout_constraintTop_toBottomOf="@id/media_progress_bar"
|
||||
app:layout_constraintBottom_toBottomOf="parent">
|
||||
</Constraint>
|
||||
|
||||
<Constraint
|
||||
android:id="@+id/action3"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:layout_marginStart="@dimen/qs_media_action_spacing"
|
||||
android:layout_marginEnd="@dimen/qs_media_action_spacing"
|
||||
android:layout_marginBottom="@dimen/qs_media_action_margin"
|
||||
app:layout_constraintLeft_toRightOf="@id/action2"
|
||||
app:layout_constraintRight_toLeftOf="@id/action4"
|
||||
app:layout_constraintTop_toBottomOf="@id/media_progress_bar"
|
||||
app:layout_constraintBottom_toBottomOf="parent">
|
||||
</Constraint>
|
||||
|
||||
<Constraint
|
||||
android:id="@+id/action4"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:layout_marginStart="@dimen/qs_media_action_spacing"
|
||||
android:layout_marginEnd="@dimen/qs_media_padding"
|
||||
android:layout_marginBottom="@dimen/qs_media_action_margin"
|
||||
app:layout_constraintHorizontal_chainStyle="packed"
|
||||
app:layout_constraintLeft_toRightOf="@id/action3"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/media_progress_bar"
|
||||
app:layout_constraintBottom_toBottomOf="parent">
|
||||
</Constraint>
|
||||
</ConstraintSet>
|
||||
@@ -146,7 +146,6 @@ public class Flags {
|
||||
// 900 - media
|
||||
public static final BooleanFlag MEDIA_TAP_TO_TRANSFER = new BooleanFlag(900, true);
|
||||
public static final BooleanFlag MEDIA_SESSION_ACTIONS = new BooleanFlag(901, false);
|
||||
public static final BooleanFlag MEDIA_SESSION_LAYOUT = new BooleanFlag(902, true);
|
||||
public static final BooleanFlag MEDIA_NEARBY_DEVICES = new BooleanFlag(903, true);
|
||||
public static final BooleanFlag MEDIA_MUTE_AWAIT = new BooleanFlag(904, true);
|
||||
|
||||
|
||||
@@ -45,8 +45,7 @@ class KeyguardMediaController @Inject constructor(
|
||||
private val statusBarStateController: SysuiStatusBarStateController,
|
||||
private val notifLockscreenUserManager: NotificationLockscreenUserManager,
|
||||
private val context: Context,
|
||||
configurationController: ConfigurationController,
|
||||
private val mediaFlags: MediaFlags
|
||||
configurationController: ConfigurationController
|
||||
) {
|
||||
|
||||
init {
|
||||
@@ -62,11 +61,7 @@ class KeyguardMediaController @Inject constructor(
|
||||
})
|
||||
|
||||
// First let's set the desired state that we want for this host
|
||||
mediaHost.expansion = if (mediaFlags.useMediaSessionLayout()) {
|
||||
MediaHostState.EXPANDED
|
||||
} else {
|
||||
MediaHostState.COLLAPSED
|
||||
}
|
||||
mediaHost.expansion = MediaHostState.EXPANDED
|
||||
mediaHost.showsOnlyActiveMedia = true
|
||||
mediaHost.falsingProtectionNeeded = true
|
||||
|
||||
|
||||
@@ -419,15 +419,8 @@ class MediaCarouselController @Inject constructor(
|
||||
.elementAtOrNull(mediaCarouselScrollHandler.visibleMediaIndex)
|
||||
if (existingPlayer == null) {
|
||||
var newPlayer = mediaControlPanelFactory.get()
|
||||
if (mediaFlags.useMediaSessionLayout()) {
|
||||
newPlayer.attachPlayer(
|
||||
PlayerSessionViewHolder.create(LayoutInflater.from(context), mediaContent),
|
||||
MediaViewController.TYPE.PLAYER_SESSION)
|
||||
} else {
|
||||
newPlayer.attachPlayer(
|
||||
PlayerViewHolder.create(LayoutInflater.from(context), mediaContent),
|
||||
MediaViewController.TYPE.PLAYER)
|
||||
}
|
||||
newPlayer.attachPlayer(MediaViewHolder.create(
|
||||
LayoutInflater.from(context), mediaContent))
|
||||
newPlayer.mediaViewController.sizeChangedListener = this::updateCarouselDimensions
|
||||
val lp = LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
|
||||
ViewGroup.LayoutParams.WRAP_CONTENT)
|
||||
@@ -544,11 +537,7 @@ class MediaCarouselController @Inject constructor(
|
||||
}
|
||||
|
||||
private fun getForegroundColor(): Int {
|
||||
return if (mediaFlags.useMediaSessionLayout()) {
|
||||
context.getColor(R.color.material_dynamic_neutral_variant80)
|
||||
} else {
|
||||
context.getColor(R.color.material_dynamic_secondary10)
|
||||
}
|
||||
return context.getColor(R.color.material_dynamic_neutral_variant80)
|
||||
}
|
||||
|
||||
private fun updatePageIndicator() {
|
||||
|
||||
@@ -65,7 +65,6 @@ import com.android.systemui.util.animation.TransitionLayout;
|
||||
import com.android.systemui.util.time.SystemClock;
|
||||
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
@@ -96,22 +95,22 @@ public class MediaControlPanel {
|
||||
|
||||
private static final Intent SETTINGS_INTENT = new Intent(ACTION_MEDIA_CONTROLS_SETTINGS);
|
||||
|
||||
// Button IDs for QS controls
|
||||
static final int[] ACTION_IDS = {
|
||||
R.id.action0,
|
||||
R.id.action1,
|
||||
R.id.action2,
|
||||
R.id.action3,
|
||||
R.id.action4
|
||||
};
|
||||
|
||||
// Buttons to show in small player when using semantic actions
|
||||
private static final List<Integer> SEMANTIC_ACTION_IDS = List.of(
|
||||
private static final List<Integer> SEMANTIC_ACTIONS_COMPACT = List.of(
|
||||
R.id.actionPlayPause,
|
||||
R.id.actionPrev,
|
||||
R.id.actionNext
|
||||
);
|
||||
|
||||
// Buttons to show in small player when using semantic actions
|
||||
private static final List<Integer> SEMANTIC_ACTIONS_ALL = List.of(
|
||||
R.id.actionPlayPause,
|
||||
R.id.actionPrev,
|
||||
R.id.actionNext,
|
||||
R.id.action0,
|
||||
R.id.action1
|
||||
);
|
||||
|
||||
private final SeekBarViewModel mSeekBarViewModel;
|
||||
private SeekBarObserver mSeekBarObserver;
|
||||
protected final Executor mBackgroundExecutor;
|
||||
@@ -127,8 +126,6 @@ public class MediaControlPanel {
|
||||
private MediaController mController;
|
||||
private Lazy<MediaDataManager> mMediaDataManagerLazy;
|
||||
private int mBackgroundColor;
|
||||
private int mDevicePadding;
|
||||
private int mAlbumArtSize;
|
||||
// Instance id for logging purpose.
|
||||
protected int mInstanceId = -1;
|
||||
// Uid for the media app.
|
||||
@@ -167,7 +164,6 @@ public class MediaControlPanel {
|
||||
mMediaCarouselController = mediaCarouselController;
|
||||
mFalsingManager = falsingManager;
|
||||
mSystemClock = systemClock;
|
||||
loadDimens();
|
||||
|
||||
mSeekBarViewModel.setLogSmartspaceClick(() -> {
|
||||
logSmartspaceCardReported(SMARTSPACE_CARD_CLICK_EVENT,
|
||||
@@ -184,12 +180,6 @@ public class MediaControlPanel {
|
||||
mMediaViewController.onDestroy();
|
||||
}
|
||||
|
||||
private void loadDimens() {
|
||||
mAlbumArtSize = mContext.getResources().getDimensionPixelSize(R.dimen.qs_media_album_size);
|
||||
mDevicePadding = mContext.getResources()
|
||||
.getDimensionPixelSize(R.dimen.qs_media_album_device_padding);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the view holder used to display media controls.
|
||||
*
|
||||
@@ -241,15 +231,14 @@ public class MediaControlPanel {
|
||||
}
|
||||
|
||||
/** Attaches the player to the player view holder. */
|
||||
public void attachPlayer(MediaViewHolder vh, MediaViewController.TYPE playerType) {
|
||||
public void attachPlayer(MediaViewHolder vh) {
|
||||
mMediaViewHolder = vh;
|
||||
TransitionLayout player = vh.getPlayer();
|
||||
|
||||
boolean useSessionLayout = playerType == MediaViewController.TYPE.PLAYER_SESSION;
|
||||
mSeekBarObserver = new SeekBarObserver(vh, useSessionLayout);
|
||||
mSeekBarObserver = new SeekBarObserver(vh);
|
||||
mSeekBarViewModel.getProgress().observeForever(mSeekBarObserver);
|
||||
mSeekBarViewModel.attachTouchHandlers(vh.getSeekBar());
|
||||
mMediaViewController.attach(player, playerType);
|
||||
mMediaViewController.attach(player, MediaViewController.TYPE.PLAYER);
|
||||
|
||||
vh.getPlayer().setOnLongClickListener(v -> {
|
||||
if (!mMediaViewController.isGutsVisible()) {
|
||||
@@ -305,16 +294,6 @@ public class MediaControlPanel {
|
||||
if (mMediaViewHolder == null) {
|
||||
return;
|
||||
}
|
||||
bindPlayerCommon(data, key);
|
||||
if (mMediaViewHolder instanceof PlayerViewHolder) {
|
||||
bindNotificationPlayer(data, key);
|
||||
} else if (mMediaViewHolder instanceof PlayerSessionViewHolder) {
|
||||
bindSessionPlayer(data, key);
|
||||
}
|
||||
}
|
||||
|
||||
/** Bind elements common to both layouts */
|
||||
private void bindPlayerCommon(@NonNull MediaData data, String key) {
|
||||
mKey = key;
|
||||
MediaSession.Token token = data.getToken();
|
||||
PackageManager packageManager = mContext.getPackageManager();
|
||||
@@ -371,18 +350,24 @@ public class MediaControlPanel {
|
||||
final MediaController controller = getController();
|
||||
mBackgroundExecutor.execute(() -> mSeekBarViewModel.updateController(controller));
|
||||
|
||||
// Guts label
|
||||
boolean isDismissible = data.isClearable();
|
||||
mMediaViewHolder.getLongPressText().setText(isDismissible
|
||||
? R.string.controls_media_close_session
|
||||
: R.string.controls_media_active_session);
|
||||
bindOutputSwitcherChip(data);
|
||||
bindLongPressMenu(data);
|
||||
bindActionButtons(data);
|
||||
bindArtworkAndColors(data);
|
||||
|
||||
// TODO: We don't need to refresh this state constantly, only if the state actually changed
|
||||
// to something which might impact the measurement
|
||||
mMediaViewController.refreshState();
|
||||
}
|
||||
|
||||
private void bindOutputSwitcherChip(MediaData data) {
|
||||
// Output switcher chip
|
||||
ViewGroup seamlessView = mMediaViewHolder.getSeamless();
|
||||
seamlessView.setVisibility(View.VISIBLE);
|
||||
ImageView iconView = mMediaViewHolder.getSeamlessIcon();
|
||||
TextView deviceName = mMediaViewHolder.getSeamlessText();
|
||||
final MediaDeviceData device = data.getDevice();
|
||||
|
||||
// Disable clicking on output switcher for invalid devices and resumption controls
|
||||
final boolean seamlessDisabled = (device != null && !device.getEnabled())
|
||||
|| data.getResumption();
|
||||
@@ -426,9 +411,16 @@ public class MediaControlPanel {
|
||||
mMediaOutputDialogFactory.create(data.getPackageName(), true,
|
||||
mMediaViewHolder.getSeamlessButton());
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Dismiss
|
||||
private void bindLongPressMenu(MediaData data) {
|
||||
boolean isDismissible = data.isClearable();
|
||||
mMediaViewHolder.getLongPressText().setText(isDismissible
|
||||
? R.string.controls_media_close_session
|
||||
: R.string.controls_media_active_session);
|
||||
|
||||
// Dismiss button
|
||||
mMediaViewHolder.getDismissText().setAlpha(isDismissible ? 1 : DISABLED_ALPHA);
|
||||
mMediaViewHolder.getDismiss().setEnabled(isDismissible);
|
||||
mMediaViewHolder.getDismiss().setOnClickListener(v -> {
|
||||
@@ -443,136 +435,16 @@ public class MediaControlPanel {
|
||||
MediaViewController.GUTS_ANIMATION_DURATION + 100)) {
|
||||
Log.w(TAG, "Manager failed to dismiss media " + mKey);
|
||||
// Remove directly from carousel so user isn't stuck with defunct controls
|
||||
mMediaCarouselController.removePlayer(key, false, false);
|
||||
mMediaCarouselController.removePlayer(mKey, false, false);
|
||||
}
|
||||
} else {
|
||||
Log.w(TAG, "Dismiss media with null notification. Token uid="
|
||||
+ data.getToken().getUid());
|
||||
}
|
||||
});
|
||||
|
||||
// TODO: We don't need to refresh this state constantly, only if the state actually changed
|
||||
// to something which might impact the measurement
|
||||
mMediaViewController.refreshState();
|
||||
}
|
||||
|
||||
/** Bind elements specific to PlayerViewHolder */
|
||||
private void bindNotificationPlayer(@NonNull MediaData data, String key) {
|
||||
ConstraintSet expandedSet = mMediaViewController.getExpandedLayout();
|
||||
ConstraintSet collapsedSet = mMediaViewController.getCollapsedLayout();
|
||||
|
||||
// Album art
|
||||
ImageView albumView = mMediaViewHolder.getAlbumView();
|
||||
boolean hasArtwork = data.getArtwork() != null;
|
||||
if (hasArtwork) {
|
||||
Drawable artwork = getScaledThumbnail(data.getArtwork());
|
||||
albumView.setPadding(0, 0, 0, 0);
|
||||
albumView.setImageDrawable(artwork);
|
||||
} else {
|
||||
Drawable deviceIcon;
|
||||
if (data.getDevice() != null && data.getDevice().getIcon() != null) {
|
||||
deviceIcon = data.getDevice().getIcon().getConstantState().newDrawable().mutate();
|
||||
} else {
|
||||
deviceIcon = getContext().getDrawable(R.drawable.ic_headphone);
|
||||
}
|
||||
deviceIcon.setTintList(ColorStateList.valueOf(mBackgroundColor));
|
||||
albumView.setPadding(mDevicePadding, mDevicePadding, mDevicePadding, mDevicePadding);
|
||||
albumView.setImageDrawable(deviceIcon);
|
||||
}
|
||||
|
||||
// App icon - use notification icon
|
||||
ImageView appIconView = mMediaViewHolder.getAppIcon();
|
||||
appIconView.clearColorFilter();
|
||||
if (data.getAppIcon() != null && !data.getResumption()) {
|
||||
appIconView.setImageIcon(data.getAppIcon());
|
||||
int color = mContext.getColor(R.color.material_dynamic_secondary10);
|
||||
appIconView.setColorFilter(color);
|
||||
} else {
|
||||
// Resume players use launcher icon
|
||||
appIconView.setColorFilter(getGrayscaleFilter());
|
||||
try {
|
||||
Drawable icon = mContext.getPackageManager().getApplicationIcon(
|
||||
data.getPackageName());
|
||||
appIconView.setImageDrawable(icon);
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
Log.w(TAG, "Cannot find icon for package " + data.getPackageName(), e);
|
||||
appIconView.setImageResource(R.drawable.ic_music_note);
|
||||
}
|
||||
}
|
||||
|
||||
// Media action buttons
|
||||
List<MediaAction> actionIcons = data.getActions();
|
||||
List<Integer> actionsWhenCollapsed = data.getActionsToShowInCompact();
|
||||
|
||||
// If we got session actions, use those instead
|
||||
if (data.getSemanticActions() != null) {
|
||||
MediaButton semanticActions = data.getSemanticActions();
|
||||
|
||||
actionIcons = new ArrayList<MediaAction>();
|
||||
actionIcons.add(semanticActions.getCustom0());
|
||||
actionIcons.add(semanticActions.getPrevOrCustom());
|
||||
actionIcons.add(semanticActions.getPlayOrPause());
|
||||
actionIcons.add(semanticActions.getNextOrCustom());
|
||||
actionIcons.add(semanticActions.getCustom1());
|
||||
|
||||
actionsWhenCollapsed = new ArrayList<Integer>();
|
||||
actionsWhenCollapsed.add(1);
|
||||
actionsWhenCollapsed.add(2);
|
||||
actionsWhenCollapsed.add(3);
|
||||
}
|
||||
|
||||
int i = 0;
|
||||
for (; i < actionIcons.size() && i < ACTION_IDS.length; i++) {
|
||||
int actionId = ACTION_IDS[i];
|
||||
boolean visibleInCompat = actionsWhenCollapsed.contains(i);
|
||||
final ImageButton button = mMediaViewHolder.getAction(actionId);
|
||||
MediaAction mediaAction = actionIcons.get(i);
|
||||
if (mediaAction != null) {
|
||||
button.setImageIcon(mediaAction.getIcon());
|
||||
button.setContentDescription(mediaAction.getContentDescription());
|
||||
Runnable action = mediaAction.getAction();
|
||||
|
||||
if (action == null) {
|
||||
button.setEnabled(false);
|
||||
} else {
|
||||
button.setEnabled(true);
|
||||
button.setOnClickListener(v -> {
|
||||
if (!mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
|
||||
logSmartspaceCardReported(SMARTSPACE_CARD_CLICK_EVENT,
|
||||
/* isRecommendationCard */ false);
|
||||
action.run();
|
||||
}
|
||||
});
|
||||
}
|
||||
setVisibleAndAlpha(collapsedSet, actionId, visibleInCompat);
|
||||
setVisibleAndAlpha(expandedSet, actionId, true /*visible */);
|
||||
} else {
|
||||
button.setImageIcon(null);
|
||||
button.setContentDescription(null);
|
||||
button.setEnabled(false);
|
||||
setVisibleAndAlpha(collapsedSet, actionId, visibleInCompat);
|
||||
// for expanded layout, set as INVISIBLE so that we still reserve space in the UI
|
||||
expandedSet.setVisibility(actionId, ConstraintSet.INVISIBLE);
|
||||
expandedSet.setAlpha(actionId, 0.0f);
|
||||
}
|
||||
}
|
||||
|
||||
// Hide any unused buttons
|
||||
for (; i < ACTION_IDS.length; i++) {
|
||||
setVisibleAndAlpha(collapsedSet, ACTION_IDS[i], false /*visible */);
|
||||
setVisibleAndAlpha(expandedSet, ACTION_IDS[i], false /* visible */);
|
||||
}
|
||||
// If no actions, set the first view as INVISIBLE so expanded height remains constant
|
||||
if (actionIcons.size() == 0) {
|
||||
expandedSet.setVisibility(ACTION_IDS[0], ConstraintSet.INVISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
/** Bind elements specific to PlayerSessionViewHolder */
|
||||
private void bindSessionPlayer(@NonNull MediaData data, String key) {
|
||||
ConstraintSet expandedSet = mMediaViewController.getExpandedLayout();
|
||||
ConstraintSet collapsedSet = mMediaViewController.getCollapsedLayout();
|
||||
|
||||
private void bindArtworkAndColors(MediaData data) {
|
||||
// Default colors
|
||||
int surfaceColor = mBackgroundColor;
|
||||
int accentPrimary = com.android.settingslib.Utils.getColorAttr(mContext,
|
||||
@@ -592,7 +464,7 @@ public class MediaControlPanel {
|
||||
boolean hasArtwork = data.getArtwork() != null;
|
||||
if (hasArtwork) {
|
||||
colorScheme = new ColorScheme(WallpaperColors.fromBitmap(data.getArtwork().getBitmap()),
|
||||
true);
|
||||
true);
|
||||
|
||||
// Scale artwork to fit background
|
||||
int width = mMediaViewHolder.getPlayer().getWidth();
|
||||
@@ -661,6 +533,14 @@ public class MediaControlPanel {
|
||||
seekbar.setProgressTintList(textColorList);
|
||||
seekbar.setProgressBackgroundTintList(ColorStateList.valueOf(textTertiary));
|
||||
|
||||
// Action buttons
|
||||
mMediaViewHolder.getActionPlayPause().setBackgroundTintList(accentColorList);
|
||||
mMediaViewHolder.getActionPlayPause().setImageTintList(
|
||||
ColorStateList.valueOf(textPrimaryInverse));
|
||||
for (ImageButton button : mMediaViewHolder.getTransparentActionButtons()) {
|
||||
button.setImageTintList(textColorList);
|
||||
}
|
||||
|
||||
// Output switcher
|
||||
View seamlessView = mMediaViewHolder.getSeamlessButton();
|
||||
seamlessView.setBackgroundTintList(accentColorList);
|
||||
@@ -669,70 +549,6 @@ public class MediaControlPanel {
|
||||
TextView seamlessText = mMediaViewHolder.getSeamlessText();
|
||||
seamlessText.setTextColor(surfaceColor);
|
||||
|
||||
// Media action buttons
|
||||
MediaButton semanticActions = data.getSemanticActions();
|
||||
PlayerSessionViewHolder sessionHolder = (PlayerSessionViewHolder) mMediaViewHolder;
|
||||
ImageButton[] genericButtons = new ImageButton[]{
|
||||
sessionHolder.getAction0(),
|
||||
sessionHolder.getAction1(),
|
||||
sessionHolder.getAction2(),
|
||||
sessionHolder.getAction3(),
|
||||
sessionHolder.getAction4()};
|
||||
|
||||
ImageButton[] semanticButtons = new ImageButton[]{
|
||||
sessionHolder.getActionPlayPause(),
|
||||
sessionHolder.getActionNext(),
|
||||
sessionHolder.getActionPrev()};
|
||||
|
||||
if (semanticActions != null) {
|
||||
// Hide all the generic buttons
|
||||
for (ImageButton b: genericButtons) {
|
||||
setVisibleAndAlpha(collapsedSet, b.getId(), false);
|
||||
setVisibleAndAlpha(expandedSet, b.getId(), false);
|
||||
}
|
||||
|
||||
// Play/pause button has a background
|
||||
sessionHolder.getActionPlayPause().setBackgroundTintList(accentColorList);
|
||||
setSemanticButton(sessionHolder.getActionPlayPause(), semanticActions.getPlayOrPause(),
|
||||
ColorStateList.valueOf(textPrimaryInverse), collapsedSet, expandedSet, true);
|
||||
|
||||
setSemanticButton(sessionHolder.getActionNext(), semanticActions.getNextOrCustom(),
|
||||
textColorList, collapsedSet, expandedSet, true);
|
||||
setSemanticButton(sessionHolder.getActionPrev(), semanticActions.getPrevOrCustom(),
|
||||
textColorList, collapsedSet, expandedSet, true);
|
||||
setSemanticButton(sessionHolder.getAction0(), semanticActions.getCustom0(),
|
||||
textColorList, collapsedSet, expandedSet, false);
|
||||
setSemanticButton(sessionHolder.getAction1(), semanticActions.getCustom1(),
|
||||
textColorList, collapsedSet, expandedSet, false);
|
||||
} else {
|
||||
// Hide all the semantic buttons
|
||||
for (int id : SEMANTIC_ACTION_IDS) {
|
||||
setVisibleAndAlpha(collapsedSet, id, false);
|
||||
setVisibleAndAlpha(expandedSet, id, false);
|
||||
}
|
||||
|
||||
// Set all the generic buttons
|
||||
List<Integer> actionsWhenCollapsed = data.getActionsToShowInCompact();
|
||||
List<MediaAction> actions = data.getActions();
|
||||
int i = 0;
|
||||
for (; i < actions.size(); i++) {
|
||||
boolean showInCompact = actionsWhenCollapsed.contains(i);
|
||||
setSemanticButton(genericButtons[i], actions.get(i), textColorList, collapsedSet,
|
||||
expandedSet, showInCompact);
|
||||
}
|
||||
for (; i < 5; i++) {
|
||||
// Hide any unused buttons
|
||||
setSemanticButton(genericButtons[i], null, textColorList, collapsedSet,
|
||||
expandedSet, false);
|
||||
}
|
||||
}
|
||||
|
||||
// If disabled, set progress bar to INVISIBLE instead of GONE so layout weights still work
|
||||
boolean seekbarEnabled = mSeekBarViewModel.getEnabled();
|
||||
expandedSet.setVisibility(R.id.media_progress_bar,
|
||||
seekbarEnabled ? ConstraintSet.VISIBLE : ConstraintSet.INVISIBLE);
|
||||
expandedSet.setAlpha(R.id.media_progress_bar, seekbarEnabled ? 1.0f : 0.0f);
|
||||
|
||||
// Long press buttons
|
||||
mMediaViewHolder.getLongPressText().setTextColor(textColorList);
|
||||
mMediaViewHolder.getSettingsText().setTextColor(textColorList);
|
||||
@@ -743,10 +559,61 @@ public class MediaControlPanel {
|
||||
mMediaViewHolder.getDismissText().setBackgroundTintList(accentColorList);
|
||||
}
|
||||
|
||||
private void bindActionButtons(MediaData data) {
|
||||
MediaButton semanticActions = data.getSemanticActions();
|
||||
ImageButton[] genericButtons = new ImageButton[]{
|
||||
mMediaViewHolder.getAction0(),
|
||||
mMediaViewHolder.getAction1(),
|
||||
mMediaViewHolder.getAction2(),
|
||||
mMediaViewHolder.getAction3(),
|
||||
mMediaViewHolder.getAction4()};
|
||||
|
||||
ConstraintSet expandedSet = mMediaViewController.getExpandedLayout();
|
||||
ConstraintSet collapsedSet = mMediaViewController.getCollapsedLayout();
|
||||
if (semanticActions != null) {
|
||||
// Hide all the generic buttons
|
||||
for (ImageButton b: genericButtons) {
|
||||
setVisibleAndAlpha(collapsedSet, b.getId(), false);
|
||||
setVisibleAndAlpha(expandedSet, b.getId(), false);
|
||||
}
|
||||
|
||||
for (int id : SEMANTIC_ACTIONS_ALL) {
|
||||
boolean showInCompact = SEMANTIC_ACTIONS_COMPACT.contains(id);
|
||||
ImageButton button = mMediaViewHolder.getAction(id);
|
||||
MediaAction action = semanticActions.getActionById(id);
|
||||
setSemanticButton(button, action, collapsedSet, expandedSet, showInCompact);
|
||||
}
|
||||
} else {
|
||||
// Hide buttons that only appear for semantic actions
|
||||
for (int id : SEMANTIC_ACTIONS_COMPACT) {
|
||||
setVisibleAndAlpha(collapsedSet, id, false);
|
||||
setVisibleAndAlpha(expandedSet, id, false);
|
||||
}
|
||||
|
||||
// Set all the generic buttons
|
||||
List<Integer> actionsWhenCollapsed = data.getActionsToShowInCompact();
|
||||
List<MediaAction> actions = data.getActions();
|
||||
int i = 0;
|
||||
for (; i < actions.size(); i++) {
|
||||
boolean showInCompact = actionsWhenCollapsed.contains(i);
|
||||
setSemanticButton(genericButtons[i], actions.get(i), collapsedSet,
|
||||
expandedSet, showInCompact);
|
||||
}
|
||||
for (; i < 5; i++) {
|
||||
// Hide any unused buttons
|
||||
setSemanticButton(genericButtons[i], null, collapsedSet, expandedSet, false);
|
||||
}
|
||||
}
|
||||
|
||||
// If disabled, set progress bar to INVISIBLE instead of GONE so layout weights still work
|
||||
boolean seekbarEnabled = mSeekBarViewModel.getEnabled();
|
||||
expandedSet.setVisibility(R.id.media_progress_bar,
|
||||
seekbarEnabled ? ConstraintSet.VISIBLE : ConstraintSet.INVISIBLE);
|
||||
expandedSet.setAlpha(R.id.media_progress_bar, seekbarEnabled ? 1.0f : 0.0f);
|
||||
}
|
||||
|
||||
private void setSemanticButton(final ImageButton button, MediaAction mediaAction,
|
||||
ColorStateList fgColor, ConstraintSet collapsedSet, ConstraintSet expandedSet,
|
||||
boolean showInCompact) {
|
||||
button.setImageTintList(fgColor);
|
||||
ConstraintSet collapsedSet, ConstraintSet expandedSet, boolean showInCompact) {
|
||||
if (mediaAction != null) {
|
||||
button.setImageIcon(mediaAction.getIcon());
|
||||
button.setContentDescription(mediaAction.getContentDescription());
|
||||
@@ -1007,34 +874,6 @@ public class MediaControlPanel {
|
||||
mMediaViewController.openGuts();
|
||||
}
|
||||
|
||||
/**
|
||||
* Scale drawable to fit into the square album art thumbnail
|
||||
*/
|
||||
@UiThread
|
||||
private Drawable getScaledThumbnail(Icon icon) {
|
||||
if (icon == null) {
|
||||
return null;
|
||||
}
|
||||
// Let's scale down the View, such that the content always nicely fills the view.
|
||||
// ThumbnailUtils actually scales it down such that it may not be filled for odd aspect
|
||||
// ratios
|
||||
Drawable drawable = icon.loadDrawable(mContext);
|
||||
float aspectRatio = drawable.getIntrinsicHeight() / (float) drawable.getIntrinsicWidth();
|
||||
Rect bounds;
|
||||
if (aspectRatio > 1.0f) {
|
||||
bounds = new Rect(0, 0, mAlbumArtSize, (int) (mAlbumArtSize * aspectRatio));
|
||||
} else {
|
||||
bounds = new Rect(0, 0, (int) (mAlbumArtSize / aspectRatio), mAlbumArtSize);
|
||||
}
|
||||
if (bounds.width() > mAlbumArtSize || bounds.height() > mAlbumArtSize) {
|
||||
float offsetX = (bounds.width() - mAlbumArtSize) / 2.0f;
|
||||
float offsetY = (bounds.height() - mAlbumArtSize) / 2.0f;
|
||||
bounds.offset((int) -offsetX, (int) -offsetY);
|
||||
}
|
||||
drawable.setBounds(bounds);
|
||||
return drawable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Scale artwork to fill the background of the panel
|
||||
*/
|
||||
|
||||
@@ -20,6 +20,7 @@ import android.app.PendingIntent
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.graphics.drawable.Icon
|
||||
import android.media.session.MediaSession
|
||||
import com.android.systemui.R
|
||||
|
||||
/** State of a media view. */
|
||||
data class MediaData(
|
||||
@@ -154,7 +155,18 @@ data class MediaButton(
|
||||
* Second custom action space
|
||||
*/
|
||||
var custom1: MediaAction? = null
|
||||
)
|
||||
) {
|
||||
fun getActionById(id: Int): MediaAction? {
|
||||
return when (id) {
|
||||
R.id.actionPlayPause -> playOrPause
|
||||
R.id.actionNext -> nextOrCustom
|
||||
R.id.actionPrev -> prevOrCustom
|
||||
R.id.action0 -> custom0
|
||||
R.id.action1 -> custom1
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** State of a media action. */
|
||||
data class MediaAction(
|
||||
|
||||
@@ -34,13 +34,6 @@ class MediaFlags @Inject constructor(private val featureFlags: FeatureFlags) {
|
||||
return enabled || featureFlags.isEnabled(Flags.MEDIA_SESSION_ACTIONS)
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether media controls should use the new session-based layout
|
||||
*/
|
||||
fun useMediaSessionLayout(): Boolean {
|
||||
return featureFlags.isEnabled(Flags.MEDIA_SESSION_LAYOUT)
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether we support displaying information about mute await connections.
|
||||
*/
|
||||
|
||||
@@ -22,7 +22,10 @@ import androidx.annotation.VisibleForTesting
|
||||
import androidx.constraintlayout.widget.ConstraintSet
|
||||
import com.android.systemui.R
|
||||
import com.android.systemui.statusbar.policy.ConfigurationController
|
||||
import com.android.systemui.util.animation.*
|
||||
import com.android.systemui.util.animation.MeasurementOutput
|
||||
import com.android.systemui.util.animation.TransitionLayout
|
||||
import com.android.systemui.util.animation.TransitionLayoutController
|
||||
import com.android.systemui.util.animation.TransitionViewState
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
@@ -40,7 +43,7 @@ class MediaViewController @Inject constructor(
|
||||
* session-based player, or recommendation
|
||||
*/
|
||||
enum class TYPE {
|
||||
PLAYER, PLAYER_SESSION, RECOMMENDATION
|
||||
PLAYER, RECOMMENDATION
|
||||
}
|
||||
|
||||
companion object {
|
||||
@@ -259,13 +262,11 @@ class MediaViewController @Inject constructor(
|
||||
*/
|
||||
private fun setGutsViewState(viewState: TransitionViewState) {
|
||||
val controlsIds = when (type) {
|
||||
TYPE.PLAYER -> PlayerViewHolder.controlsIds
|
||||
TYPE.PLAYER_SESSION -> PlayerSessionViewHolder.controlsIds
|
||||
TYPE.PLAYER -> MediaViewHolder.controlsIds
|
||||
TYPE.RECOMMENDATION -> RecommendationViewHolder.controlsIds
|
||||
}
|
||||
val gutsIds = when (type) {
|
||||
TYPE.PLAYER -> PlayerViewHolder.gutsIds
|
||||
TYPE.PLAYER_SESSION -> PlayerSessionViewHolder.gutsIds
|
||||
TYPE.PLAYER -> MediaViewHolder.gutsIds
|
||||
TYPE.RECOMMENDATION -> RecommendationViewHolder.gutsIds
|
||||
}
|
||||
controlsIds.forEach { id ->
|
||||
@@ -287,15 +288,17 @@ class MediaViewController @Inject constructor(
|
||||
/**
|
||||
* Apply squishFraction to a copy of viewState such that the cached version is untouched.
|
||||
*/
|
||||
private fun squishViewState(viewState: TransitionViewState,
|
||||
squishFraction: Float): TransitionViewState {
|
||||
private fun squishViewState(
|
||||
viewState: TransitionViewState,
|
||||
squishFraction: Float
|
||||
): TransitionViewState {
|
||||
val squishedViewState = viewState.copy()
|
||||
squishedViewState.height = (squishedViewState.height * squishFraction).toInt()
|
||||
val albumArtViewState = viewState.widgetStates.get(R.id.album_art)
|
||||
if (albumArtViewState != null) {
|
||||
albumArtViewState.height = squishedViewState.height
|
||||
}
|
||||
return squishedViewState;
|
||||
return squishedViewState
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -314,7 +317,7 @@ class MediaViewController @Inject constructor(
|
||||
if (viewState != null) {
|
||||
// we already have cached this measurement, let's continue
|
||||
if (state.squishFraction < 1f) {
|
||||
return squishViewState(viewState, state.squishFraction);
|
||||
return squishViewState(viewState, state.squishFraction)
|
||||
}
|
||||
return viewState
|
||||
}
|
||||
@@ -351,7 +354,7 @@ class MediaViewController @Inject constructor(
|
||||
state.expansion)
|
||||
}
|
||||
if (state.squishFraction < 1f) {
|
||||
return squishViewState(result, state.squishFraction);
|
||||
return squishViewState(result, state.squishFraction)
|
||||
}
|
||||
return result
|
||||
}
|
||||
@@ -492,10 +495,6 @@ class MediaViewController @Inject constructor(
|
||||
// These XML resources contain ConstraintSets that will apply to this player type's layout
|
||||
when (type) {
|
||||
TYPE.PLAYER -> {
|
||||
collapsedLayout.load(context, R.xml.media_collapsed)
|
||||
expandedLayout.load(context, R.xml.media_expanded)
|
||||
}
|
||||
TYPE.PLAYER_SESSION -> {
|
||||
collapsedLayout.load(context, R.xml.media_session_collapsed)
|
||||
expandedLayout.load(context, R.xml.media_session_expanded)
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package com.android.systemui.media
|
||||
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ImageButton
|
||||
@@ -29,9 +30,9 @@ import com.android.systemui.util.animation.TransitionLayout
|
||||
private const val TAG = "MediaViewHolder"
|
||||
|
||||
/**
|
||||
* Parent class for different media player views
|
||||
* Holder class for media player view
|
||||
*/
|
||||
abstract class MediaViewHolder constructor(itemView: View) {
|
||||
class MediaViewHolder constructor(itemView: View) {
|
||||
val player = itemView as TransitionLayout
|
||||
|
||||
// Player information
|
||||
@@ -61,6 +62,9 @@ abstract class MediaViewHolder constructor(itemView: View) {
|
||||
val settingsText = itemView.requireViewById<TextView>(R.id.settings_text)
|
||||
|
||||
// Action Buttons
|
||||
val actionPlayPause = itemView.requireViewById<ImageButton>(R.id.actionPlayPause)
|
||||
val actionNext = itemView.requireViewById<ImageButton>(R.id.actionNext)
|
||||
val actionPrev = itemView.requireViewById<ImageButton>(R.id.actionPrev)
|
||||
val action0 = itemView.requireViewById<ImageButton>(R.id.action0)
|
||||
val action1 = itemView.requireViewById<ImageButton>(R.id.action1)
|
||||
val action2 = itemView.requireViewById<ImageButton>(R.id.action2)
|
||||
@@ -73,6 +77,9 @@ abstract class MediaViewHolder constructor(itemView: View) {
|
||||
it.registerLightSource(cancel)
|
||||
it.registerLightSource(dismiss)
|
||||
it.registerLightSource(settings)
|
||||
it.registerLightSource(actionPlayPause)
|
||||
it.registerLightSource(actionNext)
|
||||
it.registerLightSource(actionPrev)
|
||||
it.registerLightSource(action0)
|
||||
it.registerLightSource(action1)
|
||||
it.registerLightSource(action2)
|
||||
@@ -81,7 +88,33 @@ abstract class MediaViewHolder constructor(itemView: View) {
|
||||
}
|
||||
}
|
||||
|
||||
abstract fun getAction(id: Int): ImageButton
|
||||
fun getAction(id: Int): ImageButton {
|
||||
return when (id) {
|
||||
R.id.actionPlayPause -> actionPlayPause
|
||||
R.id.actionNext -> actionNext
|
||||
R.id.actionPrev -> actionPrev
|
||||
R.id.action0 -> action0
|
||||
R.id.action1 -> action1
|
||||
R.id.action2 -> action2
|
||||
R.id.action3 -> action3
|
||||
R.id.action4 -> action4
|
||||
else -> {
|
||||
throw IllegalArgumentException()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getTransparentActionButtons(): List<ImageButton> {
|
||||
return listOf(
|
||||
actionNext,
|
||||
actionPrev,
|
||||
action0,
|
||||
action1,
|
||||
action2,
|
||||
action3,
|
||||
action4
|
||||
)
|
||||
}
|
||||
|
||||
fun marquee(start: Boolean, delay: Long) {
|
||||
val longPressTextHandler = longPressText.getHandler()
|
||||
@@ -91,4 +124,52 @@ abstract class MediaViewHolder constructor(itemView: View) {
|
||||
}
|
||||
longPressTextHandler.postDelayed({ longPressText.setSelected(start) }, delay)
|
||||
}
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* Creates a MediaViewHolder.
|
||||
*
|
||||
* @param inflater LayoutInflater to use to inflate the layout.
|
||||
* @param parent Parent of inflated view.
|
||||
*/
|
||||
@JvmStatic fun create(
|
||||
inflater: LayoutInflater,
|
||||
parent: ViewGroup
|
||||
): MediaViewHolder {
|
||||
val mediaView = inflater.inflate(R.layout.media_session_view, parent, false)
|
||||
mediaView.setLayerType(View.LAYER_TYPE_HARDWARE, null)
|
||||
// Because this media view (a TransitionLayout) is used to measure and layout the views
|
||||
// in various states before being attached to its parent, we can't depend on the default
|
||||
// LAYOUT_DIRECTION_INHERIT to correctly resolve the ltr direction.
|
||||
mediaView.layoutDirection = View.LAYOUT_DIRECTION_LOCALE
|
||||
return MediaViewHolder(mediaView).apply {
|
||||
// Media playback is in the direction of tape, not time, so it stays LTR
|
||||
seekBar.layoutDirection = View.LAYOUT_DIRECTION_LTR
|
||||
}
|
||||
}
|
||||
|
||||
val controlsIds = setOf(
|
||||
R.id.icon,
|
||||
R.id.app_name,
|
||||
R.id.header_title,
|
||||
R.id.header_artist,
|
||||
R.id.media_seamless,
|
||||
R.id.media_progress_bar,
|
||||
R.id.actionPlayPause,
|
||||
R.id.actionNext,
|
||||
R.id.actionPrev,
|
||||
R.id.action0,
|
||||
R.id.action1,
|
||||
R.id.action2,
|
||||
R.id.action3,
|
||||
R.id.action4,
|
||||
R.id.icon
|
||||
)
|
||||
val gutsIds = setOf(
|
||||
R.id.remove_text,
|
||||
R.id.cancel,
|
||||
R.id.dismiss,
|
||||
R.id.settings
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,106 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2021 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.media
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ImageButton
|
||||
import com.android.systemui.R
|
||||
|
||||
/**
|
||||
* ViewHolder for a media player with MediaSession-based controls
|
||||
*/
|
||||
class PlayerSessionViewHolder private constructor(itemView: View) : MediaViewHolder(itemView) {
|
||||
|
||||
// Action Buttons
|
||||
val actionPlayPause = itemView.requireViewById<ImageButton>(R.id.actionPlayPause)
|
||||
val actionNext = itemView.requireViewById<ImageButton>(R.id.actionNext)
|
||||
val actionPrev = itemView.requireViewById<ImageButton>(R.id.actionPrev)
|
||||
|
||||
init {
|
||||
(player.background as IlluminationDrawable).let {
|
||||
it.registerLightSource(actionPlayPause)
|
||||
it.registerLightSource(actionNext)
|
||||
it.registerLightSource(actionPrev)
|
||||
}
|
||||
}
|
||||
|
||||
override fun getAction(id: Int): ImageButton {
|
||||
return when (id) {
|
||||
R.id.actionPlayPause -> actionPlayPause
|
||||
R.id.actionNext -> actionNext
|
||||
R.id.actionPrev -> actionPrev
|
||||
R.id.action0 -> action0
|
||||
R.id.action1 -> action1
|
||||
R.id.action2 -> action2
|
||||
R.id.action3 -> action3
|
||||
R.id.action4 -> action4
|
||||
else -> {
|
||||
throw IllegalArgumentException()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* Creates a PlayerSessionViewHolder.
|
||||
*
|
||||
* @param inflater LayoutInflater to use to inflate the layout.
|
||||
* @param parent Parent of inflated view.
|
||||
*/
|
||||
@JvmStatic fun create(
|
||||
inflater: LayoutInflater,
|
||||
parent: ViewGroup
|
||||
): PlayerSessionViewHolder {
|
||||
val mediaView = inflater.inflate(R.layout.media_session_view, parent, false)
|
||||
mediaView.setLayerType(View.LAYER_TYPE_HARDWARE, null)
|
||||
// Because this media view (a TransitionLayout) is used to measure and layout the views
|
||||
// in various states before being attached to its parent, we can't depend on the default
|
||||
// LAYOUT_DIRECTION_INHERIT to correctly resolve the ltr direction.
|
||||
mediaView.layoutDirection = View.LAYOUT_DIRECTION_LOCALE
|
||||
return PlayerSessionViewHolder(mediaView).apply {
|
||||
// Media playback is in the direction of tape, not time, so it stays LTR
|
||||
seekBar.layoutDirection = View.LAYOUT_DIRECTION_LTR
|
||||
}
|
||||
}
|
||||
|
||||
val controlsIds = setOf(
|
||||
R.id.icon,
|
||||
R.id.app_name,
|
||||
R.id.header_title,
|
||||
R.id.header_artist,
|
||||
R.id.media_seamless,
|
||||
R.id.media_progress_bar,
|
||||
R.id.actionPlayPause,
|
||||
R.id.actionNext,
|
||||
R.id.actionPrev,
|
||||
R.id.action0,
|
||||
R.id.action1,
|
||||
R.id.action2,
|
||||
R.id.action3,
|
||||
R.id.action4,
|
||||
R.id.icon
|
||||
)
|
||||
val gutsIds = setOf(
|
||||
R.id.remove_text,
|
||||
R.id.cancel,
|
||||
R.id.dismiss,
|
||||
R.id.settings
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,93 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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.media
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ImageButton
|
||||
import android.widget.TextView
|
||||
import com.android.systemui.R
|
||||
|
||||
/**
|
||||
* ViewHolder for a media player.
|
||||
*/
|
||||
class PlayerViewHolder private constructor(itemView: View) : MediaViewHolder(itemView) {
|
||||
|
||||
// Seek bar
|
||||
val progressTimes = itemView.requireViewById<ViewGroup>(R.id.notification_media_progress_time)
|
||||
override val elapsedTimeView = itemView.requireViewById<TextView>(R.id.media_elapsed_time)
|
||||
override val totalTimeView = itemView.requireViewById<TextView>(R.id.media_total_time)
|
||||
|
||||
override fun getAction(id: Int): ImageButton {
|
||||
return when (id) {
|
||||
R.id.action0 -> action0
|
||||
R.id.action1 -> action1
|
||||
R.id.action2 -> action2
|
||||
R.id.action3 -> action3
|
||||
R.id.action4 -> action4
|
||||
else -> {
|
||||
throw IllegalArgumentException()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* Creates a PlayerViewHolder.
|
||||
*
|
||||
* @param inflater LayoutInflater to use to inflate the layout.
|
||||
* @param parent Parent of inflated view.
|
||||
*/
|
||||
@JvmStatic fun create(inflater: LayoutInflater, parent: ViewGroup): PlayerViewHolder {
|
||||
val mediaView = inflater.inflate(R.layout.media_view, parent, false)
|
||||
mediaView.setLayerType(View.LAYER_TYPE_HARDWARE, null)
|
||||
// Because this media view (a TransitionLayout) is used to measure and layout the views
|
||||
// in various states before being attached to its parent, we can't depend on the default
|
||||
// LAYOUT_DIRECTION_INHERIT to correctly resolve the ltr direction.
|
||||
mediaView.layoutDirection = View.LAYOUT_DIRECTION_LOCALE
|
||||
return PlayerViewHolder(mediaView).apply {
|
||||
// Media playback is in the direction of tape, not time, so it stays LTR
|
||||
seekBar.layoutDirection = View.LAYOUT_DIRECTION_LTR
|
||||
progressTimes.layoutDirection = View.LAYOUT_DIRECTION_LTR
|
||||
}
|
||||
}
|
||||
|
||||
val controlsIds = setOf(
|
||||
R.id.icon,
|
||||
R.id.app_name,
|
||||
R.id.album_art,
|
||||
R.id.header_title,
|
||||
R.id.header_artist,
|
||||
R.id.media_seamless,
|
||||
R.id.notification_media_progress_time,
|
||||
R.id.media_progress_bar,
|
||||
R.id.action0,
|
||||
R.id.action1,
|
||||
R.id.action2,
|
||||
R.id.action3,
|
||||
R.id.action4,
|
||||
R.id.icon
|
||||
)
|
||||
val gutsIds = setOf(
|
||||
R.id.remove_text,
|
||||
R.id.cancel,
|
||||
R.id.dismiss,
|
||||
R.id.settings
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -27,28 +27,17 @@ import com.android.systemui.R
|
||||
* <p>Updates the seek bar views in response to changes to the model.
|
||||
*/
|
||||
class SeekBarObserver(
|
||||
private val holder: MediaViewHolder,
|
||||
private val useSessionLayout: Boolean
|
||||
private val holder: MediaViewHolder
|
||||
) : Observer<SeekBarViewModel.Progress> {
|
||||
|
||||
val seekBarEnabledMaxHeight = holder.seekBar.context.resources
|
||||
.getDimensionPixelSize(R.dimen.qs_media_enabled_seekbar_height)
|
||||
val seekBarDisabledHeight = holder.seekBar.context.resources
|
||||
.getDimensionPixelSize(R.dimen.qs_media_disabled_seekbar_height)
|
||||
val seekBarEnabledVerticalPadding = if (useSessionLayout) {
|
||||
holder.seekBar.context.resources
|
||||
val seekBarEnabledVerticalPadding = holder.seekBar.context.resources
|
||||
.getDimensionPixelSize(R.dimen.qs_media_session_enabled_seekbar_vertical_padding)
|
||||
} else {
|
||||
holder.seekBar.context.resources
|
||||
.getDimensionPixelSize(R.dimen.qs_media_enabled_seekbar_vertical_padding)
|
||||
}
|
||||
val seekBarDisabledVerticalPadding = if (useSessionLayout) {
|
||||
holder.seekBar.context.resources
|
||||
val seekBarDisabledVerticalPadding = holder.seekBar.context.resources
|
||||
.getDimensionPixelSize(R.dimen.qs_media_session_disabled_seekbar_vertical_padding)
|
||||
} else {
|
||||
holder.seekBar.context.resources
|
||||
.getDimensionPixelSize(R.dimen.qs_media_disabled_seekbar_vertical_padding)
|
||||
}
|
||||
|
||||
init {
|
||||
val seekBarProgressWavelength = holder.seekBar.context.resources
|
||||
|
||||
@@ -33,7 +33,6 @@ import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
|
||||
import com.android.systemui.dump.DumpManager;
|
||||
import com.android.systemui.flags.FeatureFlags;
|
||||
import com.android.systemui.flags.Flags;
|
||||
import com.android.systemui.media.MediaFlags;
|
||||
import com.android.systemui.media.MediaHierarchyManager;
|
||||
import com.android.systemui.media.MediaHost;
|
||||
import com.android.systemui.media.MediaHostState;
|
||||
@@ -46,7 +45,6 @@ import com.android.systemui.settings.brightness.BrightnessMirrorHandler;
|
||||
import com.android.systemui.settings.brightness.BrightnessSliderController;
|
||||
import com.android.systemui.statusbar.policy.BrightnessMirrorController;
|
||||
import com.android.systemui.tuner.TunerService;
|
||||
import com.android.systemui.util.Utils;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
@@ -65,7 +63,6 @@ public class QSPanelController extends QSPanelControllerBase<QSPanel> {
|
||||
private final FalsingManager mFalsingManager;
|
||||
private final BrightnessController mBrightnessController;
|
||||
private final BrightnessSliderController mBrightnessSliderController;
|
||||
private final MediaFlags mMediaFlags;
|
||||
private final BrightnessMirrorHandler mBrightnessMirrorHandler;
|
||||
private final FeatureFlags mFeatureFlags;
|
||||
|
||||
@@ -75,7 +72,6 @@ public class QSPanelController extends QSPanelControllerBase<QSPanel> {
|
||||
new QSPanel.OnConfigurationChangedListener() {
|
||||
@Override
|
||||
public void onConfigurationChange(Configuration newConfig) {
|
||||
updateMediaExpansion();
|
||||
mView.updateResources();
|
||||
mQsSecurityFooter.onConfigurationChanged();
|
||||
if (mView.isListening()) {
|
||||
@@ -105,8 +101,7 @@ public class QSPanelController extends QSPanelControllerBase<QSPanel> {
|
||||
DumpManager dumpManager, MetricsLogger metricsLogger, UiEventLogger uiEventLogger,
|
||||
QSLogger qsLogger, BrightnessController.Factory brightnessControllerFactory,
|
||||
BrightnessSliderController.Factory brightnessSliderFactory,
|
||||
FalsingManager falsingManager, FeatureFlags featureFlags,
|
||||
MediaFlags mediaFlags) {
|
||||
FalsingManager falsingManager, FeatureFlags featureFlags) {
|
||||
super(view, qstileHost, qsCustomizerController, usingMediaPlayer, mediaHost,
|
||||
metricsLogger, uiEventLogger, qsLogger, dumpManager);
|
||||
mQSFgsManagerFooter = qsFgsManagerFooter;
|
||||
@@ -117,7 +112,6 @@ public class QSPanelController extends QSPanelControllerBase<QSPanel> {
|
||||
mFalsingManager = falsingManager;
|
||||
|
||||
mBrightnessSliderController = brightnessSliderFactory.create(getContext(), mView);
|
||||
mMediaFlags = mediaFlags;
|
||||
mView.setBrightnessView(mBrightnessSliderController.getRootView());
|
||||
|
||||
mBrightnessController = brightnessControllerFactory.create(mBrightnessSliderController);
|
||||
@@ -129,7 +123,7 @@ public class QSPanelController extends QSPanelControllerBase<QSPanel> {
|
||||
@Override
|
||||
public void onInit() {
|
||||
super.onInit();
|
||||
updateMediaExpansion();
|
||||
mMediaHost.setExpansion(MediaHostState.EXPANDED);
|
||||
mMediaHost.setShowsOnlyActiveMedia(false);
|
||||
mMediaHost.init(MediaHierarchyManager.LOCATION_QS);
|
||||
mQsCustomizerController.init();
|
||||
@@ -137,17 +131,6 @@ public class QSPanelController extends QSPanelControllerBase<QSPanel> {
|
||||
mQSFgsManagerFooter.init();
|
||||
}
|
||||
|
||||
private void updateMediaExpansion() {
|
||||
boolean inSplitShade = Utils.shouldUseSplitNotificationShade(getResources());
|
||||
float expansion;
|
||||
if (inSplitShade && !mMediaFlags.useMediaSessionLayout()) {
|
||||
expansion = MediaHostState.COLLAPSED;
|
||||
} else {
|
||||
expansion = MediaHostState.EXPANDED;
|
||||
}
|
||||
mMediaHost.setExpansion(expansion);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onViewAttached() {
|
||||
super.onViewAttached();
|
||||
|
||||
@@ -26,7 +26,6 @@ import com.android.internal.logging.MetricsLogger;
|
||||
import com.android.internal.logging.UiEventLogger;
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.dump.DumpManager;
|
||||
import com.android.systemui.media.MediaFlags;
|
||||
import com.android.systemui.media.MediaHierarchyManager;
|
||||
import com.android.systemui.media.MediaHost;
|
||||
import com.android.systemui.plugins.qs.QSTile;
|
||||
@@ -53,7 +52,6 @@ public class QuickQSPanelController extends QSPanelControllerBase<QuickQSPanel>
|
||||
}
|
||||
};
|
||||
|
||||
private final MediaFlags mMediaFlags;
|
||||
private final boolean mUsingCollapsedLandscapeMedia;
|
||||
|
||||
@Inject
|
||||
@@ -62,14 +60,12 @@ public class QuickQSPanelController extends QSPanelControllerBase<QuickQSPanel>
|
||||
@Named(QS_USING_MEDIA_PLAYER) boolean usingMediaPlayer,
|
||||
@Named(QUICK_QS_PANEL) MediaHost mediaHost,
|
||||
@Named(QS_USING_COLLAPSED_LANDSCAPE_MEDIA) boolean usingCollapsedLandscapeMedia,
|
||||
MediaFlags mediaFlags,
|
||||
MetricsLogger metricsLogger, UiEventLogger uiEventLogger, QSLogger qsLogger,
|
||||
DumpManager dumpManager
|
||||
) {
|
||||
super(view, qsTileHost, qsCustomizerController, usingMediaPlayer, mediaHost, metricsLogger,
|
||||
uiEventLogger, qsLogger, dumpManager);
|
||||
mUsingCollapsedLandscapeMedia = usingCollapsedLandscapeMedia;
|
||||
mMediaFlags = mediaFlags;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -84,8 +80,7 @@ public class QuickQSPanelController extends QSPanelControllerBase<QuickQSPanel>
|
||||
int rotation = getRotation();
|
||||
boolean isLandscape = rotation == RotationUtils.ROTATION_LANDSCAPE
|
||||
|| rotation == RotationUtils.ROTATION_SEASCAPE;
|
||||
if (mMediaFlags.useMediaSessionLayout()
|
||||
&& (!mUsingCollapsedLandscapeMedia || !isLandscape)) {
|
||||
if (!mUsingCollapsedLandscapeMedia || !isLandscape) {
|
||||
mMediaHost.setExpansion(MediaHost.EXPANDED);
|
||||
} else {
|
||||
mMediaHost.setExpansion(MediaHost.COLLAPSED);
|
||||
|
||||
@@ -52,8 +52,6 @@ class KeyguardMediaControllerTest : SysuiTestCase() {
|
||||
private lateinit var statusBarStateController: SysuiStatusBarStateController
|
||||
@Mock
|
||||
private lateinit var configurationController: ConfigurationController
|
||||
@Mock
|
||||
private lateinit var mediaFlags: MediaFlags
|
||||
|
||||
@Mock
|
||||
private lateinit var notificationLockscreenUserManager: NotificationLockscreenUserManager
|
||||
@@ -73,15 +71,13 @@ class KeyguardMediaControllerTest : SysuiTestCase() {
|
||||
.thenReturn(true)
|
||||
whenever(mediaHost.hostView).thenReturn(hostView)
|
||||
hostView.layoutParams = FrameLayout.LayoutParams(100, 100)
|
||||
whenever(mediaFlags.useMediaSessionLayout()).thenReturn(false)
|
||||
keyguardMediaController = KeyguardMediaController(
|
||||
mediaHost,
|
||||
bypassController,
|
||||
statusBarStateController,
|
||||
notificationLockscreenUserManager,
|
||||
context,
|
||||
configurationController,
|
||||
mediaFlags
|
||||
configurationController
|
||||
)
|
||||
keyguardMediaController.attachSinglePaneContainer(mediaContainerView)
|
||||
keyguardMediaController.useSplitShade = false
|
||||
@@ -157,22 +153,7 @@ class KeyguardMediaControllerTest : SysuiTestCase() {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testNotificationLayout_collapsedPlayer() {
|
||||
verify(mediaHost).expansion = MediaHostState.COLLAPSED
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSessionLayout_expandedPlayer() {
|
||||
whenever(mediaFlags.useMediaSessionLayout()).thenReturn(true)
|
||||
keyguardMediaController = KeyguardMediaController(
|
||||
mediaHost,
|
||||
bypassController,
|
||||
statusBarStateController,
|
||||
notificationLockscreenUserManager,
|
||||
context,
|
||||
configurationController,
|
||||
mediaFlags
|
||||
)
|
||||
fun testMediaHost_expandedPlayer() {
|
||||
verify(mediaHost).expansion = MediaHostState.EXPANDED
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,8 +89,7 @@ public class MediaControlPanelTest : SysuiTestCase() {
|
||||
@Mock private lateinit var activityStarter: ActivityStarter
|
||||
@Mock private lateinit var broadcastSender: BroadcastSender
|
||||
|
||||
@Mock private lateinit var holder: PlayerViewHolder
|
||||
@Mock private lateinit var sessionHolder: PlayerSessionViewHolder
|
||||
@Mock private lateinit var viewHolder: MediaViewHolder
|
||||
@Mock private lateinit var view: TransitionLayout
|
||||
@Mock private lateinit var seekBarViewModel: SeekBarViewModel
|
||||
@Mock private lateinit var seekBarData: LiveData<SeekBarViewModel.Progress>
|
||||
@@ -179,8 +178,7 @@ public class MediaControlPanelTest : SysuiTestCase() {
|
||||
actionPrev = ImageButton(context).also { it.setId(R.id.actionPrev) }
|
||||
actionNext = ImageButton(context).also { it.setId(R.id.actionNext) }
|
||||
|
||||
initPlayerHolderMocks()
|
||||
initSessionHolderMocks()
|
||||
initMediaViewHolderMocks()
|
||||
|
||||
// Create media session
|
||||
val metadataBuilder = MediaMetadata.Builder().apply {
|
||||
@@ -217,9 +215,9 @@ public class MediaControlPanelTest : SysuiTestCase() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize elements common to both view holders
|
||||
* Initialize elements in media view holder
|
||||
*/
|
||||
private fun initMediaViewHolderMocks(viewHolder: MediaViewHolder) {
|
||||
private fun initMediaViewHolderMocks() {
|
||||
whenever(viewHolder.player).thenReturn(view)
|
||||
whenever(viewHolder.appIcon).thenReturn(appIcon)
|
||||
whenever(viewHolder.albumView).thenReturn(albumView)
|
||||
@@ -233,6 +231,12 @@ public class MediaControlPanelTest : SysuiTestCase() {
|
||||
whenever(viewHolder.seekBar).thenReturn(seekBar)
|
||||
|
||||
// Action buttons
|
||||
whenever(viewHolder.actionPlayPause).thenReturn(actionPlayPause)
|
||||
whenever(viewHolder.getAction(R.id.actionPlayPause)).thenReturn(actionPlayPause)
|
||||
whenever(viewHolder.actionNext).thenReturn(actionNext)
|
||||
whenever(viewHolder.getAction(R.id.actionNext)).thenReturn(actionNext)
|
||||
whenever(viewHolder.actionPrev).thenReturn(actionPrev)
|
||||
whenever(viewHolder.getAction(R.id.actionPrev)).thenReturn(actionPrev)
|
||||
whenever(viewHolder.action0).thenReturn(action0)
|
||||
whenever(viewHolder.getAction(R.id.action0)).thenReturn(action0)
|
||||
whenever(viewHolder.action1).thenReturn(action1)
|
||||
@@ -255,27 +259,6 @@ public class MediaControlPanelTest : SysuiTestCase() {
|
||||
whenever(viewHolder.dismissText).thenReturn(dismissText)
|
||||
}
|
||||
|
||||
/** Mock view holder for the notification player */
|
||||
private fun initPlayerHolderMocks() {
|
||||
initMediaViewHolderMocks(holder)
|
||||
|
||||
whenever(holder.elapsedTimeView).thenReturn(elapsedTimeView)
|
||||
whenever(holder.totalTimeView).thenReturn(totalTimeView)
|
||||
}
|
||||
|
||||
/** Mock view holder for session player */
|
||||
private fun initSessionHolderMocks() {
|
||||
initMediaViewHolderMocks(sessionHolder)
|
||||
|
||||
// Semantic action buttons
|
||||
whenever(sessionHolder.actionPlayPause).thenReturn(actionPlayPause)
|
||||
whenever(sessionHolder.getAction(R.id.actionPlayPause)).thenReturn(actionPlayPause)
|
||||
whenever(sessionHolder.actionNext).thenReturn(actionNext)
|
||||
whenever(sessionHolder.getAction(R.id.actionNext)).thenReturn(actionNext)
|
||||
whenever(sessionHolder.actionPrev).thenReturn(actionPrev)
|
||||
whenever(sessionHolder.getAction(R.id.actionPrev)).thenReturn(actionPrev)
|
||||
}
|
||||
|
||||
@After
|
||||
fun tearDown() {
|
||||
session.release()
|
||||
@@ -290,41 +273,7 @@ public class MediaControlPanelTest : SysuiTestCase() {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun bindSemanticActionsOldLayout() {
|
||||
val icon = Icon.createWithResource(context, android.R.drawable.ic_media_play)
|
||||
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")
|
||||
)
|
||||
val state = mediaData.copy(semanticActions = semanticActions)
|
||||
|
||||
player.attachPlayer(holder, MediaViewController.TYPE.PLAYER)
|
||||
player.bindPlayer(state, PACKAGE)
|
||||
|
||||
verify(expandedSet).setVisibility(R.id.action0, ConstraintSet.VISIBLE)
|
||||
assertThat(action0.contentDescription).isEqualTo("custom 0")
|
||||
assertThat(action0.isEnabled()).isFalse()
|
||||
|
||||
verify(expandedSet).setVisibility(R.id.action1, ConstraintSet.INVISIBLE)
|
||||
assertThat(action1.isEnabled()).isFalse()
|
||||
|
||||
verify(expandedSet).setVisibility(R.id.action2, ConstraintSet.VISIBLE)
|
||||
assertThat(action2.isEnabled()).isTrue()
|
||||
assertThat(action2.contentDescription).isEqualTo("play")
|
||||
|
||||
verify(expandedSet).setVisibility(R.id.action3, ConstraintSet.VISIBLE)
|
||||
assertThat(action3.isEnabled()).isTrue()
|
||||
assertThat(action3.contentDescription).isEqualTo("next")
|
||||
|
||||
verify(expandedSet).setVisibility(R.id.action4, ConstraintSet.VISIBLE)
|
||||
assertThat(action4.contentDescription).isEqualTo("custom 1")
|
||||
assertThat(action4.isEnabled()).isFalse()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun bindSemanticActionsNewLayout() {
|
||||
fun bindSemanticActions() {
|
||||
val icon = Icon.createWithResource(context, android.R.drawable.ic_media_play)
|
||||
val semanticActions = MediaButton(
|
||||
playOrPause = MediaAction(icon, Runnable {}, "play"),
|
||||
@@ -334,7 +283,7 @@ public class MediaControlPanelTest : SysuiTestCase() {
|
||||
)
|
||||
val state = mediaData.copy(semanticActions = semanticActions)
|
||||
|
||||
player.attachPlayer(sessionHolder, MediaViewController.TYPE.PLAYER_SESSION)
|
||||
player.attachPlayer(viewHolder)
|
||||
player.bindPlayer(state, PACKAGE)
|
||||
|
||||
assertThat(actionPrev.isEnabled()).isFalse()
|
||||
@@ -370,7 +319,7 @@ public class MediaControlPanelTest : SysuiTestCase() {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun bindNotificationActionsNewLayout() {
|
||||
fun bindNotificationActions() {
|
||||
val icon = Icon.createWithResource(context, android.R.drawable.ic_media_play)
|
||||
val actions = listOf(
|
||||
MediaAction(icon, Runnable {}, "previous"),
|
||||
@@ -383,7 +332,7 @@ public class MediaControlPanelTest : SysuiTestCase() {
|
||||
actionsToShowInCompact = listOf(1, 2),
|
||||
semanticActions = null)
|
||||
|
||||
player.attachPlayer(sessionHolder, MediaViewController.TYPE.PLAYER_SESSION)
|
||||
player.attachPlayer(viewHolder)
|
||||
player.bindPlayer(state, PACKAGE)
|
||||
|
||||
// Verify semantic actions are hidden
|
||||
@@ -420,7 +369,7 @@ public class MediaControlPanelTest : SysuiTestCase() {
|
||||
|
||||
@Test
|
||||
fun bindText() {
|
||||
player.attachPlayer(holder, MediaViewController.TYPE.PLAYER)
|
||||
player.attachPlayer(viewHolder)
|
||||
player.bindPlayer(mediaData, PACKAGE)
|
||||
assertThat(titleText.getText()).isEqualTo(TITLE)
|
||||
assertThat(artistText.getText()).isEqualTo(ARTIST)
|
||||
@@ -428,7 +377,7 @@ public class MediaControlPanelTest : SysuiTestCase() {
|
||||
|
||||
@Test
|
||||
fun bindDevice() {
|
||||
player.attachPlayer(holder, MediaViewController.TYPE.PLAYER)
|
||||
player.attachPlayer(viewHolder)
|
||||
player.bindPlayer(mediaData, PACKAGE)
|
||||
assertThat(seamlessText.getText()).isEqualTo(DEVICE_NAME)
|
||||
assertThat(seamless.contentDescription).isEqualTo(DEVICE_NAME)
|
||||
@@ -438,7 +387,7 @@ public class MediaControlPanelTest : SysuiTestCase() {
|
||||
@Test
|
||||
fun bindDisabledDevice() {
|
||||
seamless.id = 1
|
||||
player.attachPlayer(holder, MediaViewController.TYPE.PLAYER)
|
||||
player.attachPlayer(viewHolder)
|
||||
val state = mediaData.copy(device = disabledDevice)
|
||||
player.bindPlayer(state, PACKAGE)
|
||||
assertThat(seamless.isEnabled()).isFalse()
|
||||
@@ -449,7 +398,7 @@ public class MediaControlPanelTest : SysuiTestCase() {
|
||||
@Test
|
||||
fun bindNullDevice() {
|
||||
val fallbackString = context.getResources().getString(R.string.media_seamless_other_device)
|
||||
player.attachPlayer(holder, MediaViewController.TYPE.PLAYER)
|
||||
player.attachPlayer(viewHolder)
|
||||
val state = mediaData.copy(device = null)
|
||||
player.bindPlayer(state, PACKAGE)
|
||||
assertThat(seamless.isEnabled()).isTrue()
|
||||
@@ -459,7 +408,7 @@ public class MediaControlPanelTest : SysuiTestCase() {
|
||||
|
||||
@Test
|
||||
fun bindDeviceResumptionPlayer() {
|
||||
player.attachPlayer(holder, MediaViewController.TYPE.PLAYER)
|
||||
player.attachPlayer(viewHolder)
|
||||
val state = mediaData.copy(resumption = true)
|
||||
player.bindPlayer(state, PACKAGE)
|
||||
assertThat(seamlessText.getText()).isEqualTo(DEVICE_NAME)
|
||||
@@ -468,32 +417,32 @@ public class MediaControlPanelTest : SysuiTestCase() {
|
||||
|
||||
@Test
|
||||
fun longClick_gutsClosed() {
|
||||
player.attachPlayer(holder, MediaViewController.TYPE.PLAYER)
|
||||
player.attachPlayer(viewHolder)
|
||||
whenever(mediaViewController.isGutsVisible).thenReturn(false)
|
||||
|
||||
val captor = ArgumentCaptor.forClass(View.OnLongClickListener::class.java)
|
||||
verify(holder.player).setOnLongClickListener(captor.capture())
|
||||
verify(viewHolder.player).setOnLongClickListener(captor.capture())
|
||||
|
||||
captor.value.onLongClick(holder.player)
|
||||
captor.value.onLongClick(viewHolder.player)
|
||||
verify(mediaViewController).openGuts()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun longClick_gutsOpen() {
|
||||
player.attachPlayer(holder, MediaViewController.TYPE.PLAYER)
|
||||
player.attachPlayer(viewHolder)
|
||||
whenever(mediaViewController.isGutsVisible).thenReturn(true)
|
||||
|
||||
val captor = ArgumentCaptor.forClass(View.OnLongClickListener::class.java)
|
||||
verify(holder.player).setOnLongClickListener(captor.capture())
|
||||
verify(viewHolder.player).setOnLongClickListener(captor.capture())
|
||||
|
||||
captor.value.onLongClick(holder.player)
|
||||
captor.value.onLongClick(viewHolder.player)
|
||||
verify(mediaViewController, never()).openGuts()
|
||||
verify(mediaViewController).closeGuts(false)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun cancelButtonClick_animation() {
|
||||
player.attachPlayer(holder, MediaViewController.TYPE.PLAYER)
|
||||
player.attachPlayer(viewHolder)
|
||||
|
||||
cancel.callOnClick()
|
||||
|
||||
@@ -502,7 +451,7 @@ public class MediaControlPanelTest : SysuiTestCase() {
|
||||
|
||||
@Test
|
||||
fun settingsButtonClick() {
|
||||
player.attachPlayer(holder, MediaViewController.TYPE.PLAYER)
|
||||
player.attachPlayer(viewHolder)
|
||||
|
||||
settings.callOnClick()
|
||||
|
||||
@@ -515,7 +464,7 @@ public class MediaControlPanelTest : SysuiTestCase() {
|
||||
@Test
|
||||
fun dismissButtonClick() {
|
||||
val mediaKey = "key for dismissal"
|
||||
player.attachPlayer(holder, MediaViewController.TYPE.PLAYER)
|
||||
player.attachPlayer(viewHolder)
|
||||
val state = mediaData.copy(notificationKey = KEY)
|
||||
player.bindPlayer(state, mediaKey)
|
||||
|
||||
@@ -527,7 +476,7 @@ public class MediaControlPanelTest : SysuiTestCase() {
|
||||
@Test
|
||||
fun dismissButtonDisabled() {
|
||||
val mediaKey = "key for dismissal"
|
||||
player.attachPlayer(holder, MediaViewController.TYPE.PLAYER)
|
||||
player.attachPlayer(viewHolder)
|
||||
val state = mediaData.copy(isClearable = false, notificationKey = KEY)
|
||||
player.bindPlayer(state, mediaKey)
|
||||
|
||||
@@ -539,7 +488,7 @@ public class MediaControlPanelTest : SysuiTestCase() {
|
||||
val mediaKey = "key for dismissal"
|
||||
whenever(mediaDataManager.dismissMediaData(eq(mediaKey), anyLong())).thenReturn(false)
|
||||
|
||||
player.attachPlayer(holder, MediaViewController.TYPE.PLAYER)
|
||||
player.attachPlayer(viewHolder)
|
||||
val state = mediaData.copy(notificationKey = KEY)
|
||||
player.bindPlayer(state, mediaKey)
|
||||
|
||||
|
||||
@@ -1,62 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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.media
|
||||
|
||||
import android.testing.AndroidTestingRunner
|
||||
import android.testing.TestableLooper
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import android.widget.FrameLayout
|
||||
|
||||
import androidx.test.filters.SmallTest
|
||||
|
||||
import com.android.systemui.SysuiTestCase
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
/**
|
||||
* Tests for PlayerViewHolder.
|
||||
*/
|
||||
@SmallTest
|
||||
@RunWith(AndroidTestingRunner::class)
|
||||
@TestableLooper.RunWithLooper
|
||||
class PlayerViewHolderTest : SysuiTestCase() {
|
||||
|
||||
private lateinit var inflater: LayoutInflater
|
||||
private lateinit var parent: ViewGroup
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
inflater = LayoutInflater.from(context)
|
||||
parent = FrameLayout(context)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun create() {
|
||||
val holder = PlayerViewHolder.create(inflater, parent)
|
||||
assertThat(holder.player).isNotNull()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun backgroundIsIlluminationDrawable() {
|
||||
val holder = PlayerViewHolder.create(inflater, parent)
|
||||
assertThat(holder.player.background as IlluminationDrawable).isNotNull()
|
||||
}
|
||||
}
|
||||
@@ -43,7 +43,7 @@ class SeekBarObserverTest : SysuiTestCase() {
|
||||
private val enabledHeight = 2
|
||||
|
||||
private lateinit var observer: SeekBarObserver
|
||||
@Mock private lateinit var mockHolder: PlayerViewHolder
|
||||
@Mock private lateinit var mockHolder: MediaViewHolder
|
||||
@Mock private lateinit var mockSquigglyProgress: SquigglyProgress
|
||||
private lateinit var seekBarView: SeekBar
|
||||
private lateinit var elapsedTimeView: TextView
|
||||
@@ -64,10 +64,8 @@ class SeekBarObserverTest : SysuiTestCase() {
|
||||
elapsedTimeView = TextView(context)
|
||||
totalTimeView = TextView(context)
|
||||
whenever(mockHolder.seekBar).thenReturn(seekBarView)
|
||||
whenever(mockHolder.elapsedTimeView).thenReturn(elapsedTimeView)
|
||||
whenever(mockHolder.totalTimeView).thenReturn(totalTimeView)
|
||||
|
||||
observer = SeekBarObserver(mockHolder, false /* useSessionLayout */)
|
||||
observer = SeekBarObserver(mockHolder)
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -79,8 +77,6 @@ class SeekBarObserverTest : SysuiTestCase() {
|
||||
// THEN seek bar shows just a thin line with no text
|
||||
assertThat(seekBarView.isEnabled()).isFalse()
|
||||
assertThat(seekBarView.getThumb().getAlpha()).isEqualTo(0)
|
||||
assertThat(elapsedTimeView.getText()).isEqualTo("")
|
||||
assertThat(totalTimeView.getText()).isEqualTo("")
|
||||
assertThat(seekBarView.contentDescription).isEqualTo("")
|
||||
assertThat(seekBarView.maxHeight).isEqualTo(disabledHeight)
|
||||
}
|
||||
@@ -93,8 +89,6 @@ class SeekBarObserverTest : SysuiTestCase() {
|
||||
observer.onChanged(data)
|
||||
// THEN seek bar is visible and thick
|
||||
assertThat(seekBarView.getVisibility()).isEqualTo(View.VISIBLE)
|
||||
assertThat(elapsedTimeView.getVisibility()).isEqualTo(View.VISIBLE)
|
||||
assertThat(totalTimeView.getVisibility()).isEqualTo(View.VISIBLE)
|
||||
assertThat(seekBarView.maxHeight).isEqualTo(enabledHeight)
|
||||
}
|
||||
|
||||
@@ -106,8 +100,6 @@ class SeekBarObserverTest : SysuiTestCase() {
|
||||
// THEN seek bar shows the progress
|
||||
assertThat(seekBarView.progress).isEqualTo(3000)
|
||||
assertThat(seekBarView.max).isEqualTo(120000)
|
||||
assertThat(elapsedTimeView.getText()).isEqualTo("00:03")
|
||||
assertThat(totalTimeView.getText()).isEqualTo("02:00")
|
||||
|
||||
val desc = context.getString(R.string.controls_media_seekbar_description, "00:03", "02:00")
|
||||
assertThat(seekBarView.contentDescription).isEqualTo(desc)
|
||||
|
||||
@@ -8,7 +8,6 @@ import com.android.systemui.R
|
||||
import com.android.systemui.SysuiTestCase
|
||||
import com.android.systemui.dump.DumpManager
|
||||
import com.android.systemui.flags.FeatureFlags
|
||||
import com.android.systemui.media.MediaFlags
|
||||
import com.android.systemui.media.MediaHost
|
||||
import com.android.systemui.media.MediaHostState
|
||||
import com.android.systemui.plugins.FalsingManager
|
||||
@@ -49,7 +48,6 @@ class QSPanelControllerTest : SysuiTestCase() {
|
||||
@Mock private lateinit var brightnessSliderFactory: BrightnessSliderController.Factory
|
||||
@Mock private lateinit var falsingManager: FalsingManager
|
||||
@Mock private lateinit var featureFlags: FeatureFlags
|
||||
@Mock private lateinit var mediaFlags: MediaFlags
|
||||
@Mock private lateinit var mediaHost: MediaHost
|
||||
|
||||
private lateinit var controller: QSPanelController
|
||||
@@ -79,8 +77,7 @@ class QSPanelControllerTest : SysuiTestCase() {
|
||||
brightnessControllerFactory,
|
||||
brightnessSliderFactory,
|
||||
falsingManager,
|
||||
featureFlags,
|
||||
mediaFlags
|
||||
featureFlags
|
||||
)
|
||||
}
|
||||
|
||||
@@ -90,45 +87,12 @@ class QSPanelControllerTest : SysuiTestCase() {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun onInit_notSplitShade_newMediaLayoutAvailable_setsMediaAsExpanded() {
|
||||
setSplitShadeEnabled(false)
|
||||
whenever(mediaFlags.useMediaSessionLayout()).thenReturn(true)
|
||||
|
||||
fun onInit_setsMediaAsExpanded() {
|
||||
controller.onInit()
|
||||
|
||||
verify(mediaHost).expansion = MediaHostState.EXPANDED
|
||||
}
|
||||
|
||||
@Test
|
||||
fun onInit_notSplitShade_newMediaLayoutNotAvailable_setsMediaAsExpanded() {
|
||||
setSplitShadeEnabled(false)
|
||||
whenever(mediaFlags.useMediaSessionLayout()).thenReturn(false)
|
||||
|
||||
controller.onInit()
|
||||
|
||||
verify(mediaHost).expansion = MediaHostState.EXPANDED
|
||||
}
|
||||
|
||||
@Test
|
||||
fun onInit_inSplitShade_newMediaLayoutAvailable_setsMediaAsExpanded() {
|
||||
setSplitShadeEnabled(true)
|
||||
whenever(mediaFlags.useMediaSessionLayout()).thenReturn(true)
|
||||
|
||||
controller.onInit()
|
||||
|
||||
verify(mediaHost).expansion = MediaHostState.EXPANDED
|
||||
}
|
||||
|
||||
@Test
|
||||
fun onInit_inSplitShade_newMediaLayoutNotAvailable_setsMediaAsCollapsed() {
|
||||
setSplitShadeEnabled(true)
|
||||
whenever(mediaFlags.useMediaSessionLayout()).thenReturn(false)
|
||||
|
||||
controller.onInit()
|
||||
|
||||
verify(mediaHost).expansion = MediaHostState.COLLAPSED
|
||||
}
|
||||
|
||||
private fun setSplitShadeEnabled(enabled: Boolean) {
|
||||
mContext.orCreateTestableResources
|
||||
.addOverride(R.bool.config_use_split_notification_shade, enabled)
|
||||
|
||||
@@ -23,7 +23,6 @@ import com.android.internal.logging.MetricsLogger
|
||||
import com.android.internal.logging.testing.UiEventLoggerFake
|
||||
import com.android.systemui.SysuiTestCase
|
||||
import com.android.systemui.dump.DumpManager
|
||||
import com.android.systemui.media.MediaFlags
|
||||
import com.android.systemui.media.MediaHost
|
||||
import com.android.systemui.media.MediaHostState
|
||||
import com.android.systemui.plugins.qs.QSTile
|
||||
@@ -58,8 +57,6 @@ class QuickQSPanelControllerTest : SysuiTestCase() {
|
||||
@Mock
|
||||
private lateinit var mediaHost: MediaHost
|
||||
@Mock
|
||||
private lateinit var mediaFlags: MediaFlags
|
||||
@Mock
|
||||
private lateinit var metricsLogger: MetricsLogger
|
||||
private val uiEventLogger = UiEventLoggerFake()
|
||||
@Mock
|
||||
@@ -85,7 +82,6 @@ class QuickQSPanelControllerTest : SysuiTestCase() {
|
||||
`when`(quickQSPanel.dumpableTag).thenReturn("")
|
||||
`when`(quickQSPanel.resources).thenReturn(mContext.resources)
|
||||
`when`(qsTileHost.createTileView(any(), any(), anyBoolean())).thenReturn(tileView)
|
||||
`when`(mediaFlags.useMediaSessionLayout()).thenReturn(false)
|
||||
|
||||
controller = TestQuickQSPanelController(
|
||||
quickQSPanel,
|
||||
@@ -94,7 +90,6 @@ class QuickQSPanelControllerTest : SysuiTestCase() {
|
||||
false,
|
||||
mediaHost,
|
||||
true,
|
||||
mediaFlags,
|
||||
metricsLogger,
|
||||
uiEventLogger,
|
||||
qsLogger,
|
||||
@@ -131,20 +126,17 @@ class QuickQSPanelControllerTest : SysuiTestCase() {
|
||||
|
||||
@Test
|
||||
fun testMediaExpansionUpdatedWhenConfigurationChanged() {
|
||||
`when`(mediaFlags.useMediaSessionLayout()).thenReturn(true)
|
||||
|
||||
// times(2) because both controller and base controller are registering their listeners
|
||||
verify(quickQSPanel, times(2)).addOnConfigurationChangedListener(captor.capture())
|
||||
|
||||
captor.allValues.forEach { it.onConfigurationChange(Configuration.EMPTY) }
|
||||
// verify that media starts in the expanded state by default
|
||||
verify(mediaHost).expansion = MediaHostState.EXPANDED
|
||||
|
||||
// Rotate device, verify media size updated
|
||||
controller.setRotation(RotationUtils.ROTATION_LANDSCAPE)
|
||||
captor.allValues.forEach { it.onConfigurationChange(Configuration.EMPTY) }
|
||||
|
||||
// times(2) because init will have set to collapsed because the flag was off
|
||||
verify(mediaHost, times(2)).expansion = MediaHostState.COLLAPSED
|
||||
verify(mediaHost).expansion = MediaHostState.COLLAPSED
|
||||
}
|
||||
|
||||
class TestQuickQSPanelController(
|
||||
@@ -154,13 +146,12 @@ class QuickQSPanelControllerTest : SysuiTestCase() {
|
||||
usingMediaPlayer: Boolean,
|
||||
mediaHost: MediaHost,
|
||||
usingCollapsedLandscapeMedia: Boolean,
|
||||
mediaFlags: MediaFlags,
|
||||
metricsLogger: MetricsLogger,
|
||||
uiEventLogger: UiEventLoggerFake,
|
||||
qsLogger: QSLogger,
|
||||
dumpManager: DumpManager
|
||||
) : QuickQSPanelController(view, qsTileHost, qsCustomizerController, usingMediaPlayer,
|
||||
mediaHost, usingCollapsedLandscapeMedia, mediaFlags, metricsLogger, uiEventLogger, qsLogger,
|
||||
mediaHost, usingCollapsedLandscapeMedia, metricsLogger, uiEventLogger, qsLogger,
|
||||
dumpManager) {
|
||||
|
||||
private var rotation = RotationUtils.ROTATION_NONE
|
||||
|
||||
Reference in New Issue
Block a user