summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java68
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/SmartspaceMediaData.kt31
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt82
3 files changed, 150 insertions, 31 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
index 626e185dec15..8c2cb6ded678 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
@@ -97,7 +97,7 @@ import kotlin.Unit;
* A view controller used for Media Playback.
*/
public class MediaControlPanel {
- private static final String TAG = "MediaControlPanel";
+ protected static final String TAG = "MediaControlPanel";
private static final float DISABLED_ALPHA = 0.38f;
private static final String EXPORTED_SMARTSPACE_TRAMPOLINE_ACTIVITY_NAME = "com.google"
@@ -106,7 +106,7 @@ public class MediaControlPanel {
"com.google.android.apps.gsa.smartspace.extra.SMARTSPACE_INTENT";
private static final String KEY_SMARTSPACE_ARTIST_NAME = "artist_name";
private static final String KEY_SMARTSPACE_OPEN_IN_FOREGROUND = "KEY_OPEN_IN_FOREGROUND";
- private static final String KEY_SMARTSPACE_APP_NAME = "KEY_SMARTSPACE_APP_NAME";
+ protected static final String KEY_SMARTSPACE_APP_NAME = "KEY_SMARTSPACE_APP_NAME";
// Event types logged by smartspace
private static final int SMARTSPACE_CARD_CLICK_EVENT = 760;
@@ -149,6 +149,7 @@ public class MediaControlPanel {
private RecommendationViewHolder mRecommendationViewHolder;
private String mKey;
private MediaData mMediaData;
+ private SmartspaceMediaData mRecommendationData;
private MediaViewController mMediaViewController;
private MediaSession.Token mToken;
private MediaController mController;
@@ -563,6 +564,8 @@ public class MediaControlPanel {
});
}
+ // We may want to look into unifying this with bindRecommendationContentDescription if/when we
+ // do a refactor of this class.
private void bindPlayerContentDescription(MediaData data) {
if (mMediaViewHolder == null) {
return;
@@ -583,6 +586,26 @@ public class MediaControlPanel {
mMediaViewHolder.getPlayer().setContentDescription(contentDescription);
}
+ private void bindRecommendationContentDescription(SmartspaceMediaData data) {
+ if (mRecommendationViewHolder == null) {
+ return;
+ }
+
+ CharSequence contentDescription;
+ if (mMediaViewController.isGutsVisible()) {
+ contentDescription =
+ mRecommendationViewHolder.getGutsViewHolder().getGutsText().getText();
+ } else if (data != null) {
+ contentDescription = mContext.getString(
+ R.string.controls_media_smartspace_rec_description,
+ data.getAppName(mContext));
+ } else {
+ contentDescription = null;
+ }
+
+ mRecommendationViewHolder.getRecommendations().setContentDescription(contentDescription);
+ }
+
private void bindArtworkAndColors(MediaData data, boolean updateBackground) {
final int reqId = mArtworkNextBindRequestId++;
if (updateBackground) {
@@ -969,6 +992,7 @@ public class MediaControlPanel {
return;
}
+ mRecommendationData = data;
mSmartspaceId = SmallHash.hash(data.getTargetId());
mPackageName = data.getPackageName();
mInstanceId = data.getInstanceId();
@@ -984,6 +1008,12 @@ public class MediaControlPanel {
return;
}
+ CharSequence appName = data.getAppName(mContext);
+ if (appName == null) {
+ Log.w(TAG, "Fail to get media recommendation's app name");
+ return;
+ }
+
PackageManager packageManager = mContext.getPackageManager();
// Set up media source app's logo.
Drawable icon = packageManager.getApplicationIcon(applicationInfo);
@@ -991,28 +1021,11 @@ public class MediaControlPanel {
headerLogoImageView.setImageDrawable(icon);
fetchAndUpdateRecommendationColors(icon);
- // Set up media source app's label text.
- CharSequence appName = getAppName(data.getCardAction());
- if (TextUtils.isEmpty(appName)) {
- Intent launchIntent =
- packageManager.getLaunchIntentForPackage(data.getPackageName());
- if (launchIntent != null) {
- ActivityInfo launchActivity = launchIntent.resolveActivityInfo(packageManager, 0);
- appName = launchActivity.loadLabel(packageManager);
- } else {
- Log.w(TAG, "Package " + data.getPackageName()
- + " does not have a main launcher activity. Fallback to full app name");
- appName = packageManager.getApplicationLabel(applicationInfo);
- }
- }
-
// Set up media rec card's tap action if applicable.
TransitionLayout recommendationCard = mRecommendationViewHolder.getRecommendations();
setSmartspaceRecItemOnClickListener(recommendationCard, data.getCardAction(),
/* interactedSubcardRank */ -1);
- // Set up media rec card's accessibility label.
- recommendationCard.setContentDescription(
- mContext.getString(R.string.controls_media_smartspace_rec_description, appName));
+ bindRecommendationContentDescription(data);
List<ImageView> mediaCoverItems = mRecommendationViewHolder.getMediaCoverItems();
List<ViewGroup> mediaCoverContainers = mRecommendationViewHolder.getMediaCoverContainers();
@@ -1196,6 +1209,8 @@ public class MediaControlPanel {
mMediaViewController.closeGuts(immediate);
if (mMediaViewHolder != null) {
bindPlayerContentDescription(mMediaData);
+ } else if (mRecommendationViewHolder != null) {
+ bindRecommendationContentDescription(mRecommendationData);
}
}
@@ -1212,6 +1227,8 @@ public class MediaControlPanel {
mMediaViewController.openGuts();
if (mMediaViewHolder != null) {
bindPlayerContentDescription(mMediaData);
+ } else if (mRecommendationViewHolder != null) {
+ bindRecommendationContentDescription(mRecommendationData);
}
mLogger.logLongPressOpen(mUid, mPackageName, mInstanceId);
}
@@ -1327,17 +1344,6 @@ public class MediaControlPanel {
});
}
- /** Returns the upstream app name if available. */
- @Nullable
- private String getAppName(SmartspaceAction action) {
- if (action == null || action.getIntent() == null
- || action.getIntent().getExtras() == null) {
- return null;
- }
-
- return action.getIntent().getExtras().getString(KEY_SMARTSPACE_APP_NAME);
- }
-
/** Returns if the Smartspace action will open the activity in foreground. */
private boolean shouldSmartspaceRecItemOpenInForeground(SmartspaceAction action) {
if (action == null || action.getIntent() == null
diff --git a/packages/SystemUI/src/com/android/systemui/media/SmartspaceMediaData.kt b/packages/SystemUI/src/com/android/systemui/media/SmartspaceMediaData.kt
index 50a96f601443..c8f17d93bcc8 100644
--- a/packages/SystemUI/src/com/android/systemui/media/SmartspaceMediaData.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/SmartspaceMediaData.kt
@@ -17,8 +17,13 @@
package com.android.systemui.media
import android.app.smartspace.SmartspaceAction
+import android.content.Context
import android.content.Intent
+import android.content.pm.PackageManager
+import android.text.TextUtils
+import android.util.Log
import com.android.internal.logging.InstanceId
+import com.android.systemui.media.MediaControlPanel.KEY_SMARTSPACE_APP_NAME
/** State of a Smartspace media recommendations view. */
data class SmartspaceMediaData(
@@ -67,6 +72,32 @@ data class SmartspaceMediaData(
* Returns the list of [recommendations] that have valid data.
*/
fun getValidRecommendations() = recommendations.filter { it.icon != null }
+
+ /** Returns the upstream app name if available. */
+ fun getAppName(context: Context): CharSequence? {
+ val nameFromAction = cardAction?.intent?.extras?.getString(KEY_SMARTSPACE_APP_NAME)
+ if (!TextUtils.isEmpty(nameFromAction)) {
+ return nameFromAction
+ }
+
+ val packageManager = context.packageManager
+ packageManager.getLaunchIntentForPackage(packageName)?.let {
+ val launchActivity = it.resolveActivityInfo(packageManager, 0)
+ return launchActivity.loadLabel(packageManager)
+ }
+
+ Log.w(
+ TAG,
+ "Package $packageName does not have a main launcher activity. " +
+ "Fallback to full app name")
+ return try {
+ val applicationInfo = packageManager.getApplicationInfo(packageName, /* flags= */ 0)
+ packageManager.getApplicationLabel(applicationInfo)
+ } catch (e: PackageManager.NameNotFoundException) {
+ null
+ }
+ }
}
const val NUM_REQUIRED_RECOMMENDATIONS = 3
+private val TAG = SmartspaceMediaData::class.simpleName!!
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 1310d69d0d0f..18aae0edd846 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
@@ -58,6 +58,7 @@ import com.android.systemui.ActivityIntentHelper
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.broadcast.BroadcastSender
+import com.android.systemui.media.MediaControlPanel.KEY_SMARTSPACE_APP_NAME
import com.android.systemui.media.dialog.MediaOutputDialogFactory
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.plugins.FalsingManager
@@ -102,6 +103,7 @@ private const val SESSION_KEY = "SESSION_KEY"
private const val SESSION_ARTIST = "SESSION_ARTIST"
private const val SESSION_TITLE = "SESSION_TITLE"
private const val DISABLED_DEVICE_NAME = "DISABLED_DEVICE_NAME"
+private const val REC_APP_NAME = "REC APP NAME"
@SmallTest
@RunWith(AndroidTestingRunner::class)
@@ -262,6 +264,7 @@ public class MediaControlPanelTest : SysuiTestCase() {
// Set valid recommendation data
val extras = Bundle()
+ extras.putString(KEY_SMARTSPACE_APP_NAME, REC_APP_NAME)
val intent = Intent().apply {
putExtras(extras)
setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
@@ -1275,6 +1278,85 @@ public class MediaControlPanelTest : SysuiTestCase() {
verify(mediaDataManager).dismissSmartspaceRecommendation(eq(mediaKey), anyLong())
}
+ @Test
+ fun recommendation_gutsOpen_contentDescriptionIsForGuts() {
+ whenever(mediaViewController.isGutsVisible).thenReturn(true)
+ player.attachRecommendation(recommendationViewHolder)
+
+ val gutsTextString = "gutsText"
+ whenever(gutsText.text).thenReturn(gutsTextString)
+ player.bindRecommendation(smartspaceData)
+
+ val descriptionCaptor = ArgumentCaptor.forClass(CharSequence::class.java)
+ verify(viewHolder.player).contentDescription = descriptionCaptor.capture()
+ val description = descriptionCaptor.value.toString()
+
+ assertThat(description).isEqualTo(gutsTextString)
+ }
+
+ @Test
+ fun recommendation_gutsClosed_contentDescriptionIsForPlayer() {
+ whenever(mediaViewController.isGutsVisible).thenReturn(false)
+ player.attachRecommendation(recommendationViewHolder)
+
+ player.bindRecommendation(smartspaceData)
+
+ val descriptionCaptor = ArgumentCaptor.forClass(CharSequence::class.java)
+ verify(viewHolder.player).contentDescription = descriptionCaptor.capture()
+ val description = descriptionCaptor.value.toString()
+
+ assertThat(description).contains(REC_APP_NAME)
+ }
+
+ @Test
+ fun recommendation_gutsChangesFromOpenToClosed_contentDescriptionUpdated() {
+ // Start out open
+ whenever(mediaViewController.isGutsVisible).thenReturn(true)
+ whenever(gutsText.text).thenReturn("gutsText")
+ player.attachRecommendation(recommendationViewHolder)
+ player.bindRecommendation(smartspaceData)
+
+ // 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(REC_APP_NAME)
+ }
+
+ @Test
+ fun recommendation_gutsChangesFromClosedToOpen_contentDescriptionUpdated() {
+ // Start out closed
+ whenever(mediaViewController.isGutsVisible).thenReturn(false)
+ val gutsTextString = "gutsText"
+ whenever(gutsText.text).thenReturn(gutsTextString)
+ player.attachRecommendation(recommendationViewHolder)
+ player.bindRecommendation(smartspaceData)
+
+ // 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 recommendations ***** */
@Test