介绍一种实现应用内音乐播放器方法:支持通用播控、后台播放、通知栏显示控制播放、退出播放界面时创建全局可拖动的悬浮窗控制播放。
效果图如下:
实现思路:
1.后台播放首先想到使用Service,同时要与activity通讯进行播控,此时一般通过bindService实现
2.通知栏显示并要能进行播控。使用Notification+RemoteViews实现通知栏显示UI并通过RemoteView可以发送广播特性来实现跟Service播放通讯。
3.全局悬浮窗播控,使用WindownManager.addView()显示,由于上一步通知栏是通过广播与播放器进行通讯,这里也统一使用相同的广播进行通讯即可。
小结:Service中实现播放逻辑,并接收各方广播进行控制播放,在开始播放时创建Notification通知栏。Activity绑定Service,直接通过Binder与Service通讯进行控制,当然也可以通过广播控制。退出activity时通过WindownManager创建全局悬浮窗.
主要代码如下:
MusicPlayerService.java 实现播放
import static android.app.PendingIntent.FLAG_UPDATE_CURRENT;
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.graphics.BitmapFactory;
import android.media.MediaPlayer;
import android.os.Binder;
import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
import android.text.TextUtils;
import android.util.Log;
import android.widget.RemoteViews;
import androidx.core.app.NotificationCompat;
import com.justlink.nas.R;
import com.justlink.nas.application.MyApplication;
import com.justlink.nas.bean.FileBean;
import com.justlink.nas.constans.MyConstants;
import com.justlink.nas.utils.LogUtil;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
public class MusicPlayerService extends Service {
public static final String TAG = "MusicService";
/**
* 发送音乐播放控制的广播给MianActivity,音乐的控制统一交给MainActivity管理
* 下一首,暂停,上一首,播放,继续播放
*/
private static final String ACTION_NEXT_SONG = "com.justlink.action.nextsong";
private static final String ACTION_PAUSE = "com.justlink.action.pause";
private static final String ACTION_PRE_SONG = "com.justlink.action.presong";
private static final String ACTION_PLAY_SONG = "com.justlink.action.playsong";
private static final String ACTION_PLAY_CLOSE = "com.justlink.action.close";
private static final String ACTION_CONTINUE_PLAYING_SONG = "com.justlink.action.continueplaying";
public MusicBinder mBinder;
private MediaPlayer mPlayer;
private Handler handler;
private Timer timer;
private int duration,currentPlayIndex;
private String defaultSongName;
//观察者们,播放进度更新时通知他们
private List<Watcher> watcherList = new ArrayList<Watcher>();
private MyBroadCastReceiver myBroadCastReceiver;
/**
* 通知
*/
private static Notification notification;
/**
* 通知栏视图
*/
private static RemoteViews remoteViews;
/**
* 通知ID
*/
private int NOTIFICATION_ID = 1;
/**
* 通知管理器
*/
private static NotificationManager manager;
private FileBean currentPlayFile;
public MusicPlayerService() {
}
@Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
return mBinder;
// throw new UnsupportedOperationException("Not yet implemented");
}
public class MusicBinder extends Binder {
public MusicPlayerService getService() {
return MusicPlayerService.this;
}
}
@Override
public void onCreate() {
super.onCreate();
mBinder = new MusicBinder();
mPlayer = new MediaPlayer();
timer = new Timer();
timer.scheduleAtFixedRate(new TimerTask() {
public void run() {
if (mPlayer.isPlaying()) {
Log.d(TAG, "start update proress...");
//播放进度更新通知观察者
for (Watcher watcher : watcherList) {
watcher.update(mPlayer.getCurrentPosition(),duration);
handler.sendEmptyMessage(5562);
}
}
}
}, 1000, 1000);
//初始化RemoteViews配置
initRemoteViews();
//初始化通知
initNotification();
Log.d(TAG, "onCreate");
myBroadCastReceiver = new MyBroadCastReceiver();
IntentFilter filter = new IntentFilter();
filter.addAction(ACTION_NEXT_SONG);
filter.addAction(ACTION_PAUSE);
filter.addAction(ACTION_PRE_SONG);
filter.addAction(ACTION_PLAY_SONG);
filter.addAction(ACTION_PLAY_CLOSE);
filter.addAction(ACTION_CONTINUE_PLAYING_SONG);
registerReceiver(myBroadCastReceiver,filter);
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i(TAG, "onStartCommnad");
if(intent!=null && intent.getStringExtra("url")!=null){
String path = intent.getStringExtra("url");
int position = intent.getIntExtra("currentPosition",0);
defaultSongName = intent.getStringExtra("name");
play(path);
changePlayProgress(position);
}
// showButtonNotify();
return START_STICKY;
}
public void addWatcher(Watcher watcher) {
watcherList.add(watcher);
}
public void removeWatcher(Watcher watcher) {
watcherList.remove(watcher);
}
public interface Watcher {
public void update(int currentProgress,int duration);
}
public void play(String url) {
try {
mPlayer.reset();
Log.d(TAG, "play reset ");
mPlayer.setDataSource(url);
Log.d(TAG, "play setDataSource ");
mPlayer.prepare();
Log.d(TAG, "play prepare ");
mPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer player) {
if(handler != null)
handler.sendMessage(handler.obtainMessage(5563));
Log.d(TAG, "sendBroadcast->nextsong");
}
});
mPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
@Override
public void onPrepared(MediaPlayer mediaPlayer) {
duration = mPlayer.getDuration();
LogUtil.showLog(TAG,"==duration=="+duration);
mPlayer.start();
Log.d(TAG, "play start ");
updateNotificationShow();
}
});
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
public void pauseMusic() {
mPlayer.pause();
}
public void continuePlaying() {
mPlayer.start();
}
public void stopMusic(){
mPlayer.stop();
};
public void changePlayProgress(int pos) {
mPlayer.seekTo(pos);
}
public void setHandler(Handler handler) {
this.handler = handler;
}
public void setCurrentPlayIndex(int index){
currentPlayIndex = index;
}
public void setDefaultSongName(String defaultSongName) {
this.defaultSongName = defaultSongName;
}
private class MyBroadCastReceiver extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {
LogUtil.showLog(TAG,"==action=="+intent.getAction());
switch (intent.getAction()){
case ACTION_PLAY_SONG:
if(!mPlayer.isPlaying())
mPlayer.start();
updateNotificationShow();
break;
case ACTION_PAUSE:
if(mPlayer.isPlaying())
mPlayer.pause();
updateNotificationShow();
break;
case ACTION_NEXT_SONG:
currentPlayIndex++;
playNextOrLast();
break;
case ACTION_PRE_SONG:
if(currentPlayIndex==0)
currentPlayIndex = MyApplication.audioList.size()-1;
currentPlayIndex--;
playNextOrLast();
break;
case ACTION_PLAY_CLOSE:
manager.cancel(NOTIFICATION_ID);
break;
case ACTION_CONTINUE_PLAYING_SONG:
break;
}
}
}
public void playNextOrLast(){
if(!mPlayer.isPlaying())
mPlayer.pause();
mPlayer.stop();
String path = "";
FileBean fileBean = MyApplication.audioList.get(currentPlayIndex);
if(TextUtils.isEmpty(fileBean.getDir()))
path = fileBean.getName();
else
path = fileBean.getDir()+"/"+fileBean.getName();
String currentUrl = MyConstants.file_http_base_url+path+"?disk="+ MyApplication.storeList.get(MyApplication.currentStoreId).getPosition()+"&user="+MyApplication.userLoginID+"&device_id="+MyApplication.currentDevID+"&filetype=data";
play(currentUrl);
//歌曲名
remoteViews.setTextViewText(R.id.tv_notification_song_name,fileBean.getName().substring(0,fileBean.getName().lastIndexOf(".")));
//发送通知
manager.notify(NOTIFICATION_ID, notification);
}
/**
* 初始化自定义通知栏 的按钮点击事件
*/
private void initRemoteViews() {
remoteViews = new RemoteViews(this.getPackageName(), R.layout.notification);
//通知栏控制器上一首按钮广播操作
Intent intentPrev = new Intent(ACTION_PRE_SONG);
PendingIntent prevPendingIntent = PendingIntent.getBroadcast(this, 5100, intentPrev, PendingIntent.FLAG_CANCEL_CURRENT);
//为prev控件注册事件
remoteViews.setOnClickPendingIntent(R.id.btn_notification_previous, prevPendingIntent);
//通知栏控制器播放暂停按钮广播操作 //用于接收广播时过滤意图信息
Intent intentPlay = new Intent(ACTION_PAUSE);
PendingIntent playPendingIntent = PendingIntent.getBroadcast(this, 5101, intentPlay, PendingIntent.FLAG_CANCEL_CURRENT);
//为play控件注册事件
remoteViews.setOnClickPendingIntent(R.id.btn_notification_play, playPendingIntent);
//通知栏控制器下一首按钮广播操作
Intent intentNext = new Intent(ACTION_NEXT_SONG);
PendingIntent nextPendingIntent = PendingIntent.getBroadcast(this, 5102, intentNext, PendingIntent.FLAG_CANCEL_CURRENT);
//为next控件注册事件
remoteViews.setOnClickPendingIntent(R.id.btn_notification_next, nextPendingIntent);
//通知栏控制器关闭按钮广播操作
Intent intentClose = new Intent(ACTION_PLAY_CLOSE);
PendingIntent closePendingIntent = PendingIntent.getBroadcast(this, 5103, intentClose, 0);
//为close控件注册事件
remoteViews.setOnClickPendingIntent(R.id.btn_notification_close, closePendingIntent);
}
/**
* 初始化通知
*/
@SuppressLint("NotificationTrampoline")
private void initNotification() {
String channelId = "play_control";
String channelName = "播放控制";
int importance = NotificationManager.IMPORTANCE_HIGH;
createNotificationChannel(channelId, channelName, importance);
//点击整个通知时发送广播
Intent intent = new Intent(getApplicationContext(), NotificationClickReceiver.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(getApplicationContext(), 0,
intent, FLAG_UPDATE_CURRENT);
//初始化通知
notification = new NotificationCompat.Builder(this, "play_control")
.setContentIntent(pendingIntent)
.setWhen(System.currentTimeMillis())
.setSmallIcon(R.mipmap.ic_launcher)
.setLargeIcon(BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher))
.setCustomContentView(remoteViews)
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
.setAutoCancel(false)
.setOnlyAlertOnce(true)
.setOngoing(true)
.build();
}
/**
* 创建通知渠道
*
* @param channelId 渠道id
* @param channelName 渠道名称
* @param importance 渠道重要性
*/
@TargetApi(Build.VERSION_CODES.O)
private void createNotificationChannel(String channelId, String channelName, int importance) {
NotificationChannel channel = new NotificationChannel(channelId, channelName, importance);
channel.enableLights(false);
channel.enableVibration(false);
channel.setVibrationPattern(new long[]{0});
channel.setSound(null, null);
manager = (NotificationManager) getSystemService(
NOTIFICATION_SERVICE);
manager.createNotificationChannel(channel);
}
/**
* 更改通知的信息和UI
*/
public void updateNotificationShow() {
//通知栏控制器播放暂停按钮广播操作 //用于接收广播时过滤意图信息
Intent intentPlay = new Intent(mPlayer.isPlaying()?ACTION_PAUSE:ACTION_PLAY_SONG);
PendingIntent playPendingIntent = PendingIntent.getBroadcast(this, 5101, intentPlay, PendingIntent.FLAG_CANCEL_CURRENT);
//为play控件注册事件
remoteViews.setOnClickPendingIntent(R.id.btn_notification_play, playPendingIntent);
//播放状态判断
if (mPlayer.isPlaying()) {
remoteViews.setImageViewResource(R.id.btn_notification_play, R.mipmap.notify_pause);
} else {
remoteViews.setImageViewResource(R.id.btn_notification_play, R.mipmap.notify_play);
}
//封面专辑
remoteViews.setImageViewResource(R.id.iv_album_cover,R.mipmap.ic_launcher);
//歌曲名
remoteViews.setTextViewText(R.id.tv_notification_song_name,defaultSongName.substring(0,defaultSongName.lastIndexOf(".")));
//歌手名
remoteViews.setTextViewText(R.id.tv_notification_singer,"歌星");
//发送通知
manager.notify(NOTIFICATION_ID, notification);
}
@Override
public void onDestroy() {
Log.d(TAG, "musicService:onDestroy");
timer.cancel();
if (mBinder != null) mBinder = null;
handler = null;
if (mPlayer != null) {
mPlayer.release();
mPlayer = null;
}
manager.cancel(NOTIFICATION_ID);
unregisterReceiver(myBroadCastReceiver);
super.onDestroy();
}
public FileBean getCurrentPlayFile() {
return currentPlayFile;
}
public void setCurrentPlayFile(FileBean currentPlayFile) {
this.currentPlayFile = currentPlayFile;
}
}
WindowUtils.java 实现全局悬浮窗
package com.justlink.nas.ui.musicplayer;
import android.content.Context;
import android.content.Intent;
import android.graphics.PixelFormat;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.WindowManager;
import android.widget.ImageView;
import android.widget.TextView;
import com.justlink.nas.R;
import com.justlink.nas.utils.LogUtil;
import com.justlink.nas.utils.ScreenUtils;
public class WindowUtils {
private static final String LOG_TAG = "WindowUtils";
private static View mView = null;
private static WindowManager mWindowManager = null;
private static Context mContext = null;
public static Boolean isShown = false;
public static boolean isPlaying = true;
private static final String ACTION_PAUSE = "com.justlink.action.pause";
private static final String ACTION_PLAY_SONG = "com.justlink.action.playsong";
private static final String ACTION_PLAY_CLOSE = "com.justlink.action.close";
/**
* 显示弹出框
* @param context
*/
public static void showPopupWindow(final Context context,String title) {
if (isShown) {
LogUtil.showLog(LOG_TAG, "return cause already shown");
return;
}
isShown = true;
LogUtil.showLog(LOG_TAG, "showPopupWindow");
// 获取应用的Context
mContext = context.getApplicationContext();
// 获取WindowManager
mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
mView = setUpView(context,title);
final WindowManager.LayoutParams params = new WindowManager.LayoutParams();
// 类型
params.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
// WindowManager.LayoutParams.TYPE_SYSTEM_ALERT
// 设置flag
int flags = /*WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM | */WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
// 如果设置了WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,弹出的View收不到Back键的事件
params.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
// 不设置这个弹出框的透明遮罩显示为黑色
params.format = PixelFormat.TRANSLUCENT;
// FLAG_NOT_TOUCH_MODAL不阻塞事件传递到后面的窗口
// 设置 FLAG_NOT_FOCUSABLE 悬浮窗口较小时,后面的应用图标由不可长按变为可长按
// 不设置这个flag的话,home页的划屏会有问题
params.width = WindowManager.LayoutParams.WRAP_CONTENT;
params.height = WindowManager.LayoutParams.WRAP_CONTENT;
params.x = ScreenUtils.getScreenWidth(mContext);
params.y = ScreenUtils.getScreenHeight(mContext) - ScreenUtils.dip2px(mContext,100);
// params.gravity = Gravity.RIGHT;
mWindowManager.addView(mView, params);
mView.setOnTouchListener(new View.OnTouchListener() {
private int initialX;
private int initialY;
private float initialTouchX;
private float initialTouchY;
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
initialX = params.x;
initialY = params.y;
initialTouchX = event.getRawX();
initialTouchY = event.getRawY();
return true;
case MotionEvent.ACTION_UP:
return true;
case MotionEvent.ACTION_MOVE:
params.x = initialX + (int) (event.getRawX() - initialTouchX);
params.y = initialY + (int) (event.getRawY() - initialTouchY);
mWindowManager.updateViewLayout(mView, params);
return true;
}
return false;
}
});
LogUtil.showLog(LOG_TAG, "add view");
}
/**
* 隐藏弹出框
*/
public static void hidePopupWindow() {
LogUtil.showLog(LOG_TAG, "hide " + isShown + ", " + mView);
if (isShown && null != mView) {
LogUtil.showLog(LOG_TAG, "hidePopupWindow");
mWindowManager.removeView(mView);
isShown = false;
}
}
private static View setUpView(final Context context,String title) {
LogUtil.showLog(LOG_TAG, "setUp view");
View view = LayoutInflater.from(context).inflate(R.layout.popupwindow_music_player, null);
ImageView positiveBtn = (ImageView) view.findViewById(R.id.iv_play_pause);
positiveBtn.setImageResource(isPlaying?R.mipmap.small_pause:R.mipmap.small_play);
TextView tvTitle = view.findViewById(R.id.tv_song_name);
tvTitle.setText(title);
// tvTitle.setHorizontallyScrolling(true);
// tvTitle.setMarqueeRepeatLimit(-1);
positiveBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
LogUtil.showLog(LOG_TAG, "ok on click");
isPlaying = !isPlaying;
context.sendBroadcast(new Intent(isPlaying?ACTION_PLAY_SONG:ACTION_PAUSE));
positiveBtn.setImageResource(isPlaying?R.mipmap.small_pause:R.mipmap.small_play);
}
});
ImageView negativeBtn = (ImageView)view.findViewById(R.id.iv_close);
negativeBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
LogUtil.showLog(LOG_TAG, "cancel on click");
context.sendBroadcast(new Intent(ACTION_PAUSE));
context.sendBroadcast(new Intent(ACTION_PLAY_CLOSE));
WindowUtils.hidePopupWindow();
Intent intent = new Intent(context, MusicPlayerService.class);
context.stopService(intent);
}
});
// 点击窗口外部区域可消除
// 这点的实现主要将悬浮窗设置为全屏大小,外层有个透明背景,中间一部分视为内容区域
// 所以点击内容区域外部视为点击悬浮窗外部
/* final View popupWindowView = view.findViewById(R.id.popup_window);// 非透明的内容区域
view.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
LogUtil.i(LOG_TAG, "onTouch");
int x = (int) event.getX();
int y = (int) event.getY();
Rect rect = new Rect();
popupWindowView.getGlobalVisibleRect(rect);
if (!rect.contains(x, y)) {
WindowUtils.hidePopupWindow();
}
LogUtil.showLog(LOG_TAG, "onTouch : " + x + ", " + y + ", rect: " + rect);
return false;
}
});*/
// 点击back键可消除
view.setOnKeyListener(new View.OnKeyListener() {
@Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
switch (keyCode) {
case KeyEvent.KEYCODE_BACK:
WindowUtils.hidePopupWindow();
return true;
default:
return false;
}
}
});
return view;
}
public static void setIsPlaying(boolean isPlaying) {
WindowUtils.isPlaying = isPlaying;
}
}
MusicPlayerActivity.java 实现主播放界面
// 绑定服务时的ServiceConnection参数
private ServiceConnection conn = new ServiceConnection() {
// 绑定成功后该方法回调,并获得服务端IBinder的引用
public void onServiceConnected(ComponentName name, IBinder service) {
// 通过获得的IBinder获取PlayMusicService的引用
musicService = ((MusicPlayerService.MusicBinder) service).getService();
musicService.addWatcher(MusicPlayerActivity.this);
musicService.setHandler(myHandler);
if("notify".equals(from)){
fileBean = musicService.getCurrentPlayFile();
currentSongName = fileBean.getName();
String path = getFilePath(fileBean);
currentUrl = MyConstants.file_http_base_url + path + "?disk=" + fileBean.getRootPath() + "&user=" + getOwner(fileBean) + "&device_id=" + MyApplication.currentDevID + "&filetype=data";
myViewBinding.tvSongName.setText(currentSongName);
LogUtil.showLog("music","==name=="+currentSongName+"==path=="+path+"==url=="+currentUrl);
}else {
String path = getFilePath(fileBean);
currentUrl = MyConstants.file_http_base_url + path + "?disk=" + fileBean.getRootPath() + "&user=" + getOwner(fileBean) + "&device_id=" + MyApplication.currentDevID + "&filetype=data";
musicService.setCurrentPlayFile(fileBean);
musicService.play(currentUrl);
}
musicService.setDefaultSongName(currentSongName);
goPlaylist();
}
@Override
public void onServiceDisconnected(ComponentName name) {
Log.d(TAG, "onServiceDisconnected:musicService");
}
};
// 绑定服务MusicService
private void bindToService() {
bindService(new Intent(this, MusicPlayerService.class), conn, Service.BIND_AUTO_CREATE);
}
.....
@Override
protected void onResume() {
super.onResume();
if(!"notify".equals(from))
showLoadingDialog(true, getString(R.string.image_loading));
WindowUtils.hidePopupWindow();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { // 如果API级别大于等于23(Android 6.0)
if (!Settings.canDrawOverlays(MusicPlayerActivity.this)) {
Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
Uri.parse("package:" + getPackageName()));
startActivityForResult(intent, 13001);
}
}
Intent intent = new Intent(this, MusicPlayerService.class);
startService(intent);
}
@Override
protected void onStart() {
super.onStart();
}
@Override
protected void onClickTitleBack() {
goBackByQuick();
}
@Override
public void onClick(View v) {
super.onClick(v);
switch (v.getId()) {
case R.id.iv_play_mode: //播放模式
currentPlayMode++;
if (currentPlayMode == 3)
currentPlayMode = 0;
myViewBinding.ivPlayMode.setImageResource(cycleViewResource[currentPlayMode]);
break;
case R.id.iv_play_last: //上一曲
isPlaying = true;
myViewBinding.ivPlayPause.setImageResource(R.mipmap.icon_pause);
switch (currentPlayMode) {
case 0: //单曲循环
if (isPlaying) {
musicService.pauseMusic();
musicService.stopMusic();
}
musicService.play(currentUrl);
break;
case 1: //列表循环
case 2: //顺序播放
if ("last_record".equals(from)) { //最近上传记录
musicService.play(currentUrl);
break;
}
if (currentPlayIndex == 0)
currentPlayIndex = fileList.size() - 1;
else
currentPlayIndex -= 1;
musicService.setCurrentPlayIndex(currentPlayIndex);
String path = "";
FileBean fileBean = fileList.get(currentPlayIndex);
musicService.setCurrentPlayFile(fileBean);
hasFav = fileBean.getFavMode() == 1;
path = getFilePath(fileBean);
currentUrl = MyConstants.file_http_base_url + path + "?disk=" + fileBean.getRootPath() + "&user=" + getOwner(fileBean) + "&device_id=" + MyApplication.currentDevID + "&filetype=data";
musicService.play(currentUrl);
break;
}
currentSongName = fileList.get(currentPlayIndex).getName();
myViewBinding.tvSongName.setText(currentSongName);
break;
case R.id.iv_play_pause: //播放暂停
isPlaying = !isPlaying;
if (isPlaying) {
myViewBinding.ivPlayPause.setImageResource(R.mipmap.icon_pause);
sendBroadcast(new Intent(ACTION_PLAY_SONG));
myViewBinding.ivAnim.startAnimation(playAnimation);
} else {
myViewBinding.ivPlayPause.setImageResource(R.mipmap.icon_play);
sendBroadcast(new Intent(ACTION_PAUSE));
myViewBinding.ivAnim.clearAnimation();
}
break;
case R.id.iv_play_next: //下一曲
isPlaying = true;
myViewBinding.ivPlayPause.setImageResource(R.mipmap.icon_pause);
if ("last_record".equals(from)) { //最近上传记录
musicService.play(currentUrl);
break;
}
if (currentPlayIndex == fileList.size())
currentPlayIndex = 0;
String path = "";
FileBean fileBean = fileList.get(currentPlayIndex);
musicService.setCurrentPlayFile(fileBean);
hasFav = fileBean.getFavMode() == 1;
path = getFilePath(fileBean);
currentUrl = MyConstants.file_http_base_url + path + "?disk=" + fileBean.getRootPath() + "&user=" + getOwner(fileBean) + "&device_id=" + MyApplication.currentDevID + "&filetype=data";
musicService.play(currentUrl);
currentSongName = fileList.get(currentPlayIndex).getName();
myViewBinding.tvSongName.setText(currentSongName);
currentPlayIndex += 1;
musicService.setCurrentPlayIndex(currentPlayIndex);
break;
case R.id.iv_play_list: //播放列表
if ("last_record".equals(from)) { //最近上传记录
ToastUtil.showToastShort(getString(R.string.no_more_song));
break;
}
new PlayListDialog(currentPlayIndex, currentPlayMode, new PlayListDialog.DialogListen() {
@Override
public void onItemClick(int position) {
if (isPlaying) {
musicService.pauseMusic();
musicService.stopMusic();
}
String path = "";
FileBean fileBean = fileList.get(position);
musicService.setCurrentPlayFile(fileBean);
hasFav = fileBean.getFavMode() == 1;
path = getFilePath(fileBean);
currentUrl = MyConstants.file_http_base_url + path + "?disk=" + fileBean.getRootPath() + "&user=" + getOwner(fileBean) + "&device_id=" + MyApplication.currentDevID + "&filetype=data";
musicService.play(currentUrl);
}
@Override
public void onDelete(int position) {
fileList.remove(position);
}
@Override
public void onModeChange(int mode) {
currentPlayMode = mode;
if (currentPlayMode == 3)
currentPlayMode = 0;
myViewBinding.ivPlayMode.setImageResource(cycleViewResource[currentPlayMode]);
}
@Override
public void onClean() {
fileList.clear();
}
}, fileList).showNow(getSupportFragmentManager(), "");
break;
case R.id.toolbar_right_tv: //收藏
// hasFav = !hasFav;
showLoadingDialog(true);
FileBean fileBean1 = fileList.get(currentPlayIndex);
path = getFilePath(fileBean1);
TcpClient.getInstance().sendMessage(JsonUtils.getInstance().formtFavFileOperationJson(hasFav ? "delete" : "add", fileBean1.getRootPath(), new String[]{path}));
break;
}
}
@Override
public void update(int currentProgress, int duration) {
// LogUtil.showLog(TAG,"==current play position=="+currentProgress+"==duration=="+duration);
currentPlayPosition = currentProgress;
totalDuration = duration;
}
@Override
protected void onPause() {
super.onPause();
/* Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
Uri.parse("package:" + getPackageName()));
startActivityForResult(intent,11002);*/
WindowUtils.setIsPlaying(isPlaying);
WindowUtils.showPopupWindow(getApplicationContext(), currentSongName);
}
protected void onDestroy() {
// musicService = null;
this.unbindService(conn);
/* Intent intent = new Intent(this, MusicPlayerService.class);
intent.putExtra("url",currentUrl);
intent.putExtra("currentPosition",currentPlayPosition);
intent.putExtra("name",currentSongName);
startService(intent);*/
super.onDestroy();
}
通过广播监听点击通知栏跳转到主播放界面:
/**
* 通知点击广播接收器 跳转到栈顶的Activity ,而不是new 一个新的Activity
*
* @author llw
*/
public class NotificationClickReceiver extends BroadcastReceiver {
public static final String TAG = "NotificationClickReceiver";
@Override
public void onReceive(Context context, Intent intent) {
LogUtil.showLog(TAG,"通知栏点击");
//获取栈顶的Activity
// Activity currentActivity = ActivityManager.getCurrentActivity();
intent = new Intent(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_LAUNCHER);
intent.setClass(context, MusicPlayerActivity.class);
intent.putExtra("from","notify");
// intent.putExtra("file",MyApplication.currentPlayMusic);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK|Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
context.startActivity(intent);
}
}
注意问题:
1.悬浮窗权限需动态申请。
2.通过bindService绑定播放,退出时Service也会退出,所以还要结合startService一起使用,在resume中startService即可进行后台播放。
3.通知栏点击跳转广播需静态注册:
<receiver android:name=".ui.musicplayer.NotificationClickReceiver" />