Merge "Update media recommendation card layout" into tm-qpr-dev am: 121599e660
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/21092056 Change-Id: I206bb3e7d9aa19ea2afb94782c3e633e5fb0c7b2 Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
65
packages/SystemUI/res/layout/media_recommendation_view.xml
Normal file
65
packages/SystemUI/res/layout/media_recommendation_view.xml
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ Copyright (C) 2023 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 recommendation item inside QSPanel carousel -->
|
||||||
|
<merge xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<!-- Album cover -->
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/media_cover"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:translationZ="0dp"
|
||||||
|
android:scaleType="centerCrop"
|
||||||
|
android:adjustViewBounds="true"
|
||||||
|
android:clipToOutline="true"
|
||||||
|
android:background="@drawable/bg_smartspace_media_item"/>
|
||||||
|
|
||||||
|
<!-- App icon -->
|
||||||
|
<com.android.internal.widget.CachingIconView
|
||||||
|
android:id="@+id/media_rec_app_icon"
|
||||||
|
android:layout_width="@dimen/qs_media_rec_icon_top_margin"
|
||||||
|
android:layout_height="@dimen/qs_media_rec_icon_top_margin"
|
||||||
|
android:layout_marginStart="@dimen/qs_media_info_spacing"
|
||||||
|
android:layout_marginTop="@dimen/qs_media_info_spacing"/>
|
||||||
|
|
||||||
|
<!-- Artist name -->
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/media_title"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="@dimen/qs_media_info_spacing"
|
||||||
|
android:layout_marginEnd="@dimen/qs_media_info_spacing"
|
||||||
|
android:layout_marginBottom="@dimen/qs_media_rec_album_title_bottom_margin"
|
||||||
|
android:fontFamily="@*android:string/config_headlineFontFamilyMedium"
|
||||||
|
android:singleLine="true"
|
||||||
|
android:textSize="12sp"
|
||||||
|
android:gravity="top"
|
||||||
|
android:layout_gravity="bottom"/>
|
||||||
|
|
||||||
|
<!-- Album name -->
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/media_subtitle"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="@dimen/qs_media_rec_album_subtitle_height"
|
||||||
|
android:layout_marginEnd="@dimen/qs_media_info_spacing"
|
||||||
|
android:layout_marginStart="@dimen/qs_media_info_spacing"
|
||||||
|
android:layout_marginBottom="@dimen/qs_media_info_spacing"
|
||||||
|
android:fontFamily="@*android:string/config_headlineFontFamily"
|
||||||
|
android:singleLine="true"
|
||||||
|
android:textSize="11sp"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:layout_gravity="bottom"/>
|
||||||
|
</merge>
|
||||||
75
packages/SystemUI/res/layout/media_recommendations.xml
Normal file
75
packages/SystemUI/res/layout/media_recommendations.xml
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ Copyright (C) 2023 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 recommendations inside QSPanel carousel -->
|
||||||
|
<com.android.systemui.util.animation.TransitionLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:id="@+id/media_recommendations_updated"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:clipChildren="false"
|
||||||
|
android:clipToPadding="false"
|
||||||
|
android:forceHasOverlappingRendering="false"
|
||||||
|
android:background="@drawable/qs_media_background"
|
||||||
|
android:theme="@style/MediaPlayer">
|
||||||
|
|
||||||
|
<!-- This view just ensures the full media player is a certain height. -->
|
||||||
|
<View
|
||||||
|
android:id="@+id/sizing_view"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="@dimen/qs_media_session_height_expanded" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/media_rec_title"
|
||||||
|
style="@style/MediaPlayer.Recommendation.Header"
|
||||||
|
android:text="@string/controls_media_smartspace_rec_header"/>
|
||||||
|
|
||||||
|
<FrameLayout
|
||||||
|
android:id="@+id/media_cover1_container"
|
||||||
|
style="@style/MediaPlayer.Recommendation.AlbumContainer.Updated"
|
||||||
|
>
|
||||||
|
|
||||||
|
<include
|
||||||
|
layout="@layout/media_recommendation_view"/>
|
||||||
|
|
||||||
|
</FrameLayout>
|
||||||
|
|
||||||
|
|
||||||
|
<FrameLayout
|
||||||
|
android:id="@+id/media_cover2_container"
|
||||||
|
style="@style/MediaPlayer.Recommendation.AlbumContainer.Updated"
|
||||||
|
>
|
||||||
|
|
||||||
|
<include
|
||||||
|
layout="@layout/media_recommendation_view"/>
|
||||||
|
|
||||||
|
</FrameLayout>
|
||||||
|
|
||||||
|
<FrameLayout
|
||||||
|
android:id="@+id/media_cover3_container"
|
||||||
|
style="@style/MediaPlayer.Recommendation.AlbumContainer.Updated"
|
||||||
|
>
|
||||||
|
|
||||||
|
<include
|
||||||
|
layout="@layout/media_recommendation_view"/>
|
||||||
|
|
||||||
|
</FrameLayout>
|
||||||
|
|
||||||
|
<include
|
||||||
|
layout="@layout/media_long_press_menu" />
|
||||||
|
|
||||||
|
</com.android.systemui.util.animation.TransitionLayout>
|
||||||
@@ -1068,8 +1068,13 @@
|
|||||||
<!-- Size of Smartspace media recommendations cards in the QSPanel carousel -->
|
<!-- Size of Smartspace media recommendations cards in the QSPanel carousel -->
|
||||||
<dimen name="qs_media_rec_icon_top_margin">16dp</dimen>
|
<dimen name="qs_media_rec_icon_top_margin">16dp</dimen>
|
||||||
<dimen name="qs_media_rec_album_size">88dp</dimen>
|
<dimen name="qs_media_rec_album_size">88dp</dimen>
|
||||||
|
<dimen name="qs_media_rec_album_width">110dp</dimen>
|
||||||
|
<dimen name="qs_media_rec_album_height_expanded">108dp</dimen>
|
||||||
|
<dimen name="qs_media_rec_album_height_collapsed">77dp</dimen>
|
||||||
<dimen name="qs_media_rec_album_side_margin">16dp</dimen>
|
<dimen name="qs_media_rec_album_side_margin">16dp</dimen>
|
||||||
<dimen name="qs_media_rec_album_bottom_margin">8dp</dimen>
|
<dimen name="qs_media_rec_album_bottom_margin">8dp</dimen>
|
||||||
|
<dimen name="qs_media_rec_album_title_bottom_margin">22dp</dimen>
|
||||||
|
<dimen name="qs_media_rec_album_subtitle_height">12dp</dimen>
|
||||||
|
|
||||||
<!-- Media tap-to-transfer chip for sender device -->
|
<!-- Media tap-to-transfer chip for sender device -->
|
||||||
<dimen name="media_ttt_chip_outer_padding">16dp</dimen>
|
<dimen name="media_ttt_chip_outer_padding">16dp</dimen>
|
||||||
|
|||||||
@@ -2360,6 +2360,8 @@
|
|||||||
<string name="controls_media_smartspace_rec_item_description">Play <xliff:g id="song_name" example="Daily mix">%1$s</xliff:g> by <xliff:g id="artist_name" example="Various artists">%2$s</xliff:g> from <xliff:g id="app_label" example="Spotify">%3$s</xliff:g></string>
|
<string name="controls_media_smartspace_rec_item_description">Play <xliff:g id="song_name" example="Daily mix">%1$s</xliff:g> by <xliff:g id="artist_name" example="Various artists">%2$s</xliff:g> from <xliff:g id="app_label" example="Spotify">%3$s</xliff:g></string>
|
||||||
<!-- Description for Smartspace recommendation's media item which doesn't have artist info, including information for the media's title and the source app [CHAR LIMIT=NONE]-->
|
<!-- Description for Smartspace recommendation's media item which doesn't have artist info, including information for the media's title and the source app [CHAR LIMIT=NONE]-->
|
||||||
<string name="controls_media_smartspace_rec_item_no_artist_description">Play <xliff:g id="song_name" example="Daily mix">%1$s</xliff:g> from <xliff:g id="app_label" example="Spotify">%2$s</xliff:g></string>
|
<string name="controls_media_smartspace_rec_item_no_artist_description">Play <xliff:g id="song_name" example="Daily mix">%1$s</xliff:g> from <xliff:g id="app_label" example="Spotify">%2$s</xliff:g></string>
|
||||||
|
<!-- Header title for Smartspace recommendation card within media controls. [CHAR_LIMIT=30] -->
|
||||||
|
<string name="controls_media_smartspace_rec_header">For You</string>
|
||||||
|
|
||||||
<!--- ****** Media tap-to-transfer ****** -->
|
<!--- ****** Media tap-to-transfer ****** -->
|
||||||
<!-- Text for a button to undo the media transfer. [CHAR LIMIT=20] -->
|
<!-- Text for a button to undo the media transfer. [CHAR LIMIT=20] -->
|
||||||
|
|||||||
@@ -678,6 +678,17 @@
|
|||||||
|
|
||||||
<style name="MediaPlayer.Recommendation"/>
|
<style name="MediaPlayer.Recommendation"/>
|
||||||
|
|
||||||
|
<style name="MediaPlayer.Recommendation.Header">
|
||||||
|
<item name="android:layout_width">wrap_content</item>
|
||||||
|
<item name="android:layout_height">wrap_content</item>
|
||||||
|
<item name="android:layout_marginTop">@dimen/qs_media_padding</item>
|
||||||
|
<item name="android:layout_marginStart">@dimen/qs_media_padding</item>
|
||||||
|
<item name="android:fontFamily">=@*android:string/config_headlineFontFamilyMedium</item>
|
||||||
|
<item name="android:singleLine">true</item>
|
||||||
|
<item name="android:textSize">14sp</item>
|
||||||
|
<item name="android:textColor">?android:attr/textColorPrimary</item>
|
||||||
|
</style>
|
||||||
|
|
||||||
<style name="MediaPlayer.Recommendation.AlbumContainer">
|
<style name="MediaPlayer.Recommendation.AlbumContainer">
|
||||||
<item name="android:layout_width">@dimen/qs_media_rec_album_size</item>
|
<item name="android:layout_width">@dimen/qs_media_rec_album_size</item>
|
||||||
<item name="android:layout_height">@dimen/qs_media_rec_album_size</item>
|
<item name="android:layout_height">@dimen/qs_media_rec_album_size</item>
|
||||||
@@ -686,6 +697,12 @@
|
|||||||
<item name="android:layout_marginBottom">@dimen/qs_media_rec_album_bottom_margin</item>
|
<item name="android:layout_marginBottom">@dimen/qs_media_rec_album_bottom_margin</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
<style name="MediaPlayer.Recommendation.AlbumContainer.Updated">
|
||||||
|
<item name="android:layout_width">@dimen/qs_media_rec_album_width</item>
|
||||||
|
<item name="android:background">@drawable/qs_media_light_source</item>
|
||||||
|
<item name="android:layout_marginTop">@dimen/qs_media_info_spacing</item>
|
||||||
|
</style>
|
||||||
|
|
||||||
<style name="MediaPlayer.Recommendation.Album">
|
<style name="MediaPlayer.Recommendation.Album">
|
||||||
<item name="android:backgroundTint">@color/media_player_album_bg</item>
|
<item name="android:backgroundTint">@color/media_player_album_bg</item>
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -0,0 +1,64 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ Copyright (C) 2023 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/sizing_view"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="@dimen/qs_media_session_height_collapsed"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Constraint
|
||||||
|
android:id="@+id/media_rec_title"
|
||||||
|
style="@style/MediaPlayer.Recommendation.Header"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"/>
|
||||||
|
|
||||||
|
<Constraint
|
||||||
|
android:id="@+id/media_cover1_container"
|
||||||
|
style="@style/MediaPlayer.Recommendation.AlbumContainer.Updated"
|
||||||
|
android:layout_height="@dimen/qs_media_rec_album_height_collapsed"
|
||||||
|
android:layout_marginEnd="@dimen/qs_media_info_spacing"
|
||||||
|
android:layout_marginStart="@dimen/qs_media_padding"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/media_rec_title"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintEnd_toStartOf="@id/media_cover2_container"/>
|
||||||
|
|
||||||
|
|
||||||
|
<Constraint
|
||||||
|
android:id="@+id/media_cover2_container"
|
||||||
|
style="@style/MediaPlayer.Recommendation.AlbumContainer.Updated"
|
||||||
|
android:layout_height="@dimen/qs_media_rec_album_height_collapsed"
|
||||||
|
android:layout_marginEnd="@dimen/qs_media_info_spacing"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/media_rec_title"
|
||||||
|
app:layout_constraintStart_toEndOf="@id/media_cover1_container"
|
||||||
|
app:layout_constraintEnd_toStartOf="@id/media_cover3_container"/>
|
||||||
|
|
||||||
|
<Constraint
|
||||||
|
android:id="@+id/media_cover3_container"
|
||||||
|
style="@style/MediaPlayer.Recommendation.AlbumContainer.Updated"
|
||||||
|
android:layout_height="@dimen/qs_media_rec_album_height_collapsed"
|
||||||
|
android:layout_marginEnd="@dimen/qs_media_padding"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/media_rec_title"
|
||||||
|
app:layout_constraintStart_toEndOf="@id/media_cover2_container"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"/>
|
||||||
|
|
||||||
|
|
||||||
|
</ConstraintSet>
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ Copyright (C) 2023 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/sizing_view"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="@dimen/qs_media_session_height_expanded"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Constraint
|
||||||
|
android:id="@+id/media_rec_title"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="@dimen/qs_media_padding"
|
||||||
|
android:layout_marginStart="@dimen/qs_media_padding"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
android:fontFamily="@*android:string/config_headlineFontFamilyMedium"
|
||||||
|
android:singleLine="true"
|
||||||
|
android:textSize="14sp"
|
||||||
|
android:textColor="@color/notification_primary_text_color"/>
|
||||||
|
|
||||||
|
<Constraint
|
||||||
|
android:id="@+id/media_cover1_container"
|
||||||
|
style="@style/MediaPlayer.Recommendation.AlbumContainer.Updated"
|
||||||
|
android:layout_height="@dimen/qs_media_rec_album_height_expanded"
|
||||||
|
android:layout_marginEnd="@dimen/qs_media_info_spacing"
|
||||||
|
android:layout_marginStart="@dimen/qs_media_padding"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/media_rec_title"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintEnd_toStartOf="@id/media_cover2_container"/>
|
||||||
|
|
||||||
|
|
||||||
|
<Constraint
|
||||||
|
android:id="@+id/media_cover2_container"
|
||||||
|
style="@style/MediaPlayer.Recommendation.AlbumContainer.Updated"
|
||||||
|
android:layout_height="@dimen/qs_media_rec_album_height_expanded"
|
||||||
|
android:layout_marginEnd="@dimen/qs_media_info_spacing"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/media_rec_title"
|
||||||
|
app:layout_constraintStart_toEndOf="@id/media_cover1_container"
|
||||||
|
app:layout_constraintEnd_toStartOf="@id/media_cover3_container"/>
|
||||||
|
|
||||||
|
<Constraint
|
||||||
|
android:id="@+id/media_cover3_container"
|
||||||
|
style="@style/MediaPlayer.Recommendation.AlbumContainer.Updated"
|
||||||
|
android:layout_height="@dimen/qs_media_rec_album_height_expanded"
|
||||||
|
android:layout_marginEnd="@dimen/qs_media_padding"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/media_rec_title"
|
||||||
|
app:layout_constraintStart_toEndOf="@id/media_cover2_container"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"/>
|
||||||
|
|
||||||
|
|
||||||
|
</ConstraintSet>
|
||||||
@@ -365,6 +365,10 @@ object Flags {
|
|||||||
// TODO(b/266157412): Tracking Bug
|
// TODO(b/266157412): Tracking Bug
|
||||||
val MEDIA_RETAIN_SESSIONS = unreleasedFlag(913, "media_retain_sessions")
|
val MEDIA_RETAIN_SESSIONS = unreleasedFlag(913, "media_retain_sessions")
|
||||||
|
|
||||||
|
// TODO(b/266739309): Tracking Bug
|
||||||
|
@JvmField
|
||||||
|
val MEDIA_RECOMMENDATION_CARD_UPDATE = unreleasedFlag(914, "media_recommendation_card_update")
|
||||||
|
|
||||||
// 1000 - dock
|
// 1000 - dock
|
||||||
val SIMULATE_DOCK_THROUGH_CHARGING = releasedFlag(1000, "simulate_dock_through_charging")
|
val SIMULATE_DOCK_THROUGH_CHARGING = releasedFlag(1000, "simulate_dock_through_charging")
|
||||||
|
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import android.view.View
|
|||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.ImageView
|
import android.widget.ImageView
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
|
import com.android.internal.widget.CachingIconView
|
||||||
import com.android.systemui.R
|
import com.android.systemui.R
|
||||||
import com.android.systemui.media.controls.models.GutsViewHolder
|
import com.android.systemui.media.controls.models.GutsViewHolder
|
||||||
import com.android.systemui.media.controls.ui.IlluminationDrawable
|
import com.android.systemui.media.controls.ui.IlluminationDrawable
|
||||||
@@ -29,18 +30,15 @@ import com.android.systemui.util.animation.TransitionLayout
|
|||||||
private const val TAG = "RecommendationViewHolder"
|
private const val TAG = "RecommendationViewHolder"
|
||||||
|
|
||||||
/** ViewHolder for a Smartspace media recommendation. */
|
/** ViewHolder for a Smartspace media recommendation. */
|
||||||
class RecommendationViewHolder private constructor(itemView: View) {
|
class RecommendationViewHolder private constructor(itemView: View, updatedView: Boolean) {
|
||||||
|
|
||||||
val recommendations = itemView as TransitionLayout
|
val recommendations = itemView as TransitionLayout
|
||||||
|
|
||||||
// Recommendation screen
|
// Recommendation screen
|
||||||
val cardIcon = itemView.requireViewById<ImageView>(R.id.recommendation_card_icon)
|
lateinit var cardIcon: ImageView
|
||||||
val mediaCoverItems =
|
lateinit var mediaAppIcons: List<CachingIconView>
|
||||||
listOf<ImageView>(
|
lateinit var cardTitle: TextView
|
||||||
itemView.requireViewById(R.id.media_cover1),
|
|
||||||
itemView.requireViewById(R.id.media_cover2),
|
|
||||||
itemView.requireViewById(R.id.media_cover3)
|
|
||||||
)
|
|
||||||
val mediaCoverContainers =
|
val mediaCoverContainers =
|
||||||
listOf<ViewGroup>(
|
listOf<ViewGroup>(
|
||||||
itemView.requireViewById(R.id.media_cover1_container),
|
itemView.requireViewById(R.id.media_cover1_container),
|
||||||
@@ -48,21 +46,45 @@ class RecommendationViewHolder private constructor(itemView: View) {
|
|||||||
itemView.requireViewById(R.id.media_cover3_container)
|
itemView.requireViewById(R.id.media_cover3_container)
|
||||||
)
|
)
|
||||||
val mediaTitles: List<TextView> =
|
val mediaTitles: List<TextView> =
|
||||||
listOf(
|
if (updatedView) {
|
||||||
itemView.requireViewById(R.id.media_title1),
|
mediaCoverContainers.map { it.requireViewById(R.id.media_title) }
|
||||||
itemView.requireViewById(R.id.media_title2),
|
} else {
|
||||||
itemView.requireViewById(R.id.media_title3)
|
listOf(
|
||||||
)
|
itemView.requireViewById(R.id.media_title1),
|
||||||
|
itemView.requireViewById(R.id.media_title2),
|
||||||
|
itemView.requireViewById(R.id.media_title3)
|
||||||
|
)
|
||||||
|
}
|
||||||
val mediaSubtitles: List<TextView> =
|
val mediaSubtitles: List<TextView> =
|
||||||
listOf(
|
if (updatedView) {
|
||||||
itemView.requireViewById(R.id.media_subtitle1),
|
mediaCoverContainers.map { it.requireViewById(R.id.media_subtitle) }
|
||||||
itemView.requireViewById(R.id.media_subtitle2),
|
} else {
|
||||||
itemView.requireViewById(R.id.media_subtitle3)
|
listOf(
|
||||||
)
|
itemView.requireViewById(R.id.media_subtitle1),
|
||||||
|
itemView.requireViewById(R.id.media_subtitle2),
|
||||||
|
itemView.requireViewById(R.id.media_subtitle3)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
val mediaCoverItems: List<ImageView> =
|
||||||
|
if (updatedView) {
|
||||||
|
mediaCoverContainers.map { it.requireViewById(R.id.media_cover) }
|
||||||
|
} else {
|
||||||
|
listOf(
|
||||||
|
itemView.requireViewById(R.id.media_cover1),
|
||||||
|
itemView.requireViewById(R.id.media_cover2),
|
||||||
|
itemView.requireViewById(R.id.media_cover3)
|
||||||
|
)
|
||||||
|
}
|
||||||
val gutsViewHolder = GutsViewHolder(itemView)
|
val gutsViewHolder = GutsViewHolder(itemView)
|
||||||
|
|
||||||
init {
|
init {
|
||||||
|
if (updatedView) {
|
||||||
|
mediaAppIcons = mediaCoverContainers.map { it.requireViewById(R.id.media_rec_app_icon) }
|
||||||
|
cardTitle = itemView.requireViewById(R.id.media_rec_title)
|
||||||
|
} else {
|
||||||
|
cardIcon = itemView.requireViewById<ImageView>(R.id.recommendation_card_icon)
|
||||||
|
}
|
||||||
(recommendations.background as IlluminationDrawable).let { background ->
|
(recommendations.background as IlluminationDrawable).let { background ->
|
||||||
mediaCoverContainers.forEach { background.registerLightSource(it) }
|
mediaCoverContainers.forEach { background.registerLightSource(it) }
|
||||||
background.registerLightSource(gutsViewHolder.cancel)
|
background.registerLightSource(gutsViewHolder.cancel)
|
||||||
@@ -83,36 +105,52 @@ class RecommendationViewHolder private constructor(itemView: View) {
|
|||||||
* @param parent Parent of inflated view.
|
* @param parent Parent of inflated view.
|
||||||
*/
|
*/
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun create(inflater: LayoutInflater, parent: ViewGroup): RecommendationViewHolder {
|
fun create(
|
||||||
|
inflater: LayoutInflater,
|
||||||
|
parent: ViewGroup,
|
||||||
|
updatedView: Boolean,
|
||||||
|
): RecommendationViewHolder {
|
||||||
val itemView =
|
val itemView =
|
||||||
inflater.inflate(
|
if (updatedView) {
|
||||||
R.layout.media_smartspace_recommendations,
|
inflater.inflate(
|
||||||
parent,
|
R.layout.media_recommendations,
|
||||||
false /* attachToRoot */
|
parent,
|
||||||
)
|
false /* attachToRoot */
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
inflater.inflate(
|
||||||
|
R.layout.media_smartspace_recommendations,
|
||||||
|
parent,
|
||||||
|
false /* attachToRoot */
|
||||||
|
)
|
||||||
|
}
|
||||||
// Because this media view (a TransitionLayout) is used to measure and layout the views
|
// 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
|
// 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.
|
// LAYOUT_DIRECTION_INHERIT to correctly resolve the ltr direction.
|
||||||
itemView.layoutDirection = View.LAYOUT_DIRECTION_LOCALE
|
itemView.layoutDirection = View.LAYOUT_DIRECTION_LOCALE
|
||||||
return RecommendationViewHolder(itemView)
|
return RecommendationViewHolder(itemView, updatedView)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Res Ids for the control components on the recommendation view.
|
// Res Ids for the control components on the recommendation view.
|
||||||
val controlsIds =
|
val controlsIds =
|
||||||
setOf(
|
setOf(
|
||||||
R.id.recommendation_card_icon,
|
R.id.recommendation_card_icon,
|
||||||
|
R.id.media_rec_title,
|
||||||
R.id.media_cover1,
|
R.id.media_cover1,
|
||||||
R.id.media_cover2,
|
R.id.media_cover2,
|
||||||
R.id.media_cover3,
|
R.id.media_cover3,
|
||||||
|
R.id.media_cover,
|
||||||
R.id.media_cover1_container,
|
R.id.media_cover1_container,
|
||||||
R.id.media_cover2_container,
|
R.id.media_cover2_container,
|
||||||
R.id.media_cover3_container,
|
R.id.media_cover3_container,
|
||||||
R.id.media_title1,
|
R.id.media_title1,
|
||||||
R.id.media_title2,
|
R.id.media_title2,
|
||||||
R.id.media_title3,
|
R.id.media_title3,
|
||||||
|
R.id.media_title,
|
||||||
R.id.media_subtitle1,
|
R.id.media_subtitle1,
|
||||||
R.id.media_subtitle2,
|
R.id.media_subtitle2,
|
||||||
R.id.media_subtitle3
|
R.id.media_subtitle3,
|
||||||
|
R.id.media_subtitle,
|
||||||
)
|
)
|
||||||
|
|
||||||
val mediaTitlesAndSubtitlesIds =
|
val mediaTitlesAndSubtitlesIds =
|
||||||
@@ -120,9 +158,11 @@ class RecommendationViewHolder private constructor(itemView: View) {
|
|||||||
R.id.media_title1,
|
R.id.media_title1,
|
||||||
R.id.media_title2,
|
R.id.media_title2,
|
||||||
R.id.media_title3,
|
R.id.media_title3,
|
||||||
|
R.id.media_title,
|
||||||
R.id.media_subtitle1,
|
R.id.media_subtitle1,
|
||||||
R.id.media_subtitle2,
|
R.id.media_subtitle2,
|
||||||
R.id.media_subtitle3
|
R.id.media_subtitle3,
|
||||||
|
R.id.media_subtitle,
|
||||||
)
|
)
|
||||||
|
|
||||||
val mediaContainersIds =
|
val mediaContainersIds =
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ import com.android.systemui.media.controls.models.recommendation.RecommendationV
|
|||||||
import com.android.systemui.media.controls.models.recommendation.SmartspaceMediaData
|
import com.android.systemui.media.controls.models.recommendation.SmartspaceMediaData
|
||||||
import com.android.systemui.media.controls.pipeline.MediaDataManager
|
import com.android.systemui.media.controls.pipeline.MediaDataManager
|
||||||
import com.android.systemui.media.controls.ui.MediaControlPanel.SMARTSPACE_CARD_DISMISS_EVENT
|
import com.android.systemui.media.controls.ui.MediaControlPanel.SMARTSPACE_CARD_DISMISS_EVENT
|
||||||
|
import com.android.systemui.media.controls.util.MediaFlags
|
||||||
import com.android.systemui.media.controls.util.MediaUiEventLogger
|
import com.android.systemui.media.controls.util.MediaUiEventLogger
|
||||||
import com.android.systemui.media.controls.util.SmallHash
|
import com.android.systemui.media.controls.util.SmallHash
|
||||||
import com.android.systemui.plugins.ActivityStarter
|
import com.android.systemui.plugins.ActivityStarter
|
||||||
@@ -88,7 +89,8 @@ constructor(
|
|||||||
falsingManager: FalsingManager,
|
falsingManager: FalsingManager,
|
||||||
dumpManager: DumpManager,
|
dumpManager: DumpManager,
|
||||||
private val logger: MediaUiEventLogger,
|
private val logger: MediaUiEventLogger,
|
||||||
private val debugLogger: MediaCarouselControllerLogger
|
private val debugLogger: MediaCarouselControllerLogger,
|
||||||
|
private val mediaFlags: MediaFlags,
|
||||||
) : Dumpable {
|
) : Dumpable {
|
||||||
/** The current width of the carousel */
|
/** The current width of the carousel */
|
||||||
private var currentCarouselWidth: Int = 0
|
private var currentCarouselWidth: Int = 0
|
||||||
@@ -647,7 +649,11 @@ constructor(
|
|||||||
|
|
||||||
val newRecs = mediaControlPanelFactory.get()
|
val newRecs = mediaControlPanelFactory.get()
|
||||||
newRecs.attachRecommendation(
|
newRecs.attachRecommendation(
|
||||||
RecommendationViewHolder.create(LayoutInflater.from(context), mediaContent)
|
RecommendationViewHolder.create(
|
||||||
|
LayoutInflater.from(context),
|
||||||
|
mediaContent,
|
||||||
|
mediaFlags.isRecommendationCardUpdateEnabled()
|
||||||
|
)
|
||||||
)
|
)
|
||||||
newRecs.mediaViewController.sizeChangedListener = this::updateCarouselDimensions
|
newRecs.mediaViewController.sizeChangedListener = this::updateCarouselDimensions
|
||||||
val lp =
|
val lp =
|
||||||
|
|||||||
@@ -61,6 +61,7 @@ import android.widget.TextView;
|
|||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.annotation.UiThread;
|
import androidx.annotation.UiThread;
|
||||||
|
import androidx.appcompat.content.res.AppCompatResources;
|
||||||
import androidx.constraintlayout.widget.ConstraintSet;
|
import androidx.constraintlayout.widget.ConstraintSet;
|
||||||
|
|
||||||
import com.android.internal.annotations.VisibleForTesting;
|
import com.android.internal.annotations.VisibleForTesting;
|
||||||
@@ -752,43 +753,16 @@ public class MediaControlPanel {
|
|||||||
int width = mMediaViewHolder.getAlbumView().getMeasuredWidth();
|
int width = mMediaViewHolder.getAlbumView().getMeasuredWidth();
|
||||||
int height = mMediaViewHolder.getAlbumView().getMeasuredHeight();
|
int height = mMediaViewHolder.getAlbumView().getMeasuredHeight();
|
||||||
|
|
||||||
// WallpaperColors.fromBitmap takes a good amount of time. We do that work
|
|
||||||
// on the background executor to avoid stalling animations on the UI Thread.
|
|
||||||
mBackgroundExecutor.execute(() -> {
|
mBackgroundExecutor.execute(() -> {
|
||||||
// Album art
|
// Album art
|
||||||
ColorScheme mutableColorScheme = null;
|
ColorScheme mutableColorScheme = null;
|
||||||
Drawable artwork;
|
Drawable artwork;
|
||||||
boolean isArtworkBound;
|
boolean isArtworkBound;
|
||||||
Icon artworkIcon = data.getArtwork();
|
Icon artworkIcon = data.getArtwork();
|
||||||
WallpaperColors wallpaperColors = null;
|
WallpaperColors wallpaperColors = getWallpaperColor(artworkIcon);
|
||||||
if (artworkIcon != null) {
|
|
||||||
if (artworkIcon.getType() == Icon.TYPE_BITMAP
|
|
||||||
|| artworkIcon.getType() == Icon.TYPE_ADAPTIVE_BITMAP) {
|
|
||||||
// Avoids extra processing if this is already a valid bitmap
|
|
||||||
wallpaperColors = WallpaperColors
|
|
||||||
.fromBitmap(artworkIcon.getBitmap());
|
|
||||||
} else {
|
|
||||||
Drawable artworkDrawable = artworkIcon.loadDrawable(mContext);
|
|
||||||
if (artworkDrawable != null) {
|
|
||||||
wallpaperColors = WallpaperColors
|
|
||||||
.fromDrawable(artworkIcon.loadDrawable(mContext));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (wallpaperColors != null) {
|
if (wallpaperColors != null) {
|
||||||
mutableColorScheme = new ColorScheme(wallpaperColors, true, Style.CONTENT);
|
mutableColorScheme = new ColorScheme(wallpaperColors, true, Style.CONTENT);
|
||||||
Drawable albumArt = getScaledBackground(artworkIcon, width, height);
|
artwork = addGradientToIcon(artworkIcon, mutableColorScheme, width, height);
|
||||||
GradientDrawable gradient = (GradientDrawable) mContext
|
|
||||||
.getDrawable(R.drawable.qs_media_scrim);
|
|
||||||
gradient.setColors(new int[] {
|
|
||||||
ColorUtilKt.getColorWithAlpha(
|
|
||||||
MediaColorSchemesKt.backgroundStartFromScheme(mutableColorScheme),
|
|
||||||
0.25f),
|
|
||||||
ColorUtilKt.getColorWithAlpha(
|
|
||||||
MediaColorSchemesKt.backgroundEndFromScheme(mutableColorScheme),
|
|
||||||
0.9f),
|
|
||||||
});
|
|
||||||
artwork = new LayerDrawable(new Drawable[] { albumArt, gradient });
|
|
||||||
isArtworkBound = true;
|
isArtworkBound = true;
|
||||||
} else {
|
} else {
|
||||||
// If there's no artwork, use colors from the app icon
|
// If there's no artwork, use colors from the app icon
|
||||||
@@ -867,6 +841,96 @@ public class MediaControlPanel {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void bindRecommendationArtwork(
|
||||||
|
SmartspaceAction recommendation,
|
||||||
|
String packageName,
|
||||||
|
int itemIndex
|
||||||
|
) {
|
||||||
|
final int traceCookie = recommendation.hashCode();
|
||||||
|
final String traceName =
|
||||||
|
"MediaControlPanel#bindRecommendationArtwork<" + packageName + ">";
|
||||||
|
Trace.beginAsyncSection(traceName, traceCookie);
|
||||||
|
|
||||||
|
// Capture width & height from views in foreground for artwork scaling in background
|
||||||
|
int width = mRecommendationViewHolder.getMediaCoverContainers().get(0).getMeasuredWidth();
|
||||||
|
int height = mRecommendationViewHolder.getMediaCoverContainers().get(0).getMeasuredHeight();
|
||||||
|
|
||||||
|
mBackgroundExecutor.execute(() -> {
|
||||||
|
// Album art
|
||||||
|
ColorScheme mutableColorScheme = null;
|
||||||
|
Drawable artwork;
|
||||||
|
Icon artworkIcon = recommendation.getIcon();
|
||||||
|
WallpaperColors wallpaperColors = getWallpaperColor(artworkIcon);
|
||||||
|
if (wallpaperColors != null) {
|
||||||
|
mutableColorScheme = new ColorScheme(wallpaperColors, true, Style.CONTENT);
|
||||||
|
artwork = addGradientToIcon(artworkIcon, mutableColorScheme, width, height);
|
||||||
|
} else {
|
||||||
|
artwork = new ColorDrawable(Color.TRANSPARENT);
|
||||||
|
}
|
||||||
|
|
||||||
|
mMainExecutor.execute(() -> {
|
||||||
|
// Bind the artwork drawable to media cover.
|
||||||
|
ImageView mediaCover =
|
||||||
|
mRecommendationViewHolder.getMediaCoverItems().get(itemIndex);
|
||||||
|
mediaCover.setImageDrawable(artwork);
|
||||||
|
|
||||||
|
// Set up the app icon.
|
||||||
|
ImageView appIconView = mRecommendationViewHolder.getMediaAppIcons().get(itemIndex);
|
||||||
|
appIconView.clearColorFilter();
|
||||||
|
try {
|
||||||
|
Drawable icon = mContext.getPackageManager()
|
||||||
|
.getApplicationIcon(packageName);
|
||||||
|
appIconView.setImageDrawable(icon);
|
||||||
|
} catch (PackageManager.NameNotFoundException e) {
|
||||||
|
Log.w(TAG, "Cannot find icon for package " + packageName, e);
|
||||||
|
appIconView.setImageResource(R.drawable.ic_music_note);
|
||||||
|
}
|
||||||
|
Trace.endAsyncSection(traceName, traceCookie);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// This method should be called from a background thread. WallpaperColors.fromBitmap takes a
|
||||||
|
// good amount of time. We do that work on the background executor to avoid stalling animations
|
||||||
|
// on the UI Thread.
|
||||||
|
private WallpaperColors getWallpaperColor(Icon artworkIcon) {
|
||||||
|
if (artworkIcon != null) {
|
||||||
|
if (artworkIcon.getType() == Icon.TYPE_BITMAP
|
||||||
|
|| artworkIcon.getType() == Icon.TYPE_ADAPTIVE_BITMAP) {
|
||||||
|
// Avoids extra processing if this is already a valid bitmap
|
||||||
|
return WallpaperColors
|
||||||
|
.fromBitmap(artworkIcon.getBitmap());
|
||||||
|
} else {
|
||||||
|
Drawable artworkDrawable = artworkIcon.loadDrawable(mContext);
|
||||||
|
if (artworkDrawable != null) {
|
||||||
|
return WallpaperColors
|
||||||
|
.fromDrawable(artworkIcon.loadDrawable(mContext));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private LayerDrawable addGradientToIcon(
|
||||||
|
Icon artworkIcon,
|
||||||
|
ColorScheme mutableColorScheme,
|
||||||
|
int width,
|
||||||
|
int height
|
||||||
|
) {
|
||||||
|
Drawable albumArt = getScaledBackground(artworkIcon, width, height);
|
||||||
|
GradientDrawable gradient = (GradientDrawable) AppCompatResources
|
||||||
|
.getDrawable(mContext, R.drawable.qs_media_scrim);
|
||||||
|
gradient.setColors(new int[] {
|
||||||
|
ColorUtilKt.getColorWithAlpha(
|
||||||
|
MediaColorSchemesKt.backgroundStartFromScheme(mutableColorScheme),
|
||||||
|
0.25f),
|
||||||
|
ColorUtilKt.getColorWithAlpha(
|
||||||
|
MediaColorSchemesKt.backgroundEndFromScheme(mutableColorScheme),
|
||||||
|
0.9f),
|
||||||
|
});
|
||||||
|
return new LayerDrawable(new Drawable[] { albumArt, gradient });
|
||||||
|
}
|
||||||
|
|
||||||
private void scaleTransitionDrawableLayer(TransitionDrawable transitionDrawable, int layer,
|
private void scaleTransitionDrawableLayer(TransitionDrawable transitionDrawable, int layer,
|
||||||
int targetWidth, int targetHeight) {
|
int targetWidth, int targetHeight) {
|
||||||
Drawable drawable = transitionDrawable.getDrawable(layer);
|
Drawable drawable = transitionDrawable.getDrawable(layer);
|
||||||
@@ -1224,8 +1288,10 @@ public class MediaControlPanel {
|
|||||||
PackageManager packageManager = mContext.getPackageManager();
|
PackageManager packageManager = mContext.getPackageManager();
|
||||||
// Set up media source app's logo.
|
// Set up media source app's logo.
|
||||||
Drawable icon = packageManager.getApplicationIcon(applicationInfo);
|
Drawable icon = packageManager.getApplicationIcon(applicationInfo);
|
||||||
ImageView headerLogoImageView = mRecommendationViewHolder.getCardIcon();
|
if (!mFeatureFlags.isEnabled(Flags.MEDIA_RECOMMENDATION_CARD_UPDATE)) {
|
||||||
headerLogoImageView.setImageDrawable(icon);
|
ImageView headerLogoImageView = mRecommendationViewHolder.getCardIcon();
|
||||||
|
headerLogoImageView.setImageDrawable(icon);
|
||||||
|
}
|
||||||
fetchAndUpdateRecommendationColors(icon);
|
fetchAndUpdateRecommendationColors(icon);
|
||||||
|
|
||||||
// Set up media rec card's tap action if applicable.
|
// Set up media rec card's tap action if applicable.
|
||||||
@@ -1245,7 +1311,15 @@ public class MediaControlPanel {
|
|||||||
|
|
||||||
// Set up media item cover.
|
// Set up media item cover.
|
||||||
ImageView mediaCoverImageView = mediaCoverItems.get(itemIndex);
|
ImageView mediaCoverImageView = mediaCoverItems.get(itemIndex);
|
||||||
mediaCoverImageView.setImageIcon(recommendation.getIcon());
|
if (mFeatureFlags.isEnabled(Flags.MEDIA_RECOMMENDATION_CARD_UPDATE)) {
|
||||||
|
bindRecommendationArtwork(
|
||||||
|
recommendation,
|
||||||
|
data.getPackageName(),
|
||||||
|
itemIndex
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
mediaCoverImageView.setImageIcon(recommendation.getIcon());
|
||||||
|
}
|
||||||
|
|
||||||
// Set up the media item's click listener if applicable.
|
// Set up the media item's click listener if applicable.
|
||||||
ViewGroup mediaCoverContainer = mediaCoverContainers.get(itemIndex);
|
ViewGroup mediaCoverContainer = mediaCoverContainers.get(itemIndex);
|
||||||
@@ -1275,7 +1349,6 @@ public class MediaControlPanel {
|
|||||||
recommendation.getTitle(), artistName, appName));
|
recommendation.getTitle(), artistName, appName));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Set up title
|
// Set up title
|
||||||
CharSequence title = recommendation.getTitle();
|
CharSequence title = recommendation.getTitle();
|
||||||
hasTitle |= !TextUtils.isEmpty(title);
|
hasTitle |= !TextUtils.isEmpty(title);
|
||||||
@@ -1353,6 +1426,10 @@ public class MediaControlPanel {
|
|||||||
int textPrimaryColor = MediaColorSchemesKt.textPrimaryFromScheme(colorScheme);
|
int textPrimaryColor = MediaColorSchemesKt.textPrimaryFromScheme(colorScheme);
|
||||||
int textSecondaryColor = MediaColorSchemesKt.textSecondaryFromScheme(colorScheme);
|
int textSecondaryColor = MediaColorSchemesKt.textSecondaryFromScheme(colorScheme);
|
||||||
|
|
||||||
|
if (mFeatureFlags.isEnabled(Flags.MEDIA_RECOMMENDATION_CARD_UPDATE)) {
|
||||||
|
mRecommendationViewHolder.getCardTitle().setTextColor(textPrimaryColor);
|
||||||
|
}
|
||||||
|
|
||||||
mRecommendationViewHolder.getRecommendations()
|
mRecommendationViewHolder.getRecommendations()
|
||||||
.setBackgroundTintList(ColorStateList.valueOf(backgroundColor));
|
.setBackgroundTintList(ColorStateList.valueOf(backgroundColor));
|
||||||
mRecommendationViewHolder.getMediaTitles().forEach(
|
mRecommendationViewHolder.getMediaTitles().forEach(
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import com.android.systemui.media.controls.models.GutsViewHolder
|
|||||||
import com.android.systemui.media.controls.models.player.MediaViewHolder
|
import com.android.systemui.media.controls.models.player.MediaViewHolder
|
||||||
import com.android.systemui.media.controls.models.recommendation.RecommendationViewHolder
|
import com.android.systemui.media.controls.models.recommendation.RecommendationViewHolder
|
||||||
import com.android.systemui.media.controls.ui.MediaCarouselController.Companion.calculateAlpha
|
import com.android.systemui.media.controls.ui.MediaCarouselController.Companion.calculateAlpha
|
||||||
|
import com.android.systemui.media.controls.util.MediaFlags
|
||||||
import com.android.systemui.statusbar.policy.ConfigurationController
|
import com.android.systemui.statusbar.policy.ConfigurationController
|
||||||
import com.android.systemui.util.animation.MeasurementOutput
|
import com.android.systemui.util.animation.MeasurementOutput
|
||||||
import com.android.systemui.util.animation.TransitionLayout
|
import com.android.systemui.util.animation.TransitionLayout
|
||||||
@@ -45,7 +46,8 @@ constructor(
|
|||||||
private val context: Context,
|
private val context: Context,
|
||||||
private val configurationController: ConfigurationController,
|
private val configurationController: ConfigurationController,
|
||||||
private val mediaHostStatesManager: MediaHostStatesManager,
|
private val mediaHostStatesManager: MediaHostStatesManager,
|
||||||
private val logger: MediaViewLogger
|
private val logger: MediaViewLogger,
|
||||||
|
private val mediaFlags: MediaFlags,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -646,8 +648,13 @@ constructor(
|
|||||||
expandedLayout.load(context, R.xml.media_session_expanded)
|
expandedLayout.load(context, R.xml.media_session_expanded)
|
||||||
}
|
}
|
||||||
TYPE.RECOMMENDATION -> {
|
TYPE.RECOMMENDATION -> {
|
||||||
collapsedLayout.load(context, R.xml.media_recommendation_collapsed)
|
if (mediaFlags.isRecommendationCardUpdateEnabled()) {
|
||||||
expandedLayout.load(context, R.xml.media_recommendation_expanded)
|
collapsedLayout.load(context, R.xml.media_recommendations_view_collapsed)
|
||||||
|
expandedLayout.load(context, R.xml.media_recommendations_view_expanded)
|
||||||
|
} else {
|
||||||
|
collapsedLayout.load(context, R.xml.media_recommendation_collapsed)
|
||||||
|
expandedLayout.load(context, R.xml.media_recommendation_expanded)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
refreshState()
|
refreshState()
|
||||||
|
|||||||
@@ -51,4 +51,8 @@ class MediaFlags @Inject constructor(private val featureFlags: FeatureFlags) {
|
|||||||
* whether the underlying notification was dismissed
|
* whether the underlying notification was dismissed
|
||||||
*/
|
*/
|
||||||
fun isRetainingPlayersEnabled() = featureFlags.isEnabled(Flags.MEDIA_RETAIN_SESSIONS)
|
fun isRetainingPlayersEnabled() = featureFlags.isEnabled(Flags.MEDIA_RETAIN_SESSIONS)
|
||||||
|
|
||||||
|
/** Check whether we show the updated recommendation card. */
|
||||||
|
fun isRecommendationCardUpdateEnabled() =
|
||||||
|
featureFlags.isEnabled(Flags.MEDIA_RECOMMENDATION_CARD_UPDATE)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ import com.android.systemui.media.controls.models.recommendation.SmartspaceMedia
|
|||||||
import com.android.systemui.media.controls.pipeline.EMPTY_SMARTSPACE_MEDIA_DATA
|
import com.android.systemui.media.controls.pipeline.EMPTY_SMARTSPACE_MEDIA_DATA
|
||||||
import com.android.systemui.media.controls.pipeline.MediaDataManager
|
import com.android.systemui.media.controls.pipeline.MediaDataManager
|
||||||
import com.android.systemui.media.controls.ui.MediaHierarchyManager.Companion.LOCATION_QS
|
import com.android.systemui.media.controls.ui.MediaHierarchyManager.Companion.LOCATION_QS
|
||||||
|
import com.android.systemui.media.controls.util.MediaFlags
|
||||||
import com.android.systemui.media.controls.util.MediaUiEventLogger
|
import com.android.systemui.media.controls.util.MediaUiEventLogger
|
||||||
import com.android.systemui.plugins.ActivityStarter
|
import com.android.systemui.plugins.ActivityStarter
|
||||||
import com.android.systemui.plugins.FalsingManager
|
import com.android.systemui.plugins.FalsingManager
|
||||||
@@ -87,6 +88,7 @@ class MediaCarouselControllerTest : SysuiTestCase() {
|
|||||||
@Mock lateinit var smartspaceMediaData: SmartspaceMediaData
|
@Mock lateinit var smartspaceMediaData: SmartspaceMediaData
|
||||||
@Mock lateinit var mediaCarousel: MediaScrollView
|
@Mock lateinit var mediaCarousel: MediaScrollView
|
||||||
@Mock lateinit var pageIndicator: PageIndicator
|
@Mock lateinit var pageIndicator: PageIndicator
|
||||||
|
@Mock lateinit var mediaFlags: MediaFlags
|
||||||
@Captor lateinit var listener: ArgumentCaptor<MediaDataManager.Listener>
|
@Captor lateinit var listener: ArgumentCaptor<MediaDataManager.Listener>
|
||||||
@Captor
|
@Captor
|
||||||
lateinit var configListener: ArgumentCaptor<ConfigurationController.ConfigurationListener>
|
lateinit var configListener: ArgumentCaptor<ConfigurationController.ConfigurationListener>
|
||||||
@@ -114,7 +116,8 @@ class MediaCarouselControllerTest : SysuiTestCase() {
|
|||||||
falsingManager,
|
falsingManager,
|
||||||
dumpManager,
|
dumpManager,
|
||||||
logger,
|
logger,
|
||||||
debugLogger
|
debugLogger,
|
||||||
|
mediaFlags,
|
||||||
)
|
)
|
||||||
verify(configurationController).addCallback(capture(configListener))
|
verify(configurationController).addCallback(capture(configListener))
|
||||||
verify(mediaDataManager).addListener(capture(listener))
|
verify(mediaDataManager).addListener(capture(listener))
|
||||||
|
|||||||
@@ -204,6 +204,9 @@ public class MediaControlPanelTest : SysuiTestCase() {
|
|||||||
@Mock private lateinit var coverContainer1: ViewGroup
|
@Mock private lateinit var coverContainer1: ViewGroup
|
||||||
@Mock private lateinit var coverContainer2: ViewGroup
|
@Mock private lateinit var coverContainer2: ViewGroup
|
||||||
@Mock private lateinit var coverContainer3: ViewGroup
|
@Mock private lateinit var coverContainer3: ViewGroup
|
||||||
|
@Mock private lateinit var recAppIconItem: CachingIconView
|
||||||
|
@Mock private lateinit var recCardTitle: TextView
|
||||||
|
@Mock private lateinit var coverItem: ImageView
|
||||||
private lateinit var coverItem1: ImageView
|
private lateinit var coverItem1: ImageView
|
||||||
private lateinit var coverItem2: ImageView
|
private lateinit var coverItem2: ImageView
|
||||||
private lateinit var coverItem3: ImageView
|
private lateinit var coverItem3: ImageView
|
||||||
@@ -220,6 +223,7 @@ public class MediaControlPanelTest : SysuiTestCase() {
|
|||||||
this.set(Flags.UMO_TURBULENCE_NOISE, false)
|
this.set(Flags.UMO_TURBULENCE_NOISE, false)
|
||||||
this.set(Flags.MEDIA_FALSING_PENALTY, true)
|
this.set(Flags.MEDIA_FALSING_PENALTY, true)
|
||||||
this.set(Flags.MEDIA_EXPLICIT_INDICATOR, true)
|
this.set(Flags.MEDIA_EXPLICIT_INDICATOR, true)
|
||||||
|
this.set(Flags.MEDIA_RECOMMENDATION_CARD_UPDATE, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
@JvmField @Rule val mockito = MockitoJUnit.rule()
|
@JvmField @Rule val mockito = MockitoJUnit.rule()
|
||||||
@@ -2058,6 +2062,51 @@ public class MediaControlPanelTest : SysuiTestCase() {
|
|||||||
assertThat(expandedSet.getVisibility(recSubtitle3.id)).isEqualTo(ConstraintSet.GONE)
|
assertThat(expandedSet.getVisibility(recSubtitle3.id)).isEqualTo(ConstraintSet.GONE)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun bindRecommendation_setAfterExecutors() {
|
||||||
|
fakeFeatureFlag.set(Flags.MEDIA_RECOMMENDATION_CARD_UPDATE, true)
|
||||||
|
whenever(recommendationViewHolder.mediaAppIcons)
|
||||||
|
.thenReturn(listOf(recAppIconItem, recAppIconItem, recAppIconItem))
|
||||||
|
whenever(recommendationViewHolder.cardTitle).thenReturn(recCardTitle)
|
||||||
|
whenever(recommendationViewHolder.mediaCoverItems)
|
||||||
|
.thenReturn(listOf(coverItem, coverItem, coverItem))
|
||||||
|
|
||||||
|
val bmp = Bitmap.createBitmap(10, 10, Bitmap.Config.ARGB_8888)
|
||||||
|
val canvas = Canvas(bmp)
|
||||||
|
canvas.drawColor(Color.RED)
|
||||||
|
val albumArt = Icon.createWithBitmap(bmp)
|
||||||
|
val data =
|
||||||
|
smartspaceData.copy(
|
||||||
|
recommendations =
|
||||||
|
listOf(
|
||||||
|
SmartspaceAction.Builder("id1", "title1")
|
||||||
|
.setSubtitle("subtitle1")
|
||||||
|
.setIcon(albumArt)
|
||||||
|
.setExtras(Bundle.EMPTY)
|
||||||
|
.build(),
|
||||||
|
SmartspaceAction.Builder("id2", "title2")
|
||||||
|
.setSubtitle("subtitle1")
|
||||||
|
.setIcon(albumArt)
|
||||||
|
.setExtras(Bundle.EMPTY)
|
||||||
|
.build(),
|
||||||
|
SmartspaceAction.Builder("id3", "title3")
|
||||||
|
.setSubtitle("subtitle1")
|
||||||
|
.setIcon(albumArt)
|
||||||
|
.setExtras(Bundle.EMPTY)
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
player.attachRecommendation(recommendationViewHolder)
|
||||||
|
player.bindRecommendation(data)
|
||||||
|
bgExecutor.runAllReady()
|
||||||
|
mainExecutor.runAllReady()
|
||||||
|
|
||||||
|
verify(recCardTitle).setTextColor(any<Int>())
|
||||||
|
verify(recAppIconItem, times(3)).setImageDrawable(any(Drawable::class.java))
|
||||||
|
verify(coverItem, times(3)).setImageDrawable(any(Drawable::class.java))
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun onButtonClick_touchRippleFlagEnabled_playsTouchRipple() {
|
fun onButtonClick_touchRippleFlagEnabled_playsTouchRipple() {
|
||||||
fakeFeatureFlag.set(Flags.UMO_SURFACE_RIPPLE, true)
|
fakeFeatureFlag.set(Flags.UMO_SURFACE_RIPPLE, true)
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import android.view.View
|
|||||||
import androidx.test.filters.SmallTest
|
import androidx.test.filters.SmallTest
|
||||||
import com.android.systemui.R
|
import com.android.systemui.R
|
||||||
import com.android.systemui.SysuiTestCase
|
import com.android.systemui.SysuiTestCase
|
||||||
|
import com.android.systemui.media.controls.util.MediaFlags
|
||||||
import com.android.systemui.util.animation.MeasurementInput
|
import com.android.systemui.util.animation.MeasurementInput
|
||||||
import com.android.systemui.util.animation.TransitionLayout
|
import com.android.systemui.util.animation.TransitionLayout
|
||||||
import com.android.systemui.util.animation.TransitionViewState
|
import com.android.systemui.util.animation.TransitionViewState
|
||||||
@@ -55,6 +56,7 @@ class MediaViewControllerTest : SysuiTestCase() {
|
|||||||
@Mock private lateinit var mediaTitleWidgetState: WidgetState
|
@Mock private lateinit var mediaTitleWidgetState: WidgetState
|
||||||
@Mock private lateinit var mediaSubTitleWidgetState: WidgetState
|
@Mock private lateinit var mediaSubTitleWidgetState: WidgetState
|
||||||
@Mock private lateinit var mediaContainerWidgetState: WidgetState
|
@Mock private lateinit var mediaContainerWidgetState: WidgetState
|
||||||
|
@Mock private lateinit var mediaFlags: MediaFlags
|
||||||
|
|
||||||
val delta = 0.1F
|
val delta = 0.1F
|
||||||
|
|
||||||
@@ -64,7 +66,13 @@ class MediaViewControllerTest : SysuiTestCase() {
|
|||||||
fun setup() {
|
fun setup() {
|
||||||
MockitoAnnotations.initMocks(this)
|
MockitoAnnotations.initMocks(this)
|
||||||
mediaViewController =
|
mediaViewController =
|
||||||
MediaViewController(context, configurationController, mediaHostStatesManager, logger)
|
MediaViewController(
|
||||||
|
context,
|
||||||
|
configurationController,
|
||||||
|
mediaHostStatesManager,
|
||||||
|
logger,
|
||||||
|
mediaFlags,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
Reference in New Issue
Block a user