Work back in media uri loading that was changed in the refactor

Rebuilt ag/11180565 and fixed a few cases while doing so.
This also includes the fixes from ag/11270945

Test: play music from spotify
Bug: 154137987
Change-Id: Ib912df716d754f451b68b289920a2e138977b9bd
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
index 673fafa..6621ca1 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
@@ -26,10 +26,13 @@
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.content.res.ColorStateList;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.GradientDrawable;
+import android.graphics.drawable.Icon;
 import android.graphics.drawable.RippleDrawable;
-import android.media.MediaMetadata;
 import android.media.session.MediaController;
 import android.media.session.MediaController.PlaybackInfo;
 import android.media.session.MediaSession;
@@ -52,7 +55,10 @@
 import androidx.constraintlayout.motion.widget.KeyFrames;
 import androidx.constraintlayout.motion.widget.MotionLayout;
 import androidx.constraintlayout.widget.ConstraintSet;
+import androidx.core.graphics.drawable.RoundedBitmapDrawable;
+import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory;
 
+import com.android.settingslib.Utils;
 import com.android.settingslib.media.LocalMediaManager;
 import com.android.settingslib.media.MediaDevice;
 import com.android.settingslib.media.MediaOutputSliceConstants;
@@ -104,6 +110,8 @@
     private boolean mIsRegistered = false;
     private final List<KeyFrames> mKeyFrames;
     private String mKey;
+    private int mAlbumArtSize;
+    private int mAlbumArtRadius;
 
     public static final String MEDIA_PREFERENCES = "media_control_prefs";
     public static final String MEDIA_PREFERENCE_KEY = "browser_components";
@@ -112,13 +120,6 @@
     private boolean mIsRemotePlayback;
     private QSMediaBrowser mQSMediaBrowser;
 
-    // URI fields to try loading album art from
-    private static final String[] ART_URIS = {
-            MediaMetadata.METADATA_KEY_ALBUM_ART_URI,
-            MediaMetadata.METADATA_KEY_ART_URI,
-            MediaMetadata.METADATA_KEY_DISPLAY_ICON_URI
-    };
-
     private final MediaController.Callback mSessionCallback = new MediaController.Callback() {
         @Override
         public void onSessionDestroyed() {
@@ -210,6 +211,13 @@
         SeekBar bar = getView().findViewById(R.id.media_progress_bar);
         bar.setOnSeekBarChangeListener(mSeekBarViewModel.getSeekBarListener());
         bar.setOnTouchListener(mSeekBarViewModel.getSeekBarTouchListener());
+        loadDimens();
+    }
+
+    private void loadDimens() {
+        mAlbumArtRadius = mContext.getResources().getDimensionPixelSize(
+                Utils.getThemeAttr(mContext, android.R.attr.dialogCornerRadius));
+        mAlbumArtSize = mContext.getResources().getDimensionPixelSize(R.dimen.qs_media_album_size);
     }
 
     /**
@@ -296,7 +304,10 @@
         }
 
         ImageView albumView = mMediaNotifView.findViewById(R.id.album_art);
-        albumView.setImageDrawable(data.getArtwork());
+        // TODO: migrate this to a view with rounded corners instead of baking the rounding
+        // into the bitmap
+        Drawable artwork = createRoundedBitmap(data.getArtwork());
+        albumView.setImageDrawable(artwork);
 
         // App icon
         ImageView appIcon = mMediaNotifView.requireViewById(R.id.icon);
@@ -400,6 +411,37 @@
         makeActive();
     }
 
+    private Drawable createRoundedBitmap(Icon icon) {
+        if (icon == null) {
+            return null;
+        }
+        // Let's scale down the View, such that the content always nicely fills the view.
+        // ThumbnailUtils actually scales it down such that it may not be filled for odd aspect
+        // ratios
+        Drawable drawable = icon.loadDrawable(mContext);
+        float aspectRatio = drawable.getIntrinsicHeight() / (float) drawable.getIntrinsicWidth();
+        Rect bounds;
+        if (aspectRatio > 1.0f) {
+            bounds = new Rect(0, 0, mAlbumArtSize, (int) (mAlbumArtSize * aspectRatio));
+        } else {
+            bounds = new Rect(0, 0, (int) (mAlbumArtSize / aspectRatio), mAlbumArtSize);
+        }
+        if (bounds.width() > mAlbumArtSize || bounds.height() > mAlbumArtSize) {
+            float offsetX = (bounds.width() - mAlbumArtSize) / 2.0f;
+            float offsetY = (bounds.height() - mAlbumArtSize) / 2.0f;
+            bounds.offset((int) -offsetX,(int) -offsetY);
+        }
+        drawable.setBounds(bounds);
+        Bitmap scaled = Bitmap.createBitmap(mAlbumArtSize, mAlbumArtSize,
+                Bitmap.Config.ARGB_8888);
+        Canvas canvas = new Canvas(scaled);
+        drawable.draw(canvas);
+        RoundedBitmapDrawable artwork = RoundedBitmapDrawableFactory.create(
+                mContext.getResources(), scaled);
+        artwork.setCornerRadius(mAlbumArtRadius);
+        return artwork;
+    }
+
     /**
      * Updates the keyframe visibility such that only views that are not visible actually go
      * through a transition and fade in.
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaData.kt b/packages/SystemUI/src/com/android/systemui/media/MediaData.kt
index 8165362..6a26461 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaData.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaData.kt
@@ -18,6 +18,7 @@
 
 import android.app.PendingIntent
 import android.graphics.drawable.Drawable
+import android.graphics.drawable.Icon
 import android.media.session.MediaSession
 
 /** State of a media view. */
@@ -27,9 +28,9 @@
     val backgroundColor: Int,
     val app: String?,
     val appIcon: Drawable?,
-    val artist: String?,
-    val song: String?,
-    val artwork: Drawable?,
+    val artist: CharSequence?,
+    val song: CharSequence?,
+    val artwork: Icon?,
     val actions: List<MediaAction>,
     val actionsToShowInCompact: List<Int>,
     val packageName: String?,
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
index 227e8e7..e7d0f7e 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
@@ -17,29 +17,45 @@
 package com.android.systemui.media
 
 import android.app.Notification
+import android.content.ContentResolver
 import android.content.Context
 import android.graphics.Bitmap
+import android.graphics.Canvas
+import android.graphics.ImageDecoder
+import android.graphics.Rect
 import android.graphics.drawable.Drawable
+import android.graphics.drawable.Icon
 import android.media.MediaMetadata
 import android.media.session.MediaSession
-import android.media.session.PlaybackState
+import android.net.Uri
 import android.provider.Settings
 import android.service.notification.StatusBarNotification
-import androidx.core.graphics.drawable.RoundedBitmapDrawable
-import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory
+import android.text.TextUtils
+import android.util.Log
 import com.android.internal.util.ContrastColorUtil
-import com.android.settingslib.Utils
-import com.android.systemui.R
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.statusbar.NotificationMediaManager
 import com.android.systemui.statusbar.notification.MediaNotificationProcessor
+import com.android.systemui.statusbar.notification.row.HybridGroupManager
+import java.io.IOException
 import java.util.*
 import java.util.concurrent.Executor
 import javax.inject.Inject
 import javax.inject.Singleton
 import kotlin.collections.LinkedHashMap
 
+// URI fields to try loading album art from
+private val ART_URIS = arrayOf(
+        MediaMetadata.METADATA_KEY_ALBUM_ART_URI,
+        MediaMetadata.METADATA_KEY_ART_URI,
+        MediaMetadata.METADATA_KEY_DISPLAY_ICON_URI
+)
+
+private const val TAG = "MediaDataManager"
+
+private val LOADING = MediaData(false, 0, 0, null, null, null, null, null,
+        emptyList(), emptyList(), null, null, null)
+
 /**
  * A class that facilitates management and loading of Media Data, ready for binding.
  */
@@ -52,20 +68,8 @@
 ) {
 
     private val listeners: MutableSet<Listener> = mutableSetOf()
-    private var albumArtSize: Int = 0
-    private var albumArtRadius: Int = 0
     private val mediaEntries: LinkedHashMap<String, MediaData> = LinkedHashMap()
 
-    init {
-        loadDimens()
-    }
-
-    private fun loadDimens() {
-        albumArtRadius = context.resources.getDimensionPixelSize(
-                Utils.getThemeAttr(context, android.R.attr.dialogCornerRadius))
-        albumArtSize = context.resources.getDimensionPixelSize(R.dimen.qs_media_album_size)
-    }
-
     fun onNotificationAdded(key: String, sbn: StatusBarNotification) {
         if (isMediaNotification(sbn)) {
             if (!mediaEntries.containsKey(key)) {
@@ -111,9 +115,31 @@
         if (artworkBitmap == null) {
             artworkBitmap = metadata.getBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART)
         }
-        // TODO: load media data from uri
-        if (artworkBitmap != null) {
+        if (artworkBitmap == null) {
+            artworkBitmap = loadBitmapFromUri(metadata)
+        }
+        val artWorkIcon = if (artworkBitmap == null) {
+            notif.getLargeIcon()
+        } else {
+            Icon.createWithBitmap(artworkBitmap)
+        }
+        if (artWorkIcon != null) {
             // If we have art, get colors from that
+            if (artworkBitmap == null) {
+                if (artWorkIcon.type == Icon.TYPE_BITMAP
+                        || artWorkIcon.type == Icon.TYPE_ADAPTIVE_BITMAP) {
+                    artworkBitmap = artWorkIcon.bitmap
+                } else {
+                    val drawable: Drawable = artWorkIcon.loadDrawable(context)
+                    artworkBitmap = Bitmap.createBitmap(
+                            drawable.intrinsicWidth,
+                            drawable.intrinsicHeight,
+                            Bitmap.Config.ARGB_8888)
+                    val canvas = Canvas(artworkBitmap)
+                    drawable.setBounds(0, 0, drawable.intrinsicWidth, drawable.intrinsicHeight)
+                    drawable.draw(canvas)
+                }
+            }
             val p = MediaNotificationProcessor.generateArtworkPaletteBuilder(artworkBitmap)
                     .generate()
             val swatch = MediaNotificationProcessor.findBackgroundSwatch(p)
@@ -126,16 +152,6 @@
                 isDark)
         fgColor = ContrastColorUtil.ensureTextContrast(fgColor, bgColor, isDark)
 
-        // Album art
-        var artwork: RoundedBitmapDrawable? = null
-        if (artworkBitmap != null) {
-            val original = artworkBitmap.copy(Bitmap.Config.ARGB_8888, true)
-            val scaled = Bitmap.createScaledBitmap(original, albumArtSize, albumArtSize,
-                    false)
-            artwork = RoundedBitmapDrawableFactory.create(context.resources, scaled)
-            artwork.cornerRadius = albumArtRadius.toFloat()
-        }
-
         // App name
         val builder = Notification.Builder.recoverBuilder(context, notif)
         val app = builder.loadHeaderAppName()
@@ -144,10 +160,19 @@
         val smallIconDrawable: Drawable = sbn.notification.smallIcon.loadDrawable(context)
 
         // Song name
-        val song: String = metadata.getString(MediaMetadata.METADATA_KEY_TITLE)
+        var song: CharSequence? = metadata.getString(MediaMetadata.METADATA_KEY_DISPLAY_TITLE)
+        if (song == null) {
+            song = metadata.getString(MediaMetadata.METADATA_KEY_TITLE)
+        }
+        if (song == null) {
+            song = HybridGroupManager.resolveTitle(notif)
+        }
 
         // Artist name
-        val artist: String = metadata.getString(MediaMetadata.METADATA_KEY_ARTIST)
+        var artist: CharSequence? = metadata.getString(MediaMetadata.METADATA_KEY_ARTIST)
+        if (artist == null) {
+            artist = HybridGroupManager.resolveText(notif)
+        }
 
         // Control buttons
         val actionIcons: MutableList<MediaAction> = ArrayList()
@@ -167,12 +192,55 @@
 
         foregroundExcecutor.execute {
             onMediaDataLoaded(key, MediaData(true, fgColor, bgColor, app, smallIconDrawable, artist,
-                    song, artwork, actionIcons, actionsToShowCollapsed, sbn.packageName, token,
+                    song, artWorkIcon, actionIcons, actionsToShowCollapsed, sbn.packageName, token,
                     notif.contentIntent))
         }
 
     }
 
+    /**
+     * Load a bitmap from the various Art metadata URIs
+     */
+    private fun loadBitmapFromUri(metadata: MediaMetadata): Bitmap? {
+        for (uri in ART_URIS) {
+            val uriString = metadata.getString(uri)
+            if (!TextUtils.isEmpty(uriString)) {
+                val albumArt = loadBitmapFromUri(Uri.parse(uriString))
+                if (albumArt != null) {
+                    Log.d(TAG, "loaded art from $uri")
+                    break
+                }
+            }
+        }
+        return null
+    }
+
+    /**
+     * Load a bitmap from a URI
+     * @param uri the uri to load
+     * @return bitmap, or null if couldn't be loaded
+     */
+    private fun loadBitmapFromUri(uri: Uri): Bitmap? {
+        // ImageDecoder requires a scheme of the following types
+        if (uri.getScheme() == null) {
+            return null;
+        }
+
+        if (!uri.getScheme().equals(ContentResolver.SCHEME_CONTENT)
+                && !uri.getScheme().equals(ContentResolver.SCHEME_ANDROID_RESOURCE)
+                && !uri.getScheme().equals(ContentResolver.SCHEME_FILE)) {
+            return null;
+        }
+
+        val source = ImageDecoder.createSource(context.getContentResolver(), uri)
+        return try {
+            ImageDecoder.decodeBitmap(source)
+        } catch (e: IOException) {
+            e.printStackTrace()
+            null
+        }
+    }
+
     fun onMediaDataLoaded(key: String, data: MediaData) {
         if (mediaEntries.containsKey(key)) {
             // Otherwise this was removed already
@@ -236,6 +304,3 @@
         fun onMediaDataRemoved(key: String) {}
     }
 }
-
-private val LOADING = MediaData(false, 0, 0, null, null, null, null, null,
-        emptyList(), emptyList(), null, null, null)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridGroupManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridGroupManager.java
index 0ccebc13..56f8e08 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridGroupManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridGroupManager.java
@@ -109,7 +109,7 @@
     }
 
     @Nullable
-    private CharSequence resolveText(Notification notification) {
+    public static CharSequence resolveText(Notification notification) {
         CharSequence contentText = notification.extras.getCharSequence(Notification.EXTRA_TEXT);
         if (contentText == null) {
             contentText = notification.extras.getCharSequence(Notification.EXTRA_BIG_TEXT);
@@ -118,7 +118,7 @@
     }
 
     @Nullable
-    private CharSequence resolveTitle(Notification notification) {
+    public static CharSequence resolveTitle(Notification notification) {
         CharSequence titleText = notification.extras.getCharSequence(Notification.EXTRA_TITLE);
         if (titleText == null) {
             titleText = notification.extras.getCharSequence(Notification.EXTRA_TITLE_BIG);