增加缓存功能,优化用户体验;增加重试view
This commit is contained in:
parent
72bf148ad5
commit
5b390f4bff
@ -15,6 +15,7 @@ import com.bumptech.glide.load.resource.bitmap.RoundedCorners;
|
||||
import com.bumptech.glide.request.RequestOptions;
|
||||
import com.hi.music.player.MusicApplication;
|
||||
import com.hi.music.player.R;
|
||||
import com.hi.music.player.api.MediaControllerListener;
|
||||
import com.hi.music.player.databinding.ItemPlayListBinding;
|
||||
import com.hi.music.player.helper.CommonUtils;
|
||||
import com.hi.music.player.javabean.response.ResponsePlayListInfo;
|
||||
@ -26,7 +27,7 @@ public class AdapterPlayList extends BaseAdapter<ResponsePlayListInfo, ItemPlayL
|
||||
|
||||
private String lastVideId;
|
||||
|
||||
private int curMusicPos = 0;
|
||||
// private int curMusicPos = 0;
|
||||
private MyMediaControllerManager instance = MyMediaControllerManager.getInstance();
|
||||
|
||||
@Override
|
||||
@ -48,8 +49,25 @@ public class AdapterPlayList extends BaseAdapter<ResponsePlayListInfo, ItemPlayL
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void updateCurPlayStatus(String curVideId) {
|
||||
lastVideId = mCurVideId;
|
||||
this.mCurVideId = curVideId;
|
||||
for (int i = 0; i < data.size(); i++) {
|
||||
ResponsePlayListInfo listInfo = data.get(i);
|
||||
if (listInfo.getVideoId().equals(curVideId)) {
|
||||
notifyItemChanged(i);
|
||||
}
|
||||
if (listInfo.getVideoId().equals(lastVideId)) {
|
||||
notifyItemChanged(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void updateCurMusicAnimation(){
|
||||
notifyItemChanged(curMusicPos);
|
||||
int curIndex = instance.getCurIndex();
|
||||
CommonUtils.LogMsg("--curIndex="+curIndex);
|
||||
notifyItemChanged(curIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -69,18 +87,19 @@ public class AdapterPlayList extends BaseAdapter<ResponsePlayListInfo, ItemPlayL
|
||||
MediaItem currentMediaItem = instance.getMediaController().getCurrentMediaItem();
|
||||
if (currentMediaItem != null && currentMediaItem.mediaId.equals(listInfo.getVideoId())) {
|
||||
vb.viewPlaying.setVisibility(View.VISIBLE);
|
||||
vb.getRoot().setBackgroundColor(CommonUtils.getMyColor(R.color.cur_play_music));
|
||||
vb.itemLayout.setBackgroundColor(CommonUtils.getMyColor(R.color.cur_play_music));
|
||||
if (instance.getMediaController().isPlaying()) {
|
||||
vb.viewPlaying.startAnimating();
|
||||
lastVideId = listInfo.getVideoId();
|
||||
CommonUtils.LogMsg("-------playAnimation");
|
||||
CommonUtils.LogMsg("-------playAnimation "+itemHolder.getAbsoluteAdapterPosition());
|
||||
} else {
|
||||
vb.viewPlaying.pauseAnimating();
|
||||
CommonUtils.LogMsg("-------pauseAnimation");
|
||||
CommonUtils.LogMsg("-------pauseAnimation "+itemHolder.getAbsoluteAdapterPosition());
|
||||
}
|
||||
} else {
|
||||
vb.viewPlaying.setVisibility(View.GONE);
|
||||
// CommonUtils.LogMsg("-------GONE");
|
||||
vb.itemLayout.setBackgroundColor(CommonUtils.getMyColor(R.color.color_transparent));
|
||||
|
||||
}
|
||||
|
||||
vb.itemLayout.setOnClickListener(new View.OnClickListener() {
|
||||
|
||||
@ -35,6 +35,13 @@ abstract public class BaseAdapter<K, T extends ViewBinding> extends RecyclerView
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
|
||||
public void setData(List<K> data) {
|
||||
this.data.clear();
|
||||
this.data.addAll(data);
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
public void addLoadingFooter() {
|
||||
isLoadingAdded = true;
|
||||
// notifyItemInserted(data.size());
|
||||
|
||||
@ -18,5 +18,5 @@ public interface MediaControllerListener {
|
||||
void onRequestNextUri(String videoId,int playListIndex,boolean playNow);
|
||||
|
||||
|
||||
void onChangeMusic(MediaItem mediaItem);
|
||||
void onChangeMusic(MediaItem mediaItem);
|
||||
}
|
||||
|
||||
@ -0,0 +1,5 @@
|
||||
package com.hi.music.player.api;
|
||||
|
||||
public interface OnHasUrlAction {
|
||||
void onHasUrl();
|
||||
}
|
||||
@ -11,6 +11,8 @@ import android.view.animation.LinearInterpolator;
|
||||
|
||||
import com.hi.music.player.helper.CommonUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
|
||||
public class AnimaPlayingView extends View {
|
||||
@ -23,7 +25,7 @@ public class AnimaPlayingView extends View {
|
||||
private int minHeight = CommonUtils.dpToPx(4); // 音量条的最小高度
|
||||
|
||||
// 每个柱子的宽度
|
||||
private int barWidth =CommonUtils. dpToPx(4);
|
||||
private int barWidth = CommonUtils.dpToPx(4);
|
||||
// 柱子之间的间距
|
||||
private int barSpacing = CommonUtils.dpToPx(2);
|
||||
|
||||
@ -31,6 +33,7 @@ public class AnimaPlayingView extends View {
|
||||
private Random random;
|
||||
|
||||
private ValueAnimator animator;
|
||||
private List<ValueAnimator> valueAnimatorList;
|
||||
|
||||
public AnimaPlayingView(Context context) {
|
||||
super(context);
|
||||
@ -48,6 +51,7 @@ public class AnimaPlayingView extends View {
|
||||
}
|
||||
|
||||
private void init() {
|
||||
valueAnimatorList = new ArrayList<>();
|
||||
paint = new Paint();
|
||||
paint.setColor(barColor);
|
||||
paint.setStyle(Paint.Style.FILL);
|
||||
@ -58,6 +62,7 @@ public class AnimaPlayingView extends View {
|
||||
for (int i = 0; i < numBars; i++) {
|
||||
barHeights[i] = 0;
|
||||
}
|
||||
initAnimating();
|
||||
|
||||
}
|
||||
|
||||
@ -84,9 +89,14 @@ public class AnimaPlayingView extends View {
|
||||
}
|
||||
}
|
||||
|
||||
// 启动固定动画,使音量条的高度变化
|
||||
public void startAnimating() {
|
||||
for (ValueAnimator valueAnimator : valueAnimatorList) {
|
||||
valueAnimator.start();
|
||||
}
|
||||
}
|
||||
|
||||
// 启动固定动画,使音量条的高度变化
|
||||
public void initAnimating() {
|
||||
for (int i = 0; i < numBars; i++) {
|
||||
animator = ValueAnimator.ofFloat(0, 1);
|
||||
animator.setDuration(1000); // 每0.5秒更新一次
|
||||
@ -104,16 +114,16 @@ public class AnimaPlayingView extends View {
|
||||
invalidate(); // 重绘视图
|
||||
}
|
||||
});
|
||||
animator.setStartDelay(i* 200L);
|
||||
animator.start();
|
||||
valueAnimatorList.add(animator);
|
||||
animator.setStartDelay(i * 200L);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
public void pauseAnimating(){
|
||||
if(animator!= null){
|
||||
animator.pause();
|
||||
public void pauseAnimating() {
|
||||
for (ValueAnimator valueAnimator : valueAnimatorList) {
|
||||
valueAnimator.pause();
|
||||
}
|
||||
for (int i = 0; i < numBars; i++) {
|
||||
barHeights[i] = minHeight;
|
||||
@ -122,7 +132,6 @@ public class AnimaPlayingView extends View {
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 随机生成音量条的高度
|
||||
private float randomHeight() {
|
||||
return minHeight + random.nextFloat() * (maxHeight - minHeight);
|
||||
|
||||
@ -31,7 +31,7 @@ public class CommonUtils {
|
||||
|
||||
private static String TAG = "----MUSIC---------";
|
||||
|
||||
private static String TAG_ERROR = "----ERROR MUSIC---------";
|
||||
private static String TAG_ERROR = "----MUSIC---------ERROR--";
|
||||
|
||||
public static void LogMsg(String msg) {
|
||||
Log.d(TAG, msg);
|
||||
@ -191,7 +191,6 @@ public class CommonUtils {
|
||||
// 3. 检查 URI 是否具有正确的格式
|
||||
try {
|
||||
if (parsedUri.getScheme() == null) {
|
||||
LogMsg( "URI has no valid scheme.");
|
||||
return false;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
|
||||
@ -7,8 +7,17 @@ public class CustomerUrlInfo {
|
||||
private ResponsePlayUrl playUrl;
|
||||
private int playMusicIndex;
|
||||
private boolean needPlayNow;
|
||||
private String videoId;
|
||||
|
||||
|
||||
public String getVideoId() {
|
||||
return videoId;
|
||||
}
|
||||
|
||||
public void setVideoId(String videoId) {
|
||||
this.videoId = videoId;
|
||||
}
|
||||
|
||||
public ResponsePlayUrl getPlayUrl() {
|
||||
return playUrl;
|
||||
}
|
||||
|
||||
@ -6,8 +6,12 @@ import androidx.media3.common.MediaItem;
|
||||
import androidx.media3.common.util.UnstableApi;
|
||||
import androidx.media3.datasource.DataSource;
|
||||
import androidx.media3.datasource.DataSpec;
|
||||
import androidx.media3.datasource.DefaultDataSource;
|
||||
import androidx.media3.datasource.DefaultDataSourceFactory;
|
||||
import androidx.media3.datasource.DefaultHttpDataSource;
|
||||
import androidx.media3.datasource.ResolvingDataSource;
|
||||
import androidx.media3.datasource.cache.CacheDataSource;
|
||||
import androidx.media3.datasource.cache.SimpleCache;
|
||||
import androidx.media3.exoplayer.ExoPlayer;
|
||||
import androidx.media3.exoplayer.drm.DrmSessionManagerProvider;
|
||||
import androidx.media3.exoplayer.source.DefaultMediaSourceFactory;
|
||||
@ -16,6 +20,7 @@ import androidx.media3.exoplayer.source.MediaSourceFactory;
|
||||
import androidx.media3.exoplayer.source.ProgressiveMediaSource;
|
||||
import androidx.media3.exoplayer.upstream.LoadErrorHandlingPolicy;
|
||||
|
||||
import com.hi.music.player.MusicApplication;
|
||||
import com.hi.music.player.api.RequestListener;
|
||||
import com.hi.music.player.helper.CommonUtils;
|
||||
import com.hi.music.player.javabean.response.ResponsePlayUrl;
|
||||
@ -32,16 +37,18 @@ import okhttp3.ResponseBody;
|
||||
public class DynamicMediaSourceFactory implements MediaSource.Factory {
|
||||
// private final CacheDataSource.Factory cacheDataSourceFactory;
|
||||
// private final ExoPlayer player;
|
||||
private DefaultMediaSourceFactory mediaSourceFactory;
|
||||
private DataSource.Factory mediaSourceFactory;
|
||||
private SimpleCache simpleCache;
|
||||
|
||||
// public DynamicMediaSourceFactory(DefaultMediaSourceFactory factory,CacheDataSource.Factory cacheDataSourceFactory, ExoPlayer exoPlayer) {
|
||||
// this.cacheDataSourceFactory = cacheDataSourceFactory;
|
||||
// this.player = exoPlayer;
|
||||
// this.mediaSourceFactory = factory;
|
||||
// }
|
||||
public DynamicMediaSourceFactory(DefaultMediaSourceFactory factory) {
|
||||
public DynamicMediaSourceFactory(DataSource.Factory factory, SimpleCache cache) {
|
||||
|
||||
this.mediaSourceFactory = factory;
|
||||
this.simpleCache = cache;
|
||||
}
|
||||
|
||||
|
||||
@ -105,22 +112,40 @@ public class DynamicMediaSourceFactory implements MediaSource.Factory {
|
||||
// }
|
||||
// });
|
||||
|
||||
|
||||
|
||||
// DefaultHttpDataSource.Factory httpDataSourceFactory = new DefaultHttpDataSource.Factory().setAllowCrossProtocolRedirects(true);
|
||||
// 这里的DefaultDataSource同时支持本地和HTTP请求的资源,自动实现检测 (The DefaultDataSource supports both local and Http sources. It automatically detects which one to use.)
|
||||
|
||||
//实现缓存
|
||||
CacheDataSource.Factory cacheDataSourceFactory = new CacheDataSource.Factory()
|
||||
.setCache(simpleCache)
|
||||
.setUpstreamDataSourceFactory(mediaSourceFactory)
|
||||
.setFlags(CacheDataSource.FLAG_IGNORE_CACHE_ON_ERROR);
|
||||
|
||||
MediaSource mediaSource = new ProgressiveMediaSource.Factory(cacheDataSourceFactory)
|
||||
.createMediaSource(mediaItem);
|
||||
|
||||
return mediaSource;
|
||||
|
||||
|
||||
|
||||
// return new ProgressiveMediaSource.Factory(cacheDataSourceFactory)
|
||||
// .createMediaSource(MediaItem.fromUri(responsePlayUrl.getAudioUrlMedium()));
|
||||
// 可以在这里根据 mediaItem 的 ID 动态生成新的 URI
|
||||
String updatedUri = "https://rr1---sn-tt1e7nlz.googlevideo.com/videoplayback?expire=1727628605&ei=3TD5ZuWFEe-yzN0P27WuqQ4&ip=146.19.167.8&id=o-ANGM0PjEvsfYH7TmYV_DFuD-65tipJeeLe2URDOk90sL&itag=140&source=youtube&requiressl=yes&xpc=EgVo2aDSNQ%3D%3D&mh=xj&mm=31%2C29&mn=sn-tt1e7nlz%2Csn-vgqsknsk&ms=au%2Crdu&mv=m&mvi=1&pl=24&pcm2=no&gcr=us&initcwndbps=7610000&vprv=1&svpuc=1&mime=audio%2Fmp4&rqh=1&gir=yes&clen=5440168&dur=335.973&lmt=1709326903801285&mt=1727606690&fvip=5&keepalive=yes&fexp=51299152&c=ANDROID_MUSIC&txp=2318224&sparams=expire%2Cei%2Cip%2Cid%2Citag%2Csource%2Crequiressl%2Cxpc%2Cpcm2%2Cgcr%2Cvprv%2Csvpuc%2Cmime%2Crqh%2Cgir%2Cclen%2Cdur%2Clmt&sig=AJfQdSswRQIhALmM_S8Cmagr60muB3wDOby0OdcjF-x6f7TcEenixH0KAiAnR0-hmA03MeVzSg2wi5ncJ4Ve5FFlpZnlSoRNGWgGhQ%3D%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&lsig=ABPmVW0wRQIhAPv5slfEnf8_E7o6yjEkussQ6JIFFaSY6QtP9HXncTTcAiAjlhMa71t76Wu1R1rcmsHoO6pyxjhGYouio4D0deJqEA%3D%3D";
|
||||
Uri updateUri = null;
|
||||
// String updatedUri = "https://rr1---sn-tt1e7nlz.googlevideo.com/videoplayback?expire=1727628605&ei=3TD5ZuWFEe-yzN0P27WuqQ4&ip=146.19.167.8&id=o-ANGM0PjEvsfYH7TmYV_DFuD-65tipJeeLe2URDOk90sL&itag=140&source=youtube&requiressl=yes&xpc=EgVo2aDSNQ%3D%3D&mh=xj&mm=31%2C29&mn=sn-tt1e7nlz%2Csn-vgqsknsk&ms=au%2Crdu&mv=m&mvi=1&pl=24&pcm2=no&gcr=us&initcwndbps=7610000&vprv=1&svpuc=1&mime=audio%2Fmp4&rqh=1&gir=yes&clen=5440168&dur=335.973&lmt=1709326903801285&mt=1727606690&fvip=5&keepalive=yes&fexp=51299152&c=ANDROID_MUSIC&txp=2318224&sparams=expire%2Cei%2Cip%2Cid%2Citag%2Csource%2Crequiressl%2Cxpc%2Cpcm2%2Cgcr%2Cvprv%2Csvpuc%2Cmime%2Crqh%2Cgir%2Cclen%2Cdur%2Clmt&sig=AJfQdSswRQIhALmM_S8Cmagr60muB3wDOby0OdcjF-x6f7TcEenixH0KAiAnR0-hmA03MeVzSg2wi5ncJ4Ve5FFlpZnlSoRNGWgGhQ%3D%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&lsig=ABPmVW0wRQIhAPv5slfEnf8_E7o6yjEkussQ6JIFFaSY6QtP9HXncTTcAiAjlhMa71t76Wu1R1rcmsHoO6pyxjhGYouio4D0deJqEA%3D%3D";
|
||||
// Uri updateUri = null;
|
||||
//
|
||||
// MediaItem.Builder builder = mediaItem.buildUpon();
|
||||
// if (mediaItem.localConfiguration != null) {
|
||||
// updateUri = mediaItem.localConfiguration.uri;
|
||||
// builder.setUri(updateUri);
|
||||
// CommonUtils.LogErrorMsg("----updateUri=成功 builder="+mediaItem.mediaId+"--updateUri="+updateUri);
|
||||
// }else {
|
||||
// CommonUtils.LogErrorMsg("----updateUri=null builder="+mediaItem.mediaId);
|
||||
// }
|
||||
|
||||
MediaItem.Builder builder = mediaItem.buildUpon();
|
||||
if (mediaItem.localConfiguration != null) {
|
||||
updateUri = mediaItem.localConfiguration.uri;
|
||||
builder.setUri(updateUri);
|
||||
CommonUtils.LogErrorMsg("----updateUri=成功 builder="+mediaItem.mediaId+"--updateUri="+updateUri);
|
||||
}else {
|
||||
CommonUtils.LogErrorMsg("----updateUri=null builder="+mediaItem.mediaId);
|
||||
}
|
||||
|
||||
return mediaSourceFactory.createMediaSource(builder.build());
|
||||
// return mediaSourceFactory.createMediaSource(builder.build());
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -18,6 +18,7 @@ import com.google.common.util.concurrent.MoreExecutors;
|
||||
import com.hi.music.player.MusicApplication;
|
||||
import com.hi.music.player.api.MediaControllerListener;
|
||||
import com.hi.music.player.api.MediaControllerStatusListener;
|
||||
import com.hi.music.player.api.OnHasUrlAction;
|
||||
import com.hi.music.player.helper.CommonUtils;
|
||||
import com.hi.music.player.helper.MyValue;
|
||||
import com.hi.music.player.javabean.response.ResponsePlayListInfo;
|
||||
@ -78,7 +79,6 @@ public class MyMediaControllerManager {
|
||||
mediaController.addListener(new Player.Listener() {
|
||||
@Override
|
||||
public void onPlayerError(PlaybackException error) {
|
||||
CommonUtils.LogMsg("=-----PlaybackException+" + error.getMessage());
|
||||
mListener.onPlayStatus(MyValue.PLAY_STATUS_CODE_ERROR);
|
||||
}
|
||||
|
||||
@ -99,13 +99,22 @@ public class MyMediaControllerManager {
|
||||
mListener.onPlayStatus(MyValue.PLAY_STATUS_CODE_PLAYING);
|
||||
// TODO: 2024/9/26 自动播放完成切歌到下一首播放没有触发这里请求下一首
|
||||
if (mediaController.hasNextMediaItem()) {
|
||||
MediaItem mediaItemAt = mediaController.getMediaItemAt(mediaController.getNextMediaItemIndex());
|
||||
if (!CommonUtils.hasValidUri(mediaItemAt)) {
|
||||
CommonUtils.LogMsg("----0000000000-请求下一首的uri" + mediaController.getMediaItemCount());
|
||||
int nextIndex = mediaController.getNextMediaItemIndex();
|
||||
mListener.onRequestNextUri(mediaController.getMediaItemAt(nextIndex).mediaId, nextIndex, false);
|
||||
}
|
||||
|
||||
int nextMediaItemIndex = mediaController.getNextMediaItemIndex();
|
||||
onCallRequestUrl(nextMediaItemIndex, false, new OnHasUrlAction() {
|
||||
@Override
|
||||
public void onHasUrl() {
|
||||
CommonUtils.LogMsg("-------------有有效URl--播放检查下一首 位置="+nextMediaItemIndex );
|
||||
}
|
||||
});
|
||||
}
|
||||
if (mediaController.hasPreviousMediaItem()) {
|
||||
int previousMediaItemIndex = mediaController.getPreviousMediaItemIndex();
|
||||
onCallRequestUrl(previousMediaItemIndex, false, new OnHasUrlAction() {
|
||||
@Override
|
||||
public void onHasUrl() {
|
||||
CommonUtils.LogMsg("-------------有有效URl--播放检查上一首 位置="+previousMediaItemIndex );
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
} else {
|
||||
@ -145,11 +154,15 @@ public class MyMediaControllerManager {
|
||||
}
|
||||
|
||||
|
||||
// /**
|
||||
// * 更新播放列表中的音频url
|
||||
// *
|
||||
// * @param playUrl
|
||||
// */
|
||||
public int getCurIndex(){
|
||||
return mediaController.getCurrentMediaItemIndex();
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新播放列表中的音频url
|
||||
*
|
||||
* @param playUrl
|
||||
*/
|
||||
public void UpdateAudioUrl(ResponsePlayUrl playUrl, int index) {
|
||||
CommonUtils.LogMsg("-------------更新播放列表中的音频url= index=" + index);
|
||||
for (int i = 0; i < mediaController.getMediaItemCount(); i++) {
|
||||
@ -285,15 +298,14 @@ public class MyMediaControllerManager {
|
||||
public void playNext() {
|
||||
if (mediaController.hasNextMediaItem()) {
|
||||
int nextMediaItemIndex = mediaController.getNextMediaItemIndex();
|
||||
MediaItem mediaItemAt = mediaController.getMediaItemAt(nextMediaItemIndex);
|
||||
boolean b = CommonUtils.hasValidUri(mediaItemAt);
|
||||
if (b) {
|
||||
mediaController.seekToNextMediaItem();
|
||||
return;
|
||||
}
|
||||
CommonUtils.LogMsg("-------------有下一首,但是歌曲url请求失败,需要重新请求");
|
||||
mListener.onRequestNextUri(mediaController.getMediaItemAt(nextMediaItemIndex).mediaId, nextMediaItemIndex, true);
|
||||
|
||||
onCallRequestUrl(nextMediaItemIndex, true, new OnHasUrlAction() {
|
||||
@Override
|
||||
public void onHasUrl() {
|
||||
mediaController.seekToNextMediaItem();
|
||||
mediaController.play();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// int currentMediaItemIndex = mediaController.getCurrentMediaItemIndex();
|
||||
// if (currentMediaItemIndex < playList.size() - 1) {
|
||||
@ -307,7 +319,16 @@ public class MyMediaControllerManager {
|
||||
|
||||
public void playPrevious() {
|
||||
if (mediaController.hasPreviousMediaItem()) {
|
||||
mediaController.seekToPreviousMediaItem();
|
||||
|
||||
int nextMediaItemIndex = mediaController.getNextMediaItemIndex();
|
||||
onCallRequestUrl(nextMediaItemIndex, true, new OnHasUrlAction() {
|
||||
@Override
|
||||
public void onHasUrl() {
|
||||
mediaController.seekToPreviousMediaItem();
|
||||
mediaController.play();
|
||||
}
|
||||
});
|
||||
|
||||
} else {
|
||||
mediaController.seekTo(0);
|
||||
mediaController.play();
|
||||
@ -327,15 +348,25 @@ public class MyMediaControllerManager {
|
||||
}
|
||||
|
||||
stop();
|
||||
mediaController.prepare();
|
||||
mediaController.seekTo(index);
|
||||
mediaController.seekTo(index,0);
|
||||
onCallRequestUrl(index, true, new OnHasUrlAction() {
|
||||
@Override
|
||||
public void onHasUrl() {
|
||||
mediaController.play();
|
||||
CommonUtils.LogMsg("-------------有有效URl--播放指定播放列表位置的歌曲" );
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
private void onCallRequestUrl(int index, boolean playNow, OnHasUrlAction action){
|
||||
MediaItem mediaItemAt = mediaController.getMediaItemAt(index);
|
||||
boolean b = CommonUtils.hasValidUri(mediaItemAt);
|
||||
if (!b) {
|
||||
CommonUtils.LogMsg("-------------播放指定播放列表位置的歌曲,需要重新请求");
|
||||
mListener.onRequestNextUri(mediaItemAt.mediaId, index, true);
|
||||
CommonUtils.LogMsg("-------------请求URl index"+index+"---playNow="+playNow);
|
||||
mListener.onRequestNextUri(mediaItemAt.mediaId, index, playNow);
|
||||
}else {
|
||||
action.onHasUrl();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -7,10 +7,13 @@ import androidx.annotation.Nullable;
|
||||
import androidx.annotation.OptIn;
|
||||
import androidx.media3.common.Player;
|
||||
import androidx.media3.common.util.UnstableApi;
|
||||
import androidx.media3.database.DatabaseProvider;
|
||||
import androidx.media3.database.ExoDatabaseProvider;
|
||||
import androidx.media3.database.StandaloneDatabaseProvider;
|
||||
import androidx.media3.datasource.DataSource;
|
||||
import androidx.media3.datasource.DataSpec;
|
||||
import androidx.media3.datasource.DefaultDataSource;
|
||||
import androidx.media3.datasource.DefaultDataSourceFactory;
|
||||
import androidx.media3.datasource.DefaultHttpDataSource;
|
||||
import androidx.media3.datasource.ResolvingDataSource;
|
||||
import androidx.media3.datasource.cache.CacheDataSource;
|
||||
@ -40,8 +43,21 @@ public class PlaybackService extends MediaSessionService {
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
|
||||
// 1. 初始化缓存
|
||||
File cacheDir = new File(getCacheDir(), "media_cache");
|
||||
long maxCacheSize = 100 * 1024 * 1024; // 缓存大小 100MB
|
||||
StandaloneDatabaseProvider databaseProvider = new StandaloneDatabaseProvider(this);
|
||||
SimpleCache cache = new SimpleCache(cacheDir, new LeastRecentlyUsedCacheEvictor(maxCacheSize), databaseProvider);
|
||||
|
||||
DynamicMediaSourceFactory customMediaSourceFactory = new DynamicMediaSourceFactory(new DefaultMediaSourceFactory(new DefaultHttpDataSource.Factory()));
|
||||
|
||||
|
||||
DefaultHttpDataSource.Factory httpDataSourceFactory = new DefaultHttpDataSource.Factory().setAllowCrossProtocolRedirects(true);
|
||||
// 这里的DefaultDataSource同时支持本地和HTTP请求的资源,自动实现检测 (The DefaultDataSource supports both local and Http sources. It automatically detects which one to use.)
|
||||
// DefaultDataSource.Factory defaultDataSourceFactory = new DefaultDataSourceFactory(MusicApplication.myApplication, httpDataSourceFactory);
|
||||
DefaultDataSource.Factory upstreamFactory = new DefaultDataSource.Factory(this);
|
||||
|
||||
DynamicMediaSourceFactory customMediaSourceFactory = new DynamicMediaSourceFactory(upstreamFactory,cache);
|
||||
// DynamicMediaSourceFactory customMediaSourceFactory = new DynamicMediaSourceFactory(new DefaultMediaSourceFactory(new DefaultHttpDataSource.Factory()),cache);
|
||||
|
||||
DefaultMediaSourceFactory defaultMediaSourceFactory1 = new DefaultMediaSourceFactory(getUrlFactory());
|
||||
|
||||
@ -53,8 +69,13 @@ public class PlaybackService extends MediaSessionService {
|
||||
10000, // bufferForPlaybackMs: 播放时的目标缓冲时间(毫秒)
|
||||
5000) // bufferForPlaybackAfterRebufferMs: 重新缓冲后的缓冲时间(毫秒)
|
||||
.build();
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
player = new ExoPlayer.Builder(this)
|
||||
// .setMediaSourceFactory(customMediaSourceFactory)
|
||||
.setMediaSourceFactory(customMediaSourceFactory)
|
||||
// .setLoadControl(loadControl)
|
||||
.build();
|
||||
mediaSession = new MediaSession.Builder(this, player)
|
||||
|
||||
@ -26,7 +26,7 @@ public class ObserverWrapper<T> implements Observer<T> {
|
||||
|
||||
@Override
|
||||
public void onError(Throwable e) {
|
||||
CommonUtils.LogMsg("----------onError"+e.getMessage());
|
||||
CommonUtils.LogMsg("----------onError---"+e.getMessage());
|
||||
requestListener.onFail(e.getMessage());
|
||||
}
|
||||
|
||||
|
||||
@ -53,7 +53,7 @@ public class RetrofitManager {
|
||||
.connectTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS)
|
||||
.writeTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS)
|
||||
.readTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS)
|
||||
.addInterceptor(httpLoggingInterceptor)
|
||||
// .addInterceptor(httpLoggingInterceptor)
|
||||
.build();
|
||||
retrofit = new Retrofit.Builder()
|
||||
.baseUrl(base_Host)
|
||||
|
||||
@ -73,6 +73,12 @@ public class PlayActivity extends BaseActivity<ActivityPlayBinding> implements S
|
||||
private GradientDrawable gradientDrawable;
|
||||
private int lighterColor, darkerColor;
|
||||
|
||||
// 0 播放列表请求失败 1 立即播放的歌曲请求失败
|
||||
private int netError = 0;
|
||||
|
||||
|
||||
// 请求失败的立即播放的歌曲信息
|
||||
private CustomerUrlInfo mCustomerUrlInfo;
|
||||
|
||||
@Override
|
||||
protected ActivityPlayBinding getViewBinding() {
|
||||
@ -94,7 +100,9 @@ public class PlayActivity extends BaseActivity<ActivityPlayBinding> implements S
|
||||
@Override
|
||||
public void onChanged(List<ResponsePlayListInfo> playList) {
|
||||
if (playList == null) {
|
||||
CommonUtils.LogMsg("---------playList = null");
|
||||
CommonUtils.LogErrorMsg("---------playList = null");
|
||||
netError = 0;
|
||||
vb.linearRetry.setVisibility(View.VISIBLE);
|
||||
return;
|
||||
}
|
||||
if (playList.size() > 0) {
|
||||
@ -111,7 +119,12 @@ public class PlayActivity extends BaseActivity<ActivityPlayBinding> implements S
|
||||
public void onChanged(CustomerUrlInfo customerUrlInfo) {
|
||||
if (customerUrlInfo.isNeedPlayNow() && customerUrlInfo.getPlayUrl() == null) {
|
||||
// TODO: 2024/9/26 需要马上播放这首歌曲,但是此次网络请求失败
|
||||
CommonUtils.LogMsg("-------------需要马上播放这首歌曲,但是此次网络请求失败");
|
||||
CommonUtils.LogErrorMsg("-------------需要马上播放这首歌曲,但是此次网络请求失败");
|
||||
netError = 1;
|
||||
mCustomerUrlInfo = customerUrlInfo;
|
||||
initShowPlayList(false);
|
||||
vb.linearRetry.setVisibility(View.VISIBLE);
|
||||
mediaControllerManager.pause();
|
||||
return;
|
||||
}
|
||||
|
||||
@ -119,13 +132,13 @@ public class PlayActivity extends BaseActivity<ActivityPlayBinding> implements S
|
||||
int second = customerUrlInfo.getPlayMusicIndex();
|
||||
boolean needPlayNow = customerUrlInfo.isNeedPlayNow();
|
||||
if (needPlayNow) {
|
||||
mediaControllerManager.playPositionMusic(second);
|
||||
mediaControllerManager.play();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@OptIn(markerClass = UnstableApi.class)
|
||||
private void initPlayerView() {
|
||||
vb.playerView.setShowRewindButton(false);
|
||||
@ -154,35 +167,45 @@ public class PlayActivity extends BaseActivity<ActivityPlayBinding> implements S
|
||||
|
||||
switch (playStatus) {
|
||||
case Player.STATE_IDLE:
|
||||
CommonUtils.LogMsg("-------------playStatus=STATE_IDLE");
|
||||
CommonUtils.LogMsg("-------------IDLE");
|
||||
break;
|
||||
case Player.STATE_BUFFERING:
|
||||
//快进没有缓冲的时候触发
|
||||
vb.btnPlay.setSelected(false);
|
||||
vb.progressBarLoading.setVisibility(View.VISIBLE);
|
||||
CommonUtils.LogMsg("-------------playStatus=STATE_BUFFERING");
|
||||
CommonUtils.LogMsg("-------------缓冲");
|
||||
break;
|
||||
case Player.STATE_READY:
|
||||
vb.btnPlay.setSelected(true);
|
||||
mHandler.post(mRunnable);
|
||||
vb.progressBarLoading.setVisibility(View.GONE);
|
||||
CommonUtils.LogMsg("-------------playStatus=STATE_READY");
|
||||
CommonUtils.LogMsg("-------------准备");
|
||||
break;
|
||||
|
||||
case Player.STATE_ENDED:
|
||||
//播放完成
|
||||
vb.btnPlay.setSelected(false);
|
||||
CommonUtils.LogMsg("-------------playStatus=STATE_ENDED 播放完成");
|
||||
CommonUtils.LogMsg("------------- 播放完成");
|
||||
mHandler.removeCallbacks(mRunnable); // 停止更新
|
||||
updatePlayComplete();
|
||||
mediaControllerManager.playNext();
|
||||
break;
|
||||
|
||||
case MyValue.PLAY_STATUS_CODE_PAUSE:
|
||||
CommonUtils.LogMsg("------------- 暂停");
|
||||
vb.btnPlay.setSelected(false);
|
||||
vb.layoutPlayList.imPlay.setSelected(false);
|
||||
break;
|
||||
case MyValue.PLAY_STATUS_CODE_PLAYING:
|
||||
CommonUtils.LogMsg("------------- 播放ing getCurIndex=" + mediaControllerManager.getCurIndex());
|
||||
vb.progressBarLoading.setVisibility(View.GONE);
|
||||
vb.btnPlay.setSelected(true);
|
||||
vb.layoutPlayList.imPlay.setSelected(true);
|
||||
break;
|
||||
case MyValue.PLAY_STATUS_CODE_ERROR:
|
||||
vb.progressBarLoading.setVisibility(View.GONE);
|
||||
int currentMediaItemIndex = mediaControllerManager.getMediaController().getCurrentMediaItemIndex();
|
||||
CommonUtils.LogMsg("------------- 播放错误 currentMediaItemIndex=" + currentMediaItemIndex);
|
||||
break;
|
||||
|
||||
}
|
||||
@ -190,7 +213,6 @@ public class PlayActivity extends BaseActivity<ActivityPlayBinding> implements S
|
||||
|
||||
@Override
|
||||
public void onRequestNextUri(String videoId, int playListIndex, boolean playNow) {
|
||||
CommonUtils.LogMsg("------------onRequestNextUri= videoId=" + videoId);
|
||||
if (playNow) {
|
||||
vb.progressBarLoading.setVisibility(View.VISIBLE);
|
||||
}
|
||||
@ -215,6 +237,7 @@ public class PlayActivity extends BaseActivity<ActivityPlayBinding> implements S
|
||||
vb.btnPrevious.setOnClickListener(this);
|
||||
vb.imBack.setOnClickListener(this);
|
||||
vb.btnMusicList.setOnClickListener(this);
|
||||
vb.tvRetry.setOnClickListener(this);
|
||||
}
|
||||
|
||||
|
||||
@ -363,9 +386,19 @@ public class PlayActivity extends BaseActivity<ActivityPlayBinding> implements S
|
||||
} else {
|
||||
mediaControllerManager.pause();
|
||||
}
|
||||
if(adapterPlayList!= null){
|
||||
if (adapterPlayList != null) {
|
||||
adapterPlayList.updateCurMusicAnimation();
|
||||
}
|
||||
} else if (v.equals(vb.tvRetry)) {
|
||||
vb.linearRetry.setVisibility(View.GONE);
|
||||
if (netError == 0) {
|
||||
vmPlay.getPlayMusicList(responseSingle);
|
||||
} else {
|
||||
if (mCustomerUrlInfo != null) {
|
||||
vmPlay.getPlayUrl(mCustomerUrlInfo.getVideoId(), mCustomerUrlInfo.getPlayMusicIndex(), true);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -387,7 +420,7 @@ public class PlayActivity extends BaseActivity<ActivityPlayBinding> implements S
|
||||
List<ResponsePlayListInfo> playList = mediaControllerManager.getPlayList();
|
||||
adapterPlayList = new AdapterPlayList();
|
||||
vb.layoutPlayList.recyclerList.setLayoutManager(new LinearLayoutManager(MusicApplication.myApplication));
|
||||
adapterPlayList.addData(playList);
|
||||
adapterPlayList.setData(playList);
|
||||
vb.layoutPlayList.recyclerList.setAdapter(adapterPlayList);
|
||||
vb.layoutPlayList.imPlay.setOnClickListener(this);
|
||||
initPlayList = true;
|
||||
|
||||
@ -41,6 +41,7 @@ public class VMPlay extends ViewModel {
|
||||
|
||||
@Override
|
||||
public void onFail(String errorMsg) {
|
||||
// TODO: 2024/10/8 播放列表拉取失败
|
||||
_playList.setValue(null);
|
||||
}
|
||||
|
||||
@ -63,10 +64,12 @@ public class VMPlay extends ViewModel {
|
||||
public void getPlayUrl(String videoId, int playListIndex,boolean playNow) {
|
||||
CustomerUrlInfo customerUrlInfo = new CustomerUrlInfo();
|
||||
customerUrlInfo.setNeedPlayNow(playNow);
|
||||
customerUrlInfo.setVideoId(videoId);
|
||||
RetrofitManager.getInstance().getPlayUrl(videoId, new RequestListener<ResponseBody>() {
|
||||
|
||||
@Override
|
||||
public void onFail(String errorMsg) {
|
||||
CommonUtils.LogMsg("-------------此次网络请求失败 playNow="+playNow);
|
||||
_playUrlMutableLiveData.setValue(customerUrlInfo);
|
||||
}
|
||||
|
||||
|
||||
7
app/src/main/res/drawable/bg_retry.xml
Normal file
7
app/src/main/res/drawable/bg_retry.xml
Normal file
@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<corners android:radius="10dp"/>
|
||||
<solid android:color="@color/cur_play_music"/>
|
||||
|
||||
</shape>
|
||||
@ -37,6 +37,39 @@
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/im_back" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="140dp"
|
||||
android:layout_height="100dp"
|
||||
android:background="@drawable/bg_retry"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical"
|
||||
android:id="@+id/linear_retry"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="@id/im_covert"
|
||||
app:layout_constraintLeft_toLeftOf="@id/im_covert"
|
||||
app:layout_constraintRight_toRightOf="@id/im_covert"
|
||||
app:layout_constraintTop_toTopOf="@id/im_covert">
|
||||
|
||||
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/An_error_occurred"
|
||||
android:textColor="@color/black"
|
||||
android:textSize="15sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_retry"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="10dp"
|
||||
android:text="@string/retry"
|
||||
android:textColor="@color/black"
|
||||
android:textSize="17sp" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
|
||||
<androidx.media3.ui.PlayerView
|
||||
android:id="@+id/player_view"
|
||||
@ -183,6 +216,6 @@
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:visibility="gone"
|
||||
android:indeterminateTint="@color/white" />
|
||||
android:indeterminateTint="@color/white"
|
||||
android:visibility="gone" />
|
||||
</FrameLayout>
|
||||
@ -22,4 +22,6 @@
|
||||
<string name="privacy_policy">Privacy Policy</string>
|
||||
<string name="terms_of_service">Terms of Service</string>
|
||||
<string name="play_next">Play next</string>
|
||||
<string name="retry">Retry</string>
|
||||
<string name="An_error_occurred">An error occurred</string>
|
||||
</resources>
|
||||
Loading…
Reference in New Issue
Block a user