From b5c0f6c60ca63b1f5b0be2940b2d8a21d0116185 Mon Sep 17 00:00:00 2001 From: Caitlin Cassidy Date: Thu, 5 May 2022 19:54:30 +0000 Subject: [PATCH] [Media] Update player's a11y content description based on whether guts is open or not. Note: I was hoping that just setting the content description to null if guts was open would work. When I tried that, it *did* announce "Hide this control" if I tapped on the player via TalkBack, but it *did not* announce anything if I used navigation swiping until I was selecting the media player. So, this code just always sets the description. Bug: 229574744 Test: manual Test: MediaControlPanelTest Change-Id: Ifa69521289a4b7a5cd7a35f092a76827a560e3b1 --- .../systemui/media/MediaControlPanel.java | 33 +++++-- .../systemui/media/MediaControlPanelTest.kt | 85 +++++++++++++++++++ 2 files changed, 112 insertions(+), 6 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java index 2fec49955c2d2..82f935c7193f9 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java +++ b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java @@ -448,6 +448,7 @@ public class MediaControlPanel { bindOutputSwitcherChip(data); bindGutsMenuForPlayer(data); + bindPlayerContentDescription(data); bindScrubbingTime(data); bindActionButtons(data); @@ -541,12 +542,6 @@ public class MediaControlPanel { } private boolean bindSongMetadata(MediaData data) { - // Accessibility label - mMediaViewHolder.getPlayer().setContentDescription( - mContext.getString( - R.string.controls_media_playing_item_description, - data.getSong(), data.getArtist(), data.getApp())); - TextView titleText = mMediaViewHolder.getTitleText(); TextView artistText = mMediaViewHolder.getArtistText(); return mMetadataAnimationHandler.setNext( @@ -568,6 +563,26 @@ public class MediaControlPanel { }); } + private void bindPlayerContentDescription(MediaData data) { + if (mMediaViewHolder == null) { + return; + } + + CharSequence contentDescription; + if (mMediaViewController.isGutsVisible()) { + contentDescription = mMediaViewHolder.getGutsViewHolder().getGutsText().getText(); + } else if (data != null) { + contentDescription = mContext.getString( + R.string.controls_media_playing_item_description, + data.getSong(), + data.getArtist(), + data.getApp()); + } else { + contentDescription = null; + } + mMediaViewHolder.getPlayer().setContentDescription(contentDescription); + } + private void bindArtworkAndColors(MediaData data, boolean updateBackground) { final int reqId = mArtworkNextBindRequestId++; if (updateBackground) { @@ -1175,6 +1190,9 @@ public class MediaControlPanel { mRecommendationViewHolder.marquee(false, mMediaViewController.GUTS_ANIMATION_DURATION); } mMediaViewController.closeGuts(immediate); + if (mMediaViewHolder != null) { + bindPlayerContentDescription(mMediaData); + } } private void closeGuts() { @@ -1188,6 +1206,9 @@ public class MediaControlPanel { mRecommendationViewHolder.marquee(true, mMediaViewController.GUTS_ANIMATION_DURATION); } mMediaViewController.openGuts(); + if (mMediaViewHolder != null) { + bindPlayerContentDescription(mMediaData); + } mLogger.logLongPressOpen(mUid, mPackageName, mInstanceId); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt index f9fb8657d0f6f..63a0de8578d71 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt @@ -1121,6 +1121,91 @@ public class MediaControlPanelTest : SysuiTestCase() { verify(mediaCarouselController).removePlayer(eq(mediaKey), eq(false), eq(false)) } + @Test + fun player_gutsOpen_contentDescriptionIsForGuts() { + whenever(mediaViewController.isGutsVisible).thenReturn(true) + player.attachPlayer(viewHolder) + + val gutsTextString = "gutsText" + whenever(gutsText.text).thenReturn(gutsTextString) + player.bindPlayer(mediaData, KEY) + + val descriptionCaptor = ArgumentCaptor.forClass(CharSequence::class.java) + verify(viewHolder.player).contentDescription = descriptionCaptor.capture() + val description = descriptionCaptor.value.toString() + + assertThat(description).isEqualTo(gutsTextString) + } + + @Test + fun player_gutsClosed_contentDescriptionIsForPlayer() { + whenever(mediaViewController.isGutsVisible).thenReturn(false) + player.attachPlayer(viewHolder) + + val app = "appName" + player.bindPlayer(mediaData.copy(app = app), KEY) + + val descriptionCaptor = ArgumentCaptor.forClass(CharSequence::class.java) + verify(viewHolder.player).contentDescription = descriptionCaptor.capture() + val description = descriptionCaptor.value.toString() + + assertThat(description).contains(mediaData.song!!) + assertThat(description).contains(mediaData.artist!!) + assertThat(description).contains(app) + } + + @Test + fun player_gutsChangesFromOpenToClosed_contentDescriptionUpdated() { + // Start out open + whenever(mediaViewController.isGutsVisible).thenReturn(true) + whenever(gutsText.text).thenReturn("gutsText") + player.attachPlayer(viewHolder) + val app = "appName" + player.bindPlayer(mediaData.copy(app = app), KEY) + + // Update to closed by long pressing + val captor = ArgumentCaptor.forClass(View.OnLongClickListener::class.java) + verify(viewHolder.player).onLongClickListener = captor.capture() + reset(viewHolder.player) + + whenever(mediaViewController.isGutsVisible).thenReturn(false) + captor.value.onLongClick(viewHolder.player) + + // Then content description is now the player content description + val descriptionCaptor = ArgumentCaptor.forClass(CharSequence::class.java) + verify(viewHolder.player).contentDescription = descriptionCaptor.capture() + val description = descriptionCaptor.value.toString() + + assertThat(description).contains(mediaData.song!!) + assertThat(description).contains(mediaData.artist!!) + assertThat(description).contains(app) + } + + @Test + fun player_gutsChangesFromClosedToOpen_contentDescriptionUpdated() { + // Start out closed + whenever(mediaViewController.isGutsVisible).thenReturn(false) + val gutsTextString = "gutsText" + whenever(gutsText.text).thenReturn(gutsTextString) + player.attachPlayer(viewHolder) + player.bindPlayer(mediaData.copy(app = "appName"), KEY) + + // Update to open by long pressing + val captor = ArgumentCaptor.forClass(View.OnLongClickListener::class.java) + verify(viewHolder.player).onLongClickListener = captor.capture() + reset(viewHolder.player) + + whenever(mediaViewController.isGutsVisible).thenReturn(true) + captor.value.onLongClick(viewHolder.player) + + // Then content description is now the guts content description + val descriptionCaptor = ArgumentCaptor.forClass(CharSequence::class.java) + verify(viewHolder.player).contentDescription = descriptionCaptor.capture() + val description = descriptionCaptor.value.toString() + + assertThat(description).isEqualTo(gutsTextString) + } + /* ***** END guts tests for the player ***** */ /* ***** Guts tests for the recommendations ***** */