Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0ae9b438 authored by Caitlin Cassidy's avatar Caitlin Cassidy
Browse files

[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
parent 320a27d5
Loading
Loading
Loading
Loading
+2 −47
Original line number Diff line number Diff line
@@ -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
+4 −2
Original line number Diff line number Diff line
@@ -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"
+98 −1
Original line number Diff line number Diff line
@@ -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>
+18 −8
Original line number Diff line number Diff line
@@ -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();
+133 −3
Original line number Diff line number Diff line
@@ -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()) }