连续播放,前后切歌功能

This commit is contained in:
litingting 2024-09-26 19:06:05 +08:00
parent 0787a71679
commit 0f59e337be
16 changed files with 535 additions and 155 deletions

View File

@ -57,6 +57,8 @@ dependencies {
implementation("com.github.bumptech.glide:glide:4.16.0")
implementation ("androidx.palette:palette:1.0.0")
//----------media3
implementation("androidx.media3:media3-exoplayer:1.4.1")
implementation("androidx.media3:media3-exoplayer-dash:1.4.1")

View File

@ -3,6 +3,7 @@
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />

View File

@ -1,8 +1,22 @@
package com.hi.music.player.api;
import androidx.media3.common.MediaItem;
public interface MediaControllerListener {
void onPlayStatus(int playStatus);
/**
*
* @param videoId
* @param playListIndex 在播放列表中的位置索引
* @param playNow 立即播放
*/
void onRequestNextUri(String videoId,int playListIndex,boolean playNow);
void onChangeMusic(MediaItem mediaItem);
}

View File

@ -0,0 +1,5 @@
package com.hi.music.player.api;
public interface onImageColorListener {
void onImageColor(int color);
}

View File

@ -0,0 +1,61 @@
package com.hi.music.player.dialog;
import android.app.Dialog;
import android.os.Bundle;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.DialogFragment;
import androidx.viewbinding.ViewBinding;
public abstract class BaseDialog<T extends ViewBinding> extends DialogFragment {
protected T vb;
protected abstract T getViewBinding(LayoutInflater inflater, ViewGroup container);
protected abstract void initView();
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
vb = getViewBinding(inflater,container);
init();
initView();
return vb.getRoot();
}
private void init() {
Dialog dialog = getDialog();
setCancelable(true);
if (dialog != null) {
Window window = dialog.getWindow();
if (window != null) {
// window.setBackgroundDrawableResource();
window.getDecorView().setPadding(0, 0, 0, 0);
WindowManager.LayoutParams attributes = window.getAttributes();
attributes.gravity = Gravity.BOTTOM;
attributes.width = WindowManager.LayoutParams.MATCH_PARENT;
attributes.height = WindowManager.LayoutParams.WRAP_CONTENT;
window.setAttributes(attributes);
}
}
}
public void closeDialog() {
dismiss();
}
}

View File

@ -0,0 +1,20 @@
package com.hi.music.player.dialog;
import android.view.LayoutInflater;
import android.view.ViewGroup;
import com.hi.music.player.databinding.DialogPlayListBinding;
public class DialogPlayList extends BaseDialog<DialogPlayListBinding>{
@Override
protected DialogPlayListBinding getViewBinding(LayoutInflater inflater, ViewGroup container) {
return DialogPlayListBinding.inflate(inflater,container,false);
}
@Override
protected void initView() {
}
}

View File

@ -1,10 +1,17 @@
package com.hi.music.player.helper;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.util.DisplayMetrics;
import android.util.Log;
import android.widget.ImageView;
import androidx.palette.graphics.Palette;
import com.hi.music.player.MusicApplication;
import com.hi.music.player.api.onImageColorListener;
import org.json.JSONException;
import org.json.JSONObject;
@ -80,4 +87,43 @@ public class CommonUtils {
long minutes = (millis / (1000 * 60)) % 60;
return String.format("%d:%02d", minutes, seconds); // 格式化为 mm:ss
}
/**
* 从图片中提取主色调
* @param imDraw
*/
public static void extractDominantColor(Drawable imDraw, onImageColorListener listener) {
BitmapDrawable drawable = (BitmapDrawable)imDraw;
if (drawable != null) {
Bitmap bitmap = drawable.getBitmap();
// 使用 Palette 提取颜色
Palette.from(bitmap).generate(palette -> {
if (palette != null) {
int dominantColor = palette.getDominantColor(0);
int suitableDarkColor = getSuitableDarkColor(dominantColor);
listener.onImageColor(suitableDarkColor);
}
});
}
}
private static int getSuitableDarkColor(int color) {
// 提取颜色的 R, G, B
int r = (color >> 16) & 0xff;
int g = (color >> 8) & 0xff;
int b = color & 0xff;
// 计算亮度
double luminance = 0.299 * r + 0.587 * g + 0.114 * b;
// 如果亮度较高则选择更深的颜色
if (luminance > 128) {
return 0xFF000000; // 黑色
} else {
// 如果颜色较暗选择一种深色调
return 0xFF1F1F1F; // 深灰色作为替代
}
}
}

View File

@ -0,0 +1,35 @@
package com.hi.music.player.javabean;
import com.hi.music.player.javabean.response.ResponsePlayUrl;
public class CustomerUrlInfo {
private ResponsePlayUrl playUrl;
private int playMusicIndex;
private boolean needPlayNow;
public ResponsePlayUrl getPlayUrl() {
return playUrl;
}
public void setPlayUrl(ResponsePlayUrl playUrl) {
this.playUrl = playUrl;
}
public int getPlayMusicIndex() {
return playMusicIndex;
}
public void setPlayMusicIndex(int playMusicIndex) {
this.playMusicIndex = playMusicIndex;
}
public boolean isNeedPlayNow() {
return needPlayNow;
}
public void setNeedPlayNow(boolean needPlayNow) {
this.needPlayNow = needPlayNow;
}
}

View File

@ -4,6 +4,11 @@ public class ResponsePlayListInfo {
//封面
private String covert;
//小尺寸封面
private String smallCovert;
//歌曲名字
private String SongTitle;
@ -37,11 +42,13 @@ public class ResponsePlayListInfo {
private String audioUrlMedium;
public String getSmallCovert() {
return smallCovert;
}
public void setSmallCovert(String smallCovert) {
this.smallCovert = smallCovert;
}
public long getDurationMs() {
return DurationMs;

View File

@ -1,7 +1,9 @@
package com.hi.music.player.media3;
import android.content.ComponentName;
import android.net.Uri;
import androidx.annotation.Nullable;
import androidx.annotation.OptIn;
import androidx.media3.common.MediaItem;
import androidx.media3.common.MediaMetadata;
@ -23,7 +25,9 @@ import com.hi.music.player.javabean.response.ResponsePlayUrl;
import com.hi.music.player.network.RetrofitManager;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.ExecutionException;
public class MyMediaControllerManager {
@ -32,6 +36,11 @@ public class MyMediaControllerManager {
private MediaController mediaController;
private MediaControllerListener mListener;
//播放列表总数量
private List<ResponsePlayListInfo> playList;
public static MyMediaControllerManager getInstance() {
if (myMediaControllerManagerInstance == null) {
synchronized (RetrofitManager.class) {
@ -68,36 +77,60 @@ public class MyMediaControllerManager {
}
public void addListener(MediaControllerListener listener) {
mListener = listener;
mediaController.addListener(new Player.Listener() {
@Override
public void onPlayerError(PlaybackException error) {
CommonUtils.LogMsg("=-----PlaybackException+" + error.getMessage());
listener.onPlayStatus(MyValue.PLAY_STATUS_CODE);
mListener.onPlayStatus(MyValue.PLAY_STATUS_CODE);
}
@Override
public void onMediaItemTransition(@Nullable MediaItem mediaItem, int reason) {
//当前媒体项发生变化切歌
if(mediaItem == null){
//第二次进入PlayActitivity 调用resetPlayList这里为null
return;
}
mListener.onChangeMusic(mediaItem);
}
@Override
public void onIsPlayingChanged(boolean isPlaying) {
CommonUtils.LogMsg("-----onIsPlayingChanged+" + isPlaying);
if (isPlaying) {
// TODO: 2024/9/26 自动播放完成切歌到下一首播放没有触发这里请求下一首
// 播放器开始播放
System.out.println("Playback started!");
MediaItem currentItem = mediaController.getCurrentMediaItem();
int currentMediaItemIndex = mediaController.getCurrentMediaItemIndex();
if (currentMediaItemIndex < playList.size() - 1) {
//代表有下一首
if (!mediaController.hasNextMediaItem()) {
//没有更新下一首的uri
CommonUtils.LogMsg("----0000000000-请求下一首的uri" + mediaController.getMediaItemCount());
int nextIndex = currentMediaItemIndex + 1;
mListener.onRequestNextUri(playList.get(nextIndex).getVideoId(), nextIndex,false);
}
}
} else {
// 播放器暂停或停止
System.out.println("Playback paused or stopped.");
}
}
@Override
public void onPlaybackStateChanged(int playbackState) {
listener.onPlayStatus(playbackState);
CommonUtils.LogMsg("----playbackState" + playbackState + "------------" + mediaController.getMediaItemCount());
mListener.onPlayStatus(playbackState);
}
@Override
public void onPositionDiscontinuity(Player.PositionInfo oldPosition, Player.PositionInfo newPosition, int reason) {
// 快进快退等操作
CommonUtils.LogMsg("=-----快进、快退+" + newPosition.positionMs);
// CommonUtils.LogMsg("=-----快进、快退+" + newPosition.positionMs);
// mediaControllerListener.onPlayStatus(playbackState);
}
});
@ -119,95 +152,153 @@ public class MyMediaControllerManager {
}
// /**
// * 更新播放列表中的音频url
// *
// * @param playUrl
// */
// public void UpdateAudioUrl(ResponsePlayUrl playUrl) {
//
// CommonUtils.LogMsg("-------------更新播放列表中的音频 mediaController.getMediaItemCount()=" + mediaController.getMediaItemCount());
// for (int i = 0; i < mediaController.getMediaItemCount(); i++) {
// MediaItem mediaItemAt = mediaController.getMediaItemAt(i);
// if (mediaItemAt.mediaId.equals(playUrl.getVideoId())) {
//
// MediaItem.Builder builder = mediaItemAt.buildUpon();
//// mediaController.removeMediaItem(i);
//// MediaItem.Builder builder = new MediaItem.Builder();
// builder.setMediaId(playUrl.getVideoId());
// if (playUrl.getAudioUrlMedium() != null) {
// builder.setUri(playUrl.getAudioUrlMedium());
// } else {
// builder.setUri(playUrl.getAudioUrlLow());
// }
// mediaController.replaceMediaItem(i, builder.build());
// CharSequence title = mediaController.getMediaItemAt(i).mediaMetadata.title;
// CommonUtils.LogMsg("-------------更新播放列表中的音频url= i=" + i + "---title=" + title + "----mediaController.size=" + mediaController.getMediaItemCount() + "---" + mediaController);
// break;
// }
// }
// }
public void setPlayList(List<ResponsePlayListInfo> playList) {
this.playList = playList;
}
public void resetPlayList(){
mediaController.clearMediaItems();
}
/**
* 更新播放列表中的音频url
* 添加播放列表不带音频url
* 注意没有更新有效uri的不会被实际添加到mediaController
*
* @param playUrl
*/
@OptIn(markerClass = UnstableApi.class)
public void UpdateAudioUrl(ResponsePlayUrl playUrl) {
for (int i = 0; i < mediaController.getMediaItemCount(); i++) {
MediaItem mediaItemAt = mediaController.getMediaItemAt(i);
if (mediaItemAt.mediaId.equals(playUrl.getVideoId())) {
MediaItem.Builder builder = mediaItemAt.buildUpon();
public void addMusicToPlayList(ResponsePlayUrl playUrl, int playListIndex) {
if (playListIndex < playList.size()&& playListIndex>=mediaController.getMediaItemCount()) {
ResponsePlayListInfo listInfo = playList.get(playListIndex);
if (listInfo.getVideoId().equals(playUrl.getVideoId())) {
MediaItem.Builder builder = new MediaItem.Builder();
//唯一标识符
builder.setMediaId(playUrl.getVideoId());
if (playUrl.getAudioUrlMedium() != null) {
builder.setUri(playUrl.getAudioUrlMedium());
} else {
builder.setUri(playUrl.getAudioUrlLow());
}
mediaController.setMediaItem(builder.build(), i);
CharSequence title = mediaController.getMediaItemAt(i).mediaMetadata.title;
CommonUtils.LogMsg("-------------更新播放列表中的音频url= i=" + i + "---title=" + title);
break;
MediaMetadata.Builder MediaMetadata_builder = new MediaMetadata.Builder();
MediaMetadata_builder.setArtist(listInfo.getSingerName());
/**
* 这里使用setDescription保存歌曲时长的分秒字符串
*/
MediaMetadata_builder.setDescription(listInfo.getDuration());
MediaMetadata_builder.setDurationMs(listInfo.getDurationMs());
MediaMetadata_builder.setArtworkUri(Uri.parse(listInfo.getCovert()));
// MediaMetadata_builder.setArtworkUri(Uri.parse("https://t7.baidu.com/it/u=2604797219,1573897854&fm=193&f=GIF"));
MediaMetadata_builder.setTitle(listInfo.getSongTitle());
CommonUtils.LogMsg("----------添加到播放列表 i=" + playListIndex + "---" + listInfo.getSongTitle() + "-------VideoId=" + listInfo.getVideoId());
// MediaMetadata_builder.setRecordingYear(Integer.parseInt(playInfo.getYear()));
builder.setMediaMetadata(MediaMetadata_builder.build());
mediaController.addMediaItem(builder.build());
}
}
}
// /**
// * 添加播放列表不带音频url
// * 注意没有更新有效uri的不会被实际添加到mediaController
// *
// * @param listInfo
// */
// @OptIn(markerClass = UnstableApi.class)
// public void addMusicPlay(ResponsePlayListInfo playInfo, ResponsePlayUrl responsePlay) {
// public void addMusicPlayList(List<ResponsePlayListInfo> listInfo) {
// playList = listInfo;
// List<MediaItem> mediaItems = new ArrayList<>();
// MediaItem.Builder builder = new MediaItem.Builder();
// if (responsePlay.getAudioUrlMedium() != null) {
// builder.setUri(responsePlay.getAudioUrlMedium());
// } else {
// builder.setUri(responsePlay.getAudioUrlLow());
// }
// for (int i = 0; i < listInfo.size(); i++) {
// ResponsePlayListInfo playInfo = listInfo.get(i);
//
// //唯一标识符
// builder.setMediaId(responsePlay.getVideoId());
// MediaItem.Builder builder = new MediaItem.Builder();
//
// MediaMetadata.Builder MediaMetadata_builder = new MediaMetadata.Builder();
// //唯一标识符
// builder.setMediaId(playInfo.getVideoId());
//
// MediaMetadata_builder.setArtist(playInfo.getSingerName());
// MediaMetadata_builder.setDurationMs(playInfo.getDurationMs());
// MediaMetadata_builder.setAlbumArtist(playInfo.getCovert());
// MediaMetadata_builder.setTitle(playInfo.getSongTitle());
// MediaMetadata.Builder MediaMetadata_builder = new MediaMetadata.Builder();
//
// MediaMetadata_builder.setArtist(playInfo.getSingerName());
// MediaMetadata_builder.setDurationMs(playInfo.getDurationMs());
//// MediaMetadata_builder.setArtworkUri(Uri.parse(playInfo.getCovert()));
// MediaMetadata_builder.setArtworkUri(Uri.parse("https://t7.baidu.com/it/u=2604797219,1573897854&fm=193&f=GIF"));
// MediaMetadata_builder.setTitle(playInfo.getSongTitle());
//
// CommonUtils.LogMsg("----------添加播放列表 i=" + i + "---" + playInfo.getSongTitle() + "-------VideoId=" + playInfo.getVideoId());
//// MediaMetadata_builder.setRecordingYear(Integer.parseInt(playInfo.getYear()));
// builder.setMediaMetadata(MediaMetadata_builder.build());
//
// mediaItems.add(builder.build());
// mediaController.addMediaItems(mediaItems);
// mediaController.prepare();
//
// builder.setMediaMetadata(MediaMetadata_builder.build());
// mediaController.addMediaItem(builder.build());
//// mediaItems.add(builder.build());
// }
//// mediaController.setMediaItems(mediaItems);
// }
/**
* 添加播放列表不带音频url
*
* @param listInfo
*/
@OptIn(markerClass = UnstableApi.class)
public void addMusicPlayList(List<ResponsePlayListInfo> listInfo) {
List<MediaItem> mediaItems = new ArrayList<>();
for (int i = 0; i < listInfo.size(); i++) {
ResponsePlayListInfo playInfo = listInfo.get(i);
MediaItem.Builder builder = new MediaItem.Builder();
//唯一标识符
builder.setMediaId(playInfo.getVideoId());
MediaMetadata.Builder MediaMetadata_builder = new MediaMetadata.Builder();
MediaMetadata_builder.setArtist(playInfo.getSingerName());
MediaMetadata_builder.setDurationMs(playInfo.getDurationMs());
MediaMetadata_builder.setAlbumArtist(playInfo.getCovert());
MediaMetadata_builder.setTitle(playInfo.getSongTitle());
// MediaMetadata_builder.setRecordingYear(Integer.parseInt(playInfo.getYear()));
builder.setMediaMetadata(MediaMetadata_builder.build());
mediaItems.add(builder.build());
}
mediaController.addMediaItems(mediaItems);
}
public void play() {
mediaController.prepare();
if (!mediaController.isPlaying())
if (!mediaController.isPlaying()) {
mediaController.prepare();
mediaController.play();
}
}
public void pause() {
if (mediaController.isPlaying())
mediaController.pause();
}
public void playNext() {
if (mediaController.hasNextMediaItem()) {
mediaController.seekToNextMediaItem();
} else {
int currentMediaItemIndex = mediaController.getCurrentMediaItemIndex();
if (currentMediaItemIndex < playList.size() - 1) {
//应该有下一首可能是歌曲url请求失败需要重新请求
int nextIndex = currentMediaItemIndex + 1;
mListener.onRequestNextUri(playList.get(nextIndex).getVideoId(), nextIndex,true);
CommonUtils.LogMsg("-------------应该有下一首可能是歌曲url请求失败需要重新请求");
}
}
}
public void playPrevious() {
if (mediaController.hasPreviousMediaItem()) {
mediaController.seekToPreviousMediaItem();
} else {
mediaController.seekTo(0);
mediaController.play();
}
}
}

View File

@ -4,11 +4,13 @@ import android.content.Intent;
import androidx.annotation.Nullable;
import androidx.annotation.OptIn;
import androidx.media3.common.AudioAttributes;
import androidx.media3.common.C;
import androidx.media3.common.MediaItem;
import androidx.media3.common.MediaMetadata;
import androidx.media3.common.Player;
import androidx.media3.common.util.UnstableApi;
import androidx.media3.exoplayer.ExoPlayer;
import androidx.media3.exoplayer.source.DefaultMediaSourceFactory;
@ -17,6 +19,7 @@ import androidx.media3.session.MediaSession;
import androidx.media3.session.MediaSessionService;
import com.google.common.util.concurrent.ListenableFuture;
import com.hi.music.player.R;
import com.hi.music.player.helper.CommonUtils;
import com.hi.music.player.javabean.response.ResponsePlayUrl;
@ -31,32 +34,43 @@ public class PlaybackService extends MediaSessionService {
public void onCreate() {
super.onCreate();
AudioAttributes audioAttributes = new AudioAttributes.Builder().setUsage(AudioAttributes.DEFAULT.usage)
.setContentType(AudioAttributes.DEFAULT.contentType)
.build();
// MediaSource.Factory mediaSourceFactory =
// new DefaultMediaSourceFactory(context)
// .setDataSourceFactory(cacheDataSourceFactory)
// .setLocalAdInsertionComponents(adsLoaderProvider);
player = new ExoPlayer.Builder(this)
.setWakeMode(C.WAKE_MODE_LOCAL)
// .setMediaSourceFactory(mediaSourceFactory)
.setAudioAttributes(audioAttributes, true).build();
mediaSession = new MediaSession.Builder(this, player).setCallback(new MediaSession.Callback() {
player = new ExoPlayer.Builder(this).build();
mediaSession = new MediaSession.Builder(this, player)
.setCallback(new MediaSession.Callback() {
@Override
public ListenableFuture<List<MediaItem>> onAddMediaItems(MediaSession mediaSession, MediaSession.ControllerInfo controller, List<MediaItem> mediaItems) {
CommonUtils.LogMsg("--------mediaItems="+mediaItems.get(0).mediaMetadata.title);
// player.addMediaItems(mediaItems);
CommonUtils.LogMsg("--------实际添加的媒体项="+mediaItems.size()+"----"+mediaSession.getPlayer().getMediaItemCount());
return MediaSession.Callback.super.onAddMediaItems(mediaSession, controller, mediaItems);
}
})
.build();
// player = new ExoPlayer.Builder(this)
// .setWakeMode(C.WAKE_MODE_LOCAL)
//// .setMediaSourceFactory(mediaSourceFactory)
// .setAudioAttributes(audioAttributes, true).build();
//
// mediaSession = new MediaSession.Builder(this, player)
// .setId(getString(R.string.app_name) + "_MusicId")
// .setCallback(new MediaSession.Callback() {
// @Override
// public ListenableFuture<List<MediaItem>> onAddMediaItems(MediaSession mediaSession, MediaSession.ControllerInfo controller, List<MediaItem> mediaItems) {
//
// CommonUtils.LogMsg("--------实际添加的媒体项="+player.getMediaItemCount());
//// player.addMediaItems(mediaItems);
//
// return MediaSession.Callback.super.onAddMediaItems(mediaSession, controller, mediaItems);
// }
// })
// .build();
}
@ -71,11 +85,13 @@ public class PlaybackService extends MediaSessionService {
@Override
public void onTaskRemoved(@Nullable Intent rootIntent) {
Player player = mediaSession.getPlayer();
// if (player.getPlayWhenReady()) {
// // Make sure the service is not in foreground.
// player.pause();
// }
// stopSelf();
if (!player.getPlayWhenReady()
|| player.getMediaItemCount() == 0
|| player.getPlaybackState() == Player.STATE_ENDED) {
// Stop the service if not playing, continue playing in the background
// otherwise.
stopSelf();
}
}
@Override

View File

@ -22,7 +22,7 @@ public class JsonHelper {
public static ResponseHome ResolveHomeJson(JSONObject jsonObject) {
ResponseHome responseHome = new ResponseHome();
try {
String bgUrl = getJsonUrl(jsonObject.getJSONObject("background"));
String bgUrl = getJsonUrl(jsonObject.getJSONObject("background"),true);
JSONObject sectionListRenderer = jsonObject.getJSONObject("contents").getJSONObject("singleColumnBrowseResultsRenderer").getJSONArray("tabs").getJSONObject(0).getJSONObject("tabRenderer").getJSONObject("content").getJSONObject("sectionListRenderer");
@ -106,7 +106,7 @@ public class JsonHelper {
if (musicResponsiveListItemRenderer != null) {
ResponseSingle responseSingle = new ResponseSingle();
String SingerHead = getJsonUrl(musicResponsiveListItemRenderer.getJSONObject("thumbnail"));
String SingerHead = getJsonUrl(musicResponsiveListItemRenderer.getJSONObject("thumbnail"),true);
JSONArray flexColumns = musicResponsiveListItemRenderer.getJSONArray("flexColumns");
@ -145,7 +145,7 @@ public class JsonHelper {
JSONObject musicTwoRowItemRenderer = jsonList.optJSONObject("musicTwoRowItemRenderer");
if (musicTwoRowItemRenderer != null) {
ResponseCategory responseCategory = new ResponseCategory();
String covert = getJsonUrl(musicTwoRowItemRenderer.getJSONObject("thumbnailRenderer"));
String covert = getJsonUrl(musicTwoRowItemRenderer.getJSONObject("thumbnailRenderer"),true);
JSONObject title1 = musicTwoRowItemRenderer.getJSONObject("title");
String twoTitle = getJsonTitle(title1, 0);
@ -185,7 +185,10 @@ public class JsonHelper {
ResponsePlayListInfo responsePlayListInfo = new ResponsePlayListInfo();
JSONObject playlistPanelVideoRenderer = contents.getJSONObject(i).getJSONObject("playlistPanelVideoRenderer");
String jsonUrl = getJsonUrl(playlistPanelVideoRenderer);
String maxUrl = getJsonUrl(playlistPanelVideoRenderer,true);
String smallUrl = getJsonUrl(playlistPanelVideoRenderer, false);
String songName = getJsonTitle(playlistPanelVideoRenderer.getJSONObject("title"), 0);
JSONObject longBylineText = playlistPanelVideoRenderer.getJSONObject("longBylineText");
@ -200,7 +203,8 @@ public class JsonHelper {
String[] watchEndPoint = getWatchEndPoint(playlistPanelVideoRenderer);
responsePlayListInfo.setCovert(jsonUrl);
responsePlayListInfo.setCovert(maxUrl);
responsePlayListInfo.setSmallCovert(smallUrl);
responsePlayListInfo.setSongTitle(songName);
responsePlayListInfo.setSingerName(singerName);
responsePlayListInfo.setAlbumTitle(AlbumTitle);
@ -213,7 +217,7 @@ public class JsonHelper {
responsePlayListInfo.setMusicVideoType(watchEndPoint[3]);
list.add(responsePlayListInfo);
CommonUtils.LogMsg("----------歌曲名字=" + songName + "---" + Arrays.toString(watchEndPoint));
}
@ -299,8 +303,15 @@ public class JsonHelper {
return strings;
}
//取最大的一张图片
private static String getJsonUrl(JSONObject jsonObject) {
/**
*
* @param jsonObject
* @param maxBig 是否取最大的一张图片
* @return
*/
private static String getJsonUrl(JSONObject jsonObject,boolean maxBig) {
try {
boolean b = jsonObject.has("musicThumbnailRenderer");
if (b) {
@ -308,8 +319,11 @@ public class JsonHelper {
}
JSONArray jsonArray = jsonObject.getJSONObject("thumbnail").getJSONArray("thumbnails");
int length = jsonArray.length();
CommonUtils.LogMsg("----------length=" + (length - 1));
String pngUrl = jsonArray.getJSONObject(length - 1).getString("url");
int index = 0;
if(maxBig){
index = length - 1;
}
String pngUrl = jsonArray.getJSONObject(index).getString("url");
return pngUrl;
} catch (JSONException exception) {

View File

@ -12,6 +12,8 @@ import androidx.annotation.Nullable;
import androidx.annotation.OptIn;
import androidx.core.content.ContextCompat;
import androidx.lifecycle.Observer;
import androidx.media3.common.MediaItem;
import androidx.media3.common.MediaMetadata;
import androidx.media3.common.Player;
import androidx.media3.common.util.UnstableApi;
@ -25,9 +27,11 @@ import com.bumptech.glide.request.target.Target;
import com.hi.music.player.MusicApplication;
import com.hi.music.player.R;
import com.hi.music.player.api.MediaControllerListener;
import com.hi.music.player.api.onImageColorListener;
import com.hi.music.player.databinding.ActivityPlayBinding;
import com.hi.music.player.helper.CommonUtils;
import com.hi.music.player.helper.MyValue;
import com.hi.music.player.javabean.CustomerUrlInfo;
import com.hi.music.player.javabean.response.ResponsePlayListInfo;
import com.hi.music.player.javabean.response.ResponsePlayUrl;
import com.hi.music.player.javabean.response.child.ResponseSingle;
@ -71,34 +75,41 @@ public class PlayActivity extends BaseActivity<ActivityPlayBinding> implements S
Intent intent = getIntent();
responseSingle = (ResponseSingle) intent.getSerializableExtra(MyValue.KEY_PLAY_ACTIVITY_SINGER);
vmPlay = getActivityScopeViewModel(VMPlay.class);
initMediaController();
vmPlay.getPlayMusicList(responseSingle);
initPlayerView();
initMediaController();
initProgressHandler();
vmPlay.playList.observe(this, new Observer<List<ResponsePlayListInfo>>() {
@Override
public void onChanged(List<ResponsePlayListInfo> playList) {
if (playList == null) {
return;
}
if (playList.size() > 0) {
requestPlayList = true;
mPlayList = playList;
musicInfo = playList.get(0);
loadCovert(musicInfo.getCovert());
loadInfo(musicInfo);
}
}
});
vmPlay.playUrlLiveData.observe(this, new Observer<Pair<ResponsePlayUrl, Integer>>() {
vmPlay.playUrlLiveData.observe(this, new Observer<CustomerUrlInfo>() {
@Override
public void onChanged(Pair<ResponsePlayUrl, Integer> responsePlayUrlIntegerPair) {
public void onChanged(CustomerUrlInfo customerUrlInfo) {
if (customerUrlInfo.isNeedPlayNow() && customerUrlInfo.getPlayUrl() == null) {
// TODO: 2024/9/26 需要马上播放这首歌曲但是此次网络请求失败
CommonUtils.LogMsg("-------------需要马上播放这首歌曲,但是此次网络请求失败");
return;
}
requestPlayUrl = true;
mCurPlayInfo = responsePlayUrlIntegerPair.first;
Integer second = responsePlayUrlIntegerPair.second;
CommonUtils.LogMsg("---------mCurPlayInfo=" + mCurPlayInfo.getAudioUrlLow());
updateAudioUrl(responsePlayUrlIntegerPair.first);
if (second == 0) {
mCurPlayInfo = customerUrlInfo.getPlayUrl();
int second = customerUrlInfo.getPlayMusicIndex();
boolean needPlayNow = customerUrlInfo.isNeedPlayNow();
if (needPlayNow) {
mediaControllerManager.play();
}
}
@ -115,40 +126,65 @@ public class PlayActivity extends BaseActivity<ActivityPlayBinding> implements S
private void initMediaController() {
mediaControllerManager = MyMediaControllerManager.getInstance();
mediaControllerManager.resetPlayList();
mediaControllerManager.addListener(new MediaControllerListener() {
@Override
public void onPlayStatus(int playStatus) {
CommonUtils.LogMsg("-------------playStatus=" + playStatus);
switch (playStatus) {
case Player.STATE_IDLE:
CommonUtils.LogMsg("-------------playStatus=STATE_IDLE");
break;
case Player.STATE_BUFFERING:
//快进没有缓冲的时候触发
vb.btnPlay.setSelected(false);
vb.progressBarLoading.setVisibility(View.VISIBLE);
CommonUtils.LogMsg("-------------duration STATE_BUFFERING=");
CommonUtils.LogMsg("-------------playStatus=STATE_BUFFERING");
break;
case Player.STATE_READY:
vb.btnPlay.setSelected(true);
mHandler.post(mRunnable);
vb.progressBarLoading.setVisibility(View.GONE);
CommonUtils.LogMsg("-------------playStatus=STATE_READY");
break;
case Player.STATE_ENDED:
vb.btnPlay.setSelected(false);
CommonUtils.LogMsg("-------------playStatus=STATE_ENDED 播放完成");
mHandler.removeCallbacks(mRunnable); // 停止更新
updatePlayComplete();
mediaControllerManager.playNext();
break;
}
}
@Override
public void onRequestNextUri(String videoId, int playListIndex, boolean playNow) {
CommonUtils.LogMsg("------------onRequestNextUri= videoId=" + videoId);
if (playNow) {
vb.progressBarLoading.setVisibility(View.VISIBLE);
}
vmPlay.getPlayUrl(videoId, playListIndex, playNow);
}
@Override
public void onChangeMusic(MediaItem mediaItem) {
loadInfo(mediaItem);
}
});
}
@Override
protected void onInitClick() {
vb.btnPlay.setOnClickListener(this::onClick);
vb.btnPlay.setOnClickListener(this);
vb.playProgress.setOnSeekBarChangeListener(this);
vb.btnNext.setOnClickListener(this);
vb.btnPrevious.setOnClickListener(this);
vb.imBack.setOnClickListener(this);
}
@ -163,7 +199,7 @@ public class PlayActivity extends BaseActivity<ActivityPlayBinding> implements S
String s = CommonUtils.convertMillisToTime(contentPos);
CommonUtils.LogMsg("---------播放进度-----contentPos=" + contentPos + "-----缓冲进度=" + bufferPos);
// CommonUtils.LogMsg("---------播放进度-----contentPos=" + contentPos + "-----缓冲进度=" + bufferPos);
vb.tvCurrent.setText(s);
vb.playProgress.setProgress((int) contentPos);
vb.progressBarBuffer.setProgress((int) bufferPos);
@ -179,34 +215,38 @@ public class PlayActivity extends BaseActivity<ActivityPlayBinding> implements S
/**
* 初始化当前播放歌曲信息
*
* @param data
* 加载当前播放歌曲信息
*/
private void loadInfo(ResponsePlayListInfo data) {
vb.tvSongName.setText(data.getSongTitle());
vb.tvSingerName.setText(data.getSingerName());
vb.tvDuration.setText(data.getDuration());
@OptIn(markerClass = UnstableApi.class)
private void loadInfo(MediaItem mediaItem) {
MediaMetadata mediaMetadata = mediaItem.mediaMetadata;
CommonUtils.LogMsg("--------------加载当前播放歌曲信息 "+mediaMetadata.description);
if (mediaMetadata.artworkUri != null) {
loadCovert(mediaMetadata.artworkUri.toString());
}
vb.tvSongName.setText(mediaMetadata.title);
vb.tvSingerName.setText(mediaMetadata.artist);
vb.tvDuration.setText(mediaMetadata.description);
if (mediaMetadata.durationMs != null) {
long durationMs = mediaMetadata.durationMs;
vb.playProgress.setMax((int) durationMs);
vb.progressBarBuffer.setMax((int) durationMs);
}
long durationMs = data.getDurationMs();
vb.playProgress.setMax((int) durationMs);
vb.progressBarBuffer.setMax((int) data.getDurationMs());
CommonUtils.LogMsg("--------------data.getDurationMs() 22222222222=" + durationMs);
}
/**
* 更新音频url到播放列表
*/
private void updateAudioUrl(ResponsePlayUrl playUrl) {
mediaControllerManager.UpdateAudioUrl(playUrl);
// mediaControllerManager.UpdateAudioUrl(playUrl);
}
private void loadCovert(String url) {
Glide.with(MusicApplication.myApplication)
.asDrawable()
@ -223,12 +263,20 @@ public class PlayActivity extends BaseActivity<ActivityPlayBinding> implements S
@OptIn(markerClass = UnstableApi.class)
@Override
public boolean onResourceReady(@NonNull Drawable resource, @NonNull Object model, Target<Drawable> target, @NonNull DataSource dataSource, boolean isFirstResource) {
vb.playerView.setDefaultArtwork(resource);
CommonUtils.extractDominantColor(resource, new onImageColorListener() {
@Override
public void onImageColor(int color) {
vb.rootLayout.setBackgroundColor(color);
}
});
return false;
}
})
.into(vb.imCovert);
}
@ -245,7 +293,7 @@ public class PlayActivity extends BaseActivity<ActivityPlayBinding> implements S
@Override
public boolean isFullScreen() {
return false;
return true;
}
@Override
@ -262,6 +310,13 @@ public class PlayActivity extends BaseActivity<ActivityPlayBinding> implements S
} else {
mediaControllerManager.pause();
}
} else if (v.equals(vb.btnNext)) {
mediaControllerManager.playNext();
} else if (v.equals(vb.btnPrevious)) {
mediaControllerManager.playPrevious();
} else if (v.equals(vb.imBack)) {
finish();
}
}
@ -277,7 +332,6 @@ public class PlayActivity extends BaseActivity<ActivityPlayBinding> implements S
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
if (fromUser) {
CommonUtils.LogMsg("---------onProgressChanged----progress=" + progress);
mediaControllerManager.getMediaController().seekTo(progress);
}
}

View File

@ -8,6 +8,7 @@ import androidx.lifecycle.ViewModel;
import com.hi.music.player.api.RequestListener;
import com.hi.music.player.helper.CommonUtils;
import com.hi.music.player.javabean.CustomerUrlInfo;
import com.hi.music.player.javabean.response.ResponsePlayListInfo;
import com.hi.music.player.javabean.response.ResponsePlayUrl;
import com.hi.music.player.javabean.response.child.ResponseSingle;
@ -28,10 +29,10 @@ public class VMPlay extends ViewModel {
public LiveData<List<ResponsePlayListInfo>> playList = _playList;
private MutableLiveData<Pair<ResponsePlayUrl, Integer>> _playUrlMutableLiveData = new MutableLiveData<Pair<ResponsePlayUrl, Integer>>();
public LiveData<Pair<ResponsePlayUrl, Integer>> playUrlLiveData = _playUrlMutableLiveData;
private MutableLiveData<CustomerUrlInfo> _playUrlMutableLiveData = new MutableLiveData<CustomerUrlInfo>();
public LiveData<CustomerUrlInfo> playUrlLiveData = _playUrlMutableLiveData;
private String continuation, clickTrackingParams, visitorData;
public void getPlayMusicList(ResponseSingle responseSingle) {
String playlistId = responseSingle.getPlaylistId();
@ -51,9 +52,9 @@ public class VMPlay extends ViewModel {
JSONObject jsonObject = CommonUtils.toJsonObject(data);
if (jsonObject != null) {
List<ResponsePlayListInfo> responsePlayListInfos = JsonHelper.ResolvePlayListJson(jsonObject);
MyMediaControllerManager.getInstance().addMusicPlayList(responsePlayListInfos);
MyMediaControllerManager.getInstance().setPlayList(responsePlayListInfos);
_playList.setValue(responsePlayListInfos);
getPlayUrl(responseSingle,0);
getPlayUrl(responseSingle.getVideoId(),0,true);
} else {
_playList.setValue(null);
}
@ -62,13 +63,14 @@ public class VMPlay extends ViewModel {
});
}
public void getPlayUrl(ResponseSingle responseSingle, int playListIndex) {
String videoId = responseSingle.getVideoId();
public void getPlayUrl(String videoId, int playListIndex,boolean playNow) {
CustomerUrlInfo customerUrlInfo = new CustomerUrlInfo();
customerUrlInfo.setNeedPlayNow(playNow);
RetrofitManager.getInstance().getPlayUrl(videoId, new RequestListener<ResponseBody>() {
@Override
public void onFail(String errorMsg) {
_playUrlMutableLiveData.setValue(null);
_playUrlMutableLiveData.setValue(customerUrlInfo);
}
@Override
@ -76,10 +78,12 @@ public class VMPlay extends ViewModel {
JSONObject jsonObject = CommonUtils.toJsonObject(data);
if (jsonObject != null) {
ResponsePlayUrl responsePlayUrl = JsonHelper.ResolvePlayUrlJson(jsonObject);
_playUrlMutableLiveData.setValue(new Pair<>(responsePlayUrl, playListIndex));
} else {
_playUrlMutableLiveData.setValue(null);
MyMediaControllerManager.getInstance().addMusicToPlayList(responsePlayUrl,playListIndex);
customerUrlInfo.setPlayUrl(responsePlayUrl);
customerUrlInfo.setPlayMusicIndex(playListIndex);
}
_playUrlMutableLiveData.setValue(customerUrlInfo);
}
});

View File

@ -4,7 +4,7 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/black"
android:id="@+id/root_layout"
tools:context=".ui.activity.PlayActivity">
@ -189,10 +189,11 @@
android:id="@+id/btn_music_list"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_marginBottom="40dp"
android:src="@mipmap/icon_list"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="@id/im_covert" />
app:layout_constraintTop_toTopOf="@id/btn_play"
app:layout_constraintBottom_toBottomOf="@id/btn_play"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintLeft_toRightOf="@id/btn_next" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
</androidx.constraintlayout.widget.ConstraintLayout>