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

Commit f029ae95 authored by Jaewan Kim's avatar Jaewan Kim
Browse files

MediaSession2: Move PlaybackState2 to updatable

Test: Run all MediaComponents test once
Bug: 72670371
Change-Id: Ic7953bf4bb7841238dec7082d8e9d9b412775e6d
parent 71a96f27
Loading
Loading
Loading
Loading
+72 −97
Original line number Diff line number Diff line
@@ -17,6 +17,11 @@
package android.media;

import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.media.update.ApiLoader;
import android.media.update.PlaybackState2Provider;
import android.os.Bundle;

import java.lang.annotation.Retention;
@@ -29,14 +34,51 @@ import java.lang.annotation.RetentionPolicy;
 * @hide
 */
public final class PlaybackState2 {
    private static final String TAG = "PlaybackState2";

    // Similar to the PlaybackState2 with following changes
    //    - Not implement Parcelable and added from/toBundle()
    //    - Removed playback state that doesn't match with the MediaPlayer2
    //      Full list should be finalized when the MediaPlayer2 has getter for the playback state.
    //      Here's table for the MP2 state and PlaybackState2.State.
    //         +----------------------------------------+----------------------------------------+
    //         | MediaPlayer2 state                     | Matching PlaybackState2.State          |
    //         | (Names are from MP2' Javadoc)          |                                        |
    //         +----------------------------------------+----------------------------------------+
    //         | Idle: Just finished creating MP2       | STATE_NONE                             |
    //         |     or reset() is called               |                                        |
    //         +----------------------------------------+----------------------------------------+
    //         | Initialized: setDataSource/Playlist    | N/A (Session/Controller don't          |
    //         |                                        |     differentiate with Prepared)       |
    //         +----------------------------------------+----------------------------------------+
    //         | Prepared: Prepared after initialized   | STATE_PAUSED                           |
    //         +----------------------------------------+----------------------------------------+
    //         | Started: Started playback              | STATE_PLAYING                          |
    //         +----------------------------------------+----------------------------------------+
    //         | Paused: Paused playback                | STATE_PAUSED                           |
    //         +----------------------------------------+----------------------------------------+
    //         | PlaybackCompleted: Playback is done    | STATE_PAUSED                           |
    //         +----------------------------------------+----------------------------------------+
    //         | Stopped: MP2.stop() is called.         | STATE_STOPPED                          |
    //         |     prepare() is needed to play again  |                                        |
    //         |     (Seemingly the same as initialized |                                        |
    //         |     because cannot set data source     |                                        |
    //         |     after this)                        |                                        |
    //         +----------------------------------------+----------------------------------------+
    //         | Error: an API is called in a state     | STATE_ERROR                            |
    //         |     that the API isn't supported       |                                        |
    //         +----------------------------------------+----------------------------------------+
    //         | End: MP2.close() is called to release  | N/A (MediaSession will be gone)        |
    //         |    MP2. Cannot be reused anymore       |                                        |
    //         +----------------------------------------+----------------------------------------+
    //         | Started, but                           | STATE_BUFFERING                        |
    //         |    EventCallback.onBufferingUpdate()   |                                        |
    //         +----------------------------------------+----------------------------------------+
    //    - Removed actions and custom actions.
    //    - Repeat mode / shuffle mode is now in the PlaylistParams
    // TODO(jaewan): Replace states from MediaPlayer2
    /**
     * @hide
     */
    @IntDef({STATE_NONE, STATE_STOPPED, STATE_PREPARED, STATE_PAUSED, STATE_PLAYING,
            STATE_FINISH, STATE_BUFFERING, STATE_ERROR})
    @IntDef({STATE_NONE, STATE_STOPPED, STATE_PAUSED, STATE_PLAYING, STATE_BUFFERING, STATE_ERROR})
    @Retention(RetentionPolicy.SOURCE)
    public @interface State {}

@@ -51,89 +93,47 @@ public final class PlaybackState2 {
     */
    public final static int STATE_STOPPED = 1;

    /**
     * State indicating this item is currently prepared
     */
    public final static int STATE_PREPARED = 2;

    /**
     * State indicating this item is currently paused.
     */
    public final static int STATE_PAUSED = 3;
    public final static int STATE_PAUSED = 2;

    /**
     * State indicating this item is currently playing.
     */
    public final static int STATE_PLAYING = 4;

    /**
     * State indicating the playback reaches the end of the item.
     */
    public final static int STATE_FINISH = 5;
    public final static int STATE_PLAYING = 3;

    /**
     * State indicating this item is currently buffering and will begin playing
     * when enough data has buffered.
     */
    public final static int STATE_BUFFERING = 6;
    public final static int STATE_BUFFERING = 4;

    /**
     * State indicating this item is currently in an error state. The error
     * message should also be set when entering this state.
     */
    public final static int STATE_ERROR = 7;
    public final static int STATE_ERROR = 5;

    /**
     * Use this value for the position to indicate the position is not known.
     */
    public final static long PLAYBACK_POSITION_UNKNOWN = -1;

    /**
     * Keys used for converting a PlaybackState2 to a bundle object and vice versa.
     */
    private static final String KEY_STATE = "android.media.playbackstate2.state";
    private static final String KEY_POSITION = "android.media.playbackstate2.position";
    private static final String KEY_BUFFERED_POSITION =
            "android.media.playbackstate2.buffered_position";
    private static final String KEY_SPEED = "android.media.playbackstate2.speed";
    private static final String KEY_ERROR_MESSAGE = "android.media.playbackstate2.error_message";
    private static final String KEY_UPDATE_TIME = "android.media.playbackstate2.update_time";
    private static final String KEY_ACTIVE_ITEM_ID = "android.media.playbackstate2.active_item_id";

    private final int mState;
    private final long mPosition;
    private final long mUpdateTime;
    private final float mSpeed;
    private final long mBufferedPosition;
    private final long mActiveItemId;
    private final CharSequence mErrorMessage;
    private final PlaybackState2Provider mProvider;

    // TODO(jaewan): Better error handling?
    //               E.g. media item at #2 has issue, but continue playing #3
    //                    login error. fire intent xxx to login
    public PlaybackState2(int state, long position, long updateTime, float speed,
            long bufferedPosition, long activeItemId, CharSequence error) {
        mState = state;
        mPosition = position;
        mSpeed = speed;
        mUpdateTime = updateTime;
        mBufferedPosition = bufferedPosition;
        mActiveItemId = activeItemId;
        mErrorMessage = error;
    public PlaybackState2(@NonNull Context context, int state, long position, long updateTime,
            float speed, long bufferedPosition, long activeItemId, CharSequence error) {
        mProvider = ApiLoader.getProvider(context).createPlaybackState2(context, this, state,
                position, updateTime, speed, bufferedPosition, activeItemId, error);
    }

    @Override
    public String toString() {
        StringBuilder bob = new StringBuilder("PlaybackState {");
        bob.append("state=").append(mState);
        bob.append(", position=").append(mPosition);
        bob.append(", buffered position=").append(mBufferedPosition);
        bob.append(", speed=").append(mSpeed);
        bob.append(", updated=").append(mUpdateTime);
        bob.append(", active item id=").append(mActiveItemId);
        bob.append(", error=").append(mErrorMessage);
        bob.append("}");
        return bob.toString();
        return mProvider.toString_impl();
    }

    /**
@@ -141,22 +141,24 @@ public final class PlaybackState2 {
     * <ul>
     * <li> {@link PlaybackState2#STATE_NONE}</li>
     * <li> {@link PlaybackState2#STATE_STOPPED}</li>
     * <li> {@link PlaybackState2#STATE_PLAYING}</li>
     * <li> {@link PlaybackState2#STATE_PREPARED}</li>
     * <li> {@link PlaybackState2#STATE_PAUSED}</li>
     * <li> {@link PlaybackState2#STATE_PLAYING}</li>
     * <li> {@link PlaybackState2#STATE_FINISH}</li>
     * <li> {@link PlaybackState2#STATE_BUFFERING}</li>
     * <li> {@link PlaybackState2#STATE_ERROR}</li>
     * </ul>
     */
    @State
    public int getState() {
        return mState;
        return mProvider.getState_impl();
    }

    /**
     * Get the current playback position in ms.
     */
    public long getPosition() {
        return mPosition;
        return mProvider.getPosition_impl();
    }

    /**
@@ -165,7 +167,7 @@ public final class PlaybackState2 {
     * content.
     */
    public long getBufferedPosition() {
        return mBufferedPosition;
        return mProvider.getBufferedPosition_impl();
    }

    /**
@@ -176,7 +178,7 @@ public final class PlaybackState2 {
     * @return The current speed of playback.
     */
    public float getPlaybackSpeed() {
        return mSpeed;
        return mProvider.getPlaybackSpeed_impl();
    }

    /**
@@ -184,7 +186,7 @@ public final class PlaybackState2 {
     * {@link PlaybackState2#STATE_ERROR}.
     */
    public CharSequence getErrorMessage() {
        return mErrorMessage;
        return mProvider.getErrorMessage_impl();
    }

    /**
@@ -194,7 +196,7 @@ public final class PlaybackState2 {
     * @return The last time the position was updated.
     */
    public long getLastPositionUpdateTime() {
        return mUpdateTime;
        return mProvider.getLastPositionUpdateTime_impl();
    }

    /**
@@ -203,53 +205,26 @@ public final class PlaybackState2 {
     * @return The id of the currently active item in the queue
     */
    public long getCurrentPlaylistItemIndex() {
        return mActiveItemId;
        return mProvider.getCurrentPlaylistItemIndex_impl();
    }

    /**
     * Returns this object as a bundle to share between processes.
     */
    public Bundle toBundle() {
        Bundle bundle = new Bundle();
        bundle.putInt(KEY_STATE, mState);
        bundle.putLong(KEY_POSITION, mPosition);
        bundle.putLong(KEY_UPDATE_TIME, mUpdateTime);
        bundle.putFloat(KEY_SPEED, mSpeed);
        bundle.putLong(KEY_BUFFERED_POSITION, mBufferedPosition);
        bundle.putLong(KEY_ACTIVE_ITEM_ID, mActiveItemId);
        bundle.putCharSequence(KEY_ERROR_MESSAGE, mErrorMessage);
        return bundle;
    public @NonNull Bundle toBundle() {
        return mProvider.toBundle_impl();
    }

    /**
     * Creates an instance from a bundle which is previously created by {@link #toBundle()}.
     *
     * @param context context
     * @param bundle A bundle created by {@link #toBundle()}.
     * @return A new {@link PlaybackState2} instance. Returns {@code null} if the given
     *         {@param bundle} is null, or if the {@param bundle} has no playback state parameters.
     */
    public static PlaybackState2 fromBundle(Bundle bundle) {
        if (bundle == null) {
            return null;
        }

        if (!bundle.containsKey(KEY_STATE)
                || !bundle.containsKey(KEY_POSITION)
                || !bundle.containsKey(KEY_UPDATE_TIME)
                || !bundle.containsKey(KEY_SPEED)
                || !bundle.containsKey(KEY_BUFFERED_POSITION)
                || !bundle.containsKey(KEY_ACTIVE_ITEM_ID)
                || !bundle.containsKey(KEY_ERROR_MESSAGE)) {
            return null;
        }

        return new PlaybackState2(
                bundle.getInt(KEY_STATE),
                bundle.getLong(KEY_POSITION),
                bundle.getLong(KEY_UPDATE_TIME),
                bundle.getFloat(KEY_SPEED),
                bundle.getLong(KEY_BUFFERED_POSITION),
                bundle.getLong(KEY_ACTIVE_ITEM_ID),
                bundle.getCharSequence(KEY_ERROR_MESSAGE));
    public @Nullable static PlaybackState2 fromBundle(@NonNull Context context,
            @Nullable Bundle bundle) {
        return ApiLoader.getProvider(context).fromBundle_PlaybackState2(context, bundle);
    }
}
 No newline at end of file
+43 −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.update;

import android.os.Bundle;

/**
 * @hide
 */
// TODO(jaewan): @SystemApi
public interface PlaybackState2Provider {
    String toString_impl();

    int getState_impl();

    long getPosition_impl();

    long getBufferedPosition_impl();

    float getPlaybackSpeed_impl();

    CharSequence getErrorMessage_impl();

    long getLastPositionUpdateTime_impl();

    long getCurrentPlaylistItemIndex_impl();

    Bundle toBundle_impl();
}
+6 −0
Original line number Diff line number Diff line
@@ -34,6 +34,7 @@ import android.media.MediaSession2;
import android.media.MediaSession2.PlaylistParams;
import android.media.MediaSession2.SessionCallback;
import android.media.MediaSessionService2;
import android.media.PlaybackState2;
import android.media.Rating2;
import android.media.SessionPlayer2;
import android.media.SessionToken2;
@@ -117,4 +118,9 @@ public interface StaticProvider {
    Rating2 newThumbRating_Rating2(Context context, boolean thumbIsUp);
    Rating2 newStarRating_Rating2(Context context, int starRatingStyle, float starRating);
    Rating2 newPercentageRating_Rating2(Context context, float percent);

    PlaybackState2Provider createPlaybackState2(Context context, PlaybackState2 instance, int state,
            long position, long updateTime, float speed, long bufferedPosition, long activeItemId,
            CharSequence error);
    PlaybackState2 fromBundle_PlaybackState2(Context context, Bundle bundle);
}