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

Commit 6b360d14 authored by Jaewan Kim's avatar Jaewan Kim
Browse files

MediaSession2: Move MediaMetadata2 to updatable

Bug: 72670468
Test: Run all MediaComponents test once
Change-Id: I7e2a7e81d855e0034007f208a471bcf174b51f57
parent 1e0a07b6
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -90,7 +90,7 @@ public class MediaItem2Impl implements MediaItem2Provider {
        bundle.putString(KEY_ID, mId);
        bundle.putInt(KEY_FLAGS, mFlags);
        if (mMetadata != null) {
            bundle.putBundle(KEY_METADATA, mMetadata.getBundle());
            bundle.putBundle(KEY_METADATA, mMetadata.toBundle());
        }
        return bundle;
    }
@@ -102,7 +102,7 @@ public class MediaItem2Impl implements MediaItem2Provider {
        final String id = bundle.getString(KEY_ID);
        final Bundle metadataBundle = bundle.getBundle(KEY_METADATA);
        final MediaMetadata2 metadata = metadataBundle != null
                ? new MediaMetadata2(metadataBundle) : null;
                ? MediaMetadata2.fromBundle(context, metadataBundle) : null;
        final int flags = bundle.getInt(KEY_FLAGS);
        return new MediaItem2Impl(context, id, metadata, flags).getInstance();
    }
+328 −0
Original line number Diff line number Diff line
/*
 * Copyright 2018 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.media;

import static android.media.MediaMetadata2.*;

import android.annotation.Nullable;
import android.content.Context;
import android.graphics.Bitmap;
import android.media.MediaMetadata2;
import android.media.MediaMetadata2.BitmapKey;
import android.media.MediaMetadata2.Builder;
import android.media.MediaMetadata2.LongKey;
import android.media.MediaMetadata2.RatingKey;
import android.media.MediaMetadata2.TextKey;
import android.media.Rating2;
import android.media.update.MediaMetadata2Provider;
import android.os.Bundle;
import android.util.ArrayMap;
import android.util.Log;

import java.util.Set;

public class MediaMetadata2Impl implements MediaMetadata2Provider {
    private static final String TAG = "MediaMetadata2";

    /**
     * A {@link Bundle} extra.
     * @hide
     */
    public static final String METADATA_KEY_EXTRA = "android.media.metadata.EXTRA";

    static final int METADATA_TYPE_LONG = 0;
    static final int METADATA_TYPE_TEXT = 1;
    static final int METADATA_TYPE_BITMAP = 2;
    static final int METADATA_TYPE_RATING = 3;
    static final ArrayMap<String, Integer> METADATA_KEYS_TYPE;

    static {
        METADATA_KEYS_TYPE = new ArrayMap<String, Integer>();
        METADATA_KEYS_TYPE.put(METADATA_KEY_TITLE, METADATA_TYPE_TEXT);
        METADATA_KEYS_TYPE.put(METADATA_KEY_ARTIST, METADATA_TYPE_TEXT);
        METADATA_KEYS_TYPE.put(METADATA_KEY_DURATION, METADATA_TYPE_LONG);
        METADATA_KEYS_TYPE.put(METADATA_KEY_ALBUM, METADATA_TYPE_TEXT);
        METADATA_KEYS_TYPE.put(METADATA_KEY_AUTHOR, METADATA_TYPE_TEXT);
        METADATA_KEYS_TYPE.put(METADATA_KEY_WRITER, METADATA_TYPE_TEXT);
        METADATA_KEYS_TYPE.put(METADATA_KEY_COMPOSER, METADATA_TYPE_TEXT);
        METADATA_KEYS_TYPE.put(METADATA_KEY_COMPILATION, METADATA_TYPE_TEXT);
        METADATA_KEYS_TYPE.put(METADATA_KEY_DATE, METADATA_TYPE_TEXT);
        METADATA_KEYS_TYPE.put(METADATA_KEY_YEAR, METADATA_TYPE_LONG);
        METADATA_KEYS_TYPE.put(METADATA_KEY_GENRE, METADATA_TYPE_TEXT);
        METADATA_KEYS_TYPE.put(METADATA_KEY_TRACK_NUMBER, METADATA_TYPE_LONG);
        METADATA_KEYS_TYPE.put(METADATA_KEY_NUM_TRACKS, METADATA_TYPE_LONG);
        METADATA_KEYS_TYPE.put(METADATA_KEY_DISC_NUMBER, METADATA_TYPE_LONG);
        METADATA_KEYS_TYPE.put(METADATA_KEY_ALBUM_ARTIST, METADATA_TYPE_TEXT);
        METADATA_KEYS_TYPE.put(METADATA_KEY_ART, METADATA_TYPE_BITMAP);
        METADATA_KEYS_TYPE.put(METADATA_KEY_ART_URI, METADATA_TYPE_TEXT);
        METADATA_KEYS_TYPE.put(METADATA_KEY_ALBUM_ART, METADATA_TYPE_BITMAP);
        METADATA_KEYS_TYPE.put(METADATA_KEY_ALBUM_ART_URI, METADATA_TYPE_TEXT);
        METADATA_KEYS_TYPE.put(METADATA_KEY_USER_RATING, METADATA_TYPE_RATING);
        METADATA_KEYS_TYPE.put(METADATA_KEY_RATING, METADATA_TYPE_RATING);
        METADATA_KEYS_TYPE.put(METADATA_KEY_DISPLAY_TITLE, METADATA_TYPE_TEXT);
        METADATA_KEYS_TYPE.put(METADATA_KEY_DISPLAY_SUBTITLE, METADATA_TYPE_TEXT);
        METADATA_KEYS_TYPE.put(METADATA_KEY_DISPLAY_DESCRIPTION, METADATA_TYPE_TEXT);
        METADATA_KEYS_TYPE.put(METADATA_KEY_DISPLAY_ICON, METADATA_TYPE_BITMAP);
        METADATA_KEYS_TYPE.put(METADATA_KEY_DISPLAY_ICON_URI, METADATA_TYPE_TEXT);
        METADATA_KEYS_TYPE.put(METADATA_KEY_MEDIA_ID, METADATA_TYPE_TEXT);
        METADATA_KEYS_TYPE.put(METADATA_KEY_BT_FOLDER_TYPE, METADATA_TYPE_LONG);
        METADATA_KEYS_TYPE.put(METADATA_KEY_MEDIA_URI, METADATA_TYPE_TEXT);
        METADATA_KEYS_TYPE.put(METADATA_KEY_ADVERTISEMENT, METADATA_TYPE_LONG);
        METADATA_KEYS_TYPE.put(METADATA_KEY_DOWNLOAD_STATUS, METADATA_TYPE_LONG);
    }

    private static final @TextKey
    String[] PREFERRED_DESCRIPTION_ORDER = {
            METADATA_KEY_TITLE,
            METADATA_KEY_ARTIST,
            METADATA_KEY_ALBUM,
            METADATA_KEY_ALBUM_ARTIST,
            METADATA_KEY_WRITER,
            METADATA_KEY_AUTHOR,
            METADATA_KEY_COMPOSER
    };

    private static final @BitmapKey
    String[] PREFERRED_BITMAP_ORDER = {
            METADATA_KEY_DISPLAY_ICON,
            METADATA_KEY_ART,
            METADATA_KEY_ALBUM_ART
    };

    private static final @TextKey
    String[] PREFERRED_URI_ORDER = {
            METADATA_KEY_DISPLAY_ICON_URI,
            METADATA_KEY_ART_URI,
            METADATA_KEY_ALBUM_ART_URI
    };

    private final Context mContext;
    private final MediaMetadata2 mInstance;
    private final Bundle mBundle;

    public MediaMetadata2Impl(Context context, Bundle bundle) {
        mContext = context;
        mInstance = new MediaMetadata2(this);
        mBundle = bundle;
    }

    public MediaMetadata2 getInstance() {
        return mInstance;
    }

    @Override
    public boolean containsKey_impl(String key) {
        return mBundle.containsKey(key);
    }

    @Override
    public CharSequence getText_impl(@TextKey String key) {
        return mBundle.getCharSequence(key);
    }

    @Override
    public @Nullable String getMediaId_impl() {
        return mInstance.getString(METADATA_KEY_MEDIA_ID);
    }

    @Override
    public String getString_impl(@TextKey String key) {
        CharSequence text = mBundle.getCharSequence(key);
        if (text != null) {
            return text.toString();
        }
        return null;
    }

    @Override
    public long getLong_impl(@LongKey String key) {
        return mBundle.getLong(key, 0);
    }

    @Override
    public Rating2 getRating_impl(@RatingKey String key) {
        // TODO(jaewan): Add backward compatibility
        Rating2 rating = null;
        try {
            rating = Rating2.fromBundle(mBundle.getBundle(key));
        } catch (Exception e) {
            // ignore, value was not a rating
            Log.w(TAG, "Failed to retrieve a key as Rating.", e);
        }
        return rating;
    }

    @Override
    public Bitmap getBitmap_impl(@BitmapKey String key) {
        Bitmap bmp = null;
        try {
            bmp = mBundle.getParcelable(key);
        } catch (Exception e) {
            // ignore, value was not a bitmap
            Log.w(TAG, "Failed to retrieve a key as Bitmap.", e);
        }
        return bmp;
    }

    @Override
    public Bundle getExtra_impl() {
        try {
            return mBundle.getBundle(METADATA_KEY_EXTRA);
        } catch (Exception e) {
            // ignore, value was not an bundle
            Log.w(TAG, "Failed to retrieve an extra");
        }
        return null;
    }

    @Override
    public int size_impl() {
        return mBundle.size();
    }

    @Override
    public Set<String> keySet_impl() {
        return mBundle.keySet();
    }

    @Override
    public Bundle toBundle_impl() {
        return mBundle;
    }

    public static MediaMetadata2 fromBundle(Context context, Bundle bundle) {
        return new MediaMetadata2Impl(context, bundle).getInstance();
    }

    public static final class BuilderImpl implements MediaMetadata2Provider.BuilderProvider {
        private final Context mContext;
        private final MediaMetadata2.Builder mInstance;
        private final Bundle mBundle;

        public BuilderImpl(Context context, MediaMetadata2.Builder instance) {
            mContext = context;
            mInstance = instance;
            mBundle = new Bundle();
        }

        public BuilderImpl(Context context, MediaMetadata2.Builder instance, MediaMetadata2 source) {
            if (source == null) {
                throw new IllegalArgumentException("source shouldn't be null");
            }
            mContext = context;
            mInstance = instance;
            mBundle = new Bundle(source.toBundle());
        }

        public BuilderImpl(Context context, int maxBitmapSize) {
            mContext = context;
            mInstance = new MediaMetadata2.Builder(this);
            mBundle = new Bundle();

            for (String key : mBundle.keySet()) {
                Object value = mBundle.get(key);
                if (value instanceof Bitmap) {
                    Bitmap bmp = (Bitmap) value;
                    if (bmp.getHeight() > maxBitmapSize || bmp.getWidth() > maxBitmapSize) {
                        mInstance.putBitmap(key, scaleBitmap(bmp, maxBitmapSize));
                    }
                }
            }
        }

        @Override
        public Builder putText_impl(@TextKey String key, CharSequence value) {
            if (METADATA_KEYS_TYPE.containsKey(key)) {
                if (METADATA_KEYS_TYPE.get(key) != METADATA_TYPE_TEXT) {
                    throw new IllegalArgumentException("The " + key
                            + " key cannot be used to put a CharSequence");
                }
            }
            mBundle.putCharSequence(key, value);
            return mInstance;
        }

        @Override
        public Builder putString_impl(@TextKey String key, String value) {
            if (METADATA_KEYS_TYPE.containsKey(key)) {
                if (METADATA_KEYS_TYPE.get(key) != METADATA_TYPE_TEXT) {
                    throw new IllegalArgumentException("The " + key
                            + " key cannot be used to put a String");
                }
            }
            mBundle.putCharSequence(key, value);
            return mInstance;
        }

        @Override
        public Builder putLong_impl(@LongKey String key, long value) {
            if (METADATA_KEYS_TYPE.containsKey(key)) {
                if (METADATA_KEYS_TYPE.get(key) != METADATA_TYPE_LONG) {
                    throw new IllegalArgumentException("The " + key
                            + " key cannot be used to put a long");
                }
            }
            mBundle.putLong(key, value);
            return mInstance;
        }

        @Override
        public Builder putRating_impl(@RatingKey String key, Rating2 value) {
            if (METADATA_KEYS_TYPE.containsKey(key)) {
                if (METADATA_KEYS_TYPE.get(key) != METADATA_TYPE_RATING) {
                    throw new IllegalArgumentException("The " + key
                            + " key cannot be used to put a Rating");
                }
            }
            mBundle.putBundle(key, value.toBundle());
            return mInstance;
        }

        @Override
        public Builder putBitmap_impl(@BitmapKey String key, Bitmap value) {
            if (METADATA_KEYS_TYPE.containsKey(key)) {
                if (METADATA_KEYS_TYPE.get(key) != METADATA_TYPE_BITMAP) {
                    throw new IllegalArgumentException("The " + key
                            + " key cannot be used to put a Bitmap");
                }
            }
            mBundle.putParcelable(key, value);
            return mInstance;
        }

        @Override
        public Builder setExtra_impl(Bundle bundle) {
            mBundle.putBundle(METADATA_KEY_EXTRA, bundle);
            return mInstance;
        }

        @Override
        public MediaMetadata2 build_impl() {
            return new MediaMetadata2Impl(mContext, mBundle).getInstance();
        }

        private Bitmap scaleBitmap(Bitmap bmp, int maxSize) { float maxSizeF = maxSize;
            float widthScale = maxSizeF / bmp.getWidth();
            float heightScale = maxSizeF / bmp.getHeight();
            float scale = Math.min(widthScale, heightScale);
            int height = (int) (bmp.getHeight() * scale);
            int width = (int) (bmp.getWidth() * scale);
            return Bitmap.createScaledBitmap(bmp, width, height, true);
        }
    }
}
+22 −3
Original line number Diff line number Diff line
@@ -30,6 +30,7 @@ import android.media.MediaLibraryService2;
import android.media.MediaLibraryService2.MediaLibrarySession;
import android.media.MediaLibraryService2.MediaLibrarySessionCallback;
import android.media.MediaMetadata2;
import android.media.MediaMetadata2.Builder;
import android.media.MediaPlayerInterface;
import android.media.MediaSession2;
import android.media.MediaSession2.Command;
@@ -45,6 +46,7 @@ import android.media.update.MediaControlView2Provider;
import android.media.update.MediaController2Provider;
import android.media.update.MediaItem2Provider;
import android.media.update.MediaLibraryService2Provider.MediaLibrarySessionProvider;
import android.media.update.MediaMetadata2Provider;
import android.media.update.MediaSession2Provider;
import android.media.update.MediaSessionService2Provider;
import android.media.update.SessionPlayer2Provider;
@@ -66,6 +68,7 @@ import com.android.media.MediaController2Impl;
import com.android.media.MediaItem2Impl;
import com.android.media.MediaLibraryService2Impl;
import com.android.media.MediaLibraryService2Impl.MediaLibrarySessionImpl;
import com.android.media.MediaMetadata2Impl;
import com.android.media.MediaSession2Impl;
import com.android.media.MediaSessionService2Impl;
import com.android.media.SessionToken2Impl;
@@ -129,8 +132,7 @@ public class ApiFactory implements StaticProvider {
        return MediaSession2Impl.CommandGroupImpl.fromBundle_impl(context, commands);
    }

    @Override
    public MediaSession2Provider.ControllerInfoProvider createMediaSession2ControllerInfoProvider(
    public MediaSession2Provider.ControllerInfoProvider createMediaSession2ControllerInfo(
            Context context, ControllerInfo instance, int uid, int pid, String packageName,
            IInterface callback) {
        return new MediaSession2Impl.ControllerInfoImpl(context,
@@ -189,7 +191,7 @@ public class ApiFactory implements StaticProvider {
    }

    @Override
    public MediaItem2Provider createMediaItem2Provider(Context context, MediaItem2 instance,
    public MediaItem2Provider createMediaItem2(Context context, MediaItem2 instance,
            String mediaId, DataSourceDesc dsd, MediaMetadata2 metadata, int flags) {
        return new MediaItem2Impl(context, instance, mediaId, dsd, metadata, flags);
    }
@@ -198,4 +200,21 @@ public class ApiFactory implements StaticProvider {
    public MediaItem2 fromBundle_MediaItem2(Context context, Bundle bundle) {
        return MediaItem2Impl.fromBundle(context, bundle);
    }

    @Override
    public MediaMetadata2 fromBundle_MediaMetadata2(Context context, Bundle bundle) {
        return MediaMetadata2Impl.fromBundle(context, bundle);
    }

    @Override
    public MediaMetadata2Provider.BuilderProvider createMediaMetadata2Builder(
            Context context, MediaMetadata2.Builder builder) {
        return new MediaMetadata2Impl.BuilderImpl(context, builder);
    }

    @Override
    public MediaMetadata2Provider.BuilderProvider createMediaMetadata2Builder(
            Context context, MediaMetadata2.Builder builder, MediaMetadata2 source) {
        return new MediaMetadata2Impl.BuilderImpl(context, builder, source);
    }
}
+65 −0
Original line number Diff line number Diff line
/*
 * Copyright 2018 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.media;

import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertTrue;

import android.content.Context;
import android.media.MediaMetadata2.Builder;
import android.os.Bundle;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
import android.support.test.InstrumentationRegistry;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;

@RunWith(AndroidJUnit4.class)
@SmallTest
public class MediaMetadata2Test {
    private Context mContext;

    @Before
    public void setUp() throws Exception {
        mContext = InstrumentationRegistry.getTargetContext();
    }

    @Test
    public void testBuilder() {
        final Bundle extra = new Bundle();
        extra.putString("MediaMetadata2Test", "testBuilder");
        final String title = "title";
        final long discNumber = 10;
        final Rating2 rating = Rating2.newThumbRating(true);

        MediaMetadata2.Builder builder = new Builder(mContext);
        builder.setExtra(extra);
        builder.putString(MediaMetadata2.METADATA_KEY_DISPLAY_TITLE, title);
        builder.putLong(MediaMetadata2.METADATA_KEY_DISC_NUMBER, discNumber);
        builder.putRating(MediaMetadata2.METADATA_KEY_USER_RATING, rating);

        MediaMetadata2 metadata = builder.build();
        assertTrue(TestUtils.equals(extra, metadata.getExtra()));
        assertEquals(title, metadata.getString(MediaMetadata2.METADATA_KEY_DISPLAY_TITLE));
        assertEquals(discNumber, metadata.getLong(MediaMetadata2.METADATA_KEY_DISC_NUMBER));

        // TODO(jaewan): Uncomment here when Rating2.equals are there.
        //assertEquals(rating, metadata.getRating(MediaMetadata2.METADATA_KEY_USER_RATING));
    }
}
+1 −1
Original line number Diff line number Diff line
@@ -390,7 +390,7 @@ public class MediaSession2Test extends MediaSession2TestBase {

            assertEquals(aItem.getMediaId(), bItem.getMediaId());
            assertEquals(aItem.getFlags(), bItem.getFlags());
            TestUtils.equals(aItem.getMetadata().getBundle(), bItem.getMetadata().getBundle());
            TestUtils.equals(aItem.getMetadata().toBundle(), bItem.getMetadata().toBundle());

            // Note: Here it does not check whether DataSourceDesc are equal,
            // since there DataSourceDec is not comparable.