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

Commit 733c8b1d authored by Caitlin Cassidy's avatar Caitlin Cassidy Committed by Automerger Merge Worker
Browse files

Merge "[Media] Update recommendation a11y content description based on whether...

Merge "[Media] Update recommendation a11y content description based on whether guts is open or not." into tm-dev am: 2114ab9e

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/18199363



Change-Id: I78ba8431567fb6bff87317be61ca0525c89f27eb
Signed-off-by: default avatarAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
parents 03de78e7 2114ab9e
Loading
Loading
Loading
Loading
+37 −31
Original line number Diff line number Diff line
@@ -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
+31 −0
Original line number Diff line number Diff line
@@ -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!!
+82 −0
Original line number Diff line number Diff line
@@ -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