[Media Recs] Hide the title / subtitle views if none of the albums have

titles / subtitles so that the album art is centered better.

This required setting ConstraintSet values programmatically, which
re-surfaces the quirk we noticed earlier where if we set any
ConstraintSet values programmatically, then all the values in the layout
XML are erased. So, this CL also moves all constraints out of the layout
XML and into the ConstraintSet XML instead.

Fixes: 229377153
Bug: 223603970
Test: manual (verify albums are more centered) (see screenshots in bug)
Test: MediaControlPanelTest
Change-Id: Ibb3c8d9d22b30a642fef38b170c42cebdaec28cc
This commit is contained in:
Caitlin Cassidy
2022-04-20 18:21:22 +00:00
parent 320a27d5bf
commit 0ae9b43822
5 changed files with 255 additions and 61 deletions

View File

@@ -16,6 +16,8 @@
-->
<!-- Layout for media recommendations inside QSPanel carousel -->
<!-- See media_recommendation_expanded.xml and media_recommendation_collapsed.xml for the
constraints. -->
<com.android.systemui.util.animation.TransitionLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
@@ -46,14 +48,6 @@
<FrameLayout
android:id="@+id/media_cover1_container"
style="@style/MediaPlayer.Recommendation.AlbumContainer"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="@+id/media_title1"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toStartOf="@id/media_cover2_container"
android:layout_marginEnd="@dimen/qs_media_rec_album_side_margin"
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintHorizontal_bias="1.0"
app:layout_constraintVertical_bias="0.5"
>
<ImageView
android:id="@+id/media_cover1"
@@ -71,31 +65,16 @@
<TextView
android:id="@+id/media_title1"
style="@style/MediaPlayer.Recommendation.Text.Title"
app:layout_constraintStart_toStartOf="@+id/media_cover1_container"
app:layout_constraintEnd_toEndOf="@+id/media_cover1_container"
app:layout_constraintTop_toBottomOf="@+id/media_cover1_container"
app:layout_constraintBottom_toTopOf="@+id/media_subtitle1"
/>
<TextView
android:id="@+id/media_subtitle1"
style="@style/MediaPlayer.Recommendation.Text.Subtitle"
app:layout_constraintStart_toStartOf="@+id/media_cover1_container"
app:layout_constraintEnd_toEndOf="@+id/media_cover1_container"
app:layout_constraintTop_toBottomOf="@+id/media_title1"
app:layout_constraintBottom_toBottomOf="parent"
android:layout_marginBottom="@dimen/qs_media_padding"
/>
<FrameLayout
android:id="@+id/media_cover2_container"
style="@style/MediaPlayer.Recommendation.AlbumContainer"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="@id/media_title2"
app:layout_constraintStart_toEndOf="@id/media_cover1_container"
app:layout_constraintEnd_toStartOf="@id/media_cover3_container"
android:layout_marginEnd="@dimen/qs_media_rec_album_side_margin"
app:layout_constraintVertical_bias="0.5"
>
<ImageView
android:id="@+id/media_cover2"
@@ -111,31 +90,16 @@
<TextView
android:id="@+id/media_title2"
style="@style/MediaPlayer.Recommendation.Text.Title"
app:layout_constraintStart_toStartOf="@+id/media_cover2_container"
app:layout_constraintEnd_toEndOf="@+id/media_cover2_container"
app:layout_constraintTop_toBottomOf="@+id/media_cover2_container"
app:layout_constraintBottom_toTopOf="@+id/media_subtitle2"
/>
<TextView
android:id="@+id/media_subtitle2"
style="@style/MediaPlayer.Recommendation.Text.Subtitle"
app:layout_constraintStart_toStartOf="@+id/media_cover2_container"
app:layout_constraintEnd_toEndOf="@+id/media_cover2_container"
app:layout_constraintTop_toBottomOf="@+id/media_title2"
app:layout_constraintBottom_toBottomOf="parent"
android:layout_marginBottom="@dimen/qs_media_padding"
/>
<FrameLayout
android:id="@+id/media_cover3_container"
style="@style/MediaPlayer.Recommendation.AlbumContainer"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="@id/media_title3"
app:layout_constraintStart_toEndOf="@id/media_cover2_container"
app:layout_constraintEnd_toEndOf="parent"
android:layout_marginEnd="@dimen/qs_media_padding"
app:layout_constraintVertical_bias="0.5"
>
<ImageView
android:id="@+id/media_cover3"
@@ -151,20 +115,11 @@
<TextView
android:id="@+id/media_title3"
style="@style/MediaPlayer.Recommendation.Text.Title"
app:layout_constraintStart_toStartOf="@+id/media_cover3_container"
app:layout_constraintEnd_toEndOf="@+id/media_cover3_container"
app:layout_constraintTop_toBottomOf="@+id/media_cover3_container"
app:layout_constraintBottom_toTopOf="@+id/media_subtitle3"
/>
<TextView
android:id="@+id/media_subtitle3"
style="@style/MediaPlayer.Recommendation.Text.Subtitle"
app:layout_constraintStart_toStartOf="@+id/media_cover3_container"
app:layout_constraintEnd_toEndOf="@+id/media_cover3_container"
app:layout_constraintTop_toBottomOf="@+id/media_title3"
app:layout_constraintBottom_toBottomOf="parent"
android:layout_marginBottom="@dimen/qs_media_padding"
/>
<include

View File

@@ -25,8 +25,10 @@
/>
<!-- Only the constraintBottom and marginBottom are different. The rest of the constraints are
the same as the constraints in media_smartspace_recommendations. But due to how
ConstraintSets work, all the constraints need to be in the same place.
the same as the constraints in media_recommendations_expanded.xml. But, due to how
ConstraintSets work, all the constraints need to be in the same place. So, the shared
constraints can't be put in the shared layout file media_smartspace_recommendations.xml and
the constraints are instead duplicated between here and media_recommendations_expanded.xml.
Ditto for the other cover containers. -->
<Constraint
android:id="@+id/media_cover1_container"

View File

@@ -15,7 +15,9 @@
~ limitations under the License
-->
<ConstraintSet
xmlns:android="http://schemas.android.com/apk/res/android" >
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
>
<Constraint
android:id="@+id/sizing_view"
@@ -23,4 +25,99 @@
android:layout_height="@dimen/qs_media_session_height_expanded"
/>
<Constraint
android:id="@+id/media_cover1_container"
style="@style/MediaPlayer.Recommendation.AlbumContainer"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="@+id/media_title1"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toStartOf="@id/media_cover2_container"
android:layout_marginEnd="@dimen/qs_media_rec_album_side_margin"
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintVertical_chainStyle="packed"
app:layout_constraintHorizontal_bias="1.0"
app:layout_constraintVertical_bias="0.4"
/>
<Constraint
android:id="@+id/media_title1"
style="@style/MediaPlayer.Recommendation.Text.Title"
app:layout_constraintStart_toStartOf="@+id/media_cover1_container"
app:layout_constraintEnd_toEndOf="@+id/media_cover1_container"
app:layout_constraintTop_toBottomOf="@+id/media_cover1_container"
app:layout_constraintBottom_toTopOf="@+id/media_subtitle1"
/>
<Constraint
android:id="@+id/media_subtitle1"
style="@style/MediaPlayer.Recommendation.Text.Subtitle"
app:layout_constraintStart_toStartOf="@+id/media_cover1_container"
app:layout_constraintEnd_toEndOf="@+id/media_cover1_container"
app:layout_constraintTop_toBottomOf="@+id/media_title1"
app:layout_constraintBottom_toBottomOf="parent"
android:layout_marginBottom="@dimen/qs_media_padding"
/>
<Constraint
android:id="@+id/media_cover2_container"
style="@style/MediaPlayer.Recommendation.AlbumContainer"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="@id/media_title2"
app:layout_constraintStart_toEndOf="@id/media_cover1_container"
app:layout_constraintEnd_toStartOf="@id/media_cover3_container"
android:layout_marginEnd="@dimen/qs_media_rec_album_side_margin"
app:layout_constraintVertical_chainStyle="packed"
app:layout_constraintVertical_bias="0.4"
/>
<Constraint
android:id="@+id/media_title2"
style="@style/MediaPlayer.Recommendation.Text.Title"
app:layout_constraintStart_toStartOf="@+id/media_cover2_container"
app:layout_constraintEnd_toEndOf="@+id/media_cover2_container"
app:layout_constraintTop_toBottomOf="@+id/media_cover2_container"
app:layout_constraintBottom_toTopOf="@+id/media_subtitle2"
/>
<Constraint
android:id="@+id/media_subtitle2"
style="@style/MediaPlayer.Recommendation.Text.Subtitle"
app:layout_constraintStart_toStartOf="@+id/media_cover2_container"
app:layout_constraintEnd_toEndOf="@+id/media_cover2_container"
app:layout_constraintTop_toBottomOf="@+id/media_title2"
app:layout_constraintBottom_toBottomOf="parent"
android:layout_marginBottom="@dimen/qs_media_padding"
/>
<Constraint
android:id="@+id/media_cover3_container"
style="@style/MediaPlayer.Recommendation.AlbumContainer"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="@id/media_title3"
app:layout_constraintStart_toEndOf="@id/media_cover2_container"
app:layout_constraintEnd_toEndOf="parent"
android:layout_marginEnd="@dimen/qs_media_padding"
app:layout_constraintVertical_chainStyle="packed"
app:layout_constraintVertical_bias="0.4"
/>
<Constraint
android:id="@+id/media_title3"
style="@style/MediaPlayer.Recommendation.Text.Title"
app:layout_constraintStart_toStartOf="@+id/media_cover3_container"
app:layout_constraintEnd_toEndOf="@+id/media_cover3_container"
app:layout_constraintTop_toBottomOf="@+id/media_cover3_container"
app:layout_constraintBottom_toTopOf="@+id/media_subtitle3"
/>
<Constraint
android:id="@+id/media_subtitle3"
style="@style/MediaPlayer.Recommendation.Text.Subtitle"
app:layout_constraintStart_toStartOf="@+id/media_cover3_container"
app:layout_constraintEnd_toEndOf="@+id/media_cover3_container"
app:layout_constraintTop_toBottomOf="@+id/media_title3"
app:layout_constraintBottom_toBottomOf="parent"
android:layout_marginBottom="@dimen/qs_media_padding"
/>
</ConstraintSet>

View File

@@ -991,6 +991,9 @@ public class MediaControlPanel {
List<ViewGroup> mediaCoverContainers = mRecommendationViewHolder.getMediaCoverContainers();
int mediaRecommendationNum = Math.min(mediaRecommendationList.size(),
MEDIA_RECOMMENDATION_MAX_NUM);
boolean hasTitle = false;
boolean hasSubtitle = false;
int uiComponentIndex = 0;
for (int itemIndex = 0;
itemIndex < mediaRecommendationNum && uiComponentIndex < mediaRecommendationNum;
@@ -1036,26 +1039,33 @@ public class MediaControlPanel {
// Set up title
CharSequence title = recommendation.getTitle();
hasTitle |= !TextUtils.isEmpty(title);
TextView titleView =
mRecommendationViewHolder.getMediaTitles().get(uiComponentIndex);
titleView.setText(title);
// TODO(b/223603970): If none of them have titles, should we then hide the views?
// Set up subtitle
CharSequence subtitle = recommendation.getSubtitle();
TextView subtitleView =
mRecommendationViewHolder.getMediaSubtitles().get(uiComponentIndex);
// It would look awkward to show a subtitle if we don't have a title.
boolean shouldShowSubtitleText = !TextUtils.isEmpty(title);
CharSequence subtitleText = shouldShowSubtitleText ? subtitle : "";
subtitleView.setText(subtitleText);
// TODO(b/223603970): If none of them have subtitles, should we then hide the views?
CharSequence subtitle = shouldShowSubtitleText ? recommendation.getSubtitle() : "";
hasSubtitle |= !TextUtils.isEmpty(subtitle);
TextView subtitleView =
mRecommendationViewHolder.getMediaSubtitles().get(uiComponentIndex);
subtitleView.setText(subtitle);
uiComponentIndex++;
}
mSmartspaceMediaItemsCount = uiComponentIndex;
// If there's no subtitles and/or titles for any of the albums, hide those views.
ConstraintSet expandedSet = mMediaViewController.getExpandedLayout();
final boolean titlesVisible = hasTitle;
final boolean subtitlesVisible = hasSubtitle;
mRecommendationViewHolder.getMediaTitles().forEach((titleView) ->
setVisibleAndAlpha(expandedSet, titleView.getId(), titlesVisible));
mRecommendationViewHolder.getMediaSubtitles().forEach((subtitleView) ->
setVisibleAndAlpha(expandedSet, subtitleView.getId(), subtitlesVisible));
// Guts
Runnable onDismissClickedRunnable = () -> {
closeGuts();

View File

@@ -1401,22 +1401,23 @@ public class MediaControlPanelTest : SysuiTestCase() {
val subtitle1 = "Subtitle1"
val subtitle2 = "Subtitle2"
val subtitle3 = "Subtitle3"
val icon = Icon.createWithResource(context, R.drawable.ic_1x_mobiledata)
val data = smartspaceData.copy(
recommendations = listOf(
SmartspaceAction.Builder("id1", title1)
.setSubtitle(subtitle1)
.setIcon(Icon.createWithResource(context, R.drawable.ic_1x_mobiledata))
.setIcon(icon)
.setExtras(Bundle.EMPTY)
.build(),
SmartspaceAction.Builder("id2", title2)
.setSubtitle(subtitle2)
.setIcon(Icon.createWithResource(context, R.drawable.ic_alarm))
.setIcon(icon)
.setExtras(Bundle.EMPTY)
.build(),
SmartspaceAction.Builder("id3", title3)
.setSubtitle(subtitle3)
.setIcon(Icon.createWithResource(context, R.drawable.ic_3g_mobiledata))
.setIcon(icon)
.setExtras(Bundle.EMPTY)
.build()
)
@@ -1449,6 +1450,135 @@ public class MediaControlPanelTest : SysuiTestCase() {
assertThat(recSubtitle1.text).isEqualTo("")
}
@Test
fun bindRecommendation_someHaveTitles_allTitleViewsShown() {
useRealConstraintSets()
player.attachRecommendation(recommendationViewHolder)
val icon = Icon.createWithResource(context, R.drawable.ic_1x_mobiledata)
val data = smartspaceData.copy(
recommendations = listOf(
SmartspaceAction.Builder("id1", "")
.setSubtitle("fake subtitle")
.setIcon(icon)
.setExtras(Bundle.EMPTY)
.build(),
SmartspaceAction.Builder("id2", "title2")
.setSubtitle("fake subtitle")
.setIcon(icon)
.setExtras(Bundle.EMPTY)
.build(),
SmartspaceAction.Builder("id3", "")
.setSubtitle("fake subtitle")
.setIcon(icon)
.setExtras(Bundle.EMPTY)
.build()
)
)
player.bindRecommendation(data)
assertThat(expandedSet.getVisibility(recTitle1.id)).isEqualTo(ConstraintSet.VISIBLE)
assertThat(expandedSet.getVisibility(recTitle2.id)).isEqualTo(ConstraintSet.VISIBLE)
assertThat(expandedSet.getVisibility(recTitle3.id)).isEqualTo(ConstraintSet.VISIBLE)
}
@Test
fun bindRecommendation_someHaveSubtitles_allSubtitleViewsShown() {
useRealConstraintSets()
player.attachRecommendation(recommendationViewHolder)
val icon = Icon.createWithResource(context, R.drawable.ic_1x_mobiledata)
val data = smartspaceData.copy(
recommendations = listOf(
SmartspaceAction.Builder("id1", "")
.setSubtitle("")
.setIcon(icon)
.setExtras(Bundle.EMPTY)
.build(),
SmartspaceAction.Builder("id2", "title2")
.setSubtitle("")
.setIcon(icon)
.setExtras(Bundle.EMPTY)
.build(),
SmartspaceAction.Builder("id3", "title3")
.setSubtitle("subtitle3")
.setIcon(icon)
.setExtras(Bundle.EMPTY)
.build()
)
)
player.bindRecommendation(data)
assertThat(expandedSet.getVisibility(recSubtitle1.id)).isEqualTo(ConstraintSet.VISIBLE)
assertThat(expandedSet.getVisibility(recSubtitle2.id)).isEqualTo(ConstraintSet.VISIBLE)
assertThat(expandedSet.getVisibility(recSubtitle3.id)).isEqualTo(ConstraintSet.VISIBLE)
}
@Test
fun bindRecommendation_noneHaveSubtitles_subtitleViewsGone() {
useRealConstraintSets()
player.attachRecommendation(recommendationViewHolder)
val data = smartspaceData.copy(
recommendations = listOf(
SmartspaceAction.Builder("id1", "title1")
.setSubtitle("")
.setIcon(Icon.createWithResource(context, R.drawable.ic_1x_mobiledata))
.setExtras(Bundle.EMPTY)
.build(),
SmartspaceAction.Builder("id2", "title2")
.setSubtitle("")
.setIcon(Icon.createWithResource(context, R.drawable.ic_alarm))
.setExtras(Bundle.EMPTY)
.build(),
SmartspaceAction.Builder("id3", "title3")
.setSubtitle("")
.setIcon(Icon.createWithResource(context, R.drawable.ic_3g_mobiledata))
.setExtras(Bundle.EMPTY)
.build()
)
)
player.bindRecommendation(data)
assertThat(expandedSet.getVisibility(recSubtitle1.id)).isEqualTo(ConstraintSet.GONE)
assertThat(expandedSet.getVisibility(recSubtitle2.id)).isEqualTo(ConstraintSet.GONE)
assertThat(expandedSet.getVisibility(recSubtitle3.id)).isEqualTo(ConstraintSet.GONE)
}
@Test
fun bindRecommendation_noneHaveTitles_titleAndSubtitleViewsGone() {
useRealConstraintSets()
player.attachRecommendation(recommendationViewHolder)
val data = smartspaceData.copy(
recommendations = listOf(
SmartspaceAction.Builder("id1", "")
.setSubtitle("subtitle1")
.setIcon(Icon.createWithResource(context, R.drawable.ic_1x_mobiledata))
.setExtras(Bundle.EMPTY)
.build(),
SmartspaceAction.Builder("id2", "")
.setSubtitle("subtitle2")
.setIcon(Icon.createWithResource(context, R.drawable.ic_alarm))
.setExtras(Bundle.EMPTY)
.build(),
SmartspaceAction.Builder("id3", "")
.setSubtitle("subtitle3")
.setIcon(Icon.createWithResource(context, R.drawable.ic_3g_mobiledata))
.setExtras(Bundle.EMPTY)
.build()
)
)
player.bindRecommendation(data)
assertThat(expandedSet.getVisibility(recTitle1.id)).isEqualTo(ConstraintSet.GONE)
assertThat(expandedSet.getVisibility(recTitle2.id)).isEqualTo(ConstraintSet.GONE)
assertThat(expandedSet.getVisibility(recTitle3.id)).isEqualTo(ConstraintSet.GONE)
assertThat(expandedSet.getVisibility(recSubtitle1.id)).isEqualTo(ConstraintSet.GONE)
assertThat(expandedSet.getVisibility(recSubtitle2.id)).isEqualTo(ConstraintSet.GONE)
assertThat(expandedSet.getVisibility(recSubtitle3.id)).isEqualTo(ConstraintSet.GONE)
}
private fun getScrubbingChangeListener(): SeekBarViewModel.ScrubbingChangeListener =
withArgCaptor { verify(seekBarViewModel).setScrubbingChangeListener(capture()) }