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