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
diff --git a/src/com/android/settings/panel/MediaOutputPanel.java b/src/com/android/settings/panel/MediaOutputPanel.java
index c42906d..d6030cc 100644
--- a/src/com/android/settings/panel/MediaOutputPanel.java
+++ b/src/com/android/settings/panel/MediaOutputPanel.java
@@ -22,9 +22,22 @@
 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 @@
  */
 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,14 +66,79 @@
     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<>();
         MEDIA_OUTPUT_SLICE_URI =
diff --git a/src/com/android/settings/panel/PanelContent.java b/src/com/android/settings/panel/PanelContent.java
index badaeb1..5670469 100644
--- a/src/com/android/settings/panel/PanelContent.java
+++ b/src/com/android/settings/panel/PanelContent.java
@@ -19,6 +19,8 @@
 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 @@
  */
 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;
     }
 
     /**
diff --git a/src/com/android/settings/panel/PanelFragment.java b/src/com/android/settings/panel/PanelFragment.java
index 40706fe..8fda894 100644
--- a/src/com/android/settings/panel/PanelFragment.java
+++ b/src/com/android/settings/panel/PanelFragment.java
@@ -38,6 +38,7 @@
 
 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 @@
         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());
         }
diff --git a/tests/robotests/src/com/android/settings/panel/FakePanelContent.java b/tests/robotests/src/com/android/settings/panel/FakePanelContent.java
index 3b4d2c4..8888093 100644
--- a/tests/robotests/src/com/android/settings/panel/FakePanelContent.java
+++ b/tests/robotests/src/com/android/settings/panel/FakePanelContent.java
@@ -22,6 +22,8 @@
 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 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 @@
         return mSubTitle;
     }
 
-    public void setIcon(int iconRes) {
-        mIconRes = iconRes;
+    public void setIcon(IconCompat icon) {
+        mIcon = icon;
     }
 
     public void setSubTitle(CharSequence subTitle) {
diff --git a/tests/robotests/src/com/android/settings/panel/MediaOutputPanelTest.java b/tests/robotests/src/com/android/settings/panel/MediaOutputPanelTest.java
index b411037..fb7f0ae 100644
--- a/tests/robotests/src/com/android/settings/panel/MediaOutputPanelTest.java
+++ b/tests/robotests/src/com/android/settings/panel/MediaOutputPanelTest.java
@@ -20,28 +20,59 @@
 
 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 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));
+    }
 }
diff --git a/tests/robotests/src/com/android/settings/panel/PanelFragmentTest.java b/tests/robotests/src/com/android/settings/panel/PanelFragmentTest.java
index ee4131e..1976557 100644
--- a/tests/robotests/src/com/android/settings/panel/PanelFragmentTest.java
+++ b/tests/robotests/src/com/android/settings/panel/PanelFragmentTest.java
@@ -33,6 +33,8 @@
 import android.widget.LinearLayout;
 import android.widget.TextView;
 
+import androidx.core.graphics.drawable.IconCompat;
+
 import com.android.settings.R;
 import com.android.settings.testutils.FakeFeatureFactory;
 
@@ -150,7 +152,8 @@
 
     @Test
     public void supportIcon_displayIconHeaderLayout() {
-        mFakePanelContent.setIcon(R.drawable.ic_android);
+        final IconCompat icon = IconCompat.createWithResource(mContext, R.drawable.ic_android);
+        mFakePanelContent.setIcon(icon);
         mFakePanelContent.setSubTitle(SUBTITLE);
         final ActivityController<FakeSettingsPanelActivity> activityController =
                 Robolectric.buildActivity(FakeSettingsPanelActivity.class);