|
@@ -0,0 +1,866 @@
|
|
|
|
+package com.yc.music.service;
|
|
|
|
+
|
|
|
|
+import android.annotation.SuppressLint;
|
|
|
|
+import android.app.Service;
|
|
|
|
+import android.content.Context;
|
|
|
|
+import android.content.Intent;
|
|
|
|
+import android.content.IntentFilter;
|
|
|
|
+import android.media.AudioManager;
|
|
|
|
+import android.media.MediaPlayer;
|
|
|
|
+import android.os.Binder;
|
|
|
|
+import android.os.Handler;
|
|
|
|
+import android.os.IBinder;
|
|
|
|
+import android.os.Message;
|
|
|
|
+
|
|
|
|
+import androidx.annotation.NonNull;
|
|
|
|
+import androidx.annotation.Nullable;
|
|
|
|
+import com.yc.music.config.MusicPlayAction;
|
|
|
|
+import com.yc.music.config.MusicConstant;
|
|
|
|
+import com.yc.music.config.PlayModeEnum;
|
|
|
|
+import com.yc.music.inter.EventCallback;
|
|
|
|
+import com.yc.music.inter.OnPlayerEventListener;
|
|
|
|
+import com.yc.music.manager.AudioFocusManager;
|
|
|
|
+import com.yc.music.manager.MediaSessionManager;
|
|
|
|
+import com.yc.music.model.AudioBean;
|
|
|
|
+import com.yc.music.receiver.AudioBroadcastReceiver;
|
|
|
|
+import com.yc.music.receiver.AudioEarPhoneReceiver;
|
|
|
|
+import com.yc.music.tool.BaseAppHelper;
|
|
|
|
+import com.yc.music.utils.MusicLogUtils;
|
|
|
|
+import com.yc.music.utils.NotificationHelper;
|
|
|
|
+import com.yc.music.tool.QuitTimerHelper;
|
|
|
|
+import com.yc.music.utils.MusicSpUtils;
|
|
|
|
+
|
|
|
|
+import java.io.IOException;
|
|
|
|
+import java.util.List;
|
|
|
|
+import java.util.Random;
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ * Service就是用来在后台完成一些不需要和用户交互的动作
|
|
|
|
+ */
|
|
|
|
+public class PlayService extends Service {
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 正在播放的歌曲的序号
|
|
|
|
+ */
|
|
|
|
+ private int mPlayingPosition = -1;
|
|
|
|
+ /**
|
|
|
|
+ * 正在播放的歌曲[本地|网络]
|
|
|
|
+ */
|
|
|
|
+ private AudioBean mPlayingMusic;
|
|
|
|
+ /**
|
|
|
|
+ * 音频list集合
|
|
|
|
+ */
|
|
|
|
+ private List<AudioBean> audioMusics;
|
|
|
|
+ /**
|
|
|
|
+ * 播放状态
|
|
|
|
+ */
|
|
|
|
+ private int mPlayState = MusicPlayAction.STATE_IDLE;
|
|
|
|
+ /**
|
|
|
|
+ * 播放器
|
|
|
|
+ */
|
|
|
|
+ private MediaPlayer mPlayer;
|
|
|
|
+ /**
|
|
|
|
+ * 播放进度监听器
|
|
|
|
+ */
|
|
|
|
+ private OnPlayerEventListener mListener;
|
|
|
|
+ /**
|
|
|
|
+ * 更新播放进度的显示,时间的显示
|
|
|
|
+ */
|
|
|
|
+ private static final int UPDATE_PLAY_PROGRESS_SHOW = 0;
|
|
|
|
+ /**
|
|
|
|
+ * 允许与媒体控制器、音量键、媒体按钮和传输控件交互
|
|
|
|
+ */
|
|
|
|
+ private MediaSessionManager mMediaSessionManager;
|
|
|
|
+ /**
|
|
|
|
+ * 捕获/丢弃音乐焦点处理
|
|
|
|
+ */
|
|
|
|
+ private AudioFocusManager mAudioFocusManager;
|
|
|
|
+ /**
|
|
|
|
+ * 是否锁屏了,默认是false
|
|
|
|
+ */
|
|
|
|
+ private boolean mIsLocked = false;
|
|
|
|
+ /**
|
|
|
|
+ * 来电/耳机拔出时暂停播放
|
|
|
|
+ * 在播放时调用,在暂停时注销
|
|
|
|
+ */
|
|
|
|
+ private final AudioEarPhoneReceiver mNoisyReceiver = new AudioEarPhoneReceiver();
|
|
|
|
+ private final IntentFilter mFilter = new IntentFilter(AudioManager.ACTION_AUDIO_BECOMING_NOISY);
|
|
|
|
+ /**
|
|
|
|
+ * 其他广播
|
|
|
|
+ * 比如:屏幕灭了后再次亮了,会显示锁屏页面
|
|
|
|
+ * 这个在onCreate中创建,在onDestroy中销毁
|
|
|
|
+ */
|
|
|
|
+ private final AudioBroadcastReceiver mAudioReceiver = new AudioBroadcastReceiver();
|
|
|
|
+ /**
|
|
|
|
+ * 广播接受者标识,避免多次注册广播
|
|
|
|
+ */
|
|
|
|
+ private boolean mReceiverTag = false;
|
|
|
|
+
|
|
|
|
+ @SuppressLint("HandlerLeak")
|
|
|
|
+ private Handler handler = new Handler(){
|
|
|
|
+ @Override
|
|
|
|
+ public void handleMessage(@NonNull Message msg) {
|
|
|
|
+ super.handleMessage(msg);
|
|
|
|
+ switch (msg.what){
|
|
|
|
+ case UPDATE_PLAY_PROGRESS_SHOW:
|
|
|
|
+ updatePlayProgressShow();
|
|
|
|
+ break;
|
|
|
|
+ default:
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 绑定服务时才会调用
|
|
|
|
+ * 必须要实现的方法
|
|
|
|
+ * @param intent intent
|
|
|
|
+ * @return IBinder对象
|
|
|
|
+ */
|
|
|
|
+ @Nullable
|
|
|
|
+ @Override
|
|
|
|
+ public IBinder onBind(Intent intent) {
|
|
|
|
+ return new PlayBinder();
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 比如,广播,耳机声控,通知栏广播,来电或者拔下耳机广播开启服务
|
|
|
|
+ * @param context 上下文
|
|
|
|
+ * @param type 类型
|
|
|
|
+ */
|
|
|
|
+ public static void startCommand(Context context, String type) {
|
|
|
|
+ Intent intent = new Intent(context, PlayService.class);
|
|
|
|
+ intent.setAction(type);
|
|
|
|
+ context.startService(intent);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ public class PlayBinder extends Binder {
|
|
|
|
+ public PlayService getService() {
|
|
|
|
+ return PlayService.this;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 首次创建服务时,系统将调用此方法来执行一次性设置程序(在调用 onStartCommand() 或 onBind() 之前)。
|
|
|
|
+ * 如果服务已在运行,则不会调用此方法。该方法只被调用一次
|
|
|
|
+ */
|
|
|
|
+ @Override
|
|
|
|
+ public void onCreate() {
|
|
|
|
+ super.onCreate();
|
|
|
|
+ NotificationHelper.get().init(this);
|
|
|
|
+ createMediaPlayer();
|
|
|
|
+ initMediaSessionManager();
|
|
|
|
+ initAudioFocusManager();
|
|
|
|
+ initEarPhoneBroadcastReceiver();
|
|
|
|
+ initAudioBroadcastReceiver();
|
|
|
|
+ initQuitTimer();
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 服务在销毁时调用该方法
|
|
|
|
+ */
|
|
|
|
+ @Override
|
|
|
|
+ public void onDestroy() {
|
|
|
|
+ super.onDestroy();
|
|
|
|
+ //销毁handler
|
|
|
|
+ if(handler!=null){
|
|
|
|
+ handler.removeCallbacksAndMessages(null);
|
|
|
|
+ handler = null;
|
|
|
|
+ }
|
|
|
|
+ //销毁MediaPlayer
|
|
|
|
+ mPlayer.reset();
|
|
|
|
+ mPlayer.release();
|
|
|
|
+ mPlayer = null;
|
|
|
|
+ //放弃音频焦点
|
|
|
|
+ mAudioFocusManager.abandonAudioFocus();
|
|
|
|
+ mMediaSessionManager.release();
|
|
|
|
+ //注销广播接收者
|
|
|
|
+ unregisterReceiver(mAudioReceiver);
|
|
|
|
+ //结束notification通知
|
|
|
|
+ NotificationHelper.get().cancelAll();
|
|
|
|
+ //设置service为null
|
|
|
|
+ BaseAppHelper.get().setPlayService(null);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 每次通过startService()方法启动Service时都会被回调。
|
|
|
|
+ * @param intent intent
|
|
|
|
+ * @param flags flags
|
|
|
|
+ * @param startId startId
|
|
|
|
+ * @return
|
|
|
|
+ * onStartCommand方法返回值作用:
|
|
|
|
+ * START_STICKY:粘性,service进程被异常杀掉,系统重新创建进程与服务,会重新执行onCreate()、onStartCommand(Intent)
|
|
|
|
+ * START_STICKY_COMPATIBILITY:START_STICKY的兼容版本,但不保证服务被kill后一定能重启。
|
|
|
|
+ * START_NOT_STICKY:非粘性,Service进程被异常杀掉,系统不会自动重启该Service。
|
|
|
|
+ * START_REDELIVER_INTENT:重传Intent。使用这个返回值时,如果在执行完onStartCommand后,服务被异常kill掉,系统会自动重启该服务,并将Intent的值传入。
|
|
|
|
+ */
|
|
|
|
+ @Override
|
|
|
|
+ public int onStartCommand(Intent intent, int flags, int startId) {
|
|
|
|
+ if (intent != null && intent.getAction() != null) {
|
|
|
|
+ switch (intent.getAction()) {
|
|
|
|
+ //上一首
|
|
|
|
+ case MusicPlayAction.TYPE_PRE:
|
|
|
|
+ prev();
|
|
|
|
+ break;
|
|
|
|
+ //下一首
|
|
|
|
+ case MusicPlayAction.TYPE_NEXT:
|
|
|
|
+ next();
|
|
|
|
+ break;
|
|
|
|
+ //播放或暂停
|
|
|
|
+ case MusicPlayAction.TYPE_START_PAUSE:
|
|
|
|
+ playPause();
|
|
|
|
+ break;
|
|
|
|
+ //添加锁屏界面
|
|
|
|
+ case MusicConstant.LOCK_SCREEN_ACTION:
|
|
|
|
+ MusicLogUtils.e("PlayService"+"---LOCK_SCREEN"+mIsLocked);
|
|
|
|
+ break;
|
|
|
|
+ //当屏幕灭了,添加锁屏页面
|
|
|
|
+ case Intent.ACTION_SCREEN_OFF:
|
|
|
|
+ startLockAudioActivity();
|
|
|
|
+ MusicLogUtils.e("PlayService"+"---当屏幕灭了");
|
|
|
|
+ break;
|
|
|
|
+ case Intent.ACTION_SCREEN_ON:
|
|
|
|
+ MusicLogUtils.e("PlayService"+"---当屏幕亮了");
|
|
|
|
+ break;
|
|
|
|
+ default:
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ return START_NOT_STICKY;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 创建MediaPlayer对象
|
|
|
|
+ */
|
|
|
|
+ private void createMediaPlayer() {
|
|
|
|
+ if(mPlayer==null){
|
|
|
|
+ //MediaCodec codec = new MediaCodec();
|
|
|
|
+ mPlayer = new MediaPlayer();
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 允许与媒体控制器、音量键、媒体按钮和传输控件交互。
|
|
|
|
+ * 播放器除了播放了音乐之外什么都没做,就可以分别在任务管理、锁屏、负一屏控制我的播放器
|
|
|
|
+ */
|
|
|
|
+ private void initMediaSessionManager() {
|
|
|
|
+ mMediaSessionManager = new MediaSessionManager(this);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 捕获/丢弃音乐焦点处理
|
|
|
|
+ */
|
|
|
|
+ private void initAudioFocusManager() {
|
|
|
|
+ mAudioFocusManager = new AudioFocusManager(this);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 初始化耳机插入和拔出监听
|
|
|
|
+ */
|
|
|
|
+ private void initEarPhoneBroadcastReceiver() {
|
|
|
|
+ //这块直接在清单文件注册
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 初始化IntentFilter添加action意图
|
|
|
|
+ * 主要是监听屏幕亮了与灭了
|
|
|
|
+ */
|
|
|
|
+ private void initAudioBroadcastReceiver() {
|
|
|
|
+ final IntentFilter filter = new IntentFilter();
|
|
|
|
+ //来电/耳机
|
|
|
|
+ filter.addAction(AudioManager.ACTION_AUDIO_BECOMING_NOISY);
|
|
|
|
+ //锁屏
|
|
|
|
+ filter.addAction(MusicConstant.LOCK_SCREEN_ACTION);
|
|
|
|
+ //当屏幕灭了
|
|
|
|
+ filter.addAction(Intent.ACTION_SCREEN_OFF);
|
|
|
|
+ //当屏幕亮了
|
|
|
|
+ filter.addAction(Intent.ACTION_SCREEN_ON);
|
|
|
|
+ registerReceiver(mAudioReceiver, filter);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 初始化计时器
|
|
|
|
+ */
|
|
|
|
+ private void initQuitTimer() {
|
|
|
|
+ QuitTimerHelper.getInstance().init(this, handler, new EventCallback<Long>() {
|
|
|
|
+ @Override
|
|
|
|
+ public void onEvent(Long aLong) {
|
|
|
|
+ if (mListener != null) {
|
|
|
|
+ mListener.onTimer(aLong);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ });
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ /**---------------------播放或暂停,上一首,下一首-----------------------------------------*/
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 播放或暂停
|
|
|
|
+ * 逻辑:
|
|
|
|
+ * 1.如果正在准备,点击则是停止播放
|
|
|
|
+ * 2.如果是正在播放,点击则是暂停
|
|
|
|
+ * 3.如果是暂停状态,点击则是开始播放
|
|
|
|
+ * 4.其他情况是直接播放
|
|
|
|
+ */
|
|
|
|
+ public void playPause() {
|
|
|
|
+ if (isPreparing()) {
|
|
|
|
+ stop();
|
|
|
|
+ } else if (isPlaying()) {
|
|
|
|
+ pause();
|
|
|
|
+ } else if (isPausing()) {
|
|
|
|
+ start();
|
|
|
|
+ } else {
|
|
|
|
+ play(getPlayingPosition());
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 上一首
|
|
|
|
+ * 记住有播放类型,单曲循环,顺序循环,随机播放
|
|
|
|
+ * 逻辑:如果不是第一首,则还有上一首;如果没有上一首,则切换到最后一首
|
|
|
|
+ */
|
|
|
|
+ public void prev() {
|
|
|
|
+ //建议都添加这个判断
|
|
|
|
+ if (audioMusics.isEmpty()) {
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+ int playMode = MusicSpUtils.getInstance(MusicConstant.SP_NAME).getInt(MusicConstant.PLAY_MODE, 0);
|
|
|
|
+ int size = audioMusics.size();
|
|
|
|
+ PlayModeEnum mode = PlayModeEnum.valueOf(playMode);
|
|
|
|
+ switch (mode) {
|
|
|
|
+ //随机
|
|
|
|
+ case SHUFFLE:
|
|
|
|
+ mPlayingPosition = new Random().nextInt(size);
|
|
|
|
+ play(mPlayingPosition);
|
|
|
|
+ break;
|
|
|
|
+ //单曲
|
|
|
|
+ case SINGLE:
|
|
|
|
+ play(mPlayingPosition);
|
|
|
|
+ break;
|
|
|
|
+ //顺序播放并且循环
|
|
|
|
+ case LOOP:
|
|
|
|
+ default:
|
|
|
|
+ if(mPlayingPosition != 0){
|
|
|
|
+ // 如果不是第一首,则还有上一首
|
|
|
|
+ mPlayingPosition--;
|
|
|
|
+ } else {
|
|
|
|
+ // 如果没有上一首,则切换到最后一首
|
|
|
|
+ mPlayingPosition = size;
|
|
|
|
+ }
|
|
|
|
+ play(mPlayingPosition);
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 下一首
|
|
|
|
+ * 记住有播放类型,单曲循环,顺序循环,随机播放
|
|
|
|
+ * 逻辑:如果不是最后一首,则还有下一首;如果是最后一首,则切换回第一首
|
|
|
|
+ */
|
|
|
|
+ public void next() {
|
|
|
|
+ //建议都添加这个判断
|
|
|
|
+ if (audioMusics.isEmpty()) {
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+ int playMode = MusicSpUtils.getInstance(MusicConstant.SP_NAME).getInt(MusicConstant.PLAY_MODE, 0);
|
|
|
|
+ int size = audioMusics.size();
|
|
|
|
+ PlayModeEnum mode = PlayModeEnum.valueOf(playMode);
|
|
|
|
+ switch (mode) {
|
|
|
|
+ //随机
|
|
|
|
+ case SHUFFLE:
|
|
|
|
+ mPlayingPosition = new Random().nextInt(size);
|
|
|
|
+ play(mPlayingPosition);
|
|
|
|
+ break;
|
|
|
|
+ //单曲
|
|
|
|
+ case SINGLE:
|
|
|
|
+ play(mPlayingPosition);
|
|
|
|
+ break;
|
|
|
|
+ //顺序播放并且循环
|
|
|
|
+ case LOOP:
|
|
|
|
+ default:
|
|
|
|
+ if (mPlayingPosition != size - 1) {
|
|
|
|
+ // 如果不是最后一首,则还有下一首
|
|
|
|
+ mPlayingPosition++;
|
|
|
|
+ } else {
|
|
|
|
+ // 如果是最后一首,则切换回第一首
|
|
|
|
+ mPlayingPosition = 0;
|
|
|
|
+ }
|
|
|
|
+ MusicLogUtils.e("PlayService"+"----mPlayingPosition----"+ mPlayingPosition);
|
|
|
|
+ play(mPlayingPosition);
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**---------------------开始播放,暂停播放,停止播放等-----------------------------------------*/
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 开始播放
|
|
|
|
+ */
|
|
|
|
+ public void start() {
|
|
|
|
+ if (!isPreparing() && !isPausing()) {
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+ if(mPlayingMusic==null){
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+ if(mAudioFocusManager.requestAudioFocus()){
|
|
|
|
+ if(mPlayer!=null){
|
|
|
|
+ mPlayer.start();
|
|
|
|
+ mPlayState = MusicPlayAction.STATE_PLAYING;
|
|
|
|
+ //开始发送消息,执行进度条进度更新
|
|
|
|
+ handler.sendEmptyMessage(UPDATE_PLAY_PROGRESS_SHOW);
|
|
|
|
+ if (mListener != null) {
|
|
|
|
+ mListener.onPlayerStart();
|
|
|
|
+ }
|
|
|
|
+ //当点击播放按钮时(播放详情页面或者底部控制栏),同步通知栏中播放按钮状态
|
|
|
|
+ NotificationHelper.get().showPlay(mPlayingMusic);
|
|
|
|
+ //注册监听来电/耳机拔出时暂停播放广播
|
|
|
|
+ if(!mReceiverTag){
|
|
|
|
+ mReceiverTag = true;
|
|
|
|
+ registerReceiver(mNoisyReceiver, mFilter);
|
|
|
|
+ }
|
|
|
|
+ mMediaSessionManager.updatePlaybackState();
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 暂停
|
|
|
|
+ */
|
|
|
|
+ public void pause() {
|
|
|
|
+ if(mPlayer!=null){
|
|
|
|
+ //暂停
|
|
|
|
+ mPlayer.pause();
|
|
|
|
+ //切换状态
|
|
|
|
+ mPlayState = MusicPlayAction.STATE_PAUSE;
|
|
|
|
+ //移除,注意一定要移除,否则一直走更新方法
|
|
|
|
+ handler.removeMessages(UPDATE_PLAY_PROGRESS_SHOW);
|
|
|
|
+ //监听
|
|
|
|
+ if (mListener != null) {
|
|
|
|
+ mListener.onPlayerPause();
|
|
|
|
+ }
|
|
|
|
+ //当点击暂停按钮时(播放详情页面或者底部控制栏),同步通知栏中暂停按钮状态
|
|
|
|
+ NotificationHelper.get().showPause(mPlayingMusic);
|
|
|
|
+ //注销监听来电/耳机拔出时暂停播放广播
|
|
|
|
+ //判断广播是否注册
|
|
|
|
+ if (mReceiverTag) {
|
|
|
|
+ //Tag值 赋值为false 表示该广播已被注销
|
|
|
|
+ mReceiverTag = false;
|
|
|
|
+ unregisterReceiver(mNoisyReceiver);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ mMediaSessionManager.updatePlaybackState();
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 停止播放
|
|
|
|
+ */
|
|
|
|
+ public void stop() {
|
|
|
|
+ if (isDefault()) {
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+ pause();
|
|
|
|
+ if(mPlayer!=null){
|
|
|
|
+ mPlayer.reset();
|
|
|
|
+ mPlayState = MusicPlayAction.STATE_IDLE;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 播放索引为position的音乐
|
|
|
|
+ * @param position 索引
|
|
|
|
+ */
|
|
|
|
+ public void play(int position) {
|
|
|
|
+ audioMusics = BaseAppHelper.get().getMusicList();
|
|
|
|
+ if (audioMusics.isEmpty()) {
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (position < 0) {
|
|
|
|
+ position = audioMusics.size() - 1;
|
|
|
|
+ } else if (position >= audioMusics.size()) {
|
|
|
|
+ //如果是最后一首音乐,则播放时直接播放第一首音乐
|
|
|
|
+ position = 0;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ mPlayingPosition = position;
|
|
|
|
+ AudioBean music = audioMusics.get(mPlayingPosition);
|
|
|
|
+ String id = music.getId();
|
|
|
|
+ MusicLogUtils.e("PlayService"+"----id----"+ id);
|
|
|
|
+ //保存当前播放的musicId,下次进来可以记录状态
|
|
|
|
+ long musicId = Long.parseLong(id);
|
|
|
|
+ MusicSpUtils.getInstance(MusicConstant.SP_NAME).put(MusicConstant.MUSIC_ID,musicId);
|
|
|
|
+ play(music);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 拖动seekBar时,调节进度
|
|
|
|
+ * @param progress 进度
|
|
|
|
+ */
|
|
|
|
+ public void seekTo(int progress) {
|
|
|
|
+ //只有当播放或者暂停的时候才允许拖动bar
|
|
|
|
+ if (isPlaying() || isPausing()) {
|
|
|
|
+ mPlayer.seekTo(progress);
|
|
|
|
+ if(mListener!=null){
|
|
|
|
+ mListener.onUpdateProgress(progress);
|
|
|
|
+ }
|
|
|
|
+ mMediaSessionManager.updatePlaybackState();
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 播放,这种是直接传音频实体类
|
|
|
|
+ * @param music music
|
|
|
|
+ */
|
|
|
|
+ public void play(AudioBean music) {
|
|
|
|
+ mPlayingMusic = music;
|
|
|
|
+ createMediaPlayer();
|
|
|
|
+ try {
|
|
|
|
+ mPlayer.reset();
|
|
|
|
+ //把音频路径传给播放器
|
|
|
|
+ mPlayer.setDataSource(mPlayingMusic.getPath());
|
|
|
|
+ //准备
|
|
|
|
+ mPlayer.prepareAsync();
|
|
|
|
+ //设置状态为准备中
|
|
|
|
+ mPlayState = MusicPlayAction.STATE_PREPARING;
|
|
|
|
+ //监听
|
|
|
|
+ mPlayer.setOnPreparedListener(mOnPreparedListener);
|
|
|
|
+ mPlayer.setOnBufferingUpdateListener(mOnBufferingUpdateListener);
|
|
|
|
+ mPlayer.setOnCompletionListener(mOnCompletionListener);
|
|
|
|
+ mPlayer.setOnSeekCompleteListener(mOnSeekCompleteListener);
|
|
|
|
+ mPlayer.setOnErrorListener(mOnErrorListener);
|
|
|
|
+ mPlayer.setOnInfoListener(mOnInfoListener);
|
|
|
|
+ //当播放的时候,需要刷新界面信息
|
|
|
|
+ if (mListener != null) {
|
|
|
|
+ mListener.onChange(mPlayingMusic);
|
|
|
|
+ }
|
|
|
|
+ //更新通知栏
|
|
|
|
+ NotificationHelper.get().showPlay(mPlayingMusic);
|
|
|
|
+
|
|
|
|
+ //更新
|
|
|
|
+ mMediaSessionManager.updateMetaData(mPlayingMusic);
|
|
|
|
+ mMediaSessionManager.updatePlaybackState();
|
|
|
|
+ } catch (IOException e) {
|
|
|
|
+ e.printStackTrace();
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 更新播放进度的显示,时间的显示
|
|
|
|
+ */
|
|
|
|
+ private void updatePlayProgressShow() {
|
|
|
|
+ if (isPlaying() && mListener != null) {
|
|
|
|
+ int currentPosition = mPlayer.getCurrentPosition();
|
|
|
|
+ mListener.onUpdateProgress(currentPosition);
|
|
|
|
+ }
|
|
|
|
+ MusicLogUtils.e("updatePlayProgressShow");
|
|
|
|
+ // 每30毫秒更新一下显示的内容,注意这里时间不要太短,因为这个是一个循环
|
|
|
|
+ // 经过测试,60毫秒更新一次有点卡,30毫秒最为顺畅
|
|
|
|
+ handler.sendEmptyMessageDelayed(UPDATE_PLAY_PROGRESS_SHOW, 300);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ /** 音频准备好的监听器 */
|
|
|
|
+ private MediaPlayer.OnPreparedListener mOnPreparedListener = new MediaPlayer.OnPreparedListener() {
|
|
|
|
+ /** 当音频准备好可以播放了,则这个方法会被调用 */
|
|
|
|
+ @Override
|
|
|
|
+ public void onPrepared(MediaPlayer mp) {
|
|
|
|
+ if (isPreparing()) {
|
|
|
|
+ start();
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ /** 当音频播放结束的时候的监听器 */
|
|
|
|
+ private MediaPlayer.OnCompletionListener mOnCompletionListener = new MediaPlayer.OnCompletionListener() {
|
|
|
|
+ /** 当音频播放结果的时候这个方法会被调用 */
|
|
|
|
+ @Override
|
|
|
|
+ public void onCompletion(MediaPlayer mp) {
|
|
|
|
+ next();
|
|
|
|
+ }
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ /** 当音频缓冲的监听器 */
|
|
|
|
+ private MediaPlayer.OnBufferingUpdateListener mOnBufferingUpdateListener = new MediaPlayer.OnBufferingUpdateListener() {
|
|
|
|
+ @Override
|
|
|
|
+ public void onBufferingUpdate(MediaPlayer mp, int percent) {
|
|
|
|
+ if (mListener != null) {
|
|
|
|
+ // 缓冲百分比
|
|
|
|
+ mListener.onBufferingUpdate(percent);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ /** 跳转完成时的监听 */
|
|
|
|
+ private MediaPlayer.OnSeekCompleteListener mOnSeekCompleteListener = new MediaPlayer.OnSeekCompleteListener() {
|
|
|
|
+ @Override
|
|
|
|
+ public void onSeekComplete(MediaPlayer mp) {
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 播放错误的监听
|
|
|
|
+ */
|
|
|
|
+ private MediaPlayer.OnErrorListener mOnErrorListener = new MediaPlayer.OnErrorListener() {
|
|
|
|
+ @Override
|
|
|
|
+ public boolean onError(MediaPlayer mp, int what, int extra) {
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 设置音频信息监听器
|
|
|
|
+ */
|
|
|
|
+ private MediaPlayer.OnInfoListener mOnInfoListener = new MediaPlayer.OnInfoListener() {
|
|
|
|
+ @Override
|
|
|
|
+ public boolean onInfo(MediaPlayer mp, int what, int extra) {
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 是否正在播放
|
|
|
|
+ * @return true表示正在播放
|
|
|
|
+ */
|
|
|
|
+ public boolean isPlaying() {
|
|
|
|
+ return mPlayState == MusicPlayAction.STATE_PLAYING;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 是否暂停
|
|
|
|
+ * @return true表示暂停
|
|
|
|
+ */
|
|
|
|
+ public boolean isPausing() {
|
|
|
|
+ return mPlayState == MusicPlayAction.STATE_PAUSE;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 是否正在准备中
|
|
|
|
+ * @return true表示正在准备中
|
|
|
|
+ */
|
|
|
|
+ public boolean isPreparing() {
|
|
|
|
+ return mPlayState == MusicPlayAction.STATE_PREPARING;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 是否正在准备中
|
|
|
|
+ * @return true表示正在准备中
|
|
|
|
+ */
|
|
|
|
+ public boolean isDefault() {
|
|
|
|
+ return mPlayState == MusicPlayAction.STATE_IDLE;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**------------------------------------------------------------------------------------------*/
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 退出时候调用
|
|
|
|
+ */
|
|
|
|
+ public void quit() {
|
|
|
|
+ // 先停止播放
|
|
|
|
+ stop();
|
|
|
|
+ // 移除定时器
|
|
|
|
+ QuitTimerHelper.getInstance().stop();
|
|
|
|
+ // 当另一个组件(如 Activity)通过调用 startService() 请求启动服务时,系统将调用onStartCommand。
|
|
|
|
+ // 一旦执行此方法,服务即会启动并可在后台无限期运行。 如果自己实现此方法,则需要在服务工作完成后,
|
|
|
|
+ // 通过调用 stopSelf() 或 stopService() 来停止服务。
|
|
|
|
+ stopSelf();
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 获取正在播放的本地歌曲的序号
|
|
|
|
+ */
|
|
|
|
+ public int getPlayingPosition() {
|
|
|
|
+ return mPlayingPosition;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 获取正在播放的歌曲[本地|网络]
|
|
|
|
+ */
|
|
|
|
+ public AudioBean getPlayingMusic() {
|
|
|
|
+ return mPlayingMusic;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 获取播放的进度
|
|
|
|
+ * @return long类型值
|
|
|
|
+ */
|
|
|
|
+ public long getCurrentPosition() {
|
|
|
|
+ if (isPlaying() || isPausing()) {
|
|
|
|
+ return mPlayer.getCurrentPosition();
|
|
|
|
+ } else {
|
|
|
|
+ return 0;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 判斷是否有上一首音頻
|
|
|
|
+ * @return true表示有
|
|
|
|
+ */
|
|
|
|
+ public boolean isHavePre() {
|
|
|
|
+ if(audioMusics !=null && audioMusics.size()>0){
|
|
|
|
+ if(mPlayingPosition != 0){
|
|
|
|
+ // 如果不是第一首,则还有上一首
|
|
|
|
+ return true;
|
|
|
|
+ } else {
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+ }else {
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 判斷是否有下一首音頻
|
|
|
|
+ * @return true表示有
|
|
|
|
+ */
|
|
|
|
+ public boolean isHaveNext() {
|
|
|
|
+ if(audioMusics !=null && audioMusics.size()>0){
|
|
|
|
+ if (mPlayingPosition != audioMusics.size() - 1) {
|
|
|
|
+ // 如果不是最后一首,则还有下一首
|
|
|
|
+ return true;
|
|
|
|
+ } else {
|
|
|
|
+ // 如果是最后一首,则切换回第一首
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+ }else {
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+//
|
|
|
|
+// /**
|
|
|
|
+// * 扫描音乐
|
|
|
|
+// */
|
|
|
|
+// @SuppressLint("StaticFieldLeak")
|
|
|
|
+// public void updateMusicList(final EventCallback<Void> callback) {
|
|
|
|
+// new AsyncTask<Void, Void, List<AudioBean>>() {
|
|
|
|
+// @Override
|
|
|
|
+// protected List<AudioBean> doInBackground(Void... params) {
|
|
|
|
+// return FileMusicScanManager.getInstance().scanMusic(PlayService.this);
|
|
|
|
+// }
|
|
|
|
+//
|
|
|
|
+// @Override
|
|
|
|
+// protected void onPostExecute(List<AudioBean> musicList) {
|
|
|
|
+// //首先先清空
|
|
|
|
+// //然后添加所有扫描到的音乐
|
|
|
|
+// BaseAppHelper.get().setMusicList(musicList);
|
|
|
|
+//
|
|
|
|
+// //如果获取音乐数据集合不为空
|
|
|
|
+// if (!BaseAppHelper.get().getMusicList().isEmpty()) {
|
|
|
|
+// //音频的集合
|
|
|
|
+// audioMusics = BaseAppHelper.get().getMusicList();
|
|
|
|
+// //刷新正在播放的本地歌曲的序号
|
|
|
|
+// updatePlayingPosition();
|
|
|
|
+// //获取正在播放的音乐
|
|
|
|
+// if(mPlayingPosition>=0){
|
|
|
|
+// mPlayingMusic = BaseAppHelper.get().getMusicList().get(mPlayingPosition);
|
|
|
|
+// }
|
|
|
|
+// }
|
|
|
|
+// if (callback != null) {
|
|
|
|
+// callback.onEvent(null);
|
|
|
|
+// }
|
|
|
|
+// }
|
|
|
|
+// }.execute();
|
|
|
|
+// }
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 删除或下载歌曲后刷新正在播放的本地歌曲的序号
|
|
|
|
+ */
|
|
|
|
+ public void updatePlayingPosition() {
|
|
|
|
+ int position = 0;
|
|
|
|
+ long id = MusicSpUtils.getInstance(MusicConstant.SP_NAME).getLong(MusicConstant.MUSIC_ID,-1);
|
|
|
|
+ if(audioMusics.isEmpty()){
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+ for (int i = 0; i < audioMusics.size(); i++) {
|
|
|
|
+ String musicId = audioMusics.get(i).getId();
|
|
|
|
+ MusicLogUtils.e("PlayService"+"----musicId----"+ musicId);
|
|
|
|
+ if (Long.parseLong(musicId) == id) {
|
|
|
|
+ position = i;
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ mPlayingPosition = position;
|
|
|
|
+ long musicId = Long.parseLong(audioMusics.get(mPlayingPosition).getId());
|
|
|
|
+ MusicSpUtils.getInstance(MusicConstant.SP_NAME).put(MusicConstant.MUSIC_ID,musicId);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 获取播放进度监听器对象
|
|
|
|
+ * @return OnPlayerEventListener对象
|
|
|
|
+ */
|
|
|
|
+ public OnPlayerEventListener getOnPlayEventListener() {
|
|
|
|
+ return mListener;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 设置播放进度监听器
|
|
|
|
+ * @param listener listener
|
|
|
|
+ */
|
|
|
|
+ public void setOnPlayEventListener(OnPlayerEventListener listener) {
|
|
|
|
+ mListener = listener;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ /**-------------------------------------添加锁屏界面----------------------------------------*/
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 打开锁屏页面,这块伤透了脑筋
|
|
|
|
+ * 不管是播放状态是哪一个,只要屏幕灭了到亮了,就展现这个锁屏页面
|
|
|
|
+ * 有些APP限制了状态,比如只有播放时才走这个逻辑
|
|
|
|
+ */
|
|
|
|
+ private void startLockAudioActivity() {
|
|
|
|
+ if(!mIsLocked && isPlaying()){
|
|
|
|
+// Intent lockScreen = new Intent(this, LockAudioActivity.class);
|
|
|
|
+// lockScreen.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
|
|
|
+// startActivity(lockScreen);
|
|
|
|
+// BaseConfig.INSTANCE.setLocked(true);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ /**-------------------------------------播放list----------------------------------------*/
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+}
|