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);