1dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli/*
2dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli * Copyright (C) 2017 The Android Open Source Project
3dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli *
4dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli * Licensed under the Apache License, Version 2.0 (the "License");
5dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli * you may not use this file except in compliance with the License.
6dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli * You may obtain a copy of the License at
7dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli *
8dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli *      http://www.apache.org/licenses/LICENSE-2.0
9dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli *
10dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli * Unless required by applicable law or agreed to in writing, software
11dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli * distributed under the License is distributed on an "AS IS" BASIS,
12dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli * See the License for the specific language governing permissions and
14dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli * limitations under the License.
15dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli */
16dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli
17dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjianglipackage com.example.android.leanback;
18dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli
19dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangliimport android.app.Service;
20dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangliimport android.content.Context;
21dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangliimport android.content.Intent;
22dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangliimport android.graphics.Bitmap;
23dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangliimport android.graphics.BitmapFactory;
24dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangliimport android.media.AudioManager;
25dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangliimport android.media.MediaPlayer;
26dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangliimport android.os.Binder;
27dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangliimport android.os.Handler;
28dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangliimport android.os.IBinder;
29dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangliimport android.os.Message;
30dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangliimport android.os.SystemClock;
31dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangliimport android.support.annotation.Nullable;
32dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangliimport android.support.v4.media.MediaMetadataCompat;
33dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangliimport android.support.v4.media.session.MediaSessionCompat;
34dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangliimport android.support.v4.media.session.PlaybackStateCompat;
35dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangliimport android.util.Log;
36dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli
37dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangliimport java.io.IOException;
38dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangliimport java.util.ArrayList;
39dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangliimport java.util.List;
40dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangliimport java.util.Random;
41dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli
42dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli/**
43dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli * The service to play music. It also contains the media session.
44dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli */
45dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjianglipublic class MediaSessionService extends Service {
46dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli
47dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli
48dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    public static final String CANNOT_SET_DATA_SOURCE = "Cannot set data source";
49dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    private static final float NORMAL_SPEED = 1.0f;
50dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli
51dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    /**
52dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli     * When media player is prepared, our service can send notification to UI side through this
53dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli     * callback. So UI will have chance to prepare/ pre-processing the UI status.
54dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli     */
55dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    interface MediaPlayerListener {
56dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        void onPrepared();
57dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    }
58dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli
59dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    /**
60dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli     * This LocalBinder class contains the getService() method which will return the service object.
61dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli     */
62dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    public class LocalBinder extends Binder {
63dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        MediaSessionService getService() {
64dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli            return MediaSessionService.this;
65dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        }
66dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    }
67dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli
68dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    /**
69dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli     * Constant used in this class.
70dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli     */
71dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    private static final String MUSIC_PLAYER_SESSION_TOKEN = "MusicPlayer Session token";
72dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    private static final int MEDIA_ACTION_NO_REPEAT = 0;
73dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    private static final int MEDIA_ACTION_REPEAT_ONE = 1;
74dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    private static final int MEDIA_ACTION_REPEAT_ALL = 2;
75dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    public static final String MEDIA_PLAYER_ERROR_MESSAGE = "Media player error message";
76dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    public static final String PLAYER_NOT_INITIALIZED = "Media player not initialized";
77dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    public static final String PLAYER_IS_PLAYING = "Media player is playing";
78dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    public static final String PLAYER_SET_DATA_SOURCE_ERROR =
79dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli            "Media player set new data source error";
80dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    private static final boolean DEBUG = false;
81dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    private static final String TAG = "MusicPlaybackService";
82dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    private static final int FOCUS_CHANGE = 0;
83dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli
84dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    // This handler can control media player through audio's status.
85dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    private class MediaPlayerAudioHandler extends Handler {
86dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        @Override
87dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        public void handleMessage(Message msg) {
88dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli            switch (msg.what) {
89dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                case FOCUS_CHANGE:
90dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                    switch (msg.arg1) {
91dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                        // pause media item when audio focus is lost
92dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                        case AudioManager.AUDIOFOCUS_LOSS:
93dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                            if (isPlaying()) {
94dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                                audioFocusLossHandler();
95dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                            }
96dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                            break;
97dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                        case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
98dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                            if (isPlaying()) {
99dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                                audioLossFocusTransientHandler();
100dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                            }
101dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                            break;
102dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                        case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
103dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                            if (isPlaying()) {
104dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                                audioLossFocusTransientCanDuckHanlder();
105dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                            }
106dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                            break;
107dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                        case AudioManager.AUDIOFOCUS_GAIN:
108dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                            if (!isPlaying()) {
109dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                                audioFocusGainHandler();
110dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                            }
111dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                            break;
112dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                    }
113dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli            }
114dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        }
115dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    }
116dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli
117dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    // The callbacks' collection which can be notified by this service.
118dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    private List<MediaPlayerListener> mCallbacks = new ArrayList<>();
119dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli
120dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    // audio manager obtained from system to gain audio focus
121dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    private AudioManager mAudioManager;
122dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli
123dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    // record user defined repeat mode.
124dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    private int mRepeatState = MEDIA_ACTION_NO_REPEAT;
125dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli
126dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    // record user defined shuffle mode.
127dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    private int mShuffleMode = PlaybackStateCompat.SHUFFLE_MODE_NONE;
128dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli
129dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    private MediaPlayer mPlayer;
130dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    private MediaSessionCompat mMediaSession;
131dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli
132dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    // set -1 as invalid media item for playing.
133dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    private int mCurrentIndex = -1;
134dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    // media item in media playlist.
135dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    private MusicItem mCurrentMediaItem;
136dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    // media player's current progress.
137dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    private int mCurrentPosition;
138dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    // Buffered Position which will be updated inside of OnBufferingUpdateListener
139dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    private long mBufferedProgress;
140dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    List<MusicItem> mMediaItemList = new ArrayList<>();
141dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    private boolean mInitialized;
142dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli
143dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    // fast forward/ rewind speed factors and indexes
144dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    private float[] mFastForwardSpeedFactors;
145dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    private float[] mRewindSpeedFactors;
146dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    private int mFastForwardSpeedFactorIndex = 0;
147dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    private int mRewindSpeedFactorIndex = 0;
148dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli
149dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    // Flags to indicate if current state is fast forwarding/ rewinding.
150dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    private boolean mIsFastForwarding;
151dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    private boolean mIsRewinding;
152dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli
153dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    // handle audio related event.
154dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    private Handler mMediaPlayerHandler = new MediaPlayerAudioHandler();
155dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli
156dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    // The volume we set the media player to when we lose audio focus, but are
157dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    // allowed to reduce the volume and continue playing.
158dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    private static final float REDUCED_VOLUME = 0.1f;
159dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    // The volume we set the media player when we have audio focus.
160dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    private static final float FULL_VOLUME = 1.0f;
161dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli
162dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    // Record position when current rewind action begins.
163dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    private long mRewindStartPosition;
164dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    // Record the time stamp when current rewind action is ended.
165dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    private long mRewindEndTime;
166dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    // Record the time stamp when current rewind action is started.
167dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    private long mRewindStartTime;
168dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    // Flag to represent the beginning of rewind operation.
169dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    private boolean mIsRewindBegin;
170dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli
171dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    // A runnable object which will delay the execution of mPlayer.stop()
172dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    private Runnable mDelayedStopRunnable = new Runnable() {
173dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        @Override
174dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        public void run() {
175dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli            mPlayer.stop();
176dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli            mMediaSession.setPlaybackState(createPlaybackStateBuilder(
177dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                    PlaybackStateCompat.STATE_STOPPED).build());
178dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        }
179dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    };
180dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli
181dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    // Listener for audio focus.
182dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    private AudioManager.OnAudioFocusChangeListener mOnAudioFocusChangeListener = new
183dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli            AudioManager.OnAudioFocusChangeListener() {
184dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                @Override
185dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                public void onAudioFocusChange(int focusChange) {
186dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                    if (DEBUG) {
187dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                        Log.d(TAG, "onAudioFocusChange. focusChange=" + focusChange);
188dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                    }
189dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                    mMediaPlayerHandler.obtainMessage(FOCUS_CHANGE, focusChange, 0).sendToTarget();
190dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                }
191dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli            };
192dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli
193dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    private final IBinder mBinder = new LocalBinder();
194dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli
195dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    /**
196dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli     * The public API to gain media session instance from service.
197dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli     *
198dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli     * @return Media Session Instance.
199dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli     */
200dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    public MediaSessionCompat getMediaSession() {
201dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        return mMediaSession;
202dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    }
203dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli
204dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    @Nullable
205dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    @Override
206dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    public IBinder onBind(Intent intent) {
207dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        return mBinder;
208dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    }
209dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli
210dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    @Override
211dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    public void onCreate() {
212dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        super.onCreate();
213dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli
214dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        // This service can be created for multiple times, the objects will only be created when
215dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        // it is null
216dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        if (mMediaSession == null) {
217dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli            mMediaSession = new MediaSessionCompat(this, MUSIC_PLAYER_SESSION_TOKEN);
218dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli            mMediaSession.setFlags(MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS
219dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                    | MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS);
220dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli            mMediaSession.setCallback(new MediaSessionCallback());
221dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        }
222dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli
223dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        if (mAudioManager == null) {
224dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli            // Create audio manager through system service
225dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli            mAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
226dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        }
227dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli
228dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        // initialize the player (including activate media session, request audio focus and
229dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        // set up the listener to listen to player's state)
230dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        initializePlayer();
231dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    }
232dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli
233dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    @Override
234dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    public void onDestroy() {
235dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        super.onDestroy();
236dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        stopForeground(true);
237dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        mAudioManager.abandonAudioFocus(mOnAudioFocusChangeListener);
238dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        mMediaPlayerHandler.removeCallbacksAndMessages(null);
239dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        if (mPlayer != null) {
240dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli            // stop and release the media player since it's no longer in use
241dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli            mPlayer.reset();
242dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli            mPlayer.release();
243dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli            mPlayer = null;
244dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        }
245dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        if (mMediaSession != null) {
246dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli            mMediaSession.release();
247dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli            mMediaSession = null;
248dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        }
249dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    }
250dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli
251dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    /**
252dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli     * After binding to this service, other component can set Media Item List and prepare
253dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli     * the first item in the list through this function.
254dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli     *
255dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli     * @param mediaItemList A list of media item to play.
256dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli     * @param isQueue       When this parameter is true, that meas new items should be appended to
257dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli     *                      original media item list.
258dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli     *                      If this parameter is false, the original playlist will be cleared and
259dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli     *                      replaced with a new media item list.
260dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli     */
261dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    public void setMediaList(List<MusicItem> mediaItemList, boolean isQueue) {
262dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        if (!isQueue) {
263dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli            mMediaItemList.clear();
264dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        }
265dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        mMediaItemList.addAll(mediaItemList);
266dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli
267dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        /**
268dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli         * Points to the first media item in play list.
269dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli         */
270dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        mCurrentIndex = 0;
271dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        mCurrentMediaItem = mMediaItemList.get(0);
272dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli
273dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        try {
274dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli            mPlayer.setDataSource(this.getApplicationContext(),
275dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                    mCurrentMediaItem.getMediaSourceUri(getApplicationContext()));
276dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli            // Prepare the player asynchronously, use onPrepared listener as signal.
277dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli            mPlayer.prepareAsync();
278dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        } catch (IOException e) {
279dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli            PlaybackStateCompat.Builder ret = createPlaybackStateBuilder(
280dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                    PlaybackStateCompat.STATE_ERROR);
281dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli            ret.setErrorMessage(PlaybackStateCompat.ERROR_CODE_APP_ERROR,
282dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                    PLAYER_SET_DATA_SOURCE_ERROR);
283dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        }
284dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    }
285dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli
286dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    /**
287dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli     * Set Fast Forward Speeds for this media session service.
288dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli     *
289dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli     * @param fastForwardSpeeds The array contains all fast forward speeds.
290dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli     */
291dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    public void setFastForwardSpeedFactors(int[] fastForwardSpeeds) {
292dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        mFastForwardSpeedFactors = new float[fastForwardSpeeds.length + 1];
293dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli
294dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        // Put normal speed factor at the beginning of the array
295dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        mFastForwardSpeedFactors[0] = 1.0f;
296dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli
297dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        for (int index = 1; index < mFastForwardSpeedFactors.length; ++index) {
298dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli            mFastForwardSpeedFactors[index] = fastForwardSpeeds[index - 1];
299dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        }
300dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    }
301dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli
302dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    /**
303dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli     * Set Rewind Speeds for this media session service.
304dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli     *
305dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli     * @param rewindSpeeds The array contains all rewind speeds.
306dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli     */
307dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    public void setRewindSpeedFactors(int[] rewindSpeeds) {
308dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        mRewindSpeedFactors = new float[rewindSpeeds.length];
309dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        for (int index = 0; index < mRewindSpeedFactors.length; ++index) {
310dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli            mRewindSpeedFactors[index] = -rewindSpeeds[index];
311dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        }
312dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    }
313dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli
314dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    /**
315dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli     * Prepare the first item in the list. And setup the listener for media player.
316dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli     */
317dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    private void initializePlayer() {
318dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        // This service can be created for multiple times, the objects will only be created when
319dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        // it is null
320dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        if (mPlayer != null) {
321dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli            return;
322dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        }
323dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        mPlayer = new MediaPlayer();
324dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli
325dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        // Set playback state to none to create a valid playback state. So controls row can get
326dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        // information about the supported actions.
327dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        mMediaSession.setPlaybackState(createPlaybackStateBuilder(
328dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                PlaybackStateCompat.STATE_NONE).build());
329dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        // Activate media session
330dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        if (!mMediaSession.isActive()) {
331dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli            mMediaSession.setActive(true);
332dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        }
333dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli
334dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        // Set up listener and audio stream type for underlying music player.
335dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        mPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
336dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli
337dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        // set up listener when the player is prepared.
338dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        mPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
339dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli            @Override
340dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli            public void onPrepared(MediaPlayer mp) {
341dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                mInitialized = true;
342dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                // Every time when the player is prepared (when new data source is set),
343dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                // all listeners will be notified to toggle the UI to "pause" status.
344dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                notifyUiWhenPlayerIsPrepared();
345dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli
346dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                // When media player is prepared, the callback functions will be executed to update
347dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                // the meta data and playback state.
348dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                onMediaSessionMetaDataChanged();
349dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                mMediaSession.setPlaybackState(createPlaybackStateBuilder(
350dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                        PlaybackStateCompat.STATE_PAUSED).build());
351dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli            }
352dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        });
353dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli
354dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        // set up listener for player's error.
355dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        mPlayer.setOnErrorListener(new MediaPlayer.OnErrorListener() {
356dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli            @Override
357dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli            public boolean onError(MediaPlayer mediaPlayer, int what, int extra) {
358dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                if (DEBUG) {
359dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                    PlaybackStateCompat.Builder builder = createPlaybackStateBuilder(
360dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                            PlaybackStateCompat.STATE_ERROR);
361dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                    builder.setErrorMessage(PlaybackStateCompat.ERROR_CODE_APP_ERROR,
362dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                            MEDIA_PLAYER_ERROR_MESSAGE);
363dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                    mMediaSession.setPlaybackState(builder.build());
364dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                }
365dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                return true;
366dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli            }
367dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        });
368dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli
369dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        // set up listener to respond the event when current music item is finished
370dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        mPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
371dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli
372dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli            /**
373dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli             * Expected Interaction Behavior:
374dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli             * 1. If current media item's playing speed not equal to normal speed.
375dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli             *
376dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli             *    A. MEDIA_ACTION_REPEAT_ALL
377dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli             *       a. If current media item is the last one. The first music item in the list will
378dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli             *          be prepared, but it won't play until user press play button.
379dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli             *
380dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli             *          When user press the play button, the speed will be reset to normal (1.0f)
381dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli             *          no matter what the previous media item's playing speed is.
382dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli             *
383dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli             *       b. If current media item isn't the last one, next media item will be prepared,
384dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli             *          but it won't play.
385dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli             *
386dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli             *          When user press the play button, the speed will be reset to normal (1.0f)
387dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli             *          no matter what the previous media item's playing speed is.
388dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli             *
389dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli             *    B. MEDIA_ACTION_REPEAT_ONE
390dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli             *       Different with previous scenario, current item will go back to the start point
391dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli             *       again and play automatically. (The reason to enable auto play here is for
392dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli             *       testing purpose and to make sure our designed API is flexible enough to support
393dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli             *       different situations.)
394dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli             *
395dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli             *       No matter what the previous media item's playing speed is, in this situation
396dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli             *       current media item will be replayed in normal speed.
397dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli             *
398dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli             *    C. MEDIA_ACTION_REPEAT_NONE
399dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli             *       a. If current media is the last one. The service will be closed, no music item
400dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli             *          will be prepared to play. From the UI perspective, the progress bar will not
401dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli             *          be reset to the starting point.
402dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli             *
403dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli             *       b. If current media item isn't the last one, next media item will be prepared,
404dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli             *          but it won't play.
405dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli             *
406dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli             *          When user press the play button, the speed will be reset to normal (1.0f)
407dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli             *          no matter what the previous media item's playing speed is.
408dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli             *
409dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli             * @param mp Object of MediaPlayer。
410dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli             */
411dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli            @Override
412dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli            public void onCompletion(MediaPlayer mp) {
413dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli
414dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                // When current media item finishes playing, always reset rewind/ fastforward state
415dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                mFastForwardSpeedFactorIndex = 0;
416dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                mRewindSpeedFactorIndex = 0;
417dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                // Set player's playback speed back to normal
418dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                mPlayer.setPlaybackParams(mPlayer.getPlaybackParams().setSpeed(
419dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                        mFastForwardSpeedFactors[mFastForwardSpeedFactorIndex]));
420dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                // Pause the player, and update the status accordingly.
421dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                mPlayer.pause();
422dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                mMediaSession.setPlaybackState(createPlaybackStateBuilder(
423dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                        PlaybackStateCompat.STATE_PAUSED).build());
424dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli
425dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                if (mRepeatState == MEDIA_ACTION_REPEAT_ALL
426dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                        && mCurrentIndex == mMediaItemList.size() - 1) {
427dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                    // if the repeat mode is enabled but the shuffle mode is not enabled,
428dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                    // will go back to the first music item to play
429dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                    if (mShuffleMode == PlaybackStateCompat.SHUFFLE_MODE_NONE) {
430dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                        mCurrentIndex = 0;
431dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                    } else {
432dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                        // Or will choose a music item from playing list randomly.
433dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                        mCurrentIndex = generateMediaItemIndex();
434dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                    }
435dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                    mCurrentMediaItem = mMediaItemList.get(mCurrentIndex);
436dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                    // The ui will also be changed from playing state to pause state through
437dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                    // setDataSource() operation
438dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                    setDataSource();
439dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                } else if (mRepeatState == MEDIA_ACTION_REPEAT_ONE) {
440dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                    // Play current music item again.
441dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                    // The ui will stay to be "playing" status for the reason that there is no
442dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                    // setDataSource() function call.
443dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                    mPlayer.start();
444dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                    mMediaSession.setPlaybackState(createPlaybackStateBuilder(
445dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                            PlaybackStateCompat.STATE_PLAYING).build());
446dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                } else if (mCurrentIndex < mMediaItemList.size() - 1) {
447dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                    if (mShuffleMode == PlaybackStateCompat.SHUFFLE_MODE_NONE) {
448dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                        mCurrentIndex++;
449dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                    } else {
450dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                        mCurrentIndex = generateMediaItemIndex();
451dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                    }
452dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                    mCurrentMediaItem = mMediaItemList.get(mCurrentIndex);
453dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                    // The ui will also be changed from playing state to pause state through
454dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                    // setDataSource() operation
455dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                    setDataSource();
456dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                } else {
457dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                    // close the service when the playlist is finished
458dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                    // The PlaybackState will be updated to STATE_STOPPED. And onPlayComplete
459dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                    // callback will be called by attached glue.
460dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                    mMediaSession.setPlaybackState(createPlaybackStateBuilder(
461dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                            PlaybackStateCompat.STATE_STOPPED).build());
462dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                    stopSelf();
463dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                }
464dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli            }
465dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        });
466dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli
467dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        final MediaPlayer.OnBufferingUpdateListener mOnBufferingUpdateListener =
468dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                new MediaPlayer.OnBufferingUpdateListener() {
469dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                    @Override
470dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                    public void onBufferingUpdate(MediaPlayer mp, int percent) {
471dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                        mBufferedProgress = getDuration() * percent / 100;
472dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                        PlaybackStateCompat.Builder builder = createPlaybackStateBuilder(
473dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                                PlaybackStateCompat.STATE_BUFFERING);
474dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                        builder.setBufferedPosition(mBufferedProgress);
475dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                        mMediaSession.setPlaybackState(builder.build());
476dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                    }
477dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                };
478dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        mPlayer.setOnBufferingUpdateListener(mOnBufferingUpdateListener);
479dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    }
480dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli
481dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli
482dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    /**
483dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli     * Public API to register listener for this service.
484dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli     *
485dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli     * @param listener The listener which will keep tracking current service's status
486dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli     */
487dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    public void registerCallback(MediaPlayerListener listener) {
488dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        mCallbacks.add(listener);
489dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    }
490dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli
491dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    /**
492dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli     * Instead of shuffling the who music list, we will generate a media item index randomly
493dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli     * and return it as the index for next media item to play.
494dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli     *
495dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli     * @return The index of next media item to play.
496dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli     */
497dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    private int generateMediaItemIndex() {
498dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        return new Random().nextInt(mMediaItemList.size());
499dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    }
500dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli
501dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    /**
502dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli     * When player is prepared, service will send notification to UI through calling the callback's
503dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli     * method
504dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli     */
505dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    private void notifyUiWhenPlayerIsPrepared() {
506dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        for (MediaPlayerListener callback : mCallbacks) {
507dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli            callback.onPrepared();
508dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        }
509dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    }
510dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli
511dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    /**
512dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli     * Set up media session callback to associate with player's operation.
513dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli     */
514dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    private class MediaSessionCallback extends MediaSessionCompat.Callback {
515dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        @Override
516dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        public void onPlay() {
517dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli            play();
518dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        }
519dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli
520dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        @Override
521dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        public void onPause() {
522dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli            pause();
523dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        }
524dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli
525dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        @Override
526dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        public void onSkipToNext() {
527dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli            next();
528dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        }
529dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli
530dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        @Override
531dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        public void onSkipToPrevious() {
532dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli            previous();
533dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        }
534dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli
535dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        @Override
536dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        public void onStop() {
537dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli            stop();
538dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        }
539dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli
540dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        @Override
541dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        public void onSeekTo(long pos) {
542dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli            // media player's seekTo method can only take integer as the parameter
543dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli            // so the data type need to be casted as int
544dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli            seekTo((int) pos);
545dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        }
546dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli
547dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        @Override
548dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        public void onFastForward() {
549dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli            fastForward();
550dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        }
551dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli
552dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        @Override
553dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        public void onRewind() {
554dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli            rewind();
555dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        }
556dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli
557dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        @Override
558dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        public void onSetRepeatMode(int repeatMode) {
559dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli            setRepeatState(repeatMode);
560dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        }
561dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli
562dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        @Override
563dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        public void onSetShuffleMode(int shuffleMode) {
564dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli            setShuffleMode(shuffleMode);
565dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        }
566dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    }
567dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli
568dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    /**
569dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli     * Set new data source and prepare the music player asynchronously.
570dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli     */
571dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    private void setDataSource() {
572dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        reset();
573dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        try {
574dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli            mPlayer.setDataSource(this.getApplicationContext(),
575dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                    mCurrentMediaItem.getMediaSourceUri(getApplicationContext()));
576dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli            mPlayer.prepareAsync();
577dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        } catch (IOException e) {
578dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli            PlaybackStateCompat.Builder builder = createPlaybackStateBuilder(
579dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                    PlaybackStateCompat.STATE_ERROR);
580dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli            builder.setErrorMessage(PlaybackStateCompat.ERROR_CODE_APP_ERROR,
581dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                    CANNOT_SET_DATA_SOURCE);
582dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli            mMediaSession.setPlaybackState(builder.build());
583dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        }
584dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    }
585dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli
586dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    /**
587dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli     * This function will return a playback state builder based on playbackState and current
588dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli     * media position.
589dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli     *
590dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli     * @param playState current playback state.
591dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli     * @return Object of PlaybackStateBuilder.
592dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli     */
593dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    private PlaybackStateCompat.Builder createPlaybackStateBuilder(int playState) {
594dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        PlaybackStateCompat.Builder playbackStateBuilder = new PlaybackStateCompat.Builder();
595dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        long currentPosition = getCurrentPosition();
596dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        float playbackSpeed = NORMAL_SPEED;
597dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        if (mIsFastForwarding) {
598dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli            playbackSpeed = mFastForwardSpeedFactors[mFastForwardSpeedFactorIndex];
599dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli            // After setting the playback speed, reset mIsFastForwarding flag.
600dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli            mIsFastForwarding = false;
601dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        } else if (mIsRewinding) {
602dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli            playbackSpeed = mRewindSpeedFactors[mRewindSpeedFactorIndex];
603dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli            // After setting the playback speed, reset mIsRewinding flag.
604dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli            mIsRewinding = false;
605dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        }
606dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        playbackStateBuilder.setState(playState, currentPosition, playbackSpeed
607dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        ).setActions(
608dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                getPlaybackStateActions()
609dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        );
610dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        return playbackStateBuilder;
611dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    }
612dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli
613dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    /**
614dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli     * Return supported actions related to current playback state.
615dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli     * Currently the return value from this function is a constant.
616dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli     * For demonstration purpose, the customized fast forward action and customized rewind action
617dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli     * are supported in our case.
618dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli     *
619dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli     * @return playback state actions.
620dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli     */
621dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    private long getPlaybackStateActions() {
622dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        long res = PlaybackStateCompat.ACTION_PLAY
623dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                | PlaybackStateCompat.ACTION_PAUSE
624dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                | PlaybackStateCompat.ACTION_PLAY_PAUSE
625dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                | PlaybackStateCompat.ACTION_SKIP_TO_NEXT
626dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                | PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS
627dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                | PlaybackStateCompat.ACTION_FAST_FORWARD
628dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                | PlaybackStateCompat.ACTION_REWIND
629bd2d1474b96d3045aa1048e06d9debced798455bHyundo Moon                | PlaybackStateCompat.ACTION_SET_SHUFFLE_MODE
630dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                | PlaybackStateCompat.ACTION_SET_REPEAT_MODE;
631dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        return res;
632dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    }
633dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli
634dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    /**
635dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli     * Callback function when media session's meta data is changed.
636dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli     * When this function is returned, the callback function onMetaDataChanged will be
637dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli     * executed to address the new playback state.
638dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli     */
639dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    private void onMediaSessionMetaDataChanged() {
640dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        if (mCurrentMediaItem == null) {
641dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli            throw new IllegalArgumentException(
642dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                    "mCurrentMediaItem is null in onMediaSessionMetaDataChanged!");
643dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        }
644dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        MediaMetadataCompat.Builder metaDataBuilder = new MediaMetadataCompat.Builder();
645dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli
646dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        if (mCurrentMediaItem.getMediaTitle() != null) {
647dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli            metaDataBuilder.putString(MediaMetadataCompat.METADATA_KEY_TITLE,
648dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                    mCurrentMediaItem.getMediaTitle());
649dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        }
650dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli
651dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        if (mCurrentMediaItem.getMediaDescription() != null) {
652dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli            metaDataBuilder.putString(MediaMetadataCompat.METADATA_KEY_ARTIST,
653dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                    mCurrentMediaItem.getMediaDescription());
654dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        }
655dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli
656dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        if (mCurrentMediaItem.getMediaAlbumArtResId(getApplicationContext()) != 0) {
657dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli            Bitmap albumArtBitmap = BitmapFactory.decodeResource(getResources(),
658dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                    mCurrentMediaItem.getMediaAlbumArtResId(getApplicationContext()));
659dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli            metaDataBuilder.putBitmap(MediaMetadataCompat.METADATA_KEY_ALBUM_ART, albumArtBitmap);
660dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        }
661dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli
662dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        // duration information will be fetched from player.
663dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        metaDataBuilder.putLong(MediaMetadataCompat.METADATA_KEY_DURATION, getDuration());
664dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli
665dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        mMediaSession.setMetadata(metaDataBuilder.build());
666dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    }
667dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli
668dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    // Reset player. will be executed when new data source is assigned.
669dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    private void reset() {
670dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        if (mPlayer != null) {
671dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli            mPlayer.reset();
672dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli            mInitialized = false;
673dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        }
674dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    }
675dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli
676dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    // Control the player to play the music item.
677dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    private void play() {
678dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        // Only when player is not null (meaning the player has been created), the player is
679dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        // prepared (using the mInitialized as the flag to represent it,
680dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        // this boolean variable will only be assigned to true inside of the onPrepared callback)
681dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        // and the media item is not currently playing (!isPlaying()), then the player can be
682dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        // started.
683dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli
684dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        // If the player has not been prepared, but this function is fired, it is an error state
685dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        // from the app side
686dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        if (!mInitialized) {
687dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli            PlaybackStateCompat.Builder builder = createPlaybackStateBuilder(
688dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                    PlaybackStateCompat.STATE_ERROR);
689dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli            builder.setErrorMessage(PlaybackStateCompat.ERROR_CODE_APP_ERROR,
690dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                    PLAYER_NOT_INITIALIZED);
691dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli            mMediaSession.setPlaybackState(builder.build());
692dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli
693dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli            // If the player has is playing, and this function is fired again, it is an error state
694dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli            // from the app side
695dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        }  else {
696dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli            // Request audio focus only when needed
697dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli            if (mAudioManager.requestAudioFocus(mOnAudioFocusChangeListener,
698dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                    AudioManager.STREAM_MUSIC,
699dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                    AudioManager.AUDIOFOCUS_GAIN) != AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
700dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                return;
701dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli            }
702dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli
703dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli            if (mPlayer.getPlaybackParams().getSpeed() != NORMAL_SPEED) {
704dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                // Reset to normal speed and play
705dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                resetSpeedAndPlay();
706dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli            } else {
707dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                // Continue play.
708dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                mPlayer.start();
709dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                mMediaSession.setPlaybackState(createPlaybackStateBuilder(
710dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                        PlaybackStateCompat.STATE_PLAYING).build());
711dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli            }
712dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        }
713dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli
714dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    }
715dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli
716dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    // Control the player to pause current music item.
717dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    private void pause() {
718dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        if (mPlayer != null && mPlayer.isPlaying()) {
719dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli            // abandon audio focus immediately when the music item is paused.
720dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli            mAudioManager.abandonAudioFocus(mOnAudioFocusChangeListener);
721dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli
722dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli            mPlayer.pause();
723dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli            // Update playbackState.
724dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli            mMediaSession.setPlaybackState(createPlaybackStateBuilder(
725dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                    PlaybackStateCompat.STATE_PAUSED).build());
726dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        }
727dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    }
728dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli
729dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    // Control the player to stop.
730dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    private void stop() {
731dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        if (mPlayer != null) {
732dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli            mPlayer.stop();
733dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli            // Update playbackState.
734dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli            mMediaSession.setPlaybackState(createPlaybackStateBuilder(
735dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                    PlaybackStateCompat.STATE_STOPPED).build());
736dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        }
737dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    }
738dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli
739dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli
740dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    /**
741dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli     * Control the player to play next music item.
742dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli     * Expected Interaction Behavior:
743dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli     * No matter current media item is playing or not, when use hit next button, next item will be
744dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli     * prepared but won't play unless user hit play button
745dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli     *
746dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli     * Also no matter current media item is fast forwarding or rewinding. Next music item will
747dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli     * be played in normal speed.
748dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli     */
749dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    private void next() {
750dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        if (mMediaItemList.isEmpty()) {
751dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli            return;
752dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        }
753dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        mCurrentIndex = (mCurrentIndex + 1) % mMediaItemList.size();
754dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        mCurrentMediaItem = mMediaItemList.get(mCurrentIndex);
755dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli
756dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        // Reset FastForward/ Rewind state to normal state
757dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        mFastForwardSpeedFactorIndex = 0;
758dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        mRewindSpeedFactorIndex = 0;
759dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        // Set player's playback speed back to normal
760dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        mPlayer.setPlaybackParams(mPlayer.getPlaybackParams().setSpeed(
761dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                mFastForwardSpeedFactors[mFastForwardSpeedFactorIndex]));
762dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        // Pause the player and update the play state.
763dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        // The ui will also be changed from "playing" state to "pause" state.
764dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        mPlayer.pause();
765dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        mMediaSession.setPlaybackState(createPlaybackStateBuilder(
766dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                PlaybackStateCompat.STATE_PAUSED).build());
767dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        // set new data source to play based on mCurrentIndex and prepare the player.
768dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        // The ui will also be changed from "playing" state to "pause" state through setDataSource()
769dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        // operation
770dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        setDataSource();
771dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    }
772dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli
773dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    /**
774dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli     * Control the player to play next music item.
775dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli     * Expected Interaction Behavior:
776dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli     * No matter current media item is playing or not, when use hit previous button, previous item
777dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli     * will be prepared but won't play unless user hit play button
778dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli     *
779dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli     * Also no matter current media item is fast forwarding or rewinding. Previous music item will
780dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli     * be played in normal speed.
781dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli     */
782dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    private void previous() {
783dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        if (mMediaItemList.isEmpty()) {
784dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli            return;
785dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        }
786dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        mCurrentIndex = (mCurrentIndex - 1 + mMediaItemList.size()) % mMediaItemList.size();
787dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        mCurrentMediaItem = mMediaItemList.get(mCurrentIndex);
788dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli
789dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        // Reset FastForward/ Rewind state to normal state
790dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        mFastForwardSpeedFactorIndex = 0;
791dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        mRewindSpeedFactorIndex = 0;
792dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        // Set player's playback speed back to normal
793dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        mPlayer.setPlaybackParams(mPlayer.getPlaybackParams().setSpeed(
794dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                mFastForwardSpeedFactors[mFastForwardSpeedFactorIndex]));
795dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        // Pause the player and update the play state.
796dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        // The ui will also be changed from "playing" state to "pause" state.
797dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        mPlayer.pause();
798dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        // Update playbackState.
799dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        mMediaSession.setPlaybackState(createPlaybackStateBuilder(
800dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                PlaybackStateCompat.STATE_PAUSED).build());
801dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        // set new data source to play based on mCurrentIndex and prepare the player.
802dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        // The ui will also be changed from "playing" state to "pause" state through setDataSource()
803dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        // operation
804dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        setDataSource();
805dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    }
806dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli
807dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    // Get is playing information from underlying player.
808dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    private boolean isPlaying() {
809dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        return mPlayer != null && mPlayer.isPlaying();
810dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    }
811dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli
812dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    // Play media item in a fast forward speed.
813dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    private void fastForward() {
814dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        // To support fast forward action, the mRewindSpeedFactors must be provided through
815dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        // setFastForwardSpeedFactors() method;
816dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        if (mFastForwardSpeedFactors == null) {
817dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli            if (DEBUG) {
818dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                Log.d(TAG, "FastForwardSpeedFactors are not set");
819dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli            }
820dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli            return;
821dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        }
822dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli
823dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        // Toggle the flag to indicate fast forward status.
824dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        mIsFastForwarding = true;
825dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli
826dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        // The first element in mFastForwardSpeedFactors is used to represent the normal speed.
827dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        // Will always be incremented by 1 firstly before setting the speed.
828dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        mFastForwardSpeedFactorIndex += 1;
829dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        if (mFastForwardSpeedFactorIndex > mFastForwardSpeedFactors.length - 1) {
830dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli            mFastForwardSpeedFactorIndex = mFastForwardSpeedFactors.length - 1;
831dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        }
832dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli
833dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        // In our customized fast forward operation, the media player will not be paused,
834dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        // But the player's speed will be changed accordingly.
835dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        mPlayer.setPlaybackParams(mPlayer.getPlaybackParams().setSpeed(
836dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                mFastForwardSpeedFactors[mFastForwardSpeedFactorIndex]));
837dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        // Update playback state, mIsFastForwarding will be reset to false inside of it.
838dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        mMediaSession.setPlaybackState(
839dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                createPlaybackStateBuilder(PlaybackStateCompat.STATE_FAST_FORWARDING).build());
840dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    }
841dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli
842dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli
843dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    // Play media item in a rewind speed.
844dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    // Android media player doesn't support negative speed. So for customized rewind operation,
845dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    // the player will be paused internally, but the pause state will not be published. So from
846dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    // the UI perspective, the player is still in playing status.
847dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    // Every time when the rewind speed is changed, the position will be computed through previous
848dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    // rewind speed then media player will seek to that position for seamless playing.
849dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    private void rewind() {
850dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        // To support rewind action, the mRewindSpeedFactors must be provided through
851dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        // setRewindSpeedFactors() method;
852dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        if (mRewindSpeedFactors == null) {
853dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli            if (DEBUG) {
854dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                Log.d(TAG, "RewindSpeedFactors are not set");
855dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli            }
856dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli            return;
857dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        }
858dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli
859dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        // Perform rewind operation using different speed.
860dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        if (mIsRewindBegin) {
861dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli            // record end time stamp for previous rewind operation.
862dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli            mRewindEndTime = SystemClock.elapsedRealtime();
863dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli            long position = mRewindStartPosition
864dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                    + (long) mRewindSpeedFactors[mRewindSpeedFactorIndex - 1] * (
865dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                    mRewindEndTime - mRewindStartTime);
866dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli            if (DEBUG) {
867dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                Log.e(TAG, "Last Rewind Operation Position" + position);
868dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli            }
869dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli            mPlayer.seekTo((int) position);
870dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli
871dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli            // Set new start status
872dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli            mRewindStartPosition = position;
873dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli            mRewindStartTime = mRewindEndTime;
874dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli            // It is still in rewind state, so mIsRewindBegin remains to be true.
875dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        }
876dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli
877dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        // Perform rewind operation using the first speed set.
878dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        if (!mIsRewindBegin) {
879dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli            mRewindStartPosition = getCurrentPosition();
880dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli            Log.e("REWIND_BEGIN", "REWIND BEGIN PLACE " + mRewindStartPosition);
881dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli            mIsRewindBegin = true;
882dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli            mRewindStartTime = SystemClock.elapsedRealtime();
883dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        }
884dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli
885dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        // Toggle the flag to indicate rewind status.
886dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        mIsRewinding = true;
887dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli
888dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        // Pause the player but won't update the UI status.
889dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        mPlayer.pause();
890dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli
891dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        // Update playback state, mIsRewinding will be reset to false inside of it.
892dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        mMediaSession.setPlaybackState(
893dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                createPlaybackStateBuilder(PlaybackStateCompat.STATE_REWINDING).build());
894dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli
895dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        mRewindSpeedFactorIndex += 1;
896dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        if (mRewindSpeedFactorIndex > mRewindSpeedFactors.length - 1) {
897dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli            mRewindSpeedFactorIndex = mRewindSpeedFactors.length - 1;
898dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        }
899dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    }
900dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli
901dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    // Reset the playing speed to normal.
902dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    // From PlaybackBannerGlue's key dispatching mechanism. If the player is currently in rewinding
903dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    // or fast forwarding status, moving from the rewinding/ FastForwarindg button will trigger
904dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    // the fastForwarding/ rewinding ending event.
905dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    // When customized fast forwarding or rewinding actions are supported, this function will be
906dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    // called.
907dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    // If we are in rewind mode, this function will compute the new position through rewinding
908dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    // speed and compare the start/ end rewinding time stamp.
909dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    private void resetSpeedAndPlay() {
910dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli
911dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        if (mIsRewindBegin) {
912dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli            mIsRewindBegin = false;
913dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli            mRewindEndTime = SystemClock.elapsedRealtime();
914dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli
915dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli            long position = mRewindStartPosition
916dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                    + (long) mRewindSpeedFactors[mRewindSpeedFactorIndex ] * (
917dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                    mRewindEndTime - mRewindStartTime);
918dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli
919dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli            // Seek to the computed position for seamless playing.
920dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli            mPlayer.seekTo((int) position);
921dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        }
922dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli
923dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        // Reset the state to normal state.
924dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        mFastForwardSpeedFactorIndex = 0;
925dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        mRewindSpeedFactorIndex = 0;
926dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        mPlayer.setPlaybackParams(mPlayer.getPlaybackParams().setSpeed(
927dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                mFastForwardSpeedFactors[mFastForwardSpeedFactorIndex]));
928dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli
929dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        // Update the playback status from rewinding/ fast forwardindg to STATE_PLAYING.
930dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        // Which indicates current media item is played in the normal speed.
931dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        mMediaSession.setPlaybackState(
932dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                createPlaybackStateBuilder(PlaybackStateCompat.STATE_PLAYING).build());
933dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    }
934dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli
935dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    // Get current playing progress from media player.
936dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    private int getCurrentPosition() {
937dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        if (mInitialized && mPlayer != null) {
938dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli            // Always record current position for seekTo operation.
939dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli            mCurrentPosition = mPlayer.getCurrentPosition();
940dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli            return mPlayer.getCurrentPosition();
941dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        }
942dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        return 0;
943dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    }
944dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli
945dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    // get music duration from underlying music player
946dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    private int getDuration() {
947dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        return (mInitialized && mPlayer != null) ? mPlayer.getDuration() : 0;
948dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    }
949dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli
950dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    // seek to specific position through underlying music player.
951dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    private void seekTo(int newPosition) {
952dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        if (mPlayer != null) {
953dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli            mPlayer.seekTo(newPosition);
954dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        }
955dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    }
956dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli
957dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    // set shuffle mode through passed parameter.
958dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    private void setShuffleMode(int shuffleMode) {
959dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        mShuffleMode = shuffleMode;
960dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    }
961dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli
962dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    // set shuffle mode through passed parameter.
963dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    public void setRepeatState(int repeatState) {
964dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        mRepeatState = repeatState;
965dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    }
966dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli
967dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    private void audioFocusLossHandler() {
968dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        // Permanent loss of audio focus
969dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        // Pause playback immediately
970dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        mPlayer.pause();
971dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        // Wait 30 seconds before stopping playback
972dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        mMediaPlayerHandler.postDelayed(mDelayedStopRunnable, 30);
973dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        // Update playback state.
974dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        mMediaSession.setPlaybackState(createPlaybackStateBuilder(
975dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                PlaybackStateCompat.STATE_PAUSED).build());
976dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        // Will record current player progress when losing the audio focus.
977dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        mCurrentPosition = getCurrentPosition();
978dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    }
979dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli
980dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    private void audioLossFocusTransientHandler() {
981dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        // In this case, we already have lost the audio focus, and we cannot duck.
982dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        // So the player will be paused immediately, but different with the previous state, there is
983dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        // no need to stop the player.
984dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        mPlayer.pause();
985dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        // update playback state
986dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        mMediaSession.setPlaybackState(createPlaybackStateBuilder(
987dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                PlaybackStateCompat.STATE_PAUSED).build());
988dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        // Will record current player progress when lossing the audio focus.
989dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        mCurrentPosition = getCurrentPosition();
990dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    }
991dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli
992dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    private void audioLossFocusTransientCanDuckHanlder() {
993dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        // In this case, we have lots the audio focus, but since we can duck
994dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        // the music item can continue to play but the volume will be reduced
995dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        mPlayer.setVolume(REDUCED_VOLUME, REDUCED_VOLUME);
996dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    }
997dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli
998dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    private void audioFocusGainHandler() {
999dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        // In this case the app has been granted audio focus again
1000dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        // Firstly, raise volume to normal
1001dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        mPlayer.setVolume(FULL_VOLUME, FULL_VOLUME);
1002dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli
1003dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        // If the recorded position is the same as current position
1004dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        // Start the player directly
1005dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        if (mCurrentPosition == mPlayer.getCurrentPosition()) {
1006dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli            mPlayer.start();
1007dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli            mMediaSession.setPlaybackState(createPlaybackStateBuilder(
1008dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                    PlaybackStateCompat.STATE_PLAYING).build());
1009dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli            // If the recorded position is not equal to current position
1010dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli            // The player will seek to the last recorded position firstly to continue playing the
1011dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli            // last music item
1012dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        } else {
1013dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli            mPlayer.seekTo(mCurrentPosition);
1014dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli            PlaybackStateCompat.Builder builder = createPlaybackStateBuilder(
1015dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli                    PlaybackStateCompat.STATE_BUFFERING);
1016dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli            builder.setBufferedPosition(mBufferedProgress);
1017dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli            mMediaSession.setPlaybackState(builder.build());
1018dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli        }
1019dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli    }
1020dbe6c87619d7543e819e55a7cfcb05dd0a9c7266jingjiangli}
1021