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

Commit c5436712 authored by Selim Cinek's avatar Selim Cinek
Browse files

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
parent f0f7495c
Loading
Loading
Loading
Loading
+51 −9
Original line number Diff line number Diff line
@@ -26,10 +26,13 @@ import android.content.SharedPreferences;
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.KeyAttributes;
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 @@ public class MediaControlPanel {
    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 @@ public class MediaControlPanel {
    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 @@ public class MediaControlPanel {
        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 @@ public class MediaControlPanel {
        }

        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 @@ public class MediaControlPanel {
        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.
+4 −3
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ package com.android.systemui.media

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 @@ data class MediaData(
    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?,
+101 −36
Original line number Diff line number Diff line
@@ -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 @@ class MediaDataManager @Inject constructor(
) {

    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 @@ class MediaDataManager @Inject constructor(
        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 @@ class MediaDataManager @Inject constructor(
                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 @@ class MediaDataManager @Inject constructor(
        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 @@ class MediaDataManager @Inject constructor(

        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 @@ class MediaDataManager @Inject constructor(
        fun onMediaDataRemoved(key: String) {}
    }
}

private val LOADING = MediaData(false, 0, 0, null, null, null, null, null,
        emptyList(), emptyList(), null, null, null)
+2 −2
Original line number Diff line number Diff line
@@ -109,7 +109,7 @@ public class HybridGroupManager {
    }

    @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 @@ public class HybridGroupManager {
    }

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