增加分类音乐合集列表页面

This commit is contained in:
litingting 2024-10-10 18:51:32 +08:00
parent 8f913bdac7
commit 186ac6b208
24 changed files with 949 additions and 150 deletions

View File

@ -56,6 +56,9 @@ dependencies {
implementation("androidx.paging:paging-runtime-ktx:3.3.2")
implementation("com.github.bumptech.glide:glide:4.16.0")
annotationProcessor("com.github.bumptech.glide:compiler:4.16.0")
// Glide 的图片变换库,包括高斯模糊
implementation ("jp.wasabeef:glide-transformations:4.3.0")
//提取图片主色

View File

@ -6,13 +6,11 @@
<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" />
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
<uses-permission
android:name="android.permission.READ_EXTERNAL_STORAGE"
android:maxSdkVersion="32" />
<application
android:name=".MusicApplication"
android:allowBackup="true"
@ -24,6 +22,9 @@
android:supportsRtl="true"
android:theme="@style/Theme.MusicApp"
tools:targetApi="31">
<activity
android:name=".ui.activity.CategoryListActivity"
android:exported="false" />
<activity
android:name=".ui.activity.A_PlayActivity"
android:exported="false" />
@ -46,18 +47,13 @@
</activity>
<activity
android:name=".ui.activity.A_HomeActivity"
android:exported="false">
</activity>
android:exported="false"></activity>
<activity
android:name=".ui.activity.MainActivity"
android:exported="false" />
<activity
android:name=".ui.activity.A_SplashActivity"
android:exported="true">
</activity>
android:exported="true"></activity>
<service
android:name=".media3.PlaybackService"
@ -67,14 +63,11 @@
<action android:name="androidx.media3.session.MediaSessionService" />
</intent-filter>
</service>
<service
android:name=".service.MusicPlayerForegroundService"
android:enabled="true"
android:exported="false"
android:foregroundServiceType="mediaPlayback" />
</application>
</manifest>

View File

@ -2,6 +2,7 @@ package com.hi.music.player.adapter;
import android.graphics.drawable.Drawable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
@ -57,12 +58,14 @@ public class AdapterCategory extends BaseAdapter<ResponseCategory, ItemCategoryB
.into(vb.header);
vb.tvTitle.setText(responseCategory.getTwoTitle());
vb.tvSubtitle.setText(responseCategory.getTwoSubtitle());
vb.header.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (homeItemClickListener != null) {
homeItemClickListener.onClickItemCategory(responseCategory);
}
}
});
}
}
}

View File

@ -0,0 +1,67 @@
package com.hi.music.player.adapter;
import android.graphics.drawable.Drawable;
import android.view.LayoutInflater;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.OptIn;
import androidx.media3.common.util.UnstableApi;
import androidx.recyclerview.widget.RecyclerView;
import com.bumptech.glide.Glide;
import com.bumptech.glide.load.DataSource;
import com.bumptech.glide.load.engine.GlideException;
import com.bumptech.glide.load.resource.bitmap.RoundedCorners;
import com.bumptech.glide.request.RequestListener;
import com.bumptech.glide.request.RequestOptions;
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.onImageColorListener;
import com.hi.music.player.databinding.ItemCategoryListBinding;
import com.hi.music.player.databinding.ItemSingerBinding;
import com.hi.music.player.helper.CommonUtils;
import com.hi.music.player.javabean.response.child.ResponseCategoryListChild;
import com.hi.music.player.javabean.response.child.ResponseSingle;
public class AdapterCategoryList extends BaseAdapter<ResponseCategoryListChild, ItemCategoryListBinding> {
@Override
protected ItemCategoryListBinding getViewBinding(ViewGroup parent) {
return ItemCategoryListBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false);
}
@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
VHolder<ItemCategoryListBinding> itemHolder = (VHolder<ItemCategoryListBinding>) holder;
ResponseCategoryListChild child = data.get(position);
ItemCategoryListBinding vb = itemHolder.getVb();
vb.tvSongName.setText(child.getSongTitle());
vb.tvSingerName.setText(child.getSingerName());
Glide.with(MusicApplication.myApplication)
.asDrawable()
.apply(RequestOptions.bitmapTransform(new RoundedCorners(CommonUtils.dpToPx(16))))
.load(child.getSongCovert())
.placeholder(R.mipmap.ic_launcher)
.listener(new RequestListener<Drawable>() {
@Override
public boolean onLoadFailed(@Nullable GlideException e, @Nullable Object model, @NonNull Target<Drawable> target, boolean isFirstResource) {
CommonUtils.LogMsg(e.getMessage());
return false;
}
@OptIn(markerClass = UnstableApi.class)
@Override
public boolean onResourceReady(@NonNull Drawable resource, @NonNull Object model, Target<Drawable> target, @NonNull DataSource dataSource, boolean isFirstResource) {
return false;
}
})
.into(vb.image);
}
}

View File

@ -43,7 +43,7 @@ public class AdapterHome extends BaseAdapter<ResponseHomeChild, ItemHomeBinding>
vb.recyclerSinger.setVisibility(View.VISIBLE);
CommonUtils.LogMsg("-----------singleList-=" + singleList.size());
AdapterSinger adapterSinger = new AdapterSinger();
adapterSinger.setSingleClickSingerListener(singleClickSingerListener);
adapterSinger.setHomeItemClickListener(homeItemClickListener);
adapterSinger.addData(singleList);
vb.recyclerSinger.setLayoutManager(new GridLayoutManager(MusicApplication.myApplication, 4, RecyclerView.HORIZONTAL, false));
vb.recyclerSinger.setAdapter(adapterSinger);
@ -55,6 +55,7 @@ public class AdapterHome extends BaseAdapter<ResponseHomeChild, ItemHomeBinding>
vb.recyclerCategory.setVisibility(View.VISIBLE);
CommonUtils.LogMsg("-----------categoryList-=" + categoryList.size());
AdapterCategory adapterCategory = new AdapterCategory();
adapterCategory.setHomeItemClickListener(homeItemClickListener);
adapterCategory.addData(categoryList);
vb.recyclerCategory.setLayoutManager(new LinearLayoutManager(MusicApplication.myApplication, RecyclerView.HORIZONTAL, false));
vb.recyclerCategory.setAdapter(adapterCategory);

View File

@ -61,8 +61,8 @@ public class AdapterSinger extends BaseAdapter<ResponseSingle, ItemSingerBinding
vb.getRoot().setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (singleClickSingerListener != null) {
singleClickSingerListener.onClickSingerItem(responseSingle);
if (homeItemClickListener != null) {
homeItemClickListener.onClickItemSinger(responseSingle);
}
}

View File

@ -7,8 +7,9 @@ import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import androidx.viewbinding.ViewBinding;
import com.hi.music.player.api.ClickSingerListener;
import com.hi.music.player.api.HomeItemClickListener;
import com.hi.music.player.databinding.ItemFooterLoadingBinding;
import com.hi.music.player.javabean.response.child.ResponseCategory;
import com.hi.music.player.javabean.response.child.ResponseSingle;
import java.util.ArrayList;
@ -24,10 +25,11 @@ abstract public class BaseAdapter<K, T extends ViewBinding> extends RecyclerView
protected ClickSingerListener<ResponseSingle> singleClickSingerListener;
protected HomeItemClickListener homeItemClickListener;
public void setSingleClickSingerListener(ClickSingerListener<ResponseSingle> singleClickSingerListener) {
this.singleClickSingerListener = singleClickSingerListener;
public void setHomeItemClickListener(HomeItemClickListener homeItemClickListener) {
this.homeItemClickListener = homeItemClickListener;
}
public void addData(List<K> data) {

View File

@ -0,0 +1,11 @@
package com.hi.music.player.api;
import com.hi.music.player.javabean.response.child.ResponseCategory;
import com.hi.music.player.javabean.response.child.ResponseSingle;
public interface HomeItemClickListener {
void onClickItemSinger(ResponseSingle data) ;
void onClickItemCategory(ResponseCategory data) ;
}

View File

@ -9,7 +9,9 @@ import android.graphics.drawable.GradientDrawable;
import android.net.Uri;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.View;
import android.widget.ImageView;
import android.widget.LinearLayout;
import androidx.media3.common.MediaItem;
import androidx.palette.graphics.Palette;
@ -170,7 +172,34 @@ public class CommonUtils {
drawable.setCornerRadii(radii);
return drawable;
}
public static void extractColorsFromImage(Drawable drawable, View view) {
Bitmap bitmap = ((BitmapDrawable) drawable).getBitmap();
Palette.from(bitmap).generate(palette -> {
if (palette != null) {
// 获取主色
int vibrantColor = palette.getVibrantColor(0);
int mutedColor = palette.getMutedColor(0);
// 创建左右渐变色
createGradientBackground(vibrantColor, mutedColor,view);
}
});
}
private static void createGradientBackground(int leftColor, int rightColor, View view) {
GradientDrawable gradientDrawable = new android.graphics.drawable.GradientDrawable(
android.graphics.drawable.GradientDrawable.Orientation.LEFT_RIGHT,
new int[]{leftColor, rightColor}
);
view.setBackground(gradientDrawable);
}
/**
* 当前媒体项是否有有效的播放地址

View File

@ -16,4 +16,15 @@ public class MyValue {
//-----------------------------PlayActivity
//-----------------------------CategoryListActivity
public static String KEY_PLAY_ACTIVITY_CATEGORY= "click_category";
//-----------------------------CategoryListActivity
}

View File

@ -12,6 +12,15 @@ public class BodyHome implements Serializable {
private String browseId = "FEmusic_home";
public String getBrowseId() {
return browseId;
}
public void setBrowseId(String browseId) {
this.browseId = browseId;
}
private ContextBody context = new ContextBody();
public ContextBody getContext() {

View File

@ -0,0 +1,56 @@
package com.hi.music.player.javabean.response;
import com.hi.music.player.javabean.response.child.ResponseCategoryListChild;
import java.util.List;
public class ResponseCategoryList {
private String covert;
private String title;
private String description;
private String secondSubtitle;
private List<ResponseCategoryListChild> list;
public String getCovert() {
return covert;
}
public void setCovert(String covert) {
this.covert = covert;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getSecondSubtitle() {
return secondSubtitle;
}
public void setSecondSubtitle(String secondSubtitle) {
this.secondSubtitle = secondSubtitle;
}
public List<ResponseCategoryListChild> getList() {
return list;
}
public void setList(List<ResponseCategoryListChild> list) {
this.list = list;
}
}

View File

@ -1,6 +1,8 @@
package com.hi.music.player.javabean.response.child;
public class ResponseCategory {
import java.io.Serializable;
public class ResponseCategory implements Serializable {
//类别封面
private String covert;
@ -11,6 +13,17 @@ public class ResponseCategory {
private String twoSubtitle;
private String browseId;
public String getBrowseId() {
return browseId;
}
public void setBrowseId(String browseId) {
this.browseId = browseId;
}
public String getCovert() {
return covert;
}

View File

@ -0,0 +1,44 @@
package com.hi.music.player.javabean.response.child;
public class ResponseCategoryListChild {
private String SongTitle;
private String SingerName;
private String SongCovert;
private String SongDuration;
public String getSongCovert() {
return SongCovert;
}
public void setSongCovert(String songCovert) {
SongCovert = songCovert;
}
public String getSongDuration() {
return SongDuration;
}
public void setSongDuration(String songDuration) {
SongDuration = songDuration;
}
public String getSongTitle() {
return SongTitle;
}
public void setSongTitle(String songTitle) {
SongTitle = songTitle;
}
public String getSingerName() {
return SingerName;
}
public void setSingerName(String singerName) {
SingerName = singerName;
}
}

View File

@ -1,10 +1,12 @@
package com.hi.music.player.network;
import com.hi.music.player.helper.CommonUtils;
import com.hi.music.player.javabean.response.ResponseCategoryList;
import com.hi.music.player.javabean.response.ResponsePlayListInfo;
import com.hi.music.player.javabean.response.ResponsePlayUrl;
import com.hi.music.player.javabean.response.child.ResponseCategory;
import com.hi.music.player.javabean.response.ResponseHome;
import com.hi.music.player.javabean.response.child.ResponseCategoryListChild;
import com.hi.music.player.javabean.response.child.ResponseHomeChild;
import com.hi.music.player.javabean.response.child.ResponseSingle;
@ -22,7 +24,7 @@ public class JsonHelper {
public static ResponseHome ResolveHomeJson(JSONObject jsonObject) {
ResponseHome responseHome = new ResponseHome();
try {
String bgUrl = getJsonUrl(jsonObject.getJSONObject("background"),true);
String bgUrl = getJsonUrl(jsonObject.getJSONObject("background"), true);
JSONObject sectionListRenderer = jsonObject.getJSONObject("contents").getJSONObject("singleColumnBrowseResultsRenderer").getJSONArray("tabs").getJSONObject(0).getJSONObject("tabRenderer").getJSONObject("content").getJSONObject("sectionListRenderer");
@ -66,113 +68,6 @@ public class JsonHelper {
return responseHome;
}
private static void getCommonHome(JSONObject sectionListRenderer, ResponseHome responseHome) throws JSONException {
JSONObject nextContinuationData = sectionListRenderer.getJSONArray("continuations").getJSONObject(0).getJSONObject("nextContinuationData");
//token=continuation,cit= clickTrackingParams
String clickTrackingParams = nextContinuationData.getString("clickTrackingParams");
String continuation = nextContinuationData.getString("continuation");
CommonUtils.LogMsg("---------参数---clickTrackingParams=" + clickTrackingParams);
CommonUtils.LogMsg("---------参数--continuation=" + continuation);
responseHome.setClickTrackingParams(clickTrackingParams);
responseHome.setContinuation(continuation);
JSONArray contents = sectionListRenderer.getJSONArray("contents");
List<ResponseHomeChild> childList = new ArrayList<>();
for (int i = 0; i < contents.length(); i++) {
ResponseHomeChild responseHomeChild = new ResponseHomeChild();
JSONObject object = contents.getJSONObject(i);
JSONObject musicCarouselShelfRenderer = object.optJSONObject("musicCarouselShelfRenderer");
if (musicCarouselShelfRenderer != null) {
//模块标题
String title = getJsonTitle(musicCarouselShelfRenderer.getJSONObject("header").getJSONObject("musicCarouselShelfBasicHeaderRenderer").getJSONObject("title"), 0);
CommonUtils.LogMsg("----------headertitle=" + title);
responseHomeChild.setHeaderTitle(title);
JSONArray childContents = musicCarouselShelfRenderer.getJSONArray("contents");
List<ResponseCategory> categoryList = new ArrayList<>();
List<ResponseSingle> singleList = new ArrayList<>();
for (int j = 0; j < childContents.length(); j++) {
JSONObject jsonList = childContents.getJSONObject(j);
JSONObject musicResponsiveListItemRenderer = jsonList.optJSONObject("musicResponsiveListItemRenderer");
if (musicResponsiveListItemRenderer != null) {
ResponseSingle responseSingle = new ResponseSingle();
String SingerHead = getJsonUrl(musicResponsiveListItemRenderer.getJSONObject("thumbnail"),true);
JSONArray flexColumns = musicResponsiveListItemRenderer.getJSONArray("flexColumns");
//歌名歌手名字描述
String SongTitle = "";
String SingerName = "";
String Description = "";
for (int g = 0; g < flexColumns.length(); g++) {
JSONObject jsonObject = musicResponsiveListItemRenderer.getJSONArray("flexColumns").getJSONObject(g).getJSONObject("musicResponsiveListItemFlexColumnRenderer").getJSONObject("text");
String text = getJsonTitle(jsonObject, 0);
if (g == 0) {
SongTitle = text;
JSONObject runs = jsonObject.getJSONArray("runs").getJSONObject(0);
String[] watchEndPoint = getWatchEndPoint(runs);
responseSingle.setVideoId(watchEndPoint[0]);
responseSingle.setPlaylistId(watchEndPoint[1]);
responseSingle.setParams(watchEndPoint[2]);
responseSingle.setMusicVideoType(watchEndPoint[3]);
}
if (g == 1) SingerName = text;
if (g == 2) Description = text;
}
responseSingle.setSongTitle(SongTitle);
responseSingle.setSingerName(SingerName);
responseSingle.setSingerHead(SingerHead);
responseSingle.setDescription(Description);
singleList.add(responseSingle);
CommonUtils.LogMsg(" ----------111111111111----SongTitle=" + SongTitle + "--SingerName=" + SingerName + "---Description=" + Description + "--SingerHead=" + SingerHead);
}
JSONObject musicTwoRowItemRenderer = jsonList.optJSONObject("musicTwoRowItemRenderer");
if (musicTwoRowItemRenderer != null) {
ResponseCategory responseCategory = new ResponseCategory();
String covert = getJsonUrl(musicTwoRowItemRenderer.getJSONObject("thumbnailRenderer"),true);
JSONObject title1 = musicTwoRowItemRenderer.getJSONObject("title");
String twoTitle = getJsonTitle(title1, 0);
String twoSubtitle = getJsonTextNew(musicTwoRowItemRenderer.getJSONObject("subtitle"));
// String pageType = title1.getJSONObject("navigationEndpoint")
// .getJSONObject("browseEndpoint")
// .getJSONObject("browseEndpointContextSupportedConfigs")
// .getJSONObject("browseEndpointContextMusicConfig")
// .getString("pageType");
responseCategory.setCovert(covert);
responseCategory.setTwoTitle(twoTitle);
responseCategory.setTwoSubtitle(twoSubtitle);
categoryList.add(responseCategory);
CommonUtils.LogMsg(" ----------2222222222----twoTitle=" + twoTitle + "-twoSubtitle=" + twoSubtitle);
}
}
responseHomeChild.setCategoryList(categoryList);
responseHomeChild.setSingleList(singleList);
}
childList.add(responseHomeChild);
}
responseHome.setChildList(childList);
}
public static List<ResponsePlayListInfo> ResolvePlayListJson(JSONObject jsonObject) {
List<ResponsePlayListInfo> list = new ArrayList<>();
@ -185,7 +80,7 @@ public class JsonHelper {
ResponsePlayListInfo responsePlayListInfo = new ResponsePlayListInfo();
JSONObject playlistPanelVideoRenderer = contents.getJSONObject(i).getJSONObject("playlistPanelVideoRenderer");
String maxUrl = getJsonUrl(playlistPanelVideoRenderer,true);
String maxUrl = getJsonUrl(playlistPanelVideoRenderer, true);
String smallUrl = getJsonUrl(playlistPanelVideoRenderer, false);
@ -273,6 +168,236 @@ public class JsonHelper {
}
public static ResponseCategoryList ResolveCategoryList(JSONObject jsonObject) {
try {
ResponseCategoryList responseCategoryList = new ResponseCategoryList();
JSONObject jsonObject1 = jsonObject.getJSONObject("contents")
.getJSONObject("twoColumnBrowseResultsRenderer");
JSONObject musicResponsiveHeaderRenderer = jsonObject1
.getJSONArray("tabs")
.getJSONObject(0)
.getJSONObject("tabRenderer")
.getJSONObject("content")
.getJSONObject("sectionListRenderer")
.getJSONArray("contents")
.getJSONObject(0)
.getJSONObject("musicResponsiveHeaderRenderer");
//封面图
String covert = getJsonUrl(musicResponsiveHeaderRenderer.getJSONObject("thumbnail")
.getJSONObject("musicThumbnailRenderer"), true);
//轻松愉悦的经典摇滚乐
String title = getJsonTitle(musicResponsiveHeaderRenderer.getJSONObject("title"), 0);
//Hold on to the feeling.
String description = getJsonTitle(musicResponsiveHeaderRenderer.getJSONObject("description")
.getJSONObject("musicDescriptionShelfRenderer")
.getJSONObject("description"), 0);
//110 首歌曲.超过6小时
String secondSubtitle = getJsonTextNew(musicResponsiveHeaderRenderer.getJSONObject("secondSubtitle"));
CommonUtils.LogMsg("-------------title="+title+"--description="+description+"---secondSubtitle="+secondSubtitle+"---covert="+covert);
JSONArray jsonArray = jsonObject1.getJSONObject("secondaryContents")
.getJSONObject("sectionListRenderer")
.getJSONArray("contents")
.getJSONObject(0)
.getJSONObject("musicPlaylistShelfRenderer")
.getJSONArray("contents");
responseCategoryList.setCovert(covert);
responseCategoryList.setDescription(description);
responseCategoryList.setTitle(title);
responseCategoryList.setSecondSubtitle(secondSubtitle);
List<ResponseCategoryListChild> mList = new ArrayList<>();
for(int i = 0;i<jsonArray.length();i++){
JSONObject arrayJSONObject = jsonArray.getJSONObject(i).getJSONObject("musicResponsiveListItemRenderer");
String duration = getJsonTitle(arrayJSONObject.getJSONArray("fixedColumns").getJSONObject(0).getJSONObject("musicResponsiveListItemFixedColumnRenderer").getJSONObject("text"), 0);
String thumbnail = getJsonUrl(arrayJSONObject.getJSONObject("thumbnail"), true);
JSONArray flexColumns = arrayJSONObject
.getJSONArray("flexColumns");
//歌名歌手名字描述
String SongTitle = "";
String SingerName = "";
String Description = "";
for (int g = 0; g < flexColumns.length(); g++) {
String text = getJsonTitle(flexColumns.getJSONObject(g).getJSONObject("musicResponsiveListItemFlexColumnRenderer").getJSONObject("text"), 0);
if (g == 0) {
SongTitle = text;
// JSONObject runs = jsonObject.getJSONArray("runs").getJSONObject(0);
// String[] watchEndPoint = getWatchEndPoint(runs);
//
// responseSingle.setVideoId(watchEndPoint[0]);
// responseSingle.setPlaylistId(watchEndPoint[1]);
// responseSingle.setParams(watchEndPoint[2]);
// responseSingle.setMusicVideoType(watchEndPoint[3]);
}
if (g == 1) SingerName = text;
if (g == 2) Description = text;
}
ResponseCategoryListChild child = new ResponseCategoryListChild();
child.setSongCovert(thumbnail);
child.setSongDuration(duration);
child.setSingerName(SingerName);
child.setSongTitle(SongTitle);
mList.add(child);
CommonUtils.LogMsg("--歌曲"+i+"-------------SongTitle="+SongTitle+"--SingerName="+SingerName+"---Description="+Description+"---duration="+duration+"--thumbnail="+thumbnail);
}
responseCategoryList.setList(mList);
return responseCategoryList;
} catch (JSONException e) {
e.printStackTrace();
return null;
}
}
private static void getCommonHome(JSONObject sectionListRenderer, ResponseHome responseHome) throws JSONException {
JSONObject nextContinuationData = sectionListRenderer.getJSONArray("continuations").getJSONObject(0).getJSONObject("nextContinuationData");
//token=continuation,cit= clickTrackingParams
String clickTrackingParams = nextContinuationData.getString("clickTrackingParams");
String continuation = nextContinuationData.getString("continuation");
CommonUtils.LogMsg("---------参数---clickTrackingParams=" + clickTrackingParams);
CommonUtils.LogMsg("---------参数--continuation=" + continuation);
responseHome.setClickTrackingParams(clickTrackingParams);
responseHome.setContinuation(continuation);
JSONArray contents = sectionListRenderer.getJSONArray("contents");
List<ResponseHomeChild> childList = new ArrayList<>();
for (int i = 0; i < contents.length(); i++) {
ResponseHomeChild responseHomeChild = new ResponseHomeChild();
JSONObject object = contents.getJSONObject(i);
JSONObject musicCarouselShelfRenderer = object.optJSONObject("musicCarouselShelfRenderer");
if (musicCarouselShelfRenderer != null) {
//模块标题
String title = getJsonTitle(musicCarouselShelfRenderer.getJSONObject("header").getJSONObject("musicCarouselShelfBasicHeaderRenderer").getJSONObject("title"), 0);
CommonUtils.LogMsg("----------headertitle=" + title);
responseHomeChild.setHeaderTitle(title);
JSONArray childContents = musicCarouselShelfRenderer.getJSONArray("contents");
List<ResponseCategory> categoryList = new ArrayList<>();
List<ResponseSingle> singleList = new ArrayList<>();
for (int j = 0; j < childContents.length(); j++) {
JSONObject jsonList = childContents.getJSONObject(j);
JSONObject musicResponsiveListItemRenderer = jsonList.optJSONObject("musicResponsiveListItemRenderer");
if (musicResponsiveListItemRenderer != null) {
ResponseSingle responseSingle = new ResponseSingle();
String SingerHead = getJsonUrl(musicResponsiveListItemRenderer.getJSONObject("thumbnail"), true);
JSONArray flexColumns = musicResponsiveListItemRenderer.getJSONArray("flexColumns");
//歌名歌手名字描述
String SongTitle = "";
String SingerName = "";
String Description = "";
for (int g = 0; g < flexColumns.length(); g++) {
JSONObject jsonObject = flexColumns.getJSONObject(g).getJSONObject("musicResponsiveListItemFlexColumnRenderer").getJSONObject("text");
String text = getJsonTitle(jsonObject, 0);
if (g == 0) {
SongTitle = text;
JSONObject runs = jsonObject.getJSONArray("runs").getJSONObject(0);
String[] watchEndPoint = getWatchEndPoint(runs);
responseSingle.setVideoId(watchEndPoint[0]);
responseSingle.setPlaylistId(watchEndPoint[1]);
responseSingle.setParams(watchEndPoint[2]);
responseSingle.setMusicVideoType(watchEndPoint[3]);
}
if (g == 1) SingerName = text;
if (g == 2) Description = text;
}
responseSingle.setSongTitle(SongTitle);
responseSingle.setSingerName(SingerName);
responseSingle.setSingerHead(SingerHead);
responseSingle.setDescription(Description);
singleList.add(responseSingle);
CommonUtils.LogMsg(" ----------111111111111----SongTitle=" + SongTitle + "--SingerName=" + SingerName + "---Description=" + Description + "--SingerHead=" + SingerHead);
}
JSONObject musicTwoRowItemRenderer = jsonList.optJSONObject("musicTwoRowItemRenderer");
if (musicTwoRowItemRenderer != null) {
ResponseCategory responseCategory = new ResponseCategory();
String covert = getJsonUrl(musicTwoRowItemRenderer.getJSONObject("thumbnailRenderer"), true);
JSONObject title1 = musicTwoRowItemRenderer.getJSONObject("title");
String twoTitle = getJsonTitle(title1, 0);
String twoSubtitle = getJsonTextNew(musicTwoRowItemRenderer.getJSONObject("subtitle"));
String browseId = title1
.getJSONArray("runs")
.getJSONObject(0)
.getJSONObject("navigationEndpoint")
.getJSONObject("browseEndpoint")
.getString("browseId");
responseCategory.setBrowseId(browseId);
responseCategory.setCovert(covert);
responseCategory.setTwoTitle(twoTitle);
responseCategory.setTwoSubtitle(twoSubtitle);
categoryList.add(responseCategory);
CommonUtils.LogMsg(" ----------2222222222----twoTitle=" + twoTitle + "-twoSubtitle=" + twoSubtitle);
}
}
responseHomeChild.setCategoryList(categoryList);
responseHomeChild.setSingleList(singleList);
}
childList.add(responseHomeChild);
}
responseHome.setChildList(childList);
}
private static String[] getWatchEndPoint(JSONObject job) {
String[] strings = new String[4];
@ -303,14 +428,12 @@ public class JsonHelper {
}
/**
*
* @param jsonObject
* @param maxBig 是否取最大的一张图片
* @param maxBig 是否取最大的一张图片
* @return
*/
private static String getJsonUrl(JSONObject jsonObject,boolean maxBig) {
private static String getJsonUrl(JSONObject jsonObject, boolean maxBig) {
try {
boolean b = jsonObject.has("musicThumbnailRenderer");
if (b) {
@ -318,9 +441,9 @@ public class JsonHelper {
}
JSONArray jsonArray = jsonObject.getJSONObject("thumbnail").getJSONArray("thumbnails");
int length = jsonArray.length();
int index = 3;
if(maxBig){
index = length - 1;
int index = 0;
if (maxBig) {
index = length - 1;
}
String pngUrl = jsonArray.getJSONObject(index).getString("url");
@ -346,6 +469,11 @@ public class JsonHelper {
}
/**
* runs 数组的字符串拼接
* @param jsonObject
* @return
*/
private static String getJsonTextNew(JSONObject jsonObject) {
StringBuilder text = new StringBuilder();
try {

View File

@ -42,7 +42,7 @@ public interface MusicApi {
//获取播放音频资源url
@POST("youtubei/v1/player?prettyPrint=false")
@Headers({"X-Goog-Api-Key:AIzaSyAO_FJ2SlqU8Q4STEHLGCilw_Y9_11qcW8",
"X-Goog-FieldMask:playabilityStatus.status,playerConfig.audioConfig,streamingData.adaptiveFormats,videoDetails.videoId"})
@ -53,4 +53,11 @@ public interface MusicApi {
@GET("videoplayback?expire=1727100941&ei=rSPxZoSXJ_eM2_gPgqT8mAs&ip=146.19.167.8&id=o-ADGacaLEhb3NOOc74tfR50VCTKy0vnUb2_GAm-tPlv9n&itag=137&source=youtube&requiressl=yes&xpc=EgVo2aDSNQ%3D%3D&mh=f8&mm=31%2C29&mn=sn-tt1e7nlz%2Csn-vgqsknzd&ms=au%2Crdu&mv=m&mvi=2&pl=24&gcr=us&initcwndbps=6450000&vprv=1&svpuc=1&mime=video%2Fmp4&rqh=1&gir=yes&clen=4444925&dur=293.280&lmt=1688643558849956&mt=1727078942&fvip=3&keepalive=yes&fexp=51299152&c=ANDROID_MUSIC&txp=4532434&sparams=expire%2Cei%2Cip%2Cid%2Citag%2Csource%2Crequiressl%2Cxpc%2Cgcr%2Cvprv%2Csvpuc%2Cmime%2Crqh%2Cgir%2Cclen%2Cdur%2Clmt&sig=AJfQdSswRgIhAKjjifMN7NMLqeoVXyqHPK1uHqev1PcnVMoycknt4QGfAiEAiCEcEYPDpQsCbE0tJ6MXjvPs4HmT0yM8Yoa26rWpc7M%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&lsig=ABPmVW0wRAIgeur5lMiKDgdWV5rrRTkmt0jbOQnifmVQwoTXk_Y17E0CIBfjGXpbdW2u3mtu1I-")
Observable<ResponseBody> getTest();
//首页分类项下的播放列表子页面
@POST("youtubei/v1/browse")
@Headers("X-Goog-Api-Key:AIzaSyAO_FJ2SlqU8Q4STEHLGCilw_Y9_11qcW8")
Observable<ResponseBody> getCategoryList(@Body RequestBody requestBody);
}

View File

@ -150,4 +150,18 @@ public class RetrofitManager {
public void getCategoryList(String browserId,RequestListener<ResponseBody> requestListener) {
BodyHome bodyHome = new BodyHome();
bodyHome.setBrowseId(browserId);
Gson gson = new Gson();
String s = gson.toJson(bodyHome);
RequestBody requestBody = RequestBody.Companion.create(s, JSON);
musicApi.getCategoryList(requestBody)
.subscribeOn(Schedulers.io())
.unsubscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new ObserverWrapper<ResponseBody>(requestListener));
}
}

View File

@ -0,0 +1,178 @@
package com.hi.music.player.ui.activity;
import static com.bumptech.glide.request.RequestOptions.bitmapTransform;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.OptIn;
import androidx.appcompat.app.AppCompatActivity;
import androidx.lifecycle.Observer;
import androidx.media3.common.util.UnstableApi;
import androidx.recyclerview.widget.LinearLayoutManager;
import android.content.Intent;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.GradientDrawable;
import android.os.Bundle;
import android.view.View;
import com.bumptech.glide.Glide;
import com.bumptech.glide.load.DataSource;
import com.bumptech.glide.load.engine.GlideException;
import com.bumptech.glide.load.resource.bitmap.BitmapTransitionOptions;
import com.bumptech.glide.load.resource.bitmap.RoundedCorners;
import com.bumptech.glide.request.RequestListener;
import com.bumptech.glide.request.RequestOptions;
import com.bumptech.glide.request.target.Target;
import com.hi.music.player.MusicApplication;
import com.hi.music.player.R;
import com.hi.music.player.adapter.AdapterCategoryList;
import com.hi.music.player.api.onImageColorListener;
import com.hi.music.player.databinding.ActivityCategoryListBinding;
import com.hi.music.player.databinding.ActivityHomeBinding;
import com.hi.music.player.helper.CommonUtils;
import com.hi.music.player.helper.MyValue;
import com.hi.music.player.javabean.response.ResponseCategoryList;
import com.hi.music.player.javabean.response.child.ResponseCategory;
import com.hi.music.player.ui.fragmnt.viewmodel.VMCategoryList;
import com.hi.music.player.ui.fragmnt.viewmodel.VMHome;
import java.io.Serializable;
import jp.wasabeef.glide.transformations.BlurTransformation;
public class CategoryListActivity extends BaseActivity<ActivityCategoryListBinding> {
private VMCategoryList vm;
@Override
protected ActivityCategoryListBinding getViewBinding() {
return ActivityCategoryListBinding.inflate(getLayoutInflater());
}
@Override
protected void onCreateInit() {
vm = getActivityScopeViewModel(VMCategoryList.class);
Intent intent = getIntent();
if (intent == null) {
return;
}
if (!intent.hasExtra(MyValue.KEY_PLAY_ACTIVITY_CATEGORY)) {
return;
}
ResponseCategory category = (ResponseCategory) intent.getSerializableExtra(MyValue.KEY_PLAY_ACTIVITY_CATEGORY);
if (category == null)
return;
vb.pbLoading.setVisibility(View.VISIBLE);
vm.getList(category.getBrowseId());
vm.data.observe(this, new Observer<ResponseCategoryList>() {
@Override
public void onChanged(ResponseCategoryList responseCategoryList) {
if (responseCategoryList == null) {
return;
}
loadInfo(responseCategoryList);
}
});
}
@Override
protected void onInitClick() {
vb.imBack.setOnClickListener(this);
}
@Override
public boolean isFullScreen() {
return true;
}
@Override
public boolean statusBarLight() {
return false;
}
@Override
public void onClick(View v) {
if (v.equals(vb.imBack)) {
finish();
}
}
private void loadInfo(ResponseCategoryList info) {
vb.tvTitle.setText(info.getTitle());
vb.tvSubTitle.setText(info.getSecondSubtitle());
AdapterCategoryList adapterCategoryList = new AdapterCategoryList();
vb.recyclerview.setLayoutManager(new LinearLayoutManager(this));
adapterCategoryList.setData(info.getList());
vb.recyclerview.setAdapter(adapterCategoryList);
loadCovert(info.getCovert());
}
private void loadCovert(String url) {
// 加载图片并应用高斯模糊效果
Glide.with(this)
.load(url)
.apply(bitmapTransform(new BlurTransformation(25, 3))) // 设置模糊半径和模糊采样
.listener(new RequestListener<Drawable>() {
@Override
public boolean onLoadFailed(@Nullable GlideException e, @Nullable Object model, @NonNull Target<Drawable> target, boolean isFirstResource) {
return false;
}
@Override
public boolean onResourceReady(@NonNull Drawable resource, @NonNull Object model, Target<Drawable> target, @NonNull DataSource dataSource, boolean isFirstResource) {
// CommonUtils.extractColorsFromImage(resource,vb.imBg);
return false;
}
})
.preload();
Glide.with(MusicApplication.myApplication)
.asDrawable()
.apply(bitmapTransform(new RoundedCorners(CommonUtils.dpToPx(10))))
.load(url)
.placeholder(R.mipmap.ic_launcher)
.listener(new RequestListener<Drawable>() {
@Override
public boolean onLoadFailed(@Nullable GlideException e, @Nullable Object model, @NonNull Target<Drawable> target, boolean isFirstResource) {
CommonUtils.LogMsg(e.getMessage());
vb.pbLoading.setVisibility(View.GONE);
return false;
}
@OptIn(markerClass = UnstableApi.class)
@Override
public boolean onResourceReady(@NonNull Drawable resource, @NonNull Object model, Target<Drawable> target, @NonNull DataSource dataSource, boolean isFirstResource) {
// CommonUtils.getDominantDarkColor1(resource, new onImageColorListener() {
// @Override
// public void onImageColor(int color) {
// if (color == -1) {
// return;
// }
//
// }
// });
vb.pbLoading.setVisibility(View.GONE);
return false;
}
})
.into(vb.imCovert);
}
}

View File

@ -91,6 +91,7 @@ public class PlayActivity extends BaseActivity<ActivityPlayBinding> implements S
responseSingle = (ResponseSingle) intent.getSerializableExtra(MyValue.KEY_PLAY_ACTIVITY_SINGER);
vmPlay = getActivityScopeViewModel(VMPlay.class);
initMediaController();
vb.progressBarLoading.setVisibility(View.VISIBLE);
vmPlay.getPlayMusicList(responseSingle);
initPlayerView();
@ -108,7 +109,6 @@ public class PlayActivity extends BaseActivity<ActivityPlayBinding> implements S
if (playList.size() > 0) {
mPlayList = playList;
musicInfo = playList.get(0);
}
}

View File

@ -1,6 +1,7 @@
package com.hi.music.player.ui.fragmnt;
import android.content.Intent;
import android.content.RestrictionsManager;
import androidx.annotation.NonNull;
import androidx.fragment.app.FragmentActivity;
@ -9,24 +10,33 @@ import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.hi.music.player.adapter.AdapterHome;
import com.hi.music.player.api.ClickSingerListener;
import com.hi.music.player.api.HomeItemClickListener;
import com.hi.music.player.api.OnHasUrlAction;
import com.hi.music.player.api.RequestListener;
import com.hi.music.player.databinding.FragmentHomeBinding;
import com.hi.music.player.helper.CommonUtils;
import com.hi.music.player.helper.MyValue;
import com.hi.music.player.javabean.response.ResponseHome;
import com.hi.music.player.javabean.response.child.ResponseCategory;
import com.hi.music.player.javabean.response.child.ResponseHomeChild;
import com.hi.music.player.javabean.response.child.ResponseSingle;
import com.hi.music.player.network.RetrofitManager;
import com.hi.music.player.ui.activity.CategoryListActivity;
import com.hi.music.player.ui.activity.PlayActivity;
import com.hi.music.player.ui.fragmnt.viewmodel.VMHome;
import org.json.JSONObject;
import java.util.ArrayList;
import java.util.List;
public class HomeFragment extends BaseFragment<FragmentHomeBinding> implements ClickSingerListener<ResponseSingle> {
import okhttp3.ResponseBody;
public class HomeFragment extends BaseFragment<FragmentHomeBinding> implements HomeItemClickListener {
private VMHome vmHome;
private AdapterHome adapterHome;
private int requestCount = 1;
@ -47,7 +57,8 @@ public class HomeFragment extends BaseFragment<FragmentHomeBinding> implements C
// //标题导航栏颜色设置
// Vb.toolbar.setTitleTextColor(getResources().getColor(R.color.white));
AdapterHome adapterHome = new AdapterHome();
adapterHome.setSingleClickSingerListener(this::onClickSingerItem);
adapterHome.setHomeItemClickListener(this);
vmHome = getFragmentScopeViewModel(VMHome.class);
adapterHome.addLoadingFooter();
@ -87,15 +98,24 @@ public class HomeFragment extends BaseFragment<FragmentHomeBinding> implements C
}
@Override
public void onClickSingerItem(ResponseSingle data) {
public void onClickItemSinger(ResponseSingle data) {
FragmentActivity activity = getActivity();
if(activity!= null){
Intent intent = new Intent(activity, PlayActivity.class);
intent.putExtra(MyValue.KEY_PLAY_ACTIVITY_SINGER, data);
startActivity(intent);
}
}
@Override
public void onClickItemCategory(ResponseCategory data) {
FragmentActivity activity = getActivity();
if(activity!= null){
Intent intent = new Intent(activity, CategoryListActivity.class);
intent.putExtra(MyValue.KEY_PLAY_ACTIVITY_CATEGORY, data);
startActivity(intent);
}
}
}

View File

@ -0,0 +1,48 @@
package com.hi.music.player.ui.fragmnt.viewmodel;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModel;
import com.hi.music.player.api.RequestListener;
import com.hi.music.player.helper.CommonUtils;
import com.hi.music.player.javabean.response.ResponseCategoryList;
import com.hi.music.player.javabean.response.ResponseHome;
import com.hi.music.player.javabean.response.ResponsePlayUrl;
import com.hi.music.player.network.JsonHelper;
import com.hi.music.player.network.RetrofitManager;
import org.json.JSONObject;
import okhttp3.ResponseBody;
public class VMCategoryList extends ViewModel {
private MutableLiveData<ResponseCategoryList> _data = new MutableLiveData<>();
public LiveData<ResponseCategoryList> data = _data;
public void getList(String browseId) {
RetrofitManager.getInstance().getCategoryList(browseId, new RequestListener<ResponseBody>() {
@Override
public void onFail(String errorMsg) {
_data.setValue(null);
}
@Override
public void onSuccess(ResponseBody data) {
JSONObject jsonObject = CommonUtils.toJsonObject(data);
if (jsonObject != null) {
ResponseCategoryList responseCategoryList = JsonHelper.ResolveCategoryList(jsonObject);
_data.setValue(responseCategoryList);
}
}
});
}
}

View File

@ -0,0 +1,121 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
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.CategoryListActivity">
<ImageView
android:id="@+id/im_bg"
android:layout_width="match_parent"
android:layout_height="200dp" />
<!-- <com.google.android.material.appbar.AppBarLayout-->
<!-- android:layout_width="match_parent"-->
<!-- android:layout_height="60dp"-->
<!-- app:statusBarForeground="@color/color_transparent"-->
<!-- android:background="@color/color_transparent">-->
<!-- <com.google.android.material.appbar.CollapsingToolbarLayout-->
<!-- android:layout_width="match_parent"-->
<!-- android:layout_height="60dp"-->
<!-- app:contentScrim="@color/black"-->
<!-- app:layout_scrollFlags="scroll|exitUntilCollapsed">-->
<!-- </com.google.android.material.appbar.CollapsingToolbarLayout>-->
<!-- </com.google.android.material.appbar.AppBarLayout>-->
<androidx.core.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<RelativeLayout
android:id="@+id/layout_covert"
android:layout_marginTop="60dp"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/imCovert"
android:layout_width="250dp"
android:layout_height="220dp"
android:layout_centerHorizontal="true" />
<TextView
android:id="@+id/tvTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/imCovert"
android:layout_centerHorizontal="true"
android:layout_marginTop="10dp"
android:textColor="@color/white"
android:textSize="18sp" />
<TextView
android:id="@+id/tvSubTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/tvTitle"
android:layout_centerHorizontal="true"
android:layout_marginTop="5dp"
android:textColor="@color/white"
android:textSize="14sp" />
<ImageView
android:id="@+id/btn_play"
android:layout_width="66dp"
android:layout_height="66dp"
android:layout_below="@id/tvSubTitle"
android:layout_centerHorizontal="true"
android:layout_marginTop="15dp"
android:src="@drawable/selector_icon_play" />
<ProgressBar
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:indeterminateTint="@color/white"
android:id="@+id/pb_loading"/>
</RelativeLayout>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerview"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
</androidx.core.widget.NestedScrollView>
<FrameLayout
android:layout_width="match_parent"
android:background="@color/black"
android:layout_height="60dp">
<ImageView
android:id="@+id/im_back"
android:layout_width="42dp"
android:layout_height="match_parent"
android:layout_marginStart="16dp"
android:padding="9dp"
android:src="@drawable/arrow_bottom" />
</FrameLayout>
</FrameLayout>

View File

@ -0,0 +1,40 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="70dp"
android:paddingStart="15dp"
android:paddingTop="5dp"
android:paddingBottom="5dp">
<ImageView
android:id="@+id/image"
android:layout_width="60dp"
android:layout_height="60dp" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toEndOf="@id/image"
android:layout_marginStart="12dp"
android:layout_centerVertical="true"
android:orientation="vertical">
<TextView
android:id="@+id/tv_song_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/white"
android:textSize="15sp"
android:text="@string/app_name"/>
<TextView
android:id="@+id/tv_singer_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/white_60_color"
android:text="@string/app_name"
android:textSize="12sp" />
</LinearLayout>
</RelativeLayout>

View File

@ -3,6 +3,7 @@
<style name="Base.Theme.MusicApp" parent="Theme.Material3.DayNight.NoActionBar">
<!-- Customize your light theme here. -->
<!-- <item name="colorPrimary">@color/my_light_primary</item> -->
<item name="android:windowBackground">@color/black</item>
</style>
<style name="Theme.MusicApp" parent="Base.Theme.MusicApp" />