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

Commit 0c4db317 authored by timhypeng's avatar timhypeng
Browse files

Add title and icon in output switcher panel header

-title shows artist
-subtitle shows album
-add test cases

Bug: 147776885
Test: make -j42 RunSettingsRoboTests
Change-Id: Ib33e5550e668d8cc5d70051ea2e7dd74d61c767a
parent b7f5bf46
Loading
Loading
Loading
Loading
+83 −0
Original line number Diff line number Diff line
@@ -22,9 +22,22 @@ import static com.android.settings.slices.CustomSliceRegistry.MEDIA_OUTPUT_SLICE
import android.app.settings.SettingsEnums;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.media.MediaMetadata;
import android.media.session.MediaController;
import android.media.session.MediaSessionManager;
import android.net.Uri;
import android.text.TextUtils;
import android.util.Log;

import androidx.core.graphics.drawable.IconCompat;

import com.android.settings.R;
import com.android.settings.Utils;

import java.util.ArrayList;
import java.util.List;
@@ -38,9 +51,14 @@ import java.util.List;
 */
public class MediaOutputPanel implements PanelContent {

    private static final String TAG = "MediaOutputPanel";

    private final Context mContext;
    private final String mPackageName;

    private MediaSessionManager mMediaSessionManager;
    private MediaController mMediaController;

    public static MediaOutputPanel create(Context context, String packageName) {
        return new MediaOutputPanel(context, packageName);
    }
@@ -48,13 +66,78 @@ public class MediaOutputPanel implements PanelContent {
    private MediaOutputPanel(Context context, String packageName) {
        mContext = context.getApplicationContext();
        mPackageName = packageName;
        if (mPackageName != null) {
            mMediaSessionManager = mContext.getSystemService(MediaSessionManager.class);
            for (MediaController controller : mMediaSessionManager.getActiveSessions(null)) {
                if (TextUtils.equals(controller.getPackageName(), mPackageName)) {
                    mMediaController = controller;
                    break;
                }
            }
        }
        if (mMediaController == null) {
            Log.e(TAG, "Unable to find " + mPackageName + " media controller");
        }
    }

    @Override
    public CharSequence getTitle() {
        if (mMediaController != null) {
            final MediaMetadata metadata = mMediaController.getMetadata();
            if (metadata != null) {
                return metadata.getString(MediaMetadata.METADATA_KEY_ARTIST);
            }
        }
        return mContext.getText(R.string.media_volume_title);
    }

    @Override
    public CharSequence getSubTitle() {
        if (mMediaController != null) {
            final MediaMetadata metadata = mMediaController.getMetadata();
            if (metadata != null) {
                return metadata.getString(MediaMetadata.METADATA_KEY_ALBUM);
            }
        }
        return mContext.getText(R.string.media_output_panel_title);
    }

    @Override
    public IconCompat getIcon() {
        if (mMediaController == null) {
            return IconCompat.createWithResource(mContext, R.drawable.ic_media_stream).setTint(
                    Utils.getColorAccentDefaultColor(mContext));
        }
        final MediaMetadata metadata = mMediaController.getMetadata();
        if (metadata != null) {
            final Bitmap bitmap = metadata.getDescription().getIconBitmap();
            if (bitmap != null) {
                return IconCompat.createWithBitmap(bitmap);
            }
        }
        Log.d(TAG, "Media meta data does not contain icon information");
        return getPackageIcon();
    }

    private IconCompat getPackageIcon() {
        try {
            final Drawable drawable = mContext.getPackageManager().getApplicationIcon(mPackageName);
            if (drawable instanceof BitmapDrawable) {
                return IconCompat.createWithBitmap(((BitmapDrawable) drawable).getBitmap());
            }
            final Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(),
                    drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
            final Canvas canvas = new Canvas(bitmap);
            drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
            drawable.draw(canvas);

            return IconCompat.createWithBitmap(bitmap);
        } catch (PackageManager.NameNotFoundException e) {
            Log.e(TAG, "Package is not found. Unable to get package icon.");
        }
        return null;
    }

    @Override
    public List<Uri> getSlices() {
        final List<Uri> uris = new ArrayList<>();
+5 −5
Original line number Diff line number Diff line
@@ -19,6 +19,8 @@ package com.android.settings.panel;
import android.content.Intent;
import android.net.Uri;

import androidx.core.graphics.drawable.IconCompat;

import com.android.settingslib.core.instrumentation.Instrumentable;

import java.util.List;
@@ -28,13 +30,11 @@ import java.util.List;
 */
public interface PanelContent extends Instrumentable {

    int ICON_UNAVAILABLE = -1;

    /**
     * @return a icon resource for the title of the Panel.
     * @return a icon for the title of the Panel.
     */
    default int getIcon() {
        return ICON_UNAVAILABLE;
    default IconCompat getIcon() {
        return null;
    }

    /**
+4 −4
Original line number Diff line number Diff line
@@ -38,6 +38,7 @@ import android.widget.TextView;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.graphics.drawable.IconCompat;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import androidx.lifecycle.LiveData;
@@ -185,20 +186,19 @@ public class PanelFragment extends Fragment {
        mMetricsProvider = FeatureFactory.getFactory(activity).getMetricsFeatureProvider();

        mPanelSlices.setLayoutManager(new LinearLayoutManager((activity)));

        // Add predraw listener to remove the animation and while we wait for Slices to load.
        mLayoutView.getViewTreeObserver().addOnPreDrawListener(mOnPreDrawListener);

        // Start loading Slices. When finished, the Panel will animate in.
        loadAllSlices();

        final int iconRes = mPanel.getIcon();
        if (iconRes == PanelContent.ICON_UNAVAILABLE) {
        final IconCompat icon = mPanel.getIcon();
        if (icon == null) {
            mTitleView.setText(mPanel.getTitle());
        } else {
            mTitleView.setVisibility(View.GONE);
            mPanelHeader.setVisibility(View.VISIBLE);
            mTitleIcon.setImageResource(iconRes);
            mTitleIcon.setImageIcon(icon.toIcon(getContext()));
            mHeaderTitle.setText(mPanel.getTitle());
            mHeaderSubtitle.setText(mPanel.getSubTitle());
        }
+7 −5
Original line number Diff line number Diff line
@@ -22,6 +22,8 @@ import android.app.settings.SettingsEnums;
import android.content.Intent;
import android.net.Uri;

import androidx.core.graphics.drawable.IconCompat;

import java.util.Arrays;
import java.util.List;

@@ -41,11 +43,11 @@ public class FakePanelContent implements PanelContent {
    public static final Intent INTENT = new Intent();

    private CharSequence mSubTitle;
    private int mIconRes = -1;
    private IconCompat mIcon;

    @Override
    public int getIcon() {
        return mIconRes;
    public IconCompat getIcon() {
        return mIcon;
    }

    @Override
@@ -53,8 +55,8 @@ public class FakePanelContent implements PanelContent {
        return mSubTitle;
    }

    public void setIcon(int iconRes) {
        mIconRes = iconRes;
    public void setIcon(IconCompat icon) {
        mIcon = icon;
    }

    public void setSubTitle(CharSequence subTitle) {
+104 −1
Original line number Diff line number Diff line
@@ -20,28 +20,59 @@ import static com.android.settings.media.MediaOutputSlice.MEDIA_PACKAGE_NAME;

import static com.google.common.truth.Truth.assertThat;

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;

import android.content.Context;
import android.media.MediaMetadata;
import android.media.session.MediaController;
import android.media.session.MediaSessionManager;
import android.net.Uri;

import com.android.settings.R;
import com.android.settings.slices.CustomSliceRegistry;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;

import java.util.ArrayList;
import java.util.List;

@RunWith(RobolectricTestRunner.class)
public class MediaOutputPanelTest {

    private static final String TEST_PACKAGENAME = "com.test.packagename";
    private static final String TEST_ARTIST = "test_artist";
    private static final String TEST_ALBUM = "test_album";

    @Mock
    private MediaSessionManager mMediaSessionManager;
    @Mock
    private MediaController mMediaController;
    @Mock
    private MediaMetadata mMediaMetadata;

    private MediaOutputPanel mPanel;
    private Context mContext;
    private List<MediaController> mMediaControllers = new ArrayList<>();

    @Before
    public void setUp() {
        mPanel = MediaOutputPanel.create(RuntimeEnvironment.application, TEST_PACKAGENAME);
        MockitoAnnotations.initMocks(this);

        mContext = spy(RuntimeEnvironment.application);
        mMediaControllers.add(mMediaController);
        when(mMediaController.getPackageName()).thenReturn(TEST_PACKAGENAME);
        when(mMediaSessionManager.getActiveSessions(any())).thenReturn(mMediaControllers);
        when(mContext.getApplicationContext()).thenReturn(mContext);
        when(mContext.getSystemService(MediaSessionManager.class)).thenReturn(mMediaSessionManager);
        mPanel = MediaOutputPanel.create(mContext, TEST_PACKAGENAME);
    }

    @Test
@@ -62,4 +93,76 @@ public class MediaOutputPanelTest {
    public void getSeeMoreIntent_isNull() {
        assertThat(mPanel.getSeeMoreIntent()).isNull();
    }

    @Test
    public void getTitle_withMetadata_returnArtistName() {
        when(mMediaMetadata.getString(MediaMetadata.METADATA_KEY_ARTIST)).thenReturn(TEST_ARTIST);
        when(mMediaController.getMetadata()).thenReturn(mMediaMetadata);

        assertThat(mPanel.getTitle()).isEqualTo(TEST_ARTIST);
    }

    @Test
    public void getTitle_noMetadata_returnDefaultString() {
        when(mMediaController.getMetadata()).thenReturn(null);

        assertThat(mPanel.getTitle()).isEqualTo(mContext.getText(R.string.media_volume_title));
    }

    @Test
    public void getTitle_noPackageName_returnDefaultString() {
        mPanel = MediaOutputPanel.create(mContext, null);
        when(mMediaMetadata.getString(MediaMetadata.METADATA_KEY_ARTIST)).thenReturn(TEST_ARTIST);
        when(mMediaController.getMetadata()).thenReturn(mMediaMetadata);

        assertThat(mPanel.getTitle()).isEqualTo(mContext.getText(R.string.media_volume_title));
    }

    @Test
    public void getTitle_noController_defaultString() {
        mMediaControllers.clear();
        when(mMediaMetadata.getString(MediaMetadata.METADATA_KEY_ARTIST)).thenReturn(TEST_ARTIST);
        when(mMediaController.getMetadata()).thenReturn(mMediaMetadata);
        mPanel = MediaOutputPanel.create(mContext, TEST_PACKAGENAME);

        assertThat(mPanel.getTitle()).isEqualTo(mContext.getText(R.string.media_volume_title));
    }

    @Test
    public void getSubTitle_withMetadata_returnAlbumName() {
        when(mMediaMetadata.getString(MediaMetadata.METADATA_KEY_ALBUM)).thenReturn(TEST_ALBUM);
        when(mMediaController.getMetadata()).thenReturn(mMediaMetadata);

        assertThat(mPanel.getSubTitle()).isEqualTo(TEST_ALBUM);
    }

    @Test
    public void getSubTitle_noMetadata_returnDefaultString() {
        when(mMediaController.getPackageName()).thenReturn(TEST_PACKAGENAME);
        when(mMediaController.getMetadata()).thenReturn(null);

        assertThat(mPanel.getSubTitle()).isEqualTo(mContext.getText(
                R.string.media_output_panel_title));
    }

    @Test
    public void getSubTitle_noPackageName_returnDefaultString() {
        mPanel = MediaOutputPanel.create(mContext, null);
        when(mMediaMetadata.getString(MediaMetadata.METADATA_KEY_ARTIST)).thenReturn(TEST_ARTIST);
        when(mMediaController.getMetadata()).thenReturn(mMediaMetadata);

        assertThat(mPanel.getSubTitle()).isEqualTo(mContext.getText(
                R.string.media_output_panel_title));
    }

    @Test
    public void getSubTitle_noController_returnDefaultString() {
        mMediaControllers.clear();
        mPanel = MediaOutputPanel.create(mContext, TEST_PACKAGENAME);
        when(mMediaMetadata.getString(MediaMetadata.METADATA_KEY_ALBUM)).thenReturn(TEST_ALBUM);
        when(mMediaController.getMetadata()).thenReturn(mMediaMetadata);

        assertThat(mPanel.getSubTitle()).isEqualTo(mContext.getText(
                R.string.media_output_panel_title));
    }
}
Loading