diff --git a/app/src/main/java/com/hi/music/player/MusicApplication.java b/app/src/main/java/com/hi/music/player/MusicApplication.java new file mode 100644 index 0000000..c855d88 --- /dev/null +++ b/app/src/main/java/com/hi/music/player/MusicApplication.java @@ -0,0 +1,57 @@ +package com.hi.music.player; + +import android.app.Application; +import android.content.Context; + +import androidx.annotation.OptIn; +import androidx.media3.common.util.UnstableApi; +import androidx.media3.database.StandaloneDatabaseProvider; +import androidx.media3.datasource.cache.LeastRecentlyUsedCacheEvictor; +import androidx.media3.datasource.cache.SimpleCache; + +import com.hi.music.player.api.MediaControllerStatusListener; +import com.hi.music.player.firebase.RemoteConfigJava; +import com.hi.music.player.firebase.Sp; +import com.hi.music.player.helper.CommonUtils; +import com.hi.music.player.media3.MyDownloadService; +import com.hi.music.player.media3.MyMediaControllerManager; +import com.hi.music.player.objectbox.ObjectBoxManager; + +public class MusicApplication extends Application { + + public static Context myApplication; + + public static String visitorData; + + + + + public static void setVisitorData(String visitorData) { + MusicApplication.visitorData = visitorData; + } + + public static String getVisitorData() { + return visitorData; + } + + @OptIn(markerClass = UnstableApi.class) + @Override + public void onCreate() { + super.onCreate(); + myApplication = this; + Sp.init(this); + RemoteConfigJava.getInstance().init(this); + ObjectBoxManager.init(this); + + StandaloneDatabaseProvider databaseProvider = new StandaloneDatabaseProvider(this); + MyDownloadService.init(this,databaseProvider); + MyMediaControllerManager.getInstance().init(new MediaControllerStatusListener() { + @Override + public void onMediaControllerComplete(boolean isOk) { + CommonUtils.LogMsg("=-----mediaController+" + isOk); + } + }); + } + + +} diff --git a/app/src/main/java/com/hi/music/player/adapter/AdapterCategory.java b/app/src/main/java/com/hi/music/player/adapter/AdapterCategory.java new file mode 100644 index 0000000..ba7912d --- /dev/null +++ b/app/src/main/java/com/hi/music/player/adapter/AdapterCategory.java @@ -0,0 +1,78 @@ +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; +import androidx.annotation.Nullable; +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.databinding.ItemCategoryBinding; +import com.hi.music.player.helper.CommonUtils; +import com.hi.music.player.helper.MyValue; +import com.hi.music.player.javabean.response.child.ResponseCategory; + +public class AdapterCategory extends BaseAdapter{ + + @Override + protected ItemCategoryBinding getViewBinding(ViewGroup parent) { + return ItemCategoryBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false); + } + + @Override + public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) { + + int itemViewType = getItemViewType(position); + if (itemViewType == TYPE_ITEM) { + VHolder itemHolder = (VHolder) holder; + ItemCategoryBinding vb = itemHolder.getVb(); + ResponseCategory responseCategory = data.get(position); + String pageType = responseCategory.getPageType(); + Glide.with(MusicApplication.myApplication) + .asDrawable() + .load(responseCategory.getCovert()) + .placeholder(R.mipmap.im_placeholder) + .into(vb.header); + vb.tvTitle.setText(responseCategory.getTwoTitle()); + vb.tvSubtitle.setText(responseCategory.getTwoSubtitle()); + CommonUtils.LogMsg("title="+responseCategory.getTwoTitle()+"----------pageType="+pageType + +"---browserId="+responseCategory.getBrowseId()+"---videoId="+responseCategory.getVideoId()+"---playListId="+responseCategory.getPlayListId()); + if(pageType == null){ + return; + } +// if(pageType.equals(MyValue.PAGE_TYPE_MV)||pageType.equals(MyValue.PAGE_TYPE_MV_LIST)){ +// ViewGroup.LayoutParams layoutParams = vb.header.getLayoutParams(); +// layoutParams.width = CommonUtils.dpToPx(280); +// vb.header.setLayoutParams(layoutParams); +// vb.header.setBackgroundResource(R.drawable.bg_black_13); +// +// }else { +// ViewGroup.LayoutParams layoutParams = vb.header.getLayoutParams(); +// layoutParams.width = CommonUtils.dpToPx(170); +// vb.header.setLayoutParams(layoutParams); +// vb.header.setBackground(null); +// } + + + vb.header.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (homeItemClickListener != null) { + homeItemClickListener.onClickItemCategory(responseCategory); + } + } + }); + } + } +} diff --git a/app/src/main/java/com/hi/music/player/adapter/AdapterCategoryList.java b/app/src/main/java/com/hi/music/player/adapter/AdapterCategoryList.java new file mode 100644 index 0000000..3d8d62f --- /dev/null +++ b/app/src/main/java/com/hi/music/player/adapter/AdapterCategoryList.java @@ -0,0 +1,105 @@ +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; +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.helper.MyValue; +import com.hi.music.player.javabean.response.ResponsePlayListInfo; +import com.hi.music.player.javabean.response.child.ResponseCategoryListChild; +import com.hi.music.player.javabean.response.child.ResponseSingle; + +public class AdapterCategoryList extends BaseAdapter { + + + private String mPageType; + + public void setPageType(String mPageType) { + this.mPageType = mPageType; + } + + @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 itemHolder = (VHolder) holder; + ResponsePlayListInfo child = data.get(position); + + ItemCategoryListBinding vb = itemHolder.getVb(); + vb.tvSongName.setText(child.getSongTitle()); + vb.tvDuration.setText(child.getDuration()); + String singerName = child.getSingerName(); + String describe = child.getDescribe(); + + switch (mPageType){ + case MyValue.PAGE_TYPE_ALBUM: + vb.tvSingerName.setText(describe); + vb.imCard.setVisibility(View.GONE); + vb.tvPosition.setVisibility(View.VISIBLE); + vb.tvPosition.setText(String.valueOf(position+1)); + break; + default: + vb.tvSingerName.setText(singerName); + + vb.imCard.setVisibility(View.VISIBLE); + vb.tvPosition.setVisibility(View.GONE); + + Glide.with(MusicApplication.myApplication) + .asDrawable() + .load(child.getSmallCovert()) + .placeholder(R.mipmap.im_placeholder) + .listener(new RequestListener() { + @Override + public boolean onLoadFailed(@Nullable GlideException e, @Nullable Object model, @NonNull Target target, boolean isFirstResource) { + CommonUtils.LogMsg(e.getMessage()); + return false; + } + + @OptIn(markerClass = UnstableApi.class) + @Override + public boolean onResourceReady(@NonNull Drawable resource, @NonNull Object model, Target target, @NonNull DataSource dataSource, boolean isFirstResource) { + return false; + } + }) + .into(vb.image); + break; + } + vb.getRoot().setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + homeItemClickListener.onClickItemCategoryList(child, itemHolder.getAbsoluteAdapterPosition()); + } + }); + + if(position == data.size() - 1){ + vb.place.setVisibility(View.VISIBLE); + }else { + vb.place.setVisibility(View.GONE); + } + } + + +} diff --git a/app/src/main/java/com/hi/music/player/adapter/AdapterDownloadSong.java b/app/src/main/java/com/hi/music/player/adapter/AdapterDownloadSong.java new file mode 100644 index 0000000..bcbedb4 --- /dev/null +++ b/app/src/main/java/com/hi/music/player/adapter/AdapterDownloadSong.java @@ -0,0 +1,81 @@ +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; +import androidx.annotation.Nullable; +import androidx.media3.exoplayer.offline.Download; +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.databinding.ItemLikeSongBinding; +import com.hi.music.player.helper.CommonUtils; +import com.hi.music.player.javabean.BoxDownloadSong; +import com.hi.music.player.javabean.BoxLikeSong; + +public class AdapterDownloadSong extends BaseAdapter { + + @Override + protected ItemLikeSongBinding getViewBinding(ViewGroup parent) { + return ItemLikeSongBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false); + } + + @Override + public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) { + VHolder itemHolder = (VHolder) holder; + ItemLikeSongBinding vb = itemHolder.getVb(); + Download download = data.get(position); + BoxDownloadSong boxDownloadSong = CommonUtils.downloadToBean(download); + + if (position == data.size()-1){ + vb.place.setVisibility(View.VISIBLE); + }else { + vb.place.setVisibility(View.GONE); + } + vb.layoutDownload.setVisibility(View.GONE); + Glide.with(MusicApplication.myApplication) + .asDrawable() + .load(boxDownloadSong.getCovert()) + .apply(RequestOptions.bitmapTransform(new RoundedCorners(CommonUtils.dpToPx(4)))) + .placeholder(R.mipmap.im_placeholder) + .listener(new RequestListener() { + @Override + public boolean onLoadFailed(@Nullable GlideException e, @Nullable Object model, @NonNull Target target, boolean isFirstResource) { + CommonUtils.LogMsg(e.getMessage()); + return false; + } + + @Override + public boolean onResourceReady(@NonNull Drawable resource, @NonNull Object model, Target target, @NonNull DataSource dataSource, boolean isFirstResource) { + return false; + } + }) + .into(vb.imCovert); + vb.tvTitle.setText(boxDownloadSong.getSongName()); + vb.tvSingerName.setText(boxDownloadSong.getSingerName()); + + vb.getRoot().setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if(homeItemClickListener!= null){ + int absoluteAdapterPosition = itemHolder.getAbsoluteAdapterPosition(); + homeItemClickListener.onClickDownloadSong(download,absoluteAdapterPosition); + } + } + }); + + } + + +} diff --git a/app/src/main/java/com/hi/music/player/adapter/AdapterHome.java b/app/src/main/java/com/hi/music/player/adapter/AdapterHome.java new file mode 100644 index 0000000..d395737 --- /dev/null +++ b/app/src/main/java/com/hi/music/player/adapter/AdapterHome.java @@ -0,0 +1,84 @@ +package com.hi.music.player.adapter; + +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.FrameLayout; + +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.GridLayoutManager; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; + +import com.hi.music.player.MusicApplication; +import com.hi.music.player.databinding.ItemFooterLoadingBinding; +import com.hi.music.player.databinding.ItemHomeBinding; +import com.hi.music.player.helper.CommonUtils; +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 java.util.List; + +public class AdapterHome extends BaseAdapter { + + @Override + protected ItemHomeBinding getViewBinding(ViewGroup parent) { + + return ItemHomeBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false); + } + + @Override + public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) { + int itemViewType = getItemViewType(position); + if (itemViewType == TYPE_ITEM) { + VHolder itemHolder = (VHolder) holder; + ItemHomeBinding vb = itemHolder.getVb(); + ResponseHomeChild responseHomeChild = data.get(position); + vb.headTitle.setText(responseHomeChild.getHeaderTitle()); + + List singleList = responseHomeChild.getSingleList(); + List categoryList = responseHomeChild.getCategoryList(); + CommonUtils.LogMsg("position="+position+"-----------headTitle-=" + responseHomeChild.getHeaderTitle()+"--singleList="+singleList+"---categoryList="+categoryList); + if (singleList != null && singleList.size() > 0) { + vb.recyclerSinger.setVisibility(View.VISIBLE); + AdapterSinger adapterSinger = new AdapterSinger(); + adapterSinger.setHomeItemClickListener(homeItemClickListener); + adapterSinger.addData(singleList); + vb.recyclerSinger.setLayoutManager(new GridLayoutManager(MusicApplication.myApplication, 4, RecyclerView.HORIZONTAL, false)); + vb.recyclerSinger.setAdapter(adapterSinger); + } else { + vb.recyclerSinger.setVisibility(View.GONE); + } + + if (categoryList != null && categoryList.size() > 0) { + vb.recyclerCategory.setVisibility(View.VISIBLE); + AdapterCategory adapterCategory = new AdapterCategory(); + adapterCategory.setHomeItemClickListener(homeItemClickListener); + adapterCategory.addData(categoryList); + vb.recyclerCategory.setLayoutManager(new LinearLayoutManager(MusicApplication.myApplication, RecyclerView.HORIZONTAL, false)); + vb.recyclerCategory.setAdapter(adapterCategory); + } else { + vb.recyclerCategory.setVisibility(View.GONE); + } + + if(position == data.size() - 1){ + vb.place.setVisibility(View.VISIBLE); + }else { + vb.place.setVisibility(View.GONE); + } + } else { + VHolder footerHolder = (VHolder) holder; + FrameLayout root = footerHolder.getVb().getRoot(); + if (isLoadingAdded) { + root.setVisibility(View.VISIBLE); + } else { + root.setVisibility(View.GONE); + } + + } + + } + + +} diff --git a/app/src/main/java/com/hi/music/player/adapter/AdapterLikeSong.java b/app/src/main/java/com/hi/music/player/adapter/AdapterLikeSong.java new file mode 100644 index 0000000..0ef3cde --- /dev/null +++ b/app/src/main/java/com/hi/music/player/adapter/AdapterLikeSong.java @@ -0,0 +1,215 @@ +package com.hi.music.player.adapter; + +import android.content.Context; +import android.graphics.drawable.Drawable; +import android.net.Uri; +import android.util.Pair; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Toast; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.annotation.OptIn; +import androidx.lifecycle.Observer; +import androidx.media3.common.util.UnstableApi; +import androidx.media3.exoplayer.offline.Download; +import androidx.media3.exoplayer.offline.DownloadRequest; +import androidx.media3.exoplayer.offline.DownloadService; +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.google.gson.Gson; +import com.hi.music.player.MusicApplication; +import com.hi.music.player.R; +import com.hi.music.player.api.onCheckDownload; +import com.hi.music.player.databinding.ItemLikeSongBinding; +import com.hi.music.player.databinding.ItemSingerBinding; +import com.hi.music.player.helper.CommonUtils; +import com.hi.music.player.javabean.BoxDownloadSong; +import com.hi.music.player.javabean.BoxLikeSong; +import com.hi.music.player.javabean.CustomerDownload; +import com.hi.music.player.javabean.response.ResponsePlayUrl; +import com.hi.music.player.javabean.response.child.ResponseSingle; +import com.hi.music.player.media3.MyDownloadService; +import com.hi.music.player.media3.MyMediaControllerManager; +import com.hi.music.player.network.JsonHelper; +import com.hi.music.player.network.RetrofitManager; +import com.hi.music.player.ui.activity.viewmodel.VMApplication; + +import org.json.JSONObject; + +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.List; + +import okhttp3.ResponseBody; + +public class AdapterLikeSong extends BaseAdapter { + + private VMApplication vmApplication; + + private List> status = new ArrayList<>(); + + @Override + protected ItemLikeSongBinding getViewBinding(ViewGroup parent) { + return ItemLikeSongBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false); + } + + + public AdapterLikeSong(Context mContext, VMApplication vm) { + super(mContext); + vmApplication = vm; + + + } + + public void updateDownloadStatus(boolean isSuccess, int position, String videoId) { + status.add(new Pair<>(isSuccess, videoId)); + notifyItemChanged(position); + } + + @OptIn(markerClass = UnstableApi.class) + @Override + public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) { + VHolder itemHolder = (VHolder) holder; + ItemLikeSongBinding vb = itemHolder.getVb(); + BoxLikeSong boxLikeSong = data.get(position); + String videoId = boxLikeSong.getVideoId(); + + if (position == data.size()-1){ + vb.place.setVisibility(View.VISIBLE); + }else { + vb.place.setVisibility(View.GONE); + } + + + for(Pair pair:status){ + if(pair.second.equals(videoId)){ + vb.imDownload.setSelected(pair.first); + vb.imDownload.setVisibility(View.VISIBLE); + vb.downloadPb.setVisibility(View.GONE); + CommonUtils.LogMsg("--------------------状态更新"); + } + } + + MyDownloadService.queryIsDownload(videoId, new onCheckDownload() { + @Override + public void onHasDownload(CustomerDownload customerDownload) { + boolean download = customerDownload.isDownload(); + if (download) { + vb.imDownload.setSelected(true); + } else { + vb.imDownload.setSelected(false); + } + } + }); + vb.layoutDownload.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (vb.imDownload.isSelected()) { + //已经下载 + Toast.makeText(mContext,mContext.getText(R.string.text_has_downloaded),Toast.LENGTH_SHORT).show(); + return; + } + + vb.imDownload.setVisibility(View.GONE); + vb.downloadPb.setVisibility(View.VISIBLE); + RetrofitManager.getInstance().getPlayUrl(videoId, new com.hi.music.player.api.RequestListener() { + + @Override + public void onFail(String errorMsg) { + CommonUtils.LogMsg("-------------onFail"); + + } + + @Override + public void onSuccess(JSONObject data) { +// JSONObject jsonObject = CommonUtils.toJsonObject(data); + if (data != null) { + ResponsePlayUrl responsePlayUrl = JsonHelper.ResolvePlayUrlJson(data); + if (responsePlayUrl == null) { + // TODO: 2024/9/27 + return; + } + String videoUrlMedium = responsePlayUrl.getVideoUrlMedium(); + + BoxDownloadSong downloadSong = new BoxDownloadSong(); + downloadSong.setVideoId(videoId); + downloadSong.setCovert(String.valueOf(boxLikeSong.getCovert())); + downloadSong.setSongName((String) boxLikeSong.getSongName()); + downloadSong.setSingerName((String) boxLikeSong.getSingerName()); + downloadSong.setDuration((String) boxLikeSong.getDuration()); + downloadSong.setDurationMs(boxLikeSong.getDurationMs()); + downloadSong.setVideoUrl(videoUrlMedium); + Gson gson = new Gson(); + String info = gson.toJson(downloadSong); + byte[] bytes = info.getBytes(StandardCharsets.UTF_8); + + DownloadRequest downloadRequest = new DownloadRequest.Builder(videoId, Uri.parse(videoUrlMedium)) + .setMimeType("video/mp4") + .setData(bytes) + .build(); + + + DownloadService.sendAddDownload( + mContext, + MyDownloadService.class, // 上面定义的下载服务类 + downloadRequest, + true // 是否在前台运行 + ); + if (homeItemClickListener != null) { + homeItemClickListener.onDownloadSong(videoId, itemHolder.getAbsoluteAdapterPosition()); + } + + } + + + } + }); + + + } + }); + Glide.with(MusicApplication.myApplication) + .asDrawable() + .load(boxLikeSong.getCovert()) + .apply(RequestOptions.bitmapTransform(new RoundedCorners(CommonUtils.dpToPx(4)))) + .placeholder(R.mipmap.im_placeholder) + .listener(new RequestListener() { + @Override + public boolean onLoadFailed(@Nullable GlideException e, @Nullable Object model, @NonNull Target target, boolean isFirstResource) { + CommonUtils.LogMsg(e.getMessage()); + return false; + } + + @Override + public boolean onResourceReady(@NonNull Drawable resource, @NonNull Object model, Target target, @NonNull DataSource dataSource, boolean isFirstResource) { + return false; + } + }) + .into(vb.imCovert); + vb.tvTitle.setText(boxLikeSong.getSongName()); + vb.tvSingerName.setText(boxLikeSong.getSingerName()); + + vb.getRoot().setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (homeItemClickListener != null) { + int absoluteAdapterPosition = itemHolder.getAbsoluteAdapterPosition(); + homeItemClickListener.onClickLikeSong(boxLikeSong, absoluteAdapterPosition); + } + } + }); + + } + + +} diff --git a/app/src/main/java/com/hi/music/player/adapter/AdapterPlayList.java b/app/src/main/java/com/hi/music/player/adapter/AdapterPlayList.java new file mode 100644 index 0000000..28df28d --- /dev/null +++ b/app/src/main/java/com/hi/music/player/adapter/AdapterPlayList.java @@ -0,0 +1,118 @@ +package com.hi.music.player.adapter; + +import static androidx.media3.session.legacy.MediaControllerCompat.getMediaController; + +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import androidx.annotation.NonNull; +import androidx.media3.common.MediaItem; +import androidx.recyclerview.widget.RecyclerView; + +import com.bumptech.glide.Glide; +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.helper.MyValue; +import com.hi.music.player.javabean.response.ResponsePlayListInfo; +import com.hi.music.player.media3.MyMediaControllerManager; + +public class AdapterPlayList extends BaseAdapter { + + private String mCurVideId; + + private String lastVideId; + + // private int curMusicPos = 0; + private MyMediaControllerManager instance = MyMediaControllerManager.getInstance(); + + @Override + protected ItemPlayListBinding getViewBinding(ViewGroup parent) { + return ItemPlayListBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false); + } + + public void setCurVideId(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() { + int curIndex = instance.getCurIndex(); + CommonUtils.LogMsg("--curIndex=" + curIndex); + notifyItemChanged(curIndex); + } + + @Override + public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) { + ResponsePlayListInfo listInfo = data.get(position); + VHolder itemHolder = (VHolder) holder; + ItemPlayListBinding vb = itemHolder.getVb(); + + vb.songName.setText(listInfo.getSongTitle()); + String singerName = listInfo.getSingerName(); + String describe = listInfo.getDescribe(); + if (singerName != null && !singerName.isEmpty()) { + vb.artistName.setText(singerName); + } else if (describe != null && !describe.isEmpty()) { + vb.artistName.setText(describe); + } + + String covert; + String small = listInfo.getSmallCovert(); + String covert1 = listInfo.getCovert(); + if (small == null || small.isEmpty()) { + covert = covert1; + } else { + covert = small; + } + Glide.with(MusicApplication.myApplication) + .asDrawable() +// .apply(RequestOptions.bitmapTransform(new RoundedCorners(CommonUtils.dpToPx(10)))) + .load(covert) + .placeholder(R.mipmap.im_placeholder) + .into(vb.imCovert); + + MediaItem currentMediaItem = instance.getCurMediaItem(); + if (currentMediaItem != null && currentMediaItem.mediaId.equals(listInfo.getVideoId())) { + vb.viewPlaying.setVisibility(View.VISIBLE); + vb.itemLayout.setBackgroundColor(CommonUtils.getMyColor(R.color.cur_play_music)); + if (instance.getIsPlaying()) { + vb.viewPlaying.startAnimating(); + lastVideId = listInfo.getVideoId(); + CommonUtils.LogMsg("-------playAnimation " + itemHolder.getAbsoluteAdapterPosition()); + } else { + vb.viewPlaying.pauseAnimating(); + CommonUtils.LogMsg("-------pauseAnimation " + itemHolder.getAbsoluteAdapterPosition()); + } + } else { + vb.viewPlaying.setVisibility(View.GONE); + vb.itemLayout.setBackgroundColor(CommonUtils.getMyColor(R.color.color_transparent)); + + } + + vb.itemLayout.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + CommonUtils.LogMsg("-------onClick curMusicPos=" + itemHolder.getAbsoluteAdapterPosition()); + setCurVideId(listInfo.getVideoId()); + instance.playPositionMusic(itemHolder.getAbsoluteAdapterPosition()); + + } + }); + } +} diff --git a/app/src/main/java/com/hi/music/player/adapter/AdapterResultListAlbum.java b/app/src/main/java/com/hi/music/player/adapter/AdapterResultListAlbum.java new file mode 100644 index 0000000..50139f7 --- /dev/null +++ b/app/src/main/java/com/hi/music/player/adapter/AdapterResultListAlbum.java @@ -0,0 +1,81 @@ +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; +import androidx.annotation.Nullable; +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.databinding.ItemResultListAlbumBinding; +import com.hi.music.player.helper.CommonUtils; +import com.hi.music.player.javabean.response.child.ResponseResultListChild; + +public class AdapterResultListAlbum extends BaseAdapter{ + @Override + protected ItemResultListAlbumBinding getViewBinding(ViewGroup parent) { + return ItemResultListAlbumBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false); + } + + @Override + public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) { + ResponseResultListChild listChild = data.get(position); + + + VHolder itemHolder = (VHolder) holder; + ItemResultListAlbumBinding vb = itemHolder.getVb(); + if(position == data.size()-1){ + vb.view.setVisibility(View.VISIBLE); + }else { + vb.view.setVisibility(View.GONE); + } + Glide.with(MusicApplication.myApplication) + .asDrawable() + .load(listChild.getThumbnail()) + .apply(RequestOptions.bitmapTransform(new RoundedCorners(CommonUtils.dpToPx(4)))) + .placeholder(R.mipmap.im_placeholder) + .listener(new RequestListener() { + @Override + public boolean onLoadFailed(@Nullable GlideException e, @Nullable Object model, @NonNull Target target, boolean isFirstResource) { + CommonUtils.LogMsg(e.getMessage()); + return false; + } + + @Override + public boolean onResourceReady(@NonNull Drawable resource, @NonNull Object model, Target target, @NonNull DataSource dataSource, boolean isFirstResource) { + return false; + } + }) + .into(vb.header); + vb.tvTitle.setText(listChild.getSongName()); + vb.tvSubtitle.setText(listChild.getSubTitle()); + vb.header.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if(homeItemClickListener != null){ + int absoluteAdapterPosition = itemHolder.getAbsoluteAdapterPosition(); + String videoId = listChild.getVideoId(); + String browserId = listChild.getBrowserId(); + homeItemClickListener.onClickResultSong(listChild,absoluteAdapterPosition); +// if(videoId == null||videoId.isEmpty()){ +// homeItemClickListener.onClickResultAlbum(listChild,absoluteAdapterPosition); +// }else if(browserId!= null&&!browserId.isEmpty()){ +// homeItemClickListener.onClickResultSong(listChild,absoluteAdapterPosition); +// } + } + } + }); + CommonUtils.LogMsg("-------Album getBrowserId="+listChild.getBrowserId()+"---getPlayListId= "+listChild.getPlayListId()+"---getVideoId="+listChild.getVideoId()); + } +} diff --git a/app/src/main/java/com/hi/music/player/adapter/AdapterResultListSong.java b/app/src/main/java/com/hi/music/player/adapter/AdapterResultListSong.java new file mode 100644 index 0000000..b005fc8 --- /dev/null +++ b/app/src/main/java/com/hi/music/player/adapter/AdapterResultListSong.java @@ -0,0 +1,83 @@ +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; +import androidx.annotation.Nullable; +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.databinding.ItemResultListSongBinding; +import com.hi.music.player.helper.CommonUtils; +import com.hi.music.player.javabean.response.child.ResponseResultListChild; + +public class AdapterResultListSong extends BaseAdapter { + @Override + protected ItemResultListSongBinding getViewBinding(ViewGroup parent) { + return ItemResultListSongBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false); + } + + @Override + public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) { + ResponseResultListChild listChild = data.get(position); + + VHolder itemHolder = (VHolder) holder; + ItemResultListSongBinding vb = itemHolder.getVb(); + + Glide.with(MusicApplication.myApplication) + .asDrawable() + .load(listChild.getThumbnail()) + .apply(RequestOptions.bitmapTransform(new RoundedCorners(CommonUtils.dpToPx(4)))) + .placeholder(R.mipmap.im_placeholder) + .listener(new RequestListener() { + @Override + public boolean onLoadFailed(@Nullable GlideException e, @Nullable Object model, @NonNull Target target, boolean isFirstResource) { + CommonUtils.LogMsg(e.getMessage()); + return false; + } + + @Override + public boolean onResourceReady(@NonNull Drawable resource, @NonNull Object model, Target target, @NonNull DataSource dataSource, boolean isFirstResource) { + return false; + } + }) + .into(vb.imCovert); + vb.tvSongName.setText(listChild.getSongName()); + String playCount = listChild.getPlayCount(); + String subTitle = listChild.getSubTitle(); + if (playCount == null){ + vb.tvSubtitle.setText(subTitle); + }else { + String s = subTitle + " • " + playCount; + vb.tvSubtitle.setText(s); + } + + + vb.getRoot().setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (homeItemClickListener != null) { + int absoluteAdapterPosition = itemHolder.getAbsoluteAdapterPosition(); + String videoId = listChild.getVideoId(); + String browserId = listChild.getBrowserId(); + homeItemClickListener.onClickResultSong(listChild, absoluteAdapterPosition); + + } + } + }); + + CommonUtils.LogMsg("-------Song getBrowserId=" + listChild.getBrowserId() + "---getPlayListId= " + listChild.getPlayListId() + "---getVideoId=" + listChild.getVideoId()); + + } +} diff --git a/app/src/main/java/com/hi/music/player/adapter/AdapterSearchChild.java b/app/src/main/java/com/hi/music/player/adapter/AdapterSearchChild.java new file mode 100644 index 0000000..4b8d745 --- /dev/null +++ b/app/src/main/java/com/hi/music/player/adapter/AdapterSearchChild.java @@ -0,0 +1,71 @@ +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; +import androidx.annotation.Nullable; +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.databinding.ItemSearchBinding; +import com.hi.music.player.databinding.ItemSearchChildBinding; +import com.hi.music.player.helper.CommonUtils; +import com.hi.music.player.javabean.response.ResponseSearch; +import com.hi.music.player.javabean.response.child.ResponseSearchChild; + +public class AdapterSearchChild extends BaseAdapter { + @Override + protected ItemSearchChildBinding getViewBinding(ViewGroup parent) { + return ItemSearchChildBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false); + } + + @Override + public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) { + ResponseSearchChild responseSearchChild = data.get(position); + VHolder itemHolder = (VHolder) holder; + ItemSearchChildBinding vb = itemHolder.getVb(); + + + Glide.with(MusicApplication.myApplication) + .asDrawable() + .apply(RequestOptions.bitmapTransform(new RoundedCorners(CommonUtils.dpToPx(13)))) + .load(responseSearchChild.getSongCovert()) + .placeholder(R.mipmap.im_placeholder) + .listener(new RequestListener() { + @Override + public boolean onLoadFailed(@Nullable GlideException e, @Nullable Object model, @NonNull Target target, boolean isFirstResource) { + CommonUtils.LogMsg(e.getMessage()); + return false; + } + + @Override + public boolean onResourceReady(@NonNull Drawable resource, @NonNull Object model, Target target, @NonNull DataSource dataSource, boolean isFirstResource) { + return false; + } + }) + .into(vb.imCovert); + + vb.tvTitle.setText(responseSearchChild.getSongTitle()); + vb.tvSubtitle.setText(responseSearchChild.getSongDescribe()); + vb.getRoot().setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (homeItemClickListener != null) { + homeItemClickListener.onClickSearchResult(responseSearchChild); + } + } + }); + + } +} diff --git a/app/src/main/java/com/hi/music/player/adapter/AdapterSinger.java b/app/src/main/java/com/hi/music/player/adapter/AdapterSinger.java new file mode 100644 index 0000000..99517ae --- /dev/null +++ b/app/src/main/java/com/hi/music/player/adapter/AdapterSinger.java @@ -0,0 +1,77 @@ +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; +import androidx.annotation.Nullable; +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.databinding.ItemSingerBinding; +import com.hi.music.player.helper.CommonUtils; +import com.hi.music.player.javabean.response.child.ResponseSingle; + +public class AdapterSinger extends BaseAdapter { + + @Override + protected ItemSingerBinding getViewBinding(ViewGroup parent) { + return ItemSingerBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false); + } + + @Override + public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) { + int itemViewType = getItemViewType(position); + if (itemViewType == TYPE_ITEM) { + VHolder itemHolder = (VHolder) holder; + ItemSingerBinding vb = itemHolder.getVb(); + ResponseSingle responseSingle = data.get(position); + + Glide.with(MusicApplication.myApplication) + .asDrawable() + .load(responseSingle.getSingerHead()) + .apply(RequestOptions.bitmapTransform(new RoundedCorners(CommonUtils.dpToPx(4)))) + .placeholder(R.mipmap.im_placeholder) + .listener(new RequestListener() { + @Override + public boolean onLoadFailed(@Nullable GlideException e, @Nullable Object model, @NonNull Target target, boolean isFirstResource) { + CommonUtils.LogMsg(e.getMessage()); + return false; + } + + @Override + public boolean onResourceReady(@NonNull Drawable resource, @NonNull Object model, Target target, @NonNull DataSource dataSource, boolean isFirstResource) { + return false; + } + }) + .into(vb.header); + vb.tvSingerName.setText(responseSingle.getSingerName()); + vb.tvSongName.setText(responseSingle.getSongTitle()); +// vb.tvDescribe.setText(responseSingle.getDescription()); + vb.getRoot().setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (homeItemClickListener != null) { + homeItemClickListener.onClickItemSinger(responseSingle); + } + + } + }); + + } + + + } + + +} diff --git a/app/src/main/java/com/hi/music/player/dialog/DialogPlayList.java b/app/src/main/java/com/hi/music/player/dialog/DialogPlayList.java new file mode 100644 index 0000000..322e5fe --- /dev/null +++ b/app/src/main/java/com/hi/music/player/dialog/DialogPlayList.java @@ -0,0 +1,127 @@ +package com.hi.music.player.dialog; + +import android.graphics.drawable.Drawable; +import android.graphics.drawable.GradientDrawable; +import android.net.Uri; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.annotation.OptIn; +import androidx.media3.common.MediaItem; +import androidx.media3.common.util.UnstableApi; +import androidx.recyclerview.widget.LinearLayoutManager; + +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.adapter.AdapterPlayList; +import com.hi.music.player.api.onImageColorListener; +import com.hi.music.player.databinding.DialogPlayListBinding; +import com.hi.music.player.helper.CommonUtils; +import com.hi.music.player.javabean.response.ResponsePlayListInfo; +import com.hi.music.player.media3.MyControllerView; +import com.hi.music.player.media3.MyMediaControllerManager; + +import java.util.List; + +public class DialogPlayList extends BaseDialog { + private MyMediaControllerManager instance; + + @Override + protected DialogPlayListBinding getViewBinding(LayoutInflater inflater, ViewGroup container) { + return DialogPlayListBinding.inflate(inflater, container, false); + } + + @Override + protected void initView() { + + instance = MyMediaControllerManager.getInstance(); + initPlayListUi(); + + MediaItem currentMediaItem = instance.getCurMediaItem(); + if (currentMediaItem != null) { + Uri artworkUri = currentMediaItem.mediaMetadata.artworkUri; + vb.topSongName.setText(currentMediaItem.mediaMetadata.title); + vb.topSingerName.setText(currentMediaItem.mediaMetadata.artist); + + Glide.with(MusicApplication.myApplication) + .asDrawable() + .apply(RequestOptions.bitmapTransform(new RoundedCorners(CommonUtils.dpToPx(10)))) + .load(artworkUri) + .placeholder(R.mipmap.im_placeholder) + .listener(new RequestListener() { + @Override + public boolean onLoadFailed(@Nullable GlideException e, @Nullable Object model, @NonNull Target target, boolean isFirstResource) { + CommonUtils.LogMsg(e.getMessage()); + return false; + } + + @OptIn(markerClass = UnstableApi.class) + @Override + public boolean onResourceReady(@NonNull Drawable resource, @NonNull Object model, Target target, @NonNull DataSource dataSource, boolean isFirstResource) { + CommonUtils.getDominantDarkColor1(resource, new onImageColorListener() { + @Override + public void onImageColor(int color) { + if (color == -1) { + return; + } + int lighterColor = CommonUtils.adjustBrightness(color, 1.2f); // 比原始颜色亮 20% + int darkerColor = CommonUtils.adjustBrightness(color, 0.8f); // 比原始颜色暗 20% +// GradientDrawable gradientDrawable = new GradientDrawable( +// GradientDrawable.Orientation.TOP_BOTTOM, +// new int[]{lighterColor, darkerColor} // 浅到深渐变 +// ); + vb.topLayout.setBackgroundColor(darkerColor); + Drawable newDrawable = CommonUtils.getNewDrawable(lighterColor, 24f, 24f, 0, 0); + vb.listLayout.setBackground(newDrawable); + + + } + }); + + return false; + } + }) + .into(vb.topIm); + } + vb.imPlay.setSelected(instance.getIsPlaying()); + + vb.imPlay.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + vb.imPlay.setSelected(!vb.imPlay.isSelected()); + if (vb.imPlay.isSelected()) { + instance.play(); + } else { + instance.pause(); + } + } + }); + + + } + + @Override + public boolean isFullScreen() { + return true; + } + + private void initPlayListUi() { + List playList = instance.getPlayList(); + AdapterPlayList adapterPlayList = new AdapterPlayList(); + vb.recyclerList.setLayoutManager(new LinearLayoutManager(MusicApplication.myApplication)); + adapterPlayList.addData(playList); + vb.recyclerList.setAdapter(adapterPlayList); + } + + +} diff --git a/app/src/main/java/com/hi/music/player/javabean/requestbody/child/ContextBody.java b/app/src/main/java/com/hi/music/player/javabean/requestbody/child/ContextBody.java new file mode 100644 index 0000000..ea476ca --- /dev/null +++ b/app/src/main/java/com/hi/music/player/javabean/requestbody/child/ContextBody.java @@ -0,0 +1,36 @@ +package com.hi.music.player.javabean.requestbody.child; + +import java.io.Serializable; +import java.util.Locale; + +public class ContextBody { + + private Client client = new Client(); + + private ThirdParty thirdParty = new ThirdParty(); + + +// public ThirdParty getThirdParty() { +// return thirdParty; +// } + + public Client getClient() { + return client; + } + + public void setClient(Client client) { + this.client = client; + } + + + + + public static class ThirdParty{ + //https://www.youtube.com/watch?v=UqyT8IEBkvY + private String embedUrl; + + public void setEmbedUrl(String embedUrl) { + this.embedUrl = embedUrl; + } + } +} diff --git a/app/src/main/java/com/hi/music/player/media3/MyDownloadService.java b/app/src/main/java/com/hi/music/player/media3/MyDownloadService.java new file mode 100644 index 0000000..68b8e7e --- /dev/null +++ b/app/src/main/java/com/hi/music/player/media3/MyDownloadService.java @@ -0,0 +1,199 @@ +package com.hi.music.player.media3; + +import android.app.Notification; +import android.content.Context; +import android.util.Pair; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.media3.common.util.UnstableApi; +import androidx.media3.database.StandaloneDatabaseProvider; +import androidx.media3.datasource.DefaultHttpDataSource; +import androidx.media3.datasource.cache.SimpleCache; +import androidx.media3.exoplayer.offline.Download; +import androidx.media3.exoplayer.offline.DownloadCursor; +import androidx.media3.exoplayer.offline.DownloadIndex; +import androidx.media3.exoplayer.offline.DownloadManager; +import androidx.media3.exoplayer.offline.DownloadNotificationHelper; +import androidx.media3.exoplayer.offline.DownloadService; +import androidx.media3.exoplayer.scheduler.Scheduler; + +import com.hi.music.player.R; +import com.hi.music.player.api.onCheckDownload; +import com.hi.music.player.helper.CommonUtils; +import com.hi.music.player.javabean.CustomerDownload; +import com.hi.music.player.ui.activity.viewmodel.VMApplication; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.Executor; + +import io.reactivex.Observable; +import io.reactivex.android.schedulers.AndroidSchedulers; +import io.reactivex.disposables.Disposable; +import io.reactivex.schedulers.Schedulers; + + +@UnstableApi +public class MyDownloadService extends DownloadService { + private static final int FOREGROUND_NOTIFICATION_ID = 1; + private static final String CHANNEL_ID = "download_song_channel_id"; + + private static DownloadManager mDownloadManager; + + private static SimpleCache downloadCache; + + public MyDownloadService() { + super(FOREGROUND_NOTIFICATION_ID, + DEFAULT_FOREGROUND_NOTIFICATION_UPDATE_INTERVAL, + CHANNEL_ID, // 通知渠道 ID + R.string.app_name, // 通知渠道名称 + 0 + ); + } + + public static SimpleCache getDownloadCache() { + return downloadCache; + } + + public static void init(Context context, StandaloneDatabaseProvider databaseProvider) { + if (mDownloadManager == null) { + downloadCache = MyDownloadCacheManager.getMyCache(databaseProvider); + DefaultHttpDataSource.Factory factory = new DefaultHttpDataSource.Factory(); + Executor downloadExecutor = Runnable::run; + + mDownloadManager = new DownloadManager( + context, + databaseProvider, // 数据库提供者 + downloadCache, + factory, // 数据源工厂 + downloadExecutor // 线程池 + ); + + mDownloadManager.setMaxParallelDownloads(3); // 设置最大并行下载数 + + } + } + + public static synchronized DownloadManager getMyDownloadManager() { + + return mDownloadManager; + } + + public static void addDownloadListener(VMApplication vmApplication) { + mDownloadManager.addListener(new DownloadManager.Listener() { + @Override + public void onDownloadChanged(@NonNull DownloadManager downloadManager, Download download, @Nullable Exception finalException) { + String id = download.request.id; + + + if (download.state == Download.STATE_COMPLETED) { + // 下载完成 + vmApplication.setDownloadChange(new Pair<>(true, download)); + CommonUtils.LogMsg("----------------下载完成 id=" + id + "--thread=" + Thread.currentThread().getName()); + updateDownloadUi(vmApplication); + + } else if (download.state == Download.STATE_FAILED) { + // 下载失败 + CommonUtils.LogMsg("----------------下载失败 id=" + id + "---finalException=" + finalException.getMessage()); + + vmApplication.setDownloadChange(new Pair<>(false, download)); + } + + + } + + @Override + public void onDownloadRemoved(DownloadManager downloadManager, Download download) { + String id = download.request.id; + CommonUtils.LogMsg("----------------onDownloadRemoved id=" + id); + } + }); + } + + + public static void queryIsDownload(String videoId, onCheckDownload listener) { + Disposable subscribe = Observable.fromCallable(() -> { + // 不允许返回null,所以采用CustomerDownload类包装 + DownloadIndex downloadIndex = mDownloadManager.getDownloadIndex(); + Download download = downloadIndex.getDownload(videoId); + + return new CustomerDownload(download, download != null); + + }).subscribeOn(Schedulers.io()) + + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(listener::onHasDownload); + } + + + /** + * 下载完成的数量增加 + * + * @param vmApplication + */ + @UnstableApi + public static void updateDownloadUi(VMApplication vmApplication) { + Disposable subscribe = Observable.fromCallable(() -> { + // 第一部分操作:在IO线程 + DownloadIndex downloadIndex = mDownloadManager.getDownloadIndex(); + DownloadCursor downloads = downloadIndex.getDownloads(Download.STATE_COMPLETED); + + return downloads; + }) + .subscribeOn(Schedulers.io()) +// .observeOn(Schedulers.computation()) // 切换到计算线程 +// .map(result -> { +// // 第二部分操作:在computation线程 +// return result + " -> Step 2 completed"; +// }) + .observeOn(AndroidSchedulers.mainThread()) // 切换到主线程 + .subscribe(finalResult -> { + // 最终结果处理:在主线程 + Download curDownload = null; +// int count = finalResult.getCount(); +// if (finalResult.moveToLast()) { +// curDownload = finalResult.getDownload(); +// } + List downloadList = new ArrayList<>(); + while (finalResult.moveToNext()) { + Download download = finalResult.getDownload(); + downloadList.add(download); + } + + + CommonUtils.LogMsg("----------------下载总数量 -count=" + downloadList.size()); + vmApplication.setDownloadData(downloadList); + + }); + } + + @NonNull + @Override + protected DownloadManager getDownloadManager() { + return getMyDownloadManager(); + } + + + @Nullable + @Override + protected Scheduler getScheduler() { + // 返回 null 表示不需要使用调度器 + return null; + } + + @Override + protected Notification getForegroundNotification(List downloads, int notMetRequirements) { + // 构建用于显示下载进度的通知 + return buildNotification(downloads, notMetRequirements); + } + + private Notification buildNotification( + List downloads, int notMetRequirements) { + + return new DownloadNotificationHelper(this, CHANNEL_ID) + .buildProgressNotification(this, R.drawable.ic_download, null, null, downloads, notMetRequirements); + } + + +} diff --git a/app/src/main/java/com/hi/music/player/media3/MyMediaControllerManager.java b/app/src/main/java/com/hi/music/player/media3/MyMediaControllerManager.java new file mode 100644 index 0000000..1364be9 --- /dev/null +++ b/app/src/main/java/com/hi/music/player/media3/MyMediaControllerManager.java @@ -0,0 +1,421 @@ +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; +import androidx.media3.common.PlaybackException; +import androidx.media3.common.Player; +import androidx.media3.common.util.UnstableApi; +import androidx.media3.session.MediaController; +import androidx.media3.session.SessionToken; + +import com.google.common.util.concurrent.ListenableFuture; +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.api.onCheckDownload; +import com.hi.music.player.api.onPlayNextListener; +import com.hi.music.player.helper.CommonUtils; +import com.hi.music.player.helper.MyValue; +import com.hi.music.player.javabean.CustomerDownload; +import com.hi.music.player.javabean.response.ResponsePlayListInfo; +import com.hi.music.player.javabean.response.ResponsePlayUrl; +import com.hi.music.player.network.RetrofitManager; +import com.hi.music.player.ui.activity.viewmodel.VMApplication; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.ExecutionException; + +public class MyMediaControllerManager { + + private static volatile MyMediaControllerManager myMediaControllerManagerInstance; + + private MediaController mediaController; + + private MediaControllerListener mListener; + + private VMApplication mVmApplication; + + + //播放列表总数量 + private List playList; + + public static MyMediaControllerManager getInstance() { + if (myMediaControllerManagerInstance == null) { + synchronized (RetrofitManager.class) { + if (myMediaControllerManagerInstance == null) { + myMediaControllerManagerInstance = new MyMediaControllerManager(); + } + } + } + return myMediaControllerManagerInstance; + } + + public void init(MediaControllerStatusListener statusListener) { + SessionToken sessionToken = + new SessionToken(MusicApplication.myApplication, new ComponentName(MusicApplication.myApplication, PlaybackService.class)); + ListenableFuture controllerFuture = + new MediaController.Builder(MusicApplication.myApplication, sessionToken).buildAsync(); + controllerFuture.addListener(() -> { + // Call controllerFuture.get() to retrieve the MediaController. + // MediaController implements the Player interface, so it can be + // attached to the PlayerView UI component. +// playerView.setPlayer(controllerFuture.get()); + try { + mediaController = controllerFuture.get(); + statusListener.onMediaControllerComplete(true); + CommonUtils.LogMsg("=-----mediaController+" + mediaController); + } catch (ExecutionException | InterruptedException e) { + CommonUtils.LogErrorMsg(e.getMessage()); + statusListener.onMediaControllerComplete(false); + } + }, MoreExecutors.directExecutor()); + + + } + + public void addListener(VMApplication vmApplication, MediaControllerListener listener) { + mVmApplication = vmApplication; + mListener = listener; + mediaController.addListener(new Player.Listener() { + @Override + public void onPlayerError(PlaybackException error) { + mListener.onPlayStatus(MyValue.PLAY_STATUS_CODE_ERROR); + mVmApplication.setPlayStatus(MyValue.PLAY_STATUS_CODE_ERROR); + } + + @Override + public void onPlaylistMetadataChanged(MediaMetadata mediaMetadata) { + + } + + @Override + public void onMediaItemTransition(@Nullable MediaItem mediaItem, int reason) { + //当前媒体项发生变化,切歌 + if (mediaItem == null) { + //第二次进入PlayActitivity ,调用resetPlayList(),这里为null + return; + } + mListener.onChangeMusic(mediaItem); + mVmApplication.setPlayStatus(MyValue.PLAY_STATUS_CHANGE_MUSIC); + } + + @Override + public void onIsPlayingChanged(boolean isPlaying) { + + if (isPlaying) { + mListener.onPlayStatus(MyValue.PLAY_STATUS_CODE_PLAYING); + mVmApplication.setPlayStatus(MyValue.PLAY_STATUS_CODE_PLAYING); + // TODO: 2024/10/15 自动播放完成切歌到下一首播放没有触发这里请求下一首 + checkUrl(false); + + } else { + // 播放器暂停或停止 + mListener.onPlayStatus(MyValue.PLAY_STATUS_CODE_PAUSE); + mVmApplication.setPlayStatus(MyValue.PLAY_STATUS_CODE_PAUSE); + } + } + + @Override + public void onPlaybackStateChanged(int playbackState) { + mListener.onPlayStatus(playbackState); + mVmApplication.setPlayStatus(playbackState); + } + + @Override + public void onPositionDiscontinuity(Player.PositionInfo oldPosition, Player.PositionInfo newPosition, int reason) { + // 快进、快退等操作 +// CommonUtils.LogMsg("=-----快进、快退+" + newPosition.positionMs); +// mediaControllerListener.onPlayStatus(playbackState); + } + }); + } + + public void checkUrl(boolean playNextNow) { + if (mediaController.hasNextMediaItem()) { + int nextMediaItemIndex = mediaController.getNextMediaItemIndex(); + onCallRequestUrl(nextMediaItemIndex, playNextNow, 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); + } + }); + } + } + + public MediaController getMediaController() { + return mediaController; + } + + /** + * 针对已经下载过的数据,从download构造出MediaItem 直接替换 + * @param index + * @param mediaItem + */ + public void replaceMediaItem(int index, MediaItem mediaItem) { + mediaController.replaceMediaItem(index, mediaItem); + } + + public long getContentPos() { + if (mediaController == null) return 0; + return mediaController.getContentPosition(); + } + + public long getBufferPos() { + if (mediaController == null) return 0; + return mediaController.getBufferedPosition(); + } + + public MediaItem getCurMediaItem() { + return mediaController.getCurrentMediaItem(); + } + + public Boolean getIsPlaying() { + return mediaController.isPlaying(); + } + + public int getCurIndex() { + return mediaController.getCurrentMediaItemIndex(); + } + + /** + * //0 不循环、1 单曲循环、2 列表循环、 + * + * @param mode + */ + public void setMode(int mode) { + switch (mode) { + case 0: + mediaController.setRepeatMode(Player.REPEAT_MODE_OFF); + break; + case 1: + mediaController.setRepeatMode(Player.REPEAT_MODE_ONE); + break; + case 2: + mediaController.setRepeatMode(Player.REPEAT_MODE_ALL); + break; + } + + } + + public int getRepeatMode() { + return mediaController.getRepeatMode(); + + } + + public String getCurVideoId() { + MediaItem currentMediaItem = mediaController.getCurrentMediaItem(); + if (currentMediaItem != null) { + return currentMediaItem.mediaId; + } else { + return null; + } + + + } + + /** + * 更新播放列表中的音频url + * + * @param playUrl + */ + @OptIn(markerClass = UnstableApi.class) + public void UpdateAudioUrl(ResponsePlayUrl playUrl, int index) { + for (int i = 0; i < mediaController.getMediaItemCount(); i++) { + MediaItem mediaItemAt = mediaController.getMediaItemAt(i); + if (mediaItemAt.mediaId.equals(playUrl.getVideoId())) { + + MediaItem.Builder builder = mediaItemAt.buildUpon(); + builder.setMediaId(playUrl.getVideoId()); + builder.setUri(playUrl.getVideoUrlMedium()); +// builder.setCustomCacheKey(playUrl.getVideoUrlMedium()); + //针对于,已经从分类合集列表页面进入播放页面的数据(只有小的封面图) + if (mediaItemAt.mediaMetadata.artworkUri == null) { + MediaMetadata.Builder builder1 = mediaItemAt.mediaMetadata.buildUpon(); + builder1.setArtworkUri(Uri.parse(playUrl.getBigCovert())); + builder.setMediaMetadata(builder1.build()); + } + + CharSequence title = mediaController.getMediaItemAt(i).mediaMetadata.title; + CommonUtils.LogMsg("-------------更新播放列表中的音频--id=" + playUrl.getVideoId()); + mediaController.replaceMediaItem(i, builder.build()); + break; + } + } + } + + public void setPlayList(List playList) { + this.playList = playList; + addMusicPlayList(playList); + } + + public List getPlayList() { + return playList; + } + + public void resetPlayList() { + mediaController.clearMediaItems(); + } + + + /** + * 添加播放列表(不带音频url) + * 注意没有setUri的不会被实际添加到mediaController(mediaController.getMediaItemCount()) + * 这里setUri 一个占位 + * + * @param listInfo + */ + @OptIn(markerClass = UnstableApi.class) + public void addMusicPlayList(List listInfo) { + playList = listInfo; + for (int i = 0; i < listInfo.size(); i++) { + ResponsePlayListInfo playInfo = listInfo.get(i); + + MediaItem.Builder builder = new MediaItem.Builder(); + String videoId = playInfo.getVideoId(); + //唯一标识符 + builder.setMediaId(videoId); + builder.setUri("-------test-----"); + builder.setCustomCacheKey(videoId); + MediaMetadata.Builder MediaMetadata_builder = new MediaMetadata.Builder(); + + MediaMetadata_builder.setArtist(playInfo.getSingerName()); + MediaMetadata_builder.setDescription(playInfo.getDuration()); + MediaMetadata_builder.setDurationMs(playInfo.getDurationMs()); + if (playInfo.getCovert() != null && !playInfo.getCovert().isEmpty()) + MediaMetadata_builder.setArtworkUri(Uri.parse(playInfo.getCovert())); + MediaMetadata_builder.setTitle(playInfo.getSongTitle()); + + CommonUtils.LogMsg("----------添加播放列表 i=" + i + "---" + playInfo.getSingerName() + "-------VideoId=" + videoId + "---playInfo.getDuration()=" + playInfo.getDuration()); +// MediaMetadata_builder.setRecordingYear(Integer.parseInt(playInfo.getYear())); + builder.setMediaMetadata(MediaMetadata_builder.build()); + mediaController.addMediaItem(builder.build()); + + } + + } + + + public void addMusicPlayList(MediaItem mediaItem) { + mediaController.addMediaItem(mediaItem); + } + + public void play() { + if (!mediaController.isPlaying()) { + CommonUtils.LogMsg("-----------prepare"); + mediaController.prepare(); + mediaController.play(); + } + + } + + public void pause() { + if (mediaController.isPlaying()) + mediaController.pause(); + } + + public void stop() { + if (mediaController.isPlaying()) + mediaController.stop(); + } + + public void playNext(onPlayNextListener listener) { + if (mediaController.hasNextMediaItem()) { + int nextMediaItemIndex = mediaController.getNextMediaItemIndex(); + + onCallRequestUrl(nextMediaItemIndex, true, new OnHasUrlAction() { + @Override + public void onHasUrl() { + mediaController.seekToNextMediaItem(); + mediaController.play(); + } + }); + } else { + listener.onPlayNext(false); + } + } + + public void playPrevious() { + if (mediaController.hasPreviousMediaItem()) { + + int previousMediaItemIndex = mediaController.getPreviousMediaItemIndex(); + onCallRequestUrl(previousMediaItemIndex, true, new OnHasUrlAction() { + @Override + public void onHasUrl() { + mediaController.seekToPreviousMediaItem(); + mediaController.play(); + } + }); + + } else { + mediaController.seekTo(0); + mediaController.play(); + } + } + + + /** + * 播放指定播放列表位置的歌曲 + * + * @param index + */ + public void playPositionMusic(int index) { + if (index >= mediaController.getMediaItemCount()) { + CommonUtils.LogErrorMsg("-------------数组越界"); + return; + } + + stop(); + mediaController.seekTo(index, 0); + onCallRequestUrl(index, true, new OnHasUrlAction() { + @Override + public void onHasUrl() { + mediaController.play(); + CommonUtils.LogMsg("-------------有有效URl--播放指定播放列表位置的歌曲 index=" + index); + } + }); + + } + + @OptIn(markerClass = UnstableApi.class) + private void onCallRequestUrl(int index, boolean playNow, OnHasUrlAction action) { + MediaItem mediaItemAt = mediaController.getMediaItemAt(index); + boolean b = CommonUtils.hasValidUri(mediaItemAt); + if (!b) { + //查看 + MyDownloadService.queryIsDownload(mediaItemAt.mediaId, new onCheckDownload() { + @Override + public void onHasDownload(CustomerDownload customerDownload) { + if (customerDownload.isDownload()) { + MediaItem mediaItem = CommonUtils.downloadToMediaItem(customerDownload.getDownloadData()); + mediaController.replaceMediaItem(index, mediaItem); + CommonUtils.LogMsg("-------------请求URl 已经下载过 index" + index + "---playNow=" + playNow); + } else { + CommonUtils.LogMsg("-------------请求URl index" + index + "---playNow=" + playNow); + mListener.onRequestNextUri(mediaItemAt.mediaId, index, playNow); + } + + } + }); + + } else { + action.onHasUrl(); + } + } +} diff --git a/app/src/main/java/com/hi/music/player/media3/MyPlayCacheManager.java b/app/src/main/java/com/hi/music/player/media3/MyPlayCacheManager.java new file mode 100644 index 0000000..ba86e5e --- /dev/null +++ b/app/src/main/java/com/hi/music/player/media3/MyPlayCacheManager.java @@ -0,0 +1,36 @@ +package com.hi.music.player.media3; + +import android.content.Context; + +import androidx.annotation.OptIn; +import androidx.media3.common.util.UnstableApi; +import androidx.media3.database.StandaloneDatabaseProvider; +import androidx.media3.datasource.cache.LeastRecentlyUsedCacheEvictor; +import androidx.media3.datasource.cache.SimpleCache; + +import java.io.File; + + +/** + * 播放缓存 + */ +public class MyPlayCacheManager { + + private static SimpleCache playCache; + + @OptIn(markerClass = UnstableApi.class) + public static SimpleCache getInitPlayCache(Context context) { + if (playCache == null) { + File cacheDir = new File(context.getCacheDir(), "media_cache"); + long maxCacheSize = 100 * 1024 * 1024; // 缓存大小 100MB + StandaloneDatabaseProvider databaseProvider = new StandaloneDatabaseProvider(context); + playCache = new SimpleCache(cacheDir, new LeastRecentlyUsedCacheEvictor(maxCacheSize), databaseProvider); +// MyDownloadService.init(context,databaseProvider); + } + return playCache; + } + + public static SimpleCache getPlayCache() { + return playCache; + } +} diff --git a/app/src/main/java/com/hi/music/player/media3/PlaybackService.java b/app/src/main/java/com/hi/music/player/media3/PlaybackService.java new file mode 100644 index 0000000..ec2f200 --- /dev/null +++ b/app/src/main/java/com/hi/music/player/media3/PlaybackService.java @@ -0,0 +1,176 @@ +package com.hi.music.player.media3; + +import android.content.Context; +import android.content.Intent; +import android.net.Uri; + + +import androidx.annotation.Nullable; +import androidx.annotation.OptIn; +import androidx.media3.common.Player; +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.DefaultHttpDataSource; +import androidx.media3.datasource.ResolvingDataSource; +import androidx.media3.datasource.TransferListener; +import androidx.media3.datasource.cache.CacheDataSource; +import androidx.media3.datasource.cache.SimpleCache; +import androidx.media3.exoplayer.DefaultLoadControl; +import androidx.media3.exoplayer.ExoPlayer; + +import androidx.media3.exoplayer.source.DefaultMediaSourceFactory; +import androidx.media3.exoplayer.source.MediaSource; +import androidx.media3.exoplayer.source.ProgressiveMediaSource; +import androidx.media3.session.MediaSession; +import androidx.media3.session.MediaSessionService; + +import com.hi.music.player.helper.CommonUtils; + +import java.io.IOException; + +public class PlaybackService extends MediaSessionService { + private MediaSession mediaSession = null; + private ExoPlayer player; + + private SimpleCache playCache, downloadCache; + + @OptIn(markerClass = UnstableApi.class) + @Override + public void onCreate() { + super.onCreate(); + + DynamicMediaSourceFactory customMediaSourceFactory = new DynamicMediaSourceFactory(getCacheDataSourceFactory(this)); + ProgressiveMediaSource.Factory factory = new ProgressiveMediaSource.Factory(getUrlFactory()); + + + // 创建 DefaultLoadControl,配置缓冲参数 + DefaultLoadControl loadControl = new DefaultLoadControl.Builder() + .setBufferDurationsMs(3000, // minBufferMs: 播放前的最小缓冲时间(毫秒) + 300000, // maxBufferMs: 最大缓冲时间(毫秒) + 3000, // bufferForPlaybackMs: 播放时的目标缓冲时间(毫秒) + 2000) // bufferForPlaybackAfterRebufferMs: 重新缓冲后的缓冲时间(毫秒) + .build(); + + + CacheDataSource.Factory cacheDataSourceFactory = getCacheDataSourceFactory(this); + MediaSource.Factory mediaSourceFactory = new ProgressiveMediaSource.Factory(cacheDataSourceFactory); + CacheDataSource dataSource = cacheDataSourceFactory.createDataSource(); + + player = new ExoPlayer.Builder(this) + .setMediaSourceFactory(factory) + .setLoadControl(loadControl) + .build(); + mediaSession = new MediaSession.Builder(this, player) + .build(); + + + } + + // 创建带缓存的数据源工厂 + @OptIn(markerClass = UnstableApi.class) + private CacheDataSource.Factory getCacheDataSourceFactory(Context context) { + DefaultDataSource.Factory factory = new DefaultDataSource.Factory(this); + playCache = MyPlayCacheManager.getInitPlayCache(context); + CacheDataSource.Factory factory1 = new CacheDataSource.Factory().setCache(playCache) + .setUpstreamDataSourceFactory(new DefaultHttpDataSource.Factory() + .setConnectTimeoutMs(16000) + .setReadTimeoutMs(8000) + .setUserAgent("Mozilla/5.0 (Windows NT 10.0; rv:91.0) Gecko/20100101 Firefox/91.0")) + .setEventListener(new CacheDataSource.EventListener() { + @Override + public void onCachedBytesRead(long cacheSizeBytes, long cachedBytesRead) { + CommonUtils.LogMsg("-----------缓存2 -PlayCache-" + cacheSizeBytes + "----cacheSizeBytes=" + cachedBytesRead); + } + + @Override + public void onCacheIgnored(int reason) { + CommonUtils.LogMsg("-----------缓存2忽略 PlayCache=" + reason); + } + }); + downloadCache = MyDownloadService.getDownloadCache(); + return new CacheDataSource.Factory() + .setCache(downloadCache) + .setUpstreamDataSourceFactory(factory1) + .setFlags(CacheDataSource.FLAG_IGNORE_CACHE_ON_ERROR) + .setEventListener(new CacheDataSource.EventListener() { + @Override + public void onCachedBytesRead(long cacheSizeBytes, long cachedBytesRead) { + CommonUtils.LogMsg("-----------缓存1 DownloadCache --" + cacheSizeBytes + "----cacheSizeBytes=" + cachedBytesRead); + } + + @Override + public void onCacheIgnored(int reason) { + CommonUtils.LogMsg("-----------缓存1忽略DownloadCache =" + reason); + } + }); +// .setFlags(CacheDataSource.FLAG_IGNORE_CACHE_ON_ERROR); + } + + + @OptIn(markerClass = UnstableApi.class) + private DataSource.Factory getUrlFactory() { + long chunkLength = 512 * 1024L; + return new ResolvingDataSource.Factory(getCacheDataSourceFactory(this), new ResolvingDataSource.Resolver() { + + + @Override + public DataSpec resolveDataSpec(DataSpec dataSpec) { + CommonUtils.LogMsg("--------resolveDataSpec dataSpec.key=" + dataSpec.key+"---dataSpec.uri="+dataSpec.uri); + return dataSpec; + +// long length = 1L; +// String videoId = dataSpec.key; +// long position = dataSpec.position; +// if (dataSpec.length >= 0) { +// length = dataSpec.length; +// } +// +// if (downloadCache.isCached(videoId,position,length)){ +// CommonUtils.LogMsg("--------resolveDataSpec downloadCache" ); +// return dataSpec; +// }else if(playCache.isCached(videoId,position,chunkLength)){ +// CommonUtils.LogMsg("--------resolveDataSpec playCache" ); +// return dataSpec; +// }else { +// CommonUtils.LogMsg("--------resolveDataSpec uri" ); +// return dataSpec; +// } + } + + @Override + public Uri resolveReportedUri(Uri uri) { + CommonUtils.LogMsg("--------resolveDataSpec uri=" + uri); + return ResolvingDataSource.Resolver.super.resolveReportedUri(Uri.parse("---")); + } + }); + } + + + @Nullable + @Override + public MediaSession onGetSession(MediaSession.ControllerInfo controllerInfo) { + return mediaSession; + } + + @Override + public void onTaskRemoved(@Nullable Intent rootIntent) { + Player player = mediaSession.getPlayer(); + 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 + public void onDestroy() { + mediaSession.getPlayer().release(); + mediaSession.release(); + mediaSession = null; + super.onDestroy(); + } +} diff --git a/app/src/main/java/com/hi/music/player/network/JsonHelper.java b/app/src/main/java/com/hi/music/player/network/JsonHelper.java new file mode 100644 index 0000000..711504a --- /dev/null +++ b/app/src/main/java/com/hi/music/player/network/JsonHelper.java @@ -0,0 +1,1032 @@ +package com.hi.music.player.network; + +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.ResponsePlayListInfo; +import com.hi.music.player.javabean.response.ResponsePlayUrl; +import com.hi.music.player.javabean.response.ResponseResult; +import com.hi.music.player.javabean.response.ResponseResultList; +import com.hi.music.player.javabean.response.ResponseSearch; +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.ResponseHomeChild; +import com.hi.music.player.javabean.response.child.ResponseResultListChild; +import com.hi.music.player.javabean.response.child.ResponseSearchChild; +import com.hi.music.player.javabean.response.child.ResponseSingle; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Objects; + +public class JsonHelper { + + + /** + * 解析首页单曲 + * + * @param jsonObject + * @return + */ + public static ResponseHome ResolveHomeJson(JSONObject jsonObject) { + ResponseHome responseHome = new ResponseHome(); + try { + String bgUrl = getCovertUrl(jsonObject.getJSONObject("background"), true); + + JSONObject sectionListRenderer = jsonObject.getJSONObject("contents").getJSONObject("singleColumnBrowseResultsRenderer").getJSONArray("tabs").getJSONObject(0).getJSONObject("tabRenderer").getJSONObject("content").getJSONObject("sectionListRenderer"); + + JSONObject responseContext = jsonObject.getJSONObject("responseContext"); + JSONArray serviceTrackingParams = responseContext.getJSONArray("serviceTrackingParams"); + String visitorData = responseContext.getString("visitorData"); + CommonUtils.LogMsg("---------参数-visitorData=" + visitorData); + + responseHome.setBackgroundUrl(bgUrl); + responseHome.setVisitorData(visitorData); + getCommonHome(sectionListRenderer, responseHome); + + + } catch (JSONException exception) { + CommonUtils.LogMsg("---------首页-exception="); + exception.printStackTrace(); + + } + + return responseHome; + } + + + /** + * 解析首页不同类型的音乐分类 + * + * @param jsonObject + * @return + */ + public static ResponseHome ResolveHomeMoreJson(JSONObject jsonObject) { + ResponseHome responseHome = new ResponseHome(); + try { + + JSONObject sectionListContinuation = jsonObject.getJSONObject("continuationContents").getJSONObject("sectionListContinuation"); + + + JSONObject responseContext = jsonObject.getJSONObject("responseContext"); + JSONArray serviceTrackingParams = responseContext.getJSONArray("serviceTrackingParams"); + + getCommonHome(sectionListContinuation, responseHome); + + } catch (JSONException exception) { + CommonUtils.LogMsg("----------exception=" + exception.getMessage()); + exception.printStackTrace(); + + } + return responseHome; + } + + /** + * 解析根据单曲获取的播放列表 + * + * @param jsonObject + * @return + */ + public static List ResolvePlayListJson(JSONObject jsonObject) { + List list = new ArrayList<>(); + try { + JSONObject playlistPanelRenderer = jsonObject.getJSONObject("contents").getJSONObject("singleColumnMusicWatchNextResultsRenderer").getJSONObject("tabbedRenderer").getJSONObject("watchNextTabbedResultsRenderer").getJSONArray("tabs").getJSONObject(0).getJSONObject("tabRenderer").getJSONObject("content").getJSONObject("musicQueueRenderer").getJSONObject("content").getJSONObject("playlistPanelRenderer"); + + JSONArray contents = playlistPanelRenderer.getJSONArray("contents"); + + for (int i = 0; i < contents.length(); i++) { + ResponsePlayListInfo responsePlayListInfo = new ResponsePlayListInfo(); + JSONObject playlistPanelVideoRenderer = contents.getJSONObject(i).getJSONObject("playlistPanelVideoRenderer"); + + String maxUrl = getCovertUrl(playlistPanelVideoRenderer, true); + + String smallUrl = getCovertUrl(playlistPanelVideoRenderer, false); + + CommonUtils.LogMsg("--图片--------maxUrl=" + maxUrl + "---smallUrl=" + smallUrl); + + String songName = getJsonTitle(playlistPanelVideoRenderer.getJSONObject("title"), 0); + + JSONObject longBylineText = playlistPanelVideoRenderer.getJSONObject("longBylineText"); + String singerName = getJsonTitle(longBylineText, 0); + String AlbumTitle = getJsonTitle(longBylineText, 2); + String YearRelease = getJsonTitle(longBylineText, 4); + + String SongDuration = getJsonTitle(playlistPanelVideoRenderer.getJSONObject("lengthText"), 0); + + long ms = CommonUtils.convertToMilliseconds(SongDuration); + String textTime = CommonUtils.convertMillisToTime(ms); + CommonUtils.LogMsg("----------SongDuration=" + SongDuration + "---ms=" + ms + "---textTime=" + textTime); + + String[] watchEndPoint = getWatchEndPoint(playlistPanelVideoRenderer); + + responsePlayListInfo.setCovert(maxUrl); + responsePlayListInfo.setSmallCovert(smallUrl); + responsePlayListInfo.setSongTitle(songName); + responsePlayListInfo.setSingerName(singerName); + responsePlayListInfo.setAlbumTitle(AlbumTitle); + responsePlayListInfo.setYear(YearRelease); + responsePlayListInfo.setDuration(textTime); + responsePlayListInfo.setDurationMs(ms); + responsePlayListInfo.setVideoId(watchEndPoint[0]); + if (watchEndPoint[0] == null || watchEndPoint[0].isEmpty()) { + + continue; + } + responsePlayListInfo.setPlaylistId(watchEndPoint[1]); + responsePlayListInfo.setParams(watchEndPoint[2]); + responsePlayListInfo.setMusicVideoType(watchEndPoint[3]); + + list.add(responsePlayListInfo); + + } + + + } catch (JSONException exception) { + CommonUtils.LogMsg("----------exception="); + exception.printStackTrace(); + + } + return list; + } + + /** + * 解析音乐音频源地址 + * + * @param jsonObject + * @return + */ + public static ResponsePlayUrl ResolvePlayUrlJson(JSONObject jsonObject) { + try { + ResponsePlayUrl responsePlayUrl = new ResponsePlayUrl(); + String status = jsonObject.getJSONObject("playabilityStatus").getString("status"); + JSONObject streamingData = jsonObject.getJSONObject("streamingData"); +// JSONArray jsonArray = streamingData.getJSONArray("adaptiveFormats"); +// for (int i = 0; i < jsonArray.length(); i++) { +// JSONObject jsonIndex = jsonArray.getJSONObject(i); +// +// String mimeType = jsonIndex.getString("mimeType"); +// String itag = jsonIndex.getString("itag"); +// String url = jsonIndex.getString("url"); +// if (mimeType.contains("video/mp4")) { +// String quality = jsonIndex.optString("quality"); +// if(quality.equals("medium")){ +// responsePlayUrl.setVideoUrlMedium(url); +// } +// +// } +// if (mimeType.contains("audio/mp4")) { +// String audioQuality1 = jsonIndex.optString("audioQuality"); +// if (audioQuality1.equals("AUDIO_QUALITY_MEDIUM")) { +// responsePlayUrl.setAudioUrlMedium(url); +// } else { +// responsePlayUrl.setAudioUrlLow(url); +// } +// +// } +// +// } + + + String videoUrl = streamingData.getJSONArray("formats").getJSONObject(0).getString("url"); + responsePlayUrl.setVideoUrlMedium(videoUrl); + + + JSONObject videoDetails = jsonObject.getJSONObject("videoDetails"); + + String videoId = videoDetails.getString("videoId"); + String title = videoDetails.getString("title"); + JSONArray thumbnails = videoDetails.getJSONObject("thumbnail").getJSONArray("thumbnails"); + + String url = thumbnails.getJSONObject(thumbnails.length() - 1).getString("url"); + + + responsePlayUrl.setBigCovert(url); + responsePlayUrl.setVideoId(videoId); + responsePlayUrl.setStatus(status); + return responsePlayUrl; + + } catch (JSONException e) { + e.printStackTrace(); + return null; + } + + } + + + /** + * 解析音乐分类合集列表 + * MUSIC_PAGE_TYPE_ALBUM、MUSIC_PAGE_TYPE_PLAYLIST、MUSIC_PAGE_TYPE_USER_CHANNEL + * + * @param jsonObject + * @return + */ + public static ResponseCategoryList ResolveCategoryList(JSONObject jsonObject, String browseId) { + try { + ResponseCategoryList responseCategoryList = new ResponseCategoryList(); + JSONObject contents = jsonObject.getJSONObject("contents"); + JSONObject twoColumnBrowseResultsRenderer = contents.optJSONObject("twoColumnBrowseResultsRenderer"); + if (twoColumnBrowseResultsRenderer != null) { + // MUSIC_PAGE_TYPE_ALBUM、MUSIC_PAGE_TYPE_PLAYLIST + JSONObject jsonObject1 = contents + .getJSONObject("twoColumnBrowseResultsRenderer"); + + + JSONObject musicResponsiveHeaderRenderer = jsonObject1 + .getJSONArray("tabs") + .getJSONObject(0) + .getJSONObject("tabRenderer") + .getJSONObject("content") + .getJSONObject("sectionListRenderer") + .getJSONArray("contents") + .getJSONObject(0) + .getJSONObject("musicResponsiveHeaderRenderer"); + + //封面图 + String covert = getCovertUrl(musicResponsiveHeaderRenderer.getJSONObject("thumbnail"), true); + CommonUtils.LogMsg("----图片-------------covert=" + covert); + + //轻松愉悦的经典摇滚乐 + String title = getJsonTitle(musicResponsiveHeaderRenderer.getJSONObject("title"), 0); + + String description = ""; + + + JSONObject straplineTextOne = musicResponsiveHeaderRenderer.optJSONObject("straplineTextOne"); + if (straplineTextOne != null) { + String jsonTitle = getJsonTitle(straplineTextOne, 0); + responseCategoryList.setSingName(jsonTitle); + } + + //110 首歌曲.超过6小时 + String secondSubtitle = getJsonTextNew(musicResponsiveHeaderRenderer.getJSONObject("secondSubtitle")); + + if (musicResponsiveHeaderRenderer.has("description")) { + //-----------------单曲合集类型 + //Hold on to the feeling. + description = getJsonTitle(musicResponsiveHeaderRenderer.getJSONObject("description") + .getJSONObject("musicDescriptionShelfRenderer") + .getJSONObject("description"), 0); + } + if (musicResponsiveHeaderRenderer.has("subtitle")) { + //-----------------专辑类型 + //专辑.1998 + description = getJsonTextNew(musicResponsiveHeaderRenderer.getJSONObject("subtitle")); + } + + + responseCategoryList.setCovert(covert); + responseCategoryList.setDescription(description); + responseCategoryList.setTitle(title); + responseCategoryList.setSecondSubtitle(secondSubtitle); + + CommonUtils.LogMsg("-------------title=" + title + "--description=" + description + "---secondSubtitle=" + secondSubtitle + "---covert=" + covert); + + JSONObject contentsArray = jsonObject1.getJSONObject("secondaryContents") + .getJSONObject("sectionListRenderer") + .getJSONArray("contents") + .getJSONObject(0); + List mList = new ArrayList<>(); + if (contentsArray.has("musicPlaylistShelfRenderer")) { + // MUSIC_PAGE_TYPE_PLAYLIST、MUSIC_PAGE_TYPE_USER_CHANNEL + JSONArray jsonArray = contentsArray.getJSONObject("musicPlaylistShelfRenderer") + .getJSONArray("contents"); + + + for (int i = 0; i < jsonArray.length(); i++) { + JSONObject arrayJSONObject = jsonArray.getJSONObject(i).getJSONObject("musicResponsiveListItemRenderer"); + ResponsePlayListInfo child = getCommonCategoryList(arrayJSONObject); + String thumbnail = getCovertUrl(arrayJSONObject.getJSONObject("thumbnail"), true); + CommonUtils.LogMsg("----图片-------------thumbnail=" + thumbnail); + child.setSmallCovert(thumbnail); +// child.setCovert(thumbnail); + + + if (child.getVideoId() == null || child.getVideoId().isEmpty()) { + CommonUtils.LogErrorMsg("--歌曲" + i + "-------------SongTitle=" + child.getSongTitle() + "--getVideoId=" + child.getVideoId() + "---请求参数browseId=" + browseId); + continue; + } + mList.add(child); + + } + } + if (contentsArray.has("musicShelfRenderer")) { + // MUSIC_PAGE_TYPE_ALBUM + JSONArray jsonArray = contentsArray.getJSONObject("musicShelfRenderer") + .getJSONArray("contents"); + for (int i = 0; i < jsonArray.length(); i++) { + JSONObject arrayJSONObject = jsonArray.getJSONObject(i).getJSONObject("musicResponsiveListItemRenderer"); + ResponsePlayListInfo child = getCommonCategoryList(arrayJSONObject); + child.setCovert(covert); + child.setSmallCovert(covert); + if (child.getVideoId() == null || child.getVideoId().isEmpty()) { + continue; + } + mList.add(child); + } + + } + + + responseCategoryList.setList(mList); + } + + + return responseCategoryList; + + } catch (JSONException e) { + e.printStackTrace(); + return null; + } + + } + + + public static List ResolveSearchSuggestion(JSONObject jsonObject) { + try { + List stringList = new ArrayList<>(); + JSONArray contents = jsonObject.getJSONArray("contents"); + for (int i = 0; i < contents.length(); i++) { + JSONArray jsonArray = contents.getJSONObject(i) + .getJSONObject("searchSuggestionsSectionRenderer") + .getJSONArray("contents"); + + for (int j = 0; j < jsonArray.length(); j++) { + + JSONObject searchSuggestionRenderer = jsonArray.getJSONObject(j) + .optJSONObject("searchSuggestionRenderer"); + if (searchSuggestionRenderer != null) { + String string = searchSuggestionRenderer + .getJSONObject("navigationEndpoint") + .getJSONObject("searchEndpoint") + .getString("query"); + stringList.add(string); + } + + } + + } + + return stringList; + + } catch (JSONException e) { + CommonUtils.LogErrorMsg("--" + e.getMessage()); + return null; + } + + } + + + /** + * 解析搜索结果 + * + * @param jsonObject + */ + public static List ResolveSearchResult(JSONObject jsonObject) { + try { + JSONArray jsonArray = jsonObject.getJSONObject("contents") + .getJSONObject("tabbedSearchResultsRenderer") + .getJSONArray("tabs") + .getJSONObject(0) + .getJSONObject("tabRenderer") + .getJSONObject("content") + .getJSONObject("sectionListRenderer") + .getJSONArray("contents"); + + List searchList = new ArrayList<>(); + for (int i = 0; i < jsonArray.length(); i++) { + ResponseSearch responseSearch = new ResponseSearch(); + JSONArray contents1 = null; + + String headerTitle = ""; + + JSONObject contents = jsonArray.getJSONObject(i); + + //最佳结果 + JSONObject musicCardShelfRenderer = contents.optJSONObject("musicCardShelfRenderer"); + + JSONObject musicShelfRenderer = contents.optJSONObject("musicShelfRenderer"); + + if (musicCardShelfRenderer != null) { + String covertUrl = getCovertUrl(musicCardShelfRenderer.getJSONObject("thumbnail"), true); + String title = getJsonTitle(musicCardShelfRenderer.getJSONObject("title"), 0); + String subtitle = getJsonTextNew(musicCardShelfRenderer.getJSONObject("subtitle")); + + headerTitle = getJsonTitle(musicCardShelfRenderer.getJSONObject("header") + .getJSONObject("musicCardShelfHeaderBasicRenderer") + .getJSONObject("title"), 0); + contents1 = musicCardShelfRenderer.optJSONArray("contents"); + + + JSONObject jsonObject1 = musicCardShelfRenderer.getJSONObject("title") + .getJSONArray("runs") + .getJSONObject(0); + String[] browser = getBrowser(jsonObject1); + if (browser != null) { + responseSearch.setBeastBrowserId(browser[0]); + responseSearch.setPageType(browser[1]); + + + } + String[] watchEndPoint = getWatchEndPoint(jsonObject1); + if (watchEndPoint != null) { + responseSearch.setBeastVideoId(watchEndPoint[0]); + } + + responseSearch.setBeastSongTCovert(covertUrl); + responseSearch.setBeastSongTitle(title); + responseSearch.setBeastSongDescribe(subtitle); + + CommonUtils.LogMsg("--搜索结果 最佳" + "--songTitle=" + responseSearch.getBeastSongTitle() + "---videoId=" + responseSearch.getBeastVideoId() + "---browserId=" + responseSearch.getBeastBrowserId()); + } else if (musicShelfRenderer != null) { + headerTitle = getJsonTitle(musicShelfRenderer.getJSONObject("title"), 0); + contents1 = musicShelfRenderer.optJSONArray("contents"); + } + + responseSearch.setHeaderTitle(headerTitle); + + List list = new ArrayList<>(); + if (contents1 != null) { + for (int k = 0; k < contents1.length(); k++) { + ResponseSearchChild responseSearchChild = new ResponseSearchChild(); + + JSONObject musicResponsiveListItemRenderer = contents1.getJSONObject(k) + .optJSONObject("musicResponsiveListItemRenderer"); + + String playCount = ""; + String songTitle = ""; + String videoId = ""; + String describe = ""; + String browserId = ""; + String pageType = ""; + + String covertUrl1 = ""; + if (musicResponsiveListItemRenderer != null) { + covertUrl1 = getCovertUrl(musicResponsiveListItemRenderer. + getJSONObject("thumbnail") + .getJSONObject("musicThumbnailRenderer"), true); + + + String[] browser1 = getBrowser(musicResponsiveListItemRenderer); + if (browser1 != null) { + browserId = browser1[0]; + pageType = browser1[1]; + if (Objects.equals(pageType, "MUSIC_PAGE_TYPE_PODCAST_SHOW_DETAIL_PAGE") || pageType.equals("MUSIC_PAGE_TYPE_USER_CHANNEL")) { + //博客或者个人资料 + continue; + } + } + + JSONArray flexColumns = musicResponsiveListItemRenderer.getJSONArray("flexColumns"); + for (int h = 0; h < flexColumns.length(); h++) { + JSONObject musicResponsiveListItemFlexColumnRenderer = flexColumns.getJSONObject(h) + .getJSONObject("musicResponsiveListItemFlexColumnRenderer"); + JSONObject text = musicResponsiveListItemFlexColumnRenderer.getJSONObject("text"); + if (h == 0) { + songTitle = getJsonTitle(text, 0); + String[] runs = getWatchEndPoint(text.getJSONArray("runs").getJSONObject(0)); + videoId = runs[0]; + } else if (h == 1) { + describe = getJsonTextNew(text); + } else { + playCount = getJsonTitle(text, 0); + } + + + } + boolean b = videoId == null || videoId.isEmpty(); + if (b && pageType.isEmpty()) { + continue; + } + responseSearchChild.setBrowserId(browserId); + responseSearchChild.setPageType(pageType); + responseSearchChild.setSongCovert(covertUrl1); + responseSearchChild.setSongTitle(songTitle); + responseSearchChild.setSongDescribe(describe); + responseSearchChild.setSongPlayCount(playCount); + responseSearchChild.setSongVideoId(videoId); + CommonUtils.LogMsg("--搜索结果 videoId=" + videoId + "--songTitle=" + songTitle + "---browserId=" + browserId + "---pageType=" + pageType); + list.add(responseSearchChild); + } + + + } + } + if (list.size() > 0) { + responseSearch.setList(list); + } + if (musicCardShelfRenderer != null) { + searchList.add(responseSearch); + } else if (list.size() > 0) { + searchList.add(responseSearch); + } + + } + return searchList; + + } catch (JSONException e) { + CommonUtils.LogErrorMsg("--" + e.getMessage()); + return null; + + } + } + + + /** + * 根据搜索结果返回的browserId 进行请求返回的数据列表 + * + * @return + */ + public static ResponseResult ResolveSearchResultList(JSONObject jsonObject) { + try { + ResponseResult responseResult = new ResponseResult(); + + JSONObject contents = jsonObject.getJSONObject("contents"); + JSONObject musicImmersiveHeaderRenderer = jsonObject.getJSONObject("header") + .getJSONObject("musicImmersiveHeaderRenderer"); + + String mainTitle = getJsonTitle(musicImmersiveHeaderRenderer.getJSONObject("title"), 0); + String thumbnail = getCovertUrl(musicImmersiveHeaderRenderer.getJSONObject("thumbnail"), true); + + responseResult.setMainTitle(mainTitle); + responseResult.setMainCovert(thumbnail); + + JSONObject singleColumnBrowseResultsRenderer = contents.optJSONObject("singleColumnBrowseResultsRenderer"); + if (singleColumnBrowseResultsRenderer != null) { + JSONArray contentsArray = singleColumnBrowseResultsRenderer + .getJSONArray("tabs") + .getJSONObject(0) + .getJSONObject("tabRenderer") + .getJSONObject("content") + .getJSONObject("sectionListRenderer") + .getJSONArray("contents"); + List list = new ArrayList<>(); + for (int a = 0; a < contentsArray.length(); a++) { + ResponseResultList resultList = new ResponseResultList(); + List childList = new ArrayList<>(); + + + JSONObject arrayJSONObject = contentsArray.getJSONObject(a); + JSONObject musicShelfRenderer = arrayJSONObject.optJSONObject("musicShelfRenderer"); + JSONObject musicCarouselShelfRenderer = arrayJSONObject.optJSONObject("musicCarouselShelfRenderer"); + if (musicShelfRenderer != null) { + String headerTitle = getJsonTitle(musicShelfRenderer.getJSONObject("title"), 0); + resultList.setHeaderTitle(headerTitle); + CommonUtils.LogMsg("----------结果子页面111--------------------headerTitle" + headerTitle); + JSONArray contents1 = musicShelfRenderer.getJSONArray("contents"); + for (int f = 0; f < contents1.length(); f++) { + ResponseResultListChild responseResultListChild = new ResponseResultListChild(); + + JSONObject musicResponsiveListItemRenderer = contents1.getJSONObject(f) + .getJSONObject("musicResponsiveListItemRenderer"); + + String thumbnail1 = getCovertUrl(musicResponsiveListItemRenderer.getJSONObject("thumbnail"), true); + + JSONArray flexColumns = musicResponsiveListItemRenderer.getJSONArray("flexColumns"); + + String songName = ""; + String singerName = ""; + String playCount = ""; + String videoId = ""; + String playLIstId = ""; + for (int b = 0; b < flexColumns.length(); b++) { + JSONObject jsonObject1 = flexColumns.getJSONObject(b) + .getJSONObject("musicResponsiveListItemFlexColumnRenderer") + .getJSONObject("text"); + + switch (b) { + case 0: + songName = getJsonTitle(jsonObject1, 0); + String[] watchEndPoint = getWatchEndPoint(jsonObject1.getJSONArray("runs").getJSONObject(0)); + videoId = watchEndPoint[0]; + playLIstId = watchEndPoint[1]; + break; + case 1: + singerName = getJsonTitle(jsonObject1, 0); + break; + case 2: + playCount = getJsonTitle(jsonObject1, 0); + break; + } + } + + responseResultListChild.setThumbnail(thumbnail1); + responseResultListChild.setSongName(songName); + responseResultListChild.setSubTitle(singerName); + responseResultListChild.setPlayCount(playCount); + responseResultListChild.setVideoId(videoId); + responseResultListChild.setPlayListId(playLIstId); + + CommonUtils.LogMsg("----------结果子页面111 songName=" + songName + "---videoId=" + videoId + "--playLIstId=" + playLIstId); + childList.add(responseResultListChild); + } + } else if (musicCarouselShelfRenderer != null) { + String headerTitle = getJsonTitle(musicCarouselShelfRenderer.getJSONObject("header") + .getJSONObject("musicCarouselShelfBasicHeaderRenderer") + .getJSONObject("title"), 0); + resultList.setHeaderTitle(headerTitle); + CommonUtils.LogMsg("----------结果子页面222--------------------headerTitle" + headerTitle); + JSONArray contents1 = musicCarouselShelfRenderer.getJSONArray("contents"); + for (int f = 0; f < contents1.length(); f++) { + ResponseResultListChild responseResultListChild = new ResponseResultListChild(); + + JSONObject musicTwoRowItemRenderer = contents1.getJSONObject(f) + .getJSONObject("musicTwoRowItemRenderer"); + + String thumbnail1 = getCovertUrl(musicTwoRowItemRenderer.getJSONObject("thumbnailRenderer"), true); + + JSONObject title = musicTwoRowItemRenderer.getJSONObject("title"); + String albumName = getJsonTitle(title, 0); + + String[] browser = getBrowser(title.getJSONArray("runs").getJSONObject(0)); + String subtitle = getJsonTextNew(musicTwoRowItemRenderer.getJSONObject("subtitle")); + + if (browser != null) { + String browserId = browser[0]; + responseResultListChild.setBrowserId(browserId); + responseResultListChild.setPageType(browser[1]); + } + String[] watchEndPoint = getWatchEndPoint(musicTwoRowItemRenderer); + if (watchEndPoint != null) { + responseResultListChild.setVideoId(watchEndPoint[0]); + responseResultListChild.setPlayListId(watchEndPoint[1]); + } + responseResultListChild.setThumbnail(thumbnail1); + responseResultListChild.setSongName(albumName); + responseResultListChild.setSubTitle(subtitle); + + CommonUtils.LogMsg("----------结果子页面222 songName=" + albumName + "---videoId=" + responseResultListChild.getVideoId() + "--playLIstId=" + responseResultListChild.getPlayListId() + + "---BrowserId=" + responseResultListChild.getBrowserId() + "---PageType=" + responseResultListChild.getPageType()); + childList.add(responseResultListChild); + } + } + + + resultList.setChildList(childList); + list.add(resultList); + } + + + responseResult.setList(list); + + } + return responseResult; + + } catch (JSONException e) { + e.printStackTrace(); + return null; + } + } + + + private static ResponsePlayListInfo getCommonCategoryList(JSONObject musicResponsiveListItemRenderer) throws JSONException { + JSONObject jsonText = musicResponsiveListItemRenderer.getJSONArray("fixedColumns") + .getJSONObject(0) + .getJSONObject("musicResponsiveListItemFixedColumnRenderer") + .getJSONObject("text"); + + String duration = getJsonTitle(jsonText, 0); + long ms = CommonUtils.convertToMilliseconds(duration); + //比接口返回的减少一秒 + String textTime = CommonUtils.convertMillisToTime(ms); + + JSONArray flexColumns = musicResponsiveListItemRenderer + .getJSONArray("flexColumns"); + + ResponsePlayListInfo listInfo = new ResponsePlayListInfo(); + //歌名、歌手名字、描述 + String SongTitle = ""; + String SingerName = ""; + String Description = ""; + for (int g = 0; g < flexColumns.length(); g++) { + JSONObject jsonObjectText = flexColumns.getJSONObject(g) + .getJSONObject("musicResponsiveListItemFlexColumnRenderer") + .getJSONObject("text"); + String text = getJsonTitle(jsonObjectText, 0); + + CommonUtils.LogMsg("---------g=" + g + "---text=" + text); + if (g == 0) { + SongTitle = text; + + JSONObject runs = jsonObjectText.getJSONArray("runs").getJSONObject(0); + + String[] watchEndPoint = getWatchEndPoint(runs); + + listInfo.setVideoId(watchEndPoint[0]); + if (watchEndPoint[0] == null || watchEndPoint[0].isEmpty()) { + CommonUtils.LogMsg("---------getCommonCategoryList-SongTitle=" + SongTitle + "---setVideoId=null"); + } + + listInfo.setPlaylistId(watchEndPoint[1]); + listInfo.setParams(watchEndPoint[2]); + listInfo.setMusicVideoType(watchEndPoint[3]); + + } + if (g == 1) { + SingerName = text; + } + if (g == 2) { + Description = text; + } + } + listInfo.setDescribe(Description); + listInfo.setDuration(textTime); + listInfo.setDurationMs(ms); + listInfo.setSingerName(SingerName); + listInfo.setSongTitle(SongTitle); + + return listInfo; + } + + 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"); + + responseHome.setClickTrackingParams(clickTrackingParams); + responseHome.setContinuation(continuation); + + JSONArray contents = sectionListRenderer.getJSONArray("contents"); + + List 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); + + responseHomeChild.setHeaderTitle(title); + JSONArray childContents = musicCarouselShelfRenderer.getJSONArray("contents"); + + + List categoryList = new ArrayList<>(); + List 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 = getCovertUrl(musicResponsiveListItemRenderer.getJSONObject("thumbnail"), true); + CommonUtils.LogMsg("----图片-------------SingerHead=" + SingerHead); + + 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); + + //"musicVideoType":"MUSIC_VIDEO_TYPE_UGC" + if (j == 0) + CommonUtils.LogMsg(" ----------111111111111----SongTitle=" + SongTitle + "--getMusicVideoType=" + responseSingle.getMusicVideoType()); + } + + JSONObject musicTwoRowItemRenderer = jsonList.optJSONObject("musicTwoRowItemRenderer"); + if (musicTwoRowItemRenderer != null) { + //------------------------音乐合集或者专辑 + ResponseCategory responseCategory = new ResponseCategory(); + String covert = getCovertUrl(musicTwoRowItemRenderer.getJSONObject("thumbnailRenderer"), true); + + JSONObject title1 = musicTwoRowItemRenderer.getJSONObject("title"); + String twoTitle = getJsonTitle(title1, 0); + String twoSubtitle = getJsonTextNew(musicTwoRowItemRenderer.getJSONObject("subtitle")); + + + String browseId = ""; + String pageType = ""; + JSONObject runs = title1 + .getJSONArray("runs") + .getJSONObject(0); + JSONObject subtitle = musicTwoRowItemRenderer + .getJSONObject("subtitle") + .getJSONArray("runs") + .getJSONObject(0); + + String[] browser = null; + String[] browserTiTle = getBrowser(runs); + String[] browserSub = getBrowser(subtitle); + if (browserTiTle != null) { + browser = browserTiTle; + } else { + browser = browserSub; + } + if (browser != null) { + //-------------当pageType="MUSIC_PAGE_TYPE_PLAYLIST"或"MUSIC_PAGE_TYPE_ALBUM" 走这里 + browseId = browser[0]; + pageType = browser[1]; + + responseCategory.setPageType(pageType); + responseCategory.setBrowseId(browseId); + if (pageType.equals(MyValue.PAGE_TYPE_MV) || pageType.equals(MyValue.PAGE_TYPE_MV_LIST)) { + //视频mv 需要获取videoId 和PlayListId 用于进入播放页面请求播放列表数据 + + JSONObject jsonObject = musicTwoRowItemRenderer.getJSONObject("menu") + .getJSONObject("menuRenderer") + .getJSONArray("items") + .getJSONObject(0) + .getJSONObject("menuNavigationItemRenderer"); + String[] watchEndPoint = getWatchEndPoint(jsonObject); + if (watchEndPoint[0] != null) { + responseCategory.setVideoId(watchEndPoint[0]); + } + if (watchEndPoint[1] != null) { + responseCategory.setPlayListId(watchEndPoint[1]); + } + if (watchEndPoint[2] != null) { + responseCategory.setParams(watchEndPoint[2]); + } + if (watchEndPoint[3] != null) { + responseCategory.setMusicVideoType(watchEndPoint[3]); + } + } + } + responseCategory.setCovert(covert); + responseCategory.setTwoTitle(twoTitle); + responseCategory.setTwoSubtitle(twoSubtitle); + categoryList.add(responseCategory); + if (j == 0) { + CommonUtils.LogMsg(" ----------2222222222----twoTitle=" + twoTitle + "--BrowseId=" + + responseCategory.getBrowseId() + "---pageType=" + responseCategory.getPageType() + + "--VideoId=" + responseCategory.getVideoId() + + "---PlayListId=" + responseCategory.getPlayListId()); + } + } + + } + responseHomeChild.setCategoryList(categoryList); + responseHomeChild.setSingleList(singleList); + + childList.add(responseHomeChild); + } + + } + responseHome.setChildList(childList); + + + } + + + private static String[] getWatchEndPoint(JSONObject job) { + + String[] strings = new String[4]; + try { + JSONObject navigationEndpoint = job.optJSONObject("navigationEndpoint"); + if (navigationEndpoint == null) { + return strings; + } + JSONObject watchEndpoint = navigationEndpoint.getJSONObject("watchEndpoint"); + if (watchEndpoint.has("videoId")) { + strings[0] = watchEndpoint.getString("videoId"); + } + if (watchEndpoint.has("playlistId")) { + strings[1] = watchEndpoint.getString("playlistId"); + } + if (watchEndpoint.has("params")) { + strings[2] = watchEndpoint.getString("params"); + } + if (watchEndpoint.has("watchEndpointMusicSupportedConfigs")) { + strings[3] = watchEndpoint.getJSONObject("watchEndpointMusicSupportedConfigs").getJSONObject("watchEndpointMusicConfig").getString("musicVideoType"); + } + + } catch (JSONException exception) { + exception.printStackTrace(); + } + return strings; + } + + + /** + * @param jsonObject + * @param maxBig 是否取最大的一张图片 + * @return + */ + private static String getCovertUrl(JSONObject jsonObject, boolean maxBig) { + try { + + JSONArray jsonArray; + JSONObject musicThumbnailRenderer = jsonObject.optJSONObject("musicThumbnailRenderer"); + if (musicThumbnailRenderer == null) { + JSONObject thumbnail = jsonObject.optJSONObject("thumbnail"); + if (thumbnail != null) { + jsonArray = thumbnail.getJSONArray("thumbnails"); + } else { + return null; + } + } else { + jsonArray = musicThumbnailRenderer.getJSONObject("thumbnail").getJSONArray("thumbnails"); + } + + int length = jsonArray.length(); + int index = 0; + if (maxBig) { + index = length - 1; + } else if (length > 2) { + index = length - 2; + } + String pngUrl = jsonArray.getJSONObject(index).getString("url"); + return pngUrl; + } catch (JSONException exception) { + return null; + } + } + + + private static String[] getBrowser(JSONObject jsonObject) throws JSONException { + + String[] strings = new String[2]; + JSONObject navigationEndpoint = jsonObject.optJSONObject("navigationEndpoint"); + if (navigationEndpoint != null) { + + JSONObject browseEndpoint = navigationEndpoint.optJSONObject("browseEndpoint"); + if (browseEndpoint == null) return null; + strings[0] = browseEndpoint.getString("browseId"); + + strings[1] = browseEndpoint.getJSONObject("browseEndpointContextSupportedConfigs") + .getJSONObject("browseEndpointContextMusicConfig") + .getString("pageType"); + return strings; + } + return null; + } + + private static String getJsonTitle(JSONObject jsonObject, int index) { + String text = ""; + try { + JSONArray runs = jsonObject.getJSONArray("runs"); + if (index < runs.length()) { + text = runs.getJSONObject(index).getString("text"); + } + + } catch (JSONException exception) { + return text; + } + return text; + } + + + /** + * runs 数组的字符串拼接 + * + * @param jsonObject + * @return + */ + private static String getJsonTextNew(JSONObject jsonObject) { + StringBuilder text = new StringBuilder(); + try { + JSONArray runs = jsonObject.getJSONArray("runs"); + for (int i = 0; i < runs.length(); i++) { + String text1 = runs.getJSONObject(i).getString("text"); + text.append(text1); + } + + } catch (JSONException exception) { + + } + return text.toString(); + } +} diff --git a/app/src/main/java/com/hi/music/player/network/RetrofitManager.java b/app/src/main/java/com/hi/music/player/network/RetrofitManager.java new file mode 100644 index 0000000..e99bde6 --- /dev/null +++ b/app/src/main/java/com/hi/music/player/network/RetrofitManager.java @@ -0,0 +1,207 @@ +package com.hi.music.player.network; + +import com.google.gson.Gson; +import com.hi.music.player.MusicApplication; +import com.hi.music.player.api.RequestListener; +import com.hi.music.player.javabean.requestbody.BodyHome; +import com.hi.music.player.javabean.requestbody.BodyPlay; +import com.hi.music.player.javabean.requestbody.BodyPlayUrl; +import com.hi.music.player.javabean.requestbody.BodySearch; +import com.hi.music.player.javabean.requestbody.BodySearchSuggestion; +import com.hi.music.player.javabean.requestbody.child.Client; +import com.hi.music.player.javabean.requestbody.child.ContextBody; + +import java.util.HashMap; +import java.util.concurrent.TimeUnit; + +import io.reactivex.android.schedulers.AndroidSchedulers; +import io.reactivex.schedulers.Schedulers; +import okhttp3.MediaType; +import okhttp3.OkHttpClient; +import okhttp3.RequestBody; +import okhttp3.ResponseBody; +import okhttp3.logging.HttpLoggingInterceptor; +import retrofit2.Retrofit; +import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory; +import retrofit2.converter.gson.GsonConverterFactory; + +public class RetrofitManager { + + private String base_Host = "https://music.youtube.com/"; + + private String base_Host_test = "https://rr2---sn-tt1e7nlz.googlevideo.com/"; + private static volatile RetrofitManager REQUEST_MANAGER; + + private Retrofit retrofit; + public MediaType JSON = MediaType.get("application/json; charset=utf-8"); + private MusicApi musicApi; + + private String header1 = "playlistPanelVideoRenderer(title,navigationEndpoint,longBylineText,shortBylineText,thumbnail,lengthText)"; + private String header = "contents.singleColumnMusicWatchNextResultsRenderer.tabbedRenderer.watchNextTabbedResultsRenderer.tabs.tabRenderer.content.musicQueueRenderer.content.playlistPanelRenderer(continuations,contents(automixPreviewVideoRenderer," + header1 + "))"; + + private RetrofitManager() { + + musicApi = getRetrofit().create(MusicApi.class); + } + private synchronized Retrofit getRetrofit() { + if (retrofit == null) { + long DEFAULT_TIMEOUT = 5; + HttpLoggingInterceptor httpLoggingInterceptor = new HttpLoggingInterceptor(); + httpLoggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY); + OkHttpClient client = new OkHttpClient.Builder() + .connectTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS) + .writeTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS) + .readTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS) + .addInterceptor(httpLoggingInterceptor) + .build(); + retrofit = new Retrofit.Builder() + .baseUrl(base_Host) + .client(client) + .addConverterFactory(GsonConverterFactory.create()) + .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) + .build(); + } + return retrofit; + } + + public static RetrofitManager getInstance() { + if (REQUEST_MANAGER == null) { + synchronized (RetrofitManager.class) { //锁,防止线程问题 + if (REQUEST_MANAGER == null) { + REQUEST_MANAGER = new RetrofitManager(); + } + } + } + return REQUEST_MANAGER; + } + + + public void getHomeData(RequestListener requestListener) { + BodyHome bodyHome = new BodyHome(); + Gson gson = new Gson(); + String s = gson.toJson(bodyHome); + RequestBody requestBody = RequestBody.Companion.create(s, JSON); + musicApi.getHomeData(requestBody) + .subscribeOn(Schedulers.io()) + .unsubscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(new ObserverWrapper(requestListener)); + } + + public void getHomeMoreData(String continuation, String itct, String visitorData, RequestListener requestListener) { + BodyHome bodyHome = new BodyHome(); + bodyHome.getContext().getClient().setVisitorData(visitorData); + Gson gson = new Gson(); + String s = gson.toJson(bodyHome); + RequestBody requestBody = RequestBody.Companion.create(s, JSON); + + HashMap stringHashMap = new HashMap<>(); + stringHashMap.put("ctoken", continuation); + stringHashMap.put("continuation", continuation); + stringHashMap.put("type", "next"); + stringHashMap.put("itct", itct); + stringHashMap.put("prettyPrint", "false"); + musicApi.getHomeMoreData(continuation, continuation, "next", itct, false, requestBody) + .subscribeOn(Schedulers.io()) + .unsubscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(new ObserverWrapper(requestListener)); + } + + + public void getPlayList(String params, String playlistId, String videoId, String musicVideoType, RequestListener requestListener) { + BodyPlay bodyPlay = new BodyPlay(); + bodyPlay.setParams(params); + bodyPlay.setPlaylistId(playlistId); + bodyPlay.setVideoId(videoId); + bodyPlay.getWatchEndpointMusicSupportedConfigs().setMusicVideoType(musicVideoType); + Gson gson = new Gson(); + String s = gson.toJson(bodyPlay); + RequestBody requestBody = RequestBody.Companion.create(s, JSON); + musicApi.getMusicPlayPage(header, requestBody) + .subscribeOn(Schedulers.io()) + .unsubscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(new ObserverWrapper(requestListener)); + } + + + public void getPlayUrl(String videoId, RequestListener requestListener) { + BodyPlayUrl bodyPlay = new BodyPlayUrl(); + bodyPlay.setVideoId(videoId); + Client client = bodyPlay.getContext().getClient(); + client.setClientName("ANDROID"); + client.setClientVersion("19.05.36"); + client.setPlatform("DESKTOP"); + String visitorData = MusicApplication.getVisitorData(); +// client.setVisitorData("CgtWN1RXaURPN3LNZyiZK9e4BjIKCgJVUXIEGgAgPW%3D%3D"); + client.setVisitorData(visitorData); +// bodyPlay.getContext().getThirdParty().setEmbedUrl("https://www.youtube.com/watch?v="+videoId); + + + Gson gson = new Gson(); + String s = gson.toJson(bodyPlay); + RequestBody requestBody = RequestBody.Companion.create(s, JSON); + musicApi.getMusicPlayUrl(requestBody) + .subscribeOn(Schedulers.io()) + .unsubscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(new ObserverWrapper(requestListener)); + } + + + public void getCategoryList(String browserId, RequestListener requestListener) { + BodyHome bodyHome = new BodyHome(); + bodyHome.setBrowseId(browserId); +// bodyHome.setBrowseId("VLPLI-asvmHZWNo_xjMMfD_v2O2lTyCdrjaK"); + + Client client = bodyHome.getContext().getClient(); + client.setClientVersion("1.20240506.01.00"); + + + 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<>(requestListener)); + } + + + + + public void getSearchSuggestion(String input, RequestListener requestListener) { + BodySearchSuggestion body = new BodySearchSuggestion(); + body.setInput(input); + Client client = body.getContext().getClient(); + client.setClientVersion("1.20240506.01.00"); + Gson gson = new Gson(); + String s = gson.toJson(body); + RequestBody requestBody = RequestBody.Companion.create(s, JSON); + musicApi.getSearchSuggestion(requestBody) + .subscribeOn(Schedulers.io()) + .unsubscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(new ObserverWrapper<>(requestListener)); + } + + + public void getSearch(String query, RequestListener requestListener) { + BodySearch body = new BodySearch(); + body.setQuery(query); + Client client = body.getContext().getClient(); + client.setClientVersion("1.20240506.01.00"); + Gson gson = new Gson(); + String s = gson.toJson(body); + RequestBody requestBody = RequestBody.Companion.create(s, JSON); + musicApi.getSearch(requestBody) + .subscribeOn(Schedulers.io()) + .unsubscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(new ObserverWrapper<>(requestListener)); + } +} diff --git a/app/src/main/java/com/hi/music/player/ui/activity/BaseActivity.java b/app/src/main/java/com/hi/music/player/ui/activity/BaseActivity.java new file mode 100644 index 0000000..35d24e9 --- /dev/null +++ b/app/src/main/java/com/hi/music/player/ui/activity/BaseActivity.java @@ -0,0 +1,252 @@ +package com.hi.music.player.ui.activity; + +import android.content.Intent; +import android.net.Uri; +import android.os.Bundle; +import android.os.Handler; +import android.view.Gravity; +import android.view.View; +import android.view.Window; +import android.view.WindowManager; +import android.widget.FrameLayout; +import android.widget.Toast; + +import androidx.annotation.NonNull; +import androidx.annotation.OptIn; +import androidx.appcompat.app.AppCompatActivity; +import androidx.core.view.WindowCompat; +import androidx.core.view.WindowInsetsControllerCompat; +import androidx.lifecycle.Observer; +import androidx.lifecycle.ViewModel; +import androidx.media3.common.MediaItem; +import androidx.media3.common.Player; +import androidx.media3.common.util.UnstableApi; +import androidx.viewbinding.ViewBinding; + +import com.bumptech.glide.Glide; +import com.bumptech.glide.load.resource.bitmap.CircleCrop; +import com.hi.music.player.MusicApplication; +import com.hi.music.player.R; +import com.hi.music.player.api.onPlayNextListener; +import com.hi.music.player.databinding.ActivityBaseBinding; +import com.hi.music.player.databinding.LayoutPanelBinding; +import com.hi.music.player.helper.CommonUtils; +import com.hi.music.player.helper.MyValue; +import com.hi.music.player.helper.ViewModelScope; +import com.hi.music.player.media3.MyMediaControllerManager; +import com.hi.music.player.ui.activity.viewmodel.VMApplication; + +public abstract class BaseActivity extends AppCompatActivity implements View.OnClickListener { + + private final ViewModelScope mViewModelScope = new ViewModelScope(); + + protected T vb; + + private Window window; + private View decorView; + protected View mView; + private ActivityBaseBinding rootVb; + + protected VMApplication vmApplication; + protected MyMediaControllerManager mediaControllerManager; + + private Handler mHandler; + private Runnable mRunnable; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + vb = getViewBinding(); + View root = vb.getRoot(); + rootVb = ActivityBaseBinding.inflate(getLayoutInflater(), null, false); + rootVb.frameLayout.addView(root); + setContentView(rootVb.getRoot()); + window = getWindow(); + decorView = window.getDecorView(); + mView = decorView.getRootView(); + vmApplication = getApplicationScopeViewModel(VMApplication.class); + mediaControllerManager = MyMediaControllerManager.getInstance(); + + if (showPanel()) + initPanel(); + setStatusBar(); + if (isFullScreen()) { +// ImmersionBar.with(this).init();//设置沉浸式效果 +// ImmersionBar.setStatusBarView(this,0); + initFullScreen(); + } + onCreateInit(); + onInitClick(); + } + + protected abstract T getViewBinding(); + + + protected abstract void onCreateInit(); + + protected abstract void onInitClick(); + + public abstract boolean isFullScreen(); + + public abstract boolean statusBarLight(); + + public abstract boolean showPanel(); + + + + private void initPanel() { + LayoutPanelBinding panelVb = LayoutPanelBinding.inflate(getLayoutInflater()); + View panelView = panelVb.getRoot(); + FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams( + FrameLayout.LayoutParams.MATCH_PARENT, + CommonUtils.dpToPx(74) + ); + initProgressHandler(panelVb); + panelVb.imPlay.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + panelVb.imPlay.setSelected(!panelVb.imPlay.isSelected()); + if (panelVb.imPlay.isSelected()) { + mediaControllerManager.play(); + } else { + mediaControllerManager.pause(); + } + + } + }); + panelVb.imNext.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + mediaControllerManager.playNext(new onPlayNextListener() { + @Override + public void onPlayNext(boolean hasNext) { + if (!hasNext) { + Toast.makeText(MusicApplication.myApplication, getString(R.string.no_next_song_yet), Toast.LENGTH_SHORT).show(); + } + } + }); + } + }); + panelVb.layoutPanel.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + Intent intent = new Intent(MusicApplication.myApplication, PlayActivity.class); + intent.putExtra(MyValue.KEY_ENTER_SOURCE, MyValue.TYPE_ENTER_PANEL); + startActivity(intent); + } + }); + + if (this instanceof HomeActivity) { + layoutParams.setMargins(CommonUtils.dpToPx(22), 0, CommonUtils.dpToPx(22), CommonUtils.dpToPx(82)); + } else { + layoutParams.setMargins(CommonUtils.dpToPx(22), 0, CommonUtils.dpToPx(22), CommonUtils.dpToPx(10)); + } + + layoutParams.gravity = Gravity.BOTTOM; + vmApplication.playStatus.observe(this, new Observer() { + @OptIn(markerClass = UnstableApi.class) + @Override + public void onChanged(Integer integer) { + CommonUtils.LogMsg("----------面板 播放状态更新=" + integer); + if (panelView.getParent() == null) { + CommonUtils.LogMsg("----------显示面板"); + rootVb.frameLayout.addView(panelView, layoutParams); + mHandler.post(mRunnable); + } + MediaItem currentMediaItem = mediaControllerManager.getCurMediaItem(); + if(currentMediaItem!= null){ + Uri artworkUri = currentMediaItem.mediaMetadata.artworkUri; + CharSequence title = currentMediaItem.mediaMetadata.title; + CharSequence artist = currentMediaItem.mediaMetadata.artist; + + if (currentMediaItem.mediaMetadata.durationMs != null) { + long durationMs = currentMediaItem.mediaMetadata.durationMs; + panelVb.circularPb.setMaxProgress((int) durationMs); + } + if(artworkUri!= null){ + CommonUtils.LogMsg("----------artworkUri="+artworkUri.toString()); + Glide.with(MusicApplication.myApplication) + .load(artworkUri.toString()) + .placeholder(R.mipmap.im_placeholder) + .transform(new CircleCrop()) + .into(panelVb.image); + } + panelVb.title.setText(title); + panelVb.singer.setText(artist); + } + + switch (integer) { + case Player.STATE_IDLE: + + case Player.STATE_ENDED: + case Player.STATE_READY: + + case MyValue.PLAY_STATUS_CODE_PAUSE: + case Player.STATE_BUFFERING: + case MyValue.PLAY_STATUS_CODE_ERROR: + + //快进没有缓冲的时候触发 + //播放完成 + panelVb.imPlay.setSelected(false); + break; + case MyValue.PLAY_STATUS_CHANGE_MUSIC: + case MyValue.PLAY_STATUS_CODE_PLAYING: + panelVb.imPlay.setSelected(true); + break; + + } + + } + }); + + + } + + + private void initProgressHandler(LayoutPanelBinding panelBinding) { + mHandler = new Handler(); + mRunnable = new Runnable() { + @Override + public void run() { + updatePlaybackProgress(panelBinding); + mHandler.postDelayed(this, 1000); + } + }; + } + + private void updatePlaybackProgress(LayoutPanelBinding panelBinding) { + // 获取当前播放位置 + long contentPos = mediaControllerManager.getContentPos(); + long bufferPos = mediaControllerManager.getBufferPos(); + +// CommonUtils.LogMsg("---------播放进度-----contentPos=" + contentPos + "-----缓冲进度=" + bufferPos); + panelBinding.circularPb.setProgress((int) contentPos); + } + private void setStatusBar() { + //深色模式 + WindowInsetsControllerCompat insetsController = WindowCompat.getInsetsController(getWindow(), getWindow().getDecorView()); + insetsController.setAppearanceLightStatusBars(statusBarLight()); + } + + private void initFullScreen() { + decorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR); + window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); +// mView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION); + mView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN); + } + + protected K getActivityScopeViewModel(@NonNull Class modelClass) { + return mViewModelScope.getActivityScopeViewModel(this, modelClass); + } + + protected K getApplicationScopeViewModel(@NonNull Class modelClass) { + return mViewModelScope.getApplicationScopeViewModel(modelClass); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + if (mHandler != null && mRunnable != null) + mHandler.removeCallbacks(mRunnable); + } +} \ No newline at end of file diff --git a/app/src/main/java/com/hi/music/player/ui/activity/CategoryListActivity.java b/app/src/main/java/com/hi/music/player/ui/activity/CategoryListActivity.java new file mode 100644 index 0000000..4644a37 --- /dev/null +++ b/app/src/main/java/com/hi/music/player/ui/activity/CategoryListActivity.java @@ -0,0 +1,259 @@ +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.Context; +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.HomeItemClickListener; +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.ResponsePlayListInfo; +import com.hi.music.player.javabean.response.child.ResponseCategory; +import com.hi.music.player.javabean.response.child.ResponseSingle; +import com.hi.music.player.ui.activity.viewmodel.VMApplication; +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 implements HomeItemClickListener { + + + private VMCategoryList vm; +// private VMApplication vmApplication; + + private ResponseCategoryList mCategoryList; + private String mPageType; + private String twoSubtitle; + private String browseId; + + @Override + protected ActivityCategoryListBinding getViewBinding() { + return ActivityCategoryListBinding.inflate(getLayoutInflater()); + } + + @Override + protected void onCreateInit() { + vm = getActivityScopeViewModel(VMCategoryList.class); +// vmApplication = getApplicationScopeViewModel(VMApplication.class); + Intent intent = getIntent(); + if (intent == null) { + return; + } + + mPageType= intent.getStringExtra(MyValue.KEY_CATEGORY_LIST_TYPE); + twoSubtitle = intent.getStringExtra(MyValue.KEY_CATEGORY_LIST_SINGER_NAME); + browseId = intent.getStringExtra(MyValue.KEY_CATEGORY_LIST_BROWSER_ID); + + vb.pbLoading.setVisibility(View.VISIBLE); + vm.getList(browseId); + vm.data.observe(this, new Observer() { + @Override + public void onChanged(ResponseCategoryList responseCategoryList) { + if (responseCategoryList == null) { + vb.pbLoading.setVisibility(View.GONE); + updateErrorLayout(getString(R.string.playList_loading_failed),true); + return; + } + mCategoryList = responseCategoryList; + loadInfo(mCategoryList); + + } + }); + } + private void updateErrorLayout(String msg, boolean isShow) { + if (isShow) + vb.layoutError.linearRetry.setVisibility(View.VISIBLE); + else vb.layoutError.linearRetry.setVisibility(View.GONE); + + if(msg!= null&&!msg.isEmpty()){ + vb.layoutError.tvErrorMsg.setText(msg); + } + } + @Override + protected void onInitClick() { + vb.imBack.setOnClickListener(this); + vb.btnPlay.setOnClickListener(this); + vb.layoutError.tvRetry.setOnClickListener(this); + } + + @Override + public boolean isFullScreen() { + return true; + } + + @Override + public boolean statusBarLight() { + return false; + } + + @Override + public boolean showPanel() { + return true; + } + + @Override + public void onClick(View v) { + if (v.equals(vb.imBack)) { + finish(); + } else if (v.equals(vb.btnPlay)) { + int musicIndex = 0; + Intent intent = new Intent(this, PlayActivity.class); + intent.putExtra(MyValue.KEY_PLAY_ACTIVITY_CATEGORY_LIST, mCategoryList.getList().get(musicIndex)); +// intent.putExtra(MyValue.KEY_PLAY_ACTIVITY_CATEGORY_LIST_INDEX,musicIndex); + intent.putExtra(MyValue.KEY_ENTER_SOURCE, MyValue.TYPE_ENTER_SOURCE_CATEGORY); + startActivity(intent); + vmApplication.reSetPlayList(mCategoryList.getList()); + }else if(v.equals(vb.layoutError.tvRetry)){ + vb.pbLoading.setVisibility(View.VISIBLE); + updateErrorLayout(null,false); + vm.getList(browseId); + } + } + + private void loadInfo(ResponseCategoryList info) { + vb.btnPlay.setVisibility(View.VISIBLE); + switch (mPageType) { + case MyValue.PAGE_TYPE_LIST: + vb.tvSingerName.setVisibility(View.GONE); + break; + case MyValue.PAGE_TYPE_ALBUM: + vb.tvSingerName.setVisibility(View.VISIBLE); + String singNameValue = ""; + String singName = info.getSingName(); + if(singName!= null&&!singName.isEmpty()){ + singNameValue= singName; + }else { + String[] split = twoSubtitle.split("•"); + if (split != null && split.length > 1) { + singNameValue = split[1]; + } + } + CommonUtils.LogMsg("singNameValue=" + singNameValue); + vb.tvSingerName.setText(singNameValue); + for (ResponsePlayListInfo playListInfo : info.getList()) { + playListInfo.setSingerName(singNameValue); + } + break; + } + vb.tvTitle.setText(info.getTitle()); + vb.tvSubTitle.setText(info.getDescription()); + + AdapterCategoryList adapterCategoryList = new AdapterCategoryList(); + adapterCategoryList.setHomeItemClickListener(this); + adapterCategoryList.setPageType(mPageType); + 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() { + @Override + public boolean onLoadFailed(@Nullable GlideException e, @Nullable Object model, @NonNull Target target, boolean isFirstResource) { + return false; + } + + @Override + public boolean onResourceReady(@NonNull Drawable resource, @NonNull Object model, Target 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.im_placeholder) + .listener(new RequestListener() { + @Override + public boolean onLoadFailed(@Nullable GlideException e, @Nullable Object model, @NonNull Target 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 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); + + + } + + + + @Override + public void onClickItemCategoryList(ResponsePlayListInfo data, int musicIndex) { + Intent intent = new Intent(this, PlayActivity.class); + intent.putExtra(MyValue.KEY_PLAY_ACTIVITY_CATEGORY_LIST, data); + intent.putExtra(MyValue.KEY_PLAY_ACTIVITY_CATEGORY_LIST_INDEX, musicIndex); + intent.putExtra(MyValue.KEY_ENTER_SOURCE, MyValue.TYPE_ENTER_SOURCE_CATEGORY); + startActivity(intent); + vmApplication.reSetPlayList(mCategoryList.getList()); + } + + @Override + protected void onResume() { + super.onResume(); + + + } +} \ No newline at end of file diff --git a/app/src/main/java/com/hi/music/player/ui/activity/PlayActivity.java b/app/src/main/java/com/hi/music/player/ui/activity/PlayActivity.java new file mode 100644 index 0000000..bd0caf3 --- /dev/null +++ b/app/src/main/java/com/hi/music/player/ui/activity/PlayActivity.java @@ -0,0 +1,862 @@ +package com.hi.music.player.ui.activity; + +import android.content.Intent; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.GradientDrawable; +import android.net.Uri; +import android.os.Handler; +import android.view.View; +import android.view.animation.Animation; +import android.view.animation.AnimationUtils; +import android.widget.SeekBar; +import android.widget.Toast; + +import androidx.annotation.NonNull; +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; +import androidx.media3.datasource.cache.SimpleCache; +import androidx.media3.exoplayer.offline.Download; +import androidx.media3.exoplayer.offline.DownloadManager; +import androidx.media3.exoplayer.offline.DownloadRequest; +import androidx.media3.exoplayer.offline.DownloadService; +import androidx.recyclerview.widget.LinearLayoutManager; + +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.google.gson.Gson; +import com.hi.music.player.MusicApplication; +import com.hi.music.player.R; +import com.hi.music.player.adapter.AdapterDownloadSong; +import com.hi.music.player.adapter.AdapterPlayList; +import com.hi.music.player.api.LikeSongListener; +import com.hi.music.player.api.MediaControllerListener; +import com.hi.music.player.api.onCheckDownload; +import com.hi.music.player.api.onImageColorListener; +import com.hi.music.player.api.onPlayNextListener; +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.BoxDownloadSong; +import com.hi.music.player.javabean.BoxLikeSong; +import com.hi.music.player.javabean.CustomerDownload; +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.ResponseCategory; +import com.hi.music.player.javabean.response.child.ResponseSingle; +import com.hi.music.player.media3.MyDownloadService; +import com.hi.music.player.media3.MyMediaControllerManager; +import com.hi.music.player.media3.MyPlayCacheManager; +import com.hi.music.player.objectbox.ObjectBoxManager; +import com.hi.music.player.ui.activity.viewmodel.VMApplication; +import com.hi.music.player.ui.activity.viewmodel.VMPlay; + +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.List; + +public class PlayActivity extends BaseActivity implements SeekBar.OnSeekBarChangeListener { + + + //单曲进入传递的数据 + private ResponseSingle responseSingle; + private VMPlay vmPlay; + + + private Handler mHandler; + private Runnable mRunnable; + + + private AdapterPlayList adapterPlayList; + + + //播放列表ui初始化 + private boolean initPlayList = false; + + private GradientDrawable gradientDrawable; + private int lighterColor, darkerColor; + + // 0 播放列表请求失败 1 立即播放的歌曲请求失败 2 封面图加载失败 + private int netError = 0; + + + // 请求失败的立即播放的歌曲信息 + private CustomerUrlInfo mCustomerUrlInfo; + private int mEnterType; + + + //-------单曲进入播放列表接口请求需要的参数 + private String playlistId, videoId, params, musicVideoType = ""; + //-------单曲进入播放列表接口请求需要的参数 + + private int mDefaultPlayStartIndex = 0; + + + private int[] imageStates = { + R.drawable.icon_looper_no, + R.drawable.icon_looper_1, + R.drawable.icon_looper + }; + + //0 不循环、1 列表循环、2 单曲循环、 + private int currentMode = 0; + + private List downloadSongList = new ArrayList<>(); + + @Override + protected ActivityPlayBinding getViewBinding() { + return ActivityPlayBinding.inflate(getLayoutInflater()); + } + + @Override + protected void onCreateInit() { +// vb.progressBarLoading.setVisibility(View.VISIBLE); + vmPlay = getActivityScopeViewModel(VMPlay.class); + initMediaController(); + Intent intent = getIntent(); + mEnterType = intent.getIntExtra(MyValue.KEY_ENTER_SOURCE, MyValue.TYPE_ENTER_SOURCE_SINGLE); + initPlayerView(); + initProgressHandler(); + + CommonUtils.LogMsg("--------mEnterType=" + mEnterType); + switch (mEnterType) { + case MyValue.TYPE_ENTER_SOURCE_SINGLE: + // 0--首页单曲进入 + updateMediaPlayList(); + responseSingle = (ResponseSingle) intent.getSerializableExtra(MyValue.KEY_PLAY_ACTIVITY_SINGER); + playlistId = responseSingle.getPlaylistId(); + videoId = responseSingle.getVideoId(); + params = responseSingle.getParams(); + musicVideoType = responseSingle.getMusicVideoType(); + mDefaultPlayStartIndex = intent.getIntExtra(MyValue.KEY_PLAY_ACTIVITY_CATEGORY_LIST_INDEX, mDefaultPlayStartIndex); + + SimpleCache playCache = MyPlayCacheManager.getPlayCache(); +// boolean songCached = CommonUtils.isSongCached(playCache, videoId); + +// CommonUtils.LogMsg("---------------是否有播放缓存--songCached="+songCached +"--name="+responseSingle.getSongTitle()); + + vmPlay.getPlayMusicList(playlistId, videoId, params, musicVideoType); + vmPlay.playList.observe(this, new Observer>() { + @Override + public void onChanged(List listInfos) { + vmApplication.reSetPlayList(listInfos); + } + }); + break; + case MyValue.TYPE_ENTER_SOURCE_CATEGORY: + // 1--首页音乐分类合集列表进入 + + updateMediaPlayList(); + ResponsePlayListInfo playListInfo = (ResponsePlayListInfo) intent.getSerializableExtra(MyValue.KEY_PLAY_ACTIVITY_CATEGORY_LIST); + mDefaultPlayStartIndex = intent.getIntExtra(MyValue.KEY_PLAY_ACTIVITY_CATEGORY_LIST_INDEX, mDefaultPlayStartIndex); + videoId = playListInfo.getVideoId(); + break; + + case MyValue.TYPE_ENTER_SOURCE_MV: + // 2--首页单个视频mv进入 + + updateMediaPlayList(); + ResponseCategory responseCategory = (ResponseCategory) intent.getSerializableExtra(MyValue.KEY_PLAY_ACTIVITY_MV); + videoId = responseCategory.getVideoId(); + playlistId = responseCategory.getPlayListId(); + params = responseCategory.getParams(); + musicVideoType = responseCategory.getMusicVideoType(); + vmPlay.getPlayMusicList(playlistId, videoId, params, musicVideoType); + vmPlay.playList.observe(this, new Observer>() { + @Override + public void onChanged(List listInfos) { + vmApplication.reSetPlayList(listInfos); + } + }); + + break; + + case MyValue.TYPE_ENTER_PANEL: + // 3--控制面板进入 + MediaItem curMediaItem = mediaControllerManager.getCurMediaItem(); + loadInfo(curMediaItem); + mHandler.post(mRunnable); + vb.progressBarLoading.setVisibility(View.GONE); + vb.btnPlay.setSelected(mediaControllerManager.getIsPlaying()); + break; + + case MyValue.TYPE_ENTER_LIKE: + // 4--从喜爱歌曲进入 + updateMediaPlayList(); + videoId = intent.getStringExtra(MyValue.KEY_PLAY_VIDEO_ID); + mDefaultPlayStartIndex = intent.getIntExtra(MyValue.KEY_PLAY_INDEX, mDefaultPlayStartIndex); + CommonUtils.LogMsg("-------------like进入 videoId= " + videoId + "---mDefaultPlayStartIndex=" + mDefaultPlayStartIndex); + break; + + case MyValue.TYPE_ENTER_DOWNLOAD: + // 5--从下载歌曲进入 + videoId = intent.getStringExtra(MyValue.KEY_PLAY_VIDEO_ID); + mDefaultPlayStartIndex = intent.getIntExtra(MyValue.KEY_PLAY_INDEX, mDefaultPlayStartIndex); + CommonUtils.LogMsg("-------------下载歌曲 videoId= " + videoId + "---mDefaultPlayStartIndex=" + mDefaultPlayStartIndex); + break; + } + + addDownloadListener(); + vmPlay.playUrlLiveData.observe(this, new Observer() { + @Override + public void onChanged(CustomerUrlInfo customerUrlInfo) { + if (customerUrlInfo.isNeedPlayNow() && customerUrlInfo.getPlayUrl() == null) { + // TODO: 2024/9/26 需要马上播放这首歌曲,但是此次网络请求失败 + CommonUtils.LogErrorMsg("-------------需要马上播放这首歌曲,但是此次网络请求失败"); + updateErrorLayout(getString(R.string.song_loading_failed), true); + netError = 1; + mCustomerUrlInfo = customerUrlInfo; + initShowPlayList(false); + vb.progressBarLoading.setVisibility(View.GONE); + mediaControllerManager.pause(); + return; + } + int second = customerUrlInfo.getPlayMusicIndex(); + if (customerUrlInfo.isNeedPlayNow()) { + mediaControllerManager.playPositionMusic(second); +// mediaControllerManager.play(); + } + } + }); + + + } + + + /** + * 更新播放列表 + */ + private void updateMediaPlayList() { + vmApplication.playList.observe(this, new Observer>() { + @Override + public void onChanged(List playList) { + if (playList == null) { + CommonUtils.LogErrorMsg("--------更新-playList null"); + netError = 0; + updateErrorLayout(getString(R.string.playList_loading_failed), true); + vb.progressBarLoading.setVisibility(View.GONE); + return; + } + CommonUtils.LogMsg("--------更新-playList " + playList.size() + "--videoId=" + videoId); + if (playList.size() > 0) { + updateErrorLayout(null, false); + setPlayListAndGetUrl(playList, videoId, mDefaultPlayStartIndex); + } + } + }); + } + + private void updateErrorLayout(String msg, boolean isShow) { + if (isShow) + vb.layoutError.linearRetry.setVisibility(View.VISIBLE); + else vb.layoutError.linearRetry.setVisibility(View.GONE); + + if (msg != null && !msg.isEmpty()) { + vb.layoutError.tvErrorMsg.setText(msg); + } + } + + /** + * 设置播放列表,并请求当前需要播放的音乐url + * + * @param list + * @param id + * @param index + */ + @OptIn(markerClass = UnstableApi.class) + private void setPlayListAndGetUrl(List list, String id, int index) { + mediaControllerManager.resetPlayList(); + MyMediaControllerManager.getInstance().setPlayList(list); + MyDownloadService.queryIsDownload(id, new onCheckDownload() { + @Override + public void onHasDownload(CustomerDownload customerDownload) { + if(customerDownload.isDownload()){ + MediaItem mediaItem = CommonUtils.downloadToMediaItem(customerDownload.getDownloadData()); + mediaControllerManager.replaceMediaItem(index,mediaItem); + CommonUtils.LogMsg("-------------setPlayListAndGetUrl已经下载过 index" + index + "---playNow=" + true); + mediaControllerManager.playPositionMusic(index); + }else { + CommonUtils.LogMsg("-------------setPlayListAndGetUrl index" + index + "---playNow=" + true); + vmPlay.getPlayUrl(id, index, true); + } + + } + }); + + } + + @OptIn(markerClass = UnstableApi.class) + private void initPlayerView() { + vb.playerView.setShowRewindButton(false); + vb.playerView.setShowPreviousButton(false); + vb.playerView.setPlayer(mediaControllerManager.getMediaController()); + } + + private void initMediaController() { + int repeatMode = mediaControllerManager.getRepeatMode(); + CommonUtils.LogMsg("-------------repeatMode=" + repeatMode); + vb.btnLoop.setImageResource(imageStates[repeatMode]); + mediaControllerManager.addListener(vmApplication, new MediaControllerListener() { + @Override + public void onPlayStatus(int playStatus) { + + switch (playStatus) { + case Player.STATE_IDLE: + CommonUtils.LogMsg("-------------IDLE"); + break; + case Player.STATE_BUFFERING: + //快进没有缓冲的时候触发 + vb.btnPlay.setSelected(false); +// vb.progressBarLoading.setVisibility(View.VISIBLE); + CommonUtils.LogMsg("-------------缓冲"); + break; + case Player.STATE_READY: + vb.btnPlay.setSelected(true); + mHandler.post(mRunnable); + vb.progressBarLoading.setVisibility(View.GONE); + CommonUtils.LogMsg("-------------准备"); + break; + + case Player.STATE_ENDED: + //播放完成 + vb.btnPlay.setSelected(false); + CommonUtils.LogMsg("------------- 播放完成"); + mHandler.removeCallbacks(mRunnable); // 停止更新 + updatePlayComplete(); + 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); + updateErrorLayout(null, false); + 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); + mediaControllerManager.playPositionMusic(currentMediaItemIndex); + // TODO: 2024/10/16 + break; + + } + } + + @Override + public void onRequestNextUri(String videoId, int playListIndex, boolean playNow) { + if (playNow) { +// vb.progressBarLoading.setVisibility(View.VISIBLE); + } + vmPlay.getPlayUrl(videoId, playListIndex, playNow); + } + + + @Override + public void onChangeMusic(MediaItem mediaItem) { + CommonUtils.LogMsg("歌曲切换-" + mediaItem.mediaMetadata.title + "---id=" + mediaItem.mediaId); + loadInfo(mediaItem); + } + + }); + } + + @Override + protected void onInitClick() { + vb.btnPlay.setOnClickListener(this); + vb.playProgress.setOnSeekBarChangeListener(this); + vb.btnNext.setOnClickListener(this); + vb.btnPrevious.setOnClickListener(this); + vb.imBack.setOnClickListener(this); + vb.btnMusicList.setOnClickListener(this); + vb.layoutError.tvRetry.setOnClickListener(this); + vb.btnLoop.setOnClickListener(this); + vb.layoutLike.setOnClickListener(this); + vb.layoutDownload.setOnClickListener(this); + + } + + + /** + * 更新播放进度Ui、缓冲进度 + */ + private void updatePlaybackProgress() { + // 获取当前播放位置 + long contentPos = mediaControllerManager.getContentPos(); + long bufferPos = mediaControllerManager.getBufferPos(); + + + String s = CommonUtils.convertMillisToTime(contentPos); + vb.tvCurrent.setText(s); + vb.playProgress.setProgress((int) contentPos); + vb.progressBarBuffer.setProgress((int) bufferPos); + } + + + /** + * 播放完成 更新播放进度Ui + */ + private void updatePlayComplete() { + vb.tvCurrent.setText(vb.tvDuration.getText().toString()); + } + + + /** + * 加载当前播放歌曲信息 + */ + @OptIn(markerClass = UnstableApi.class) + private void loadInfo(MediaItem mediaItem) { + MediaMetadata mediaMetadata = mediaItem.mediaMetadata; + CommonUtils.LogMsg("--------------加载当前播放歌曲信息 title=" + mediaMetadata.title); + 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); + } + if (vb.layoutPlayList.linearLayout.getVisibility() == View.VISIBLE) { + updatePlayListUi(); + } + + //刷新当前歌曲的喜爱状态 + boolean isLike = ObjectBoxManager.queryIsLike(mediaItem.mediaId); + vb.imLike.setSelected(isLike); + + //刷新当前歌曲的下载状态 + String mediaId = mediaItem.mediaId; + MyDownloadService.queryIsDownload(mediaId, new onCheckDownload() { + @Override + public void onHasDownload(CustomerDownload customerDownload) { + vb.imDownload.setSelected(customerDownload.isDownload()); + } + }); + + } + + private void loadCovert(String url) { + + Glide.with(MusicApplication.myApplication) + .asDrawable() + .load(url) + .placeholder(R.mipmap.im_placeholder) + .listener(new RequestListener() { + @Override + public boolean onLoadFailed(@Nullable GlideException e, @Nullable Object model, @NonNull Target target, boolean isFirstResource) { + CommonUtils.LogMsg(e.getMessage()); +// netError = 2; +// vb.tvErrorMsg.setText(getString(R.string.image_loading_failed)); +// vb.linearRetry.setVisibility(View.VISIBLE); + return false; + } + + @OptIn(markerClass = UnstableApi.class) + @Override + public boolean onResourceReady(@NonNull Drawable resource, @NonNull Object model, Target target, @NonNull DataSource dataSource, boolean isFirstResource) { + vb.imCovert.setImageDrawable(resource); + CommonUtils.getMainColor(resource, new onImageColorListener() { + @Override + public void onImageColor(int color) { + if (color == -1) { + return; + } + lighterColor = CommonUtils.adjustBrightness(color, 0.8f); // 比原始颜色亮 20% + darkerColor = CommonUtils.adjustBrightness(color, 0.6f); // 比原始颜色暗 20% + gradientDrawable = new GradientDrawable( + GradientDrawable.Orientation.TOP_BOTTOM, + new int[]{lighterColor, darkerColor} // 浅到深渐变 + ); + vb.rootLayout.setBackground(gradientDrawable); + + if (vb.layoutPlayList.linearLayout.getVisibility() == View.VISIBLE) { + updatePlayListColor(); + } + } + }); + return false; + } + }) + .preload(); + + + } + + + private void initProgressHandler() { + mHandler = new Handler(); + mRunnable = new Runnable() { + @Override + public void run() { + updatePlaybackProgress(); + mHandler.postDelayed(this, 1000); + } + }; + } + + @Override + public boolean isFullScreen() { + return true; + } + + @Override + public boolean statusBarLight() { + return false; + } + + @Override + public boolean showPanel() { + return false; + } + + @OptIn(markerClass = UnstableApi.class) + @Override + public void onClick(View v) { + if (v.equals(vb.btnPlay)) { + vb.btnPlay.setSelected(!vb.btnPlay.isSelected()); + if (vb.btnPlay.isSelected()) { + mediaControllerManager.play(); + } else { + mediaControllerManager.pause(); + } + } else if (v.equals(vb.btnNext)) { + mediaControllerManager.playNext(new onPlayNextListener() { + @Override + public void onPlayNext(boolean hasNext) { + if (!hasNext) { + Toast.makeText(PlayActivity.this, getString(R.string.no_next_song_yet), Toast.LENGTH_SHORT).show(); + } + } + }); + + } else if (v.equals(vb.btnPrevious)) { + mediaControllerManager.playPrevious(); + } else if (v.equals(vb.imBack)) { + finish(); + } else if (v.equals(vb.btnMusicList)) { + initShowPlayList(true); + } else if (v.equals(vb.layoutPlayList.imPlay)) { + vb.layoutPlayList.imPlay.setSelected(!vb.layoutPlayList.imPlay.isSelected()); + if (vb.layoutPlayList.imPlay.isSelected()) { + mediaControllerManager.play(); + } else { + mediaControllerManager.pause(); + } + if (adapterPlayList != null) { + adapterPlayList.updateCurMusicAnimation(); + } + } else if (v.equals(vb.layoutError.tvRetry)) { + //重试按钮 + updateErrorLayout(null, false); + switch (netError) { + case 0: + switch (mEnterType) { + case MyValue.TYPE_ENTER_SOURCE_SINGLE: + vmPlay.getPlayMusicList(playlistId, videoId, params, musicVideoType); + break; + } + break; + case 1: + vb.progressBarLoading.setVisibility(View.VISIBLE); + int curIndex = mediaControllerManager.getCurIndex(); + + String curVideoId = mediaControllerManager.getCurVideoId(); + + String videoId1 = mCustomerUrlInfo.getVideoId(); + int playMusicIndex = mCustomerUrlInfo.getPlayMusicIndex(); + + CommonUtils.LogMsg("-------重试 curIndex=" + curIndex + "----curVideoId=" + curVideoId + "---videoId1=" + videoId1 + "---playMusicIndex=" + playMusicIndex); + + vmPlay.getPlayUrl(mCustomerUrlInfo.getVideoId(), mCustomerUrlInfo.getPlayMusicIndex(), true); + break; + case 2: + MediaItem curMediaItem = mediaControllerManager.getCurMediaItem(); + MediaMetadata mediaMetadata = curMediaItem.mediaMetadata; + if (mediaMetadata.artworkUri != null) { + loadCovert(mediaMetadata.artworkUri.toString()); + } + break; + } + + } else if (v.equals(vb.btnLoop)) { + currentMode = (currentMode + 1) % imageStates.length; + vb.btnLoop.setImageResource(imageStates[currentMode]); + CommonUtils.LogMsg("----currentMode=" + currentMode); + mediaControllerManager.setMode(currentMode); + } else if (v.equals(vb.layoutLike)) { + boolean selected = vb.imLike.isSelected(); + vb.imLike.setSelected(!selected); + boolean newSelect = vb.imLike.isSelected(); + MediaItem curMediaItem = mediaControllerManager.getCurMediaItem(); + MediaMetadata mediaMetadata = curMediaItem.mediaMetadata; + if (mediaMetadata.title != null && mediaMetadata.artist != null && mediaMetadata.durationMs != null && mediaMetadata.description != null) { + BoxLikeSong boxLikeSong = new BoxLikeSong(mediaMetadata.title.toString(), + mediaMetadata.artist.toString(), + curMediaItem.mediaId, + String.valueOf(mediaMetadata.artworkUri), + mediaMetadata.durationMs, mediaMetadata.description.toString()); + if (newSelect) { + ObjectBoxManager.insertOrUpdateLike(boxLikeSong); + } else { + ObjectBoxManager.deleteLike(boxLikeSong); + } + } + + + } else if (v.equals(vb.layoutDownload)) { + if (vb.imDownload.isSelected()) { + //已经下载 + Toast.makeText(this,getText(R.string.text_has_downloaded),Toast.LENGTH_SHORT).show(); + return; + } + vb.downloadPb.setVisibility(View.VISIBLE); + vb.imDownload.setVisibility(View.INVISIBLE); + BoxDownloadSong curMediaItemInfo = getCurMediaItemInfo(); + if (curMediaItemInfo != null) { + Gson gson = new Gson(); + String info = gson.toJson(curMediaItemInfo); + byte[] data = info.getBytes(StandardCharsets.UTF_8); + + String videoId1 = curMediaItemInfo.getVideoId(); + CommonUtils.LogMsg("----------------开始下载 id=" + videoId1); + DownloadRequest downloadRequest = new DownloadRequest.Builder(videoId1, Uri.parse(curMediaItemInfo.getVideoUrl())) + .setMimeType("video/mp4") + .setData(data) + .build(); + + // 启动 DownloadService 进行下载 + DownloadService.sendAddDownload( + this, + MyDownloadService.class, // 上面定义的下载服务类 + downloadRequest, + true // 是否在前台运行 + ); + } + + + } + + } + + + @OptIn(markerClass = UnstableApi.class) + private BoxDownloadSong getCurMediaItemInfo() { + MediaItem curMediaItem = mediaControllerManager.getCurMediaItem(); + if (curMediaItem == null) { + return null; + } + MediaMetadata mediaMetadata = curMediaItem.mediaMetadata; + if (mediaMetadata.title == null || mediaMetadata.artist == null || mediaMetadata.description == null + || mediaMetadata.durationMs == null || curMediaItem.localConfiguration == null) { + return null; + } + + BoxDownloadSong boxDownloadSong = new BoxDownloadSong(); + boxDownloadSong.setVideoId(curMediaItem.mediaId); + boxDownloadSong.setCovert(String.valueOf(mediaMetadata.artworkUri)); + boxDownloadSong.setSongName((String) mediaMetadata.title); + boxDownloadSong.setSingerName((String) mediaMetadata.artist); + boxDownloadSong.setDuration((String) mediaMetadata.description); + boxDownloadSong.setDurationMs(mediaMetadata.durationMs); + boxDownloadSong.setVideoUrl(String.valueOf(curMediaItem.localConfiguration.uri)); + + return boxDownloadSong; + } + + /** + * 控制播放列表的显示 + * + * @param show + */ + private void initShowPlayList(boolean show) { + if (show) { + Animation animation = AnimationUtils.loadAnimation(this, R.anim.slide_up); + vb.layoutPlayList.linearLayout.startAnimation(animation); + vb.layoutPlayList.linearLayout.setVisibility(View.VISIBLE); + vb.contentLayout.setVisibility(View.GONE); + + if (!initPlayList) { + List playList; + if (mEnterType == MyValue.TYPE_ENTER_DOWNLOAD){ + playList = new ArrayList<>(); + for(BoxDownloadSong data:downloadSongList){ + ResponsePlayListInfo playListInfo = new ResponsePlayListInfo(); + playListInfo.setSongTitle(data.getSongName()); + playListInfo.setSingerName(data.getSingerName()); + playListInfo.setCovert(data.getCovert()); + playListInfo.setSmallCovert(data.getCovert()); + playListInfo.setVideoId(data.getVideoId()); + playListInfo.setDuration(data.getDuration()); + playListInfo.setDurationMs(data.getDurationMs()); + playList.add(playListInfo); + } + + }else { + playList = mediaControllerManager.getPlayList(); + } + adapterPlayList = new AdapterPlayList(); + vb.layoutPlayList.recyclerList.setLayoutManager(new LinearLayoutManager(MusicApplication.myApplication)); + adapterPlayList.setData(playList); + // TODO: 2024/10/16 播放列表显示 + vb.layoutPlayList.recyclerList.setAdapter(adapterPlayList); + vb.layoutPlayList.imPlay.setOnClickListener(this); + initPlayList = true; + + } + updatePlayListUi(); + + } else { + + vb.rootLayout.setBackground(gradientDrawable); + vb.contentLayout.setVisibility(View.VISIBLE); + + Animation animation = AnimationUtils.loadAnimation(this, R.anim.slide_down); + vb.layoutPlayList.linearLayout.startAnimation(animation); + vb.layoutPlayList.linearLayout.setVisibility(View.GONE); + } + + + } + + /** + * 更新播放列表的显示 + */ + private void updatePlayListUi() { + CommonUtils.LogMsg("----------更新播放列表的显示"); + + MediaItem currentMediaItem = mediaControllerManager.getCurMediaItem(); + if (currentMediaItem != null) { + if (adapterPlayList != null) { + adapterPlayList.setCurVideId(currentMediaItem.mediaId); + } + Uri artworkUri = currentMediaItem.mediaMetadata.artworkUri; + vb.layoutPlayList.topSongName.setText(currentMediaItem.mediaMetadata.title); + vb.layoutPlayList.topSingerName.setText(currentMediaItem.mediaMetadata.artist); + updatePlayListColor(); + Glide.with(MusicApplication.myApplication) + .asDrawable() +// .apply(RequestOptions.bitmapTransform(new RoundedCorners(CommonUtils.dpToPx(10)))) + .load(artworkUri) + .placeholder(R.mipmap.im_placeholder) + .listener(new RequestListener() { + @Override + public boolean onLoadFailed(@Nullable GlideException e, @Nullable Object model, @NonNull Target target, boolean isFirstResource) { + CommonUtils.LogMsg(e.getMessage()); + return false; + } + + @OptIn(markerClass = UnstableApi.class) + @Override + public boolean onResourceReady(@NonNull Drawable resource, @NonNull Object model, Target target, @NonNull DataSource dataSource, boolean isFirstResource) { + + return false; + } + }) + .into(vb.layoutPlayList.topIm); + } + vb.layoutPlayList.imPlay.setSelected(mediaControllerManager.getIsPlaying()); + } + + private void updatePlayListColor() { + vb.layoutPlayList.topLayout.setBackgroundColor(darkerColor); + GradientDrawable gradientDrawable = new GradientDrawable( + GradientDrawable.Orientation.TOP_BOTTOM, + new int[]{darkerColor, darkerColor} + ); + vb.rootLayout.setBackground(gradientDrawable); + Drawable newDrawable = CommonUtils.getNewDrawable(lighterColor, 24f, 24f, 0, 0); + vb.layoutPlayList.listLayout.setBackground(newDrawable); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + if (mHandler != null && mRunnable != null) + mHandler.removeCallbacks(mRunnable); + } + + @Override + public void onBackPressed() { + if (vb.layoutPlayList.linearLayout.getVisibility() == View.VISIBLE) { + initShowPlayList(false); + } else { + super.onBackPressed(); // 调用系统默认的返回行为 + } + } + + @Override + public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { + if (fromUser) { + mediaControllerManager.getMediaController().seekTo(progress); + } + } + + @Override + public void onStartTrackingTouch(SeekBar seekBar) { + + } + + @Override + public void onStopTrackingTouch(SeekBar seekBar) { + + } + + @OptIn(markerClass = UnstableApi.class) + private void addDownloadListener() { + MyDownloadService.addDownloadListener(vmApplication); + vmApplication.downloadData.observe(this, new Observer>() { + @Override + public void onChanged(List downloads) { + if (downloads == null || downloads.size() == 0) { + return; + } + String id = downloads.get(downloads.size() - 1).request.id; + + MediaItem curMediaItem = mediaControllerManager.getCurMediaItem(); + if (curMediaItem != null) { + String mediaId = mediaControllerManager.getCurMediaItem().mediaId; + if (mediaId.equals(id)) { + vb.downloadPb.setVisibility(View.GONE); + vb.imDownload.setVisibility(View.VISIBLE); + vb.imDownload.setSelected(true); + } + } + + CommonUtils.LogMsg("-------onChanged id=" + id); + if (mEnterType == MyValue.TYPE_ENTER_DOWNLOAD) { + //下载进入播放,这里进行重置播放列表并播放 + mediaControllerManager.resetPlayList(); + for (Download data : downloads) { + BoxDownloadSong boxDownloadSong = CommonUtils.downloadToBean(data); + downloadSongList.add(boxDownloadSong); + MediaItem mediaItem = CommonUtils.downloadToMediaItem(data); + mediaControllerManager.addMusicPlayList(mediaItem); + } + mediaControllerManager.playPositionMusic(mDefaultPlayStartIndex); + mediaControllerManager.play(); + } + } + }); + } +} \ No newline at end of file diff --git a/app/src/main/java/com/hi/music/player/ui/activity/ResultListActivity.java b/app/src/main/java/com/hi/music/player/ui/activity/ResultListActivity.java new file mode 100644 index 0000000..f5d3a32 --- /dev/null +++ b/app/src/main/java/com/hi/music/player/ui/activity/ResultListActivity.java @@ -0,0 +1,169 @@ +package com.hi.music.player.ui.activity; + +import android.content.Intent; +import android.graphics.drawable.Drawable; +import android.view.View; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.lifecycle.Observer; +import androidx.recyclerview.widget.LinearLayoutManager; + +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.adapter.AdapterResult; +import com.hi.music.player.api.HomeItemClickListener; +import com.hi.music.player.databinding.ActivityResultListBinding; +import com.hi.music.player.helper.CommonUtils; +import com.hi.music.player.helper.MyValue; +import com.hi.music.player.javabean.response.ResponseResult; +import com.hi.music.player.javabean.response.child.ResponseResultListChild; +import com.hi.music.player.javabean.response.child.ResponseSingle; +import com.hi.music.player.ui.fragmnt.viewmodel.VMCategoryList; +import com.hi.music.player.ui.fragmnt.viewmodel.VMResultList; + +public class ResultListActivity extends BaseActivity implements HomeItemClickListener { + private VMResultList vm; + private String key; + + @Override + protected ActivityResultListBinding getViewBinding() { + return ActivityResultListBinding.inflate(getLayoutInflater()); + } + + @Override + protected void onCreateInit() { + vm = getActivityScopeViewModel(VMResultList.class); + key = getIntent().getStringExtra(MyValue.KEY_SEARCH_RESULT_BROWSER_ID); + vm.getList(key); + + vm.data.observe(this, new Observer() { + @Override + public void onChanged(ResponseResult responseResult) { + vb.pbLoading.setVisibility(View.GONE); + if (responseResult == null) { + vb.layoutError.linearRetry.setVisibility(View.VISIBLE); + return; + } + vb.pbLoading.setVisibility(View.GONE); + loadInfo(responseResult); + } + }); + + } + + private void loadInfo(ResponseResult result) { + Glide.with(this) + .asDrawable() + .load(result.getMainCovert()) + .apply(RequestOptions.bitmapTransform(new RoundedCorners(CommonUtils.dpToPx(4)))) + .placeholder(R.mipmap.im_placeholder) + .listener(new RequestListener() { + @Override + public boolean onLoadFailed(@Nullable GlideException e, @Nullable Object model, @NonNull Target target, boolean isFirstResource) { + CommonUtils.LogMsg(e.getMessage()); + return false; + } + + @Override + public boolean onResourceReady(@NonNull Drawable resource, @NonNull Object model, Target target, @NonNull DataSource dataSource, boolean isFirstResource) { + return false; + } + }) + .into(vb.covert); + vb.mainTitle.setText(result.getMainTitle()); + AdapterResult adapterResult = new AdapterResult(this); + adapterResult.setHomeItemClickListener(this); + adapterResult.setData(result.getList()); + vb.listRecycler.setAdapter(adapterResult); + vb.listRecycler.setLayoutManager(new LinearLayoutManager(this)); + } + + @Override + protected void onInitClick() { + vb.imBack.setOnClickListener(this); + vb.layoutError.tvRetry.setOnClickListener(this); + } + + @Override + public boolean isFullScreen() { + return true; + } + + @Override + public boolean statusBarLight() { + return false; + } + + @Override + public boolean showPanel() { + return true; + } + + @Override + public void onClick(View v) { + if (v.equals(vb.imBack)) { + finish(); + } else if (v.equals(vb.layoutError.tvRetry)) { + vm.getList(key); + } + + } + + @Override + public void onClickResultSong(ResponseResultListChild child, int index) { + String videoId = child.getVideoId(); + String playListId = child.getPlayListId(); + String browserId = child.getBrowserId(); + String pageType = child.getPageType(); + String thumbnail = child.getThumbnail(); + String songName = child.getSongName(); + String subTitle = child.getSubTitle(); + + if (videoId != null && !videoId.isEmpty()) { + ResponseSingle responseSingle = new ResponseSingle(); + responseSingle.setSongTitle(songName); + responseSingle.setSingerHead(thumbnail); + responseSingle.setVideoId(videoId); + responseSingle.setPlaylistId(playListId); + Intent intent = new Intent(this, PlayActivity.class); + intent.putExtra(MyValue.KEY_PLAY_ACTIVITY_SINGER, responseSingle); + intent.putExtra(MyValue.KEY_PLAY_ACTIVITY_CATEGORY_LIST_INDEX, index); + CommonUtils.LogMsg("-------------index=" + index); + startActivity(intent); + } else { + switch (pageType) { + case "MUSIC_PAGE_TYPE_ALBUM": + //专辑 + CommonUtils.LogMsg("------------专辑-index=" + index + "--subTitle=" + subTitle); + case "MUSIC_PAGE_TYPE_PLAYLIST": + //精选 + Intent intent1 = new Intent(this, CategoryListActivity.class); + intent1.putExtra(MyValue.KEY_CATEGORY_LIST_TYPE, pageType); + intent1.putExtra(MyValue.KEY_CATEGORY_LIST_SINGER_NAME, subTitle); + intent1.putExtra(MyValue.KEY_CATEGORY_LIST_BROWSER_ID, browserId); + startActivity(intent1); + break; + case "MUSIC_PAGE_TYPE_ARTIST": + //粉丝可能还喜欢 + Intent intent = new Intent(this, ResultListActivity.class); + intent.putExtra(MyValue.KEY_SEARCH_RESULT_BROWSER_ID, browserId); + startActivity(intent); + finish(); + break; + } + + + } + + + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/hi/music/player/ui/fragmnt/HomeFragment.java b/app/src/main/java/com/hi/music/player/ui/fragmnt/HomeFragment.java new file mode 100644 index 0000000..f51c019 --- /dev/null +++ b/app/src/main/java/com/hi/music/player/ui/fragmnt/HomeFragment.java @@ -0,0 +1,168 @@ +package com.hi.music.player.ui.fragmnt; + +import android.content.Intent; +import android.content.RestrictionsManager; +import android.graphics.drawable.Drawable; +import android.view.View; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.fragment.app.FragmentActivity; +import androidx.lifecycle.Observer; +import androidx.recyclerview.widget.LinearLayoutManager; +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.request.RequestListener; +import com.bumptech.glide.request.target.Target; +import com.hi.music.player.R; +import com.hi.music.player.adapter.AdapterHome; +import com.hi.music.player.api.HomeItemClickListener; +import com.hi.music.player.api.OnHasUrlAction; +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; + +import okhttp3.ResponseBody; + +public class HomeFragment extends BaseFragment implements HomeItemClickListener { + + + private VMHome vmHome; + + + private int requestCount = 1; + + private int totalPage = 3; + + + private List childList = new ArrayList<>(); + + + @Override + protected FragmentHomeBinding getFragmentVb() { + // 返回正确的 ViewBinding 实例 + return FragmentHomeBinding.inflate(getLayoutInflater()); + } + + @Override + protected void initView() { + +// //标题导航栏颜色设置 +// Vb.toolbar.setTitleTextColor(getResources().getColor(R.color.white)); + AdapterHome adapterHome = new AdapterHome(); + adapterHome.setHomeItemClickListener(this); + + + vmHome = getFragmentScopeViewModel(VMHome.class); + adapterHome.addLoadingFooter(); + vmHome.getHome(); + Vb.tvRetry.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + adapterHome.addLoadingFooter(); + vmHome.getHome(); + } + }); + + vmHome.data.observe(getViewLifecycleOwner(), new Observer() { + @Override + public void onChanged(ResponseHome responseHome) { + if (responseHome == null) { + adapterHome.removeLoadingFooter(); + requestCount--; + if(requestCount == 0){ + Vb.layoutError.setVisibility(View.VISIBLE); + Vb.recyclerSongOfTheDay.setVisibility(View.GONE); + } + return; + } + Vb.layoutError.setVisibility(View.GONE); + Vb.recyclerSongOfTheDay.setVisibility(View.VISIBLE); + + List childList1 = responseHome.getChildList(); + if (childList1 == null) return; + childList.addAll(childList1); + adapterHome.removeLoadingFooter(); + adapterHome.addData(childList1); + if (requestCount == 1) { + adapterHome.addLoadingFooter(); + vmHome.getHomeMore(); + requestCount++; + } + + + } + }); + Vb.recyclerSongOfTheDay.setLayoutManager(new LinearLayoutManager(requireContext())); + Vb.recyclerSongOfTheDay.setAdapter(adapterHome); + Vb.recyclerSongOfTheDay.addOnScrollListener(new RecyclerView.OnScrollListener() { + @Override + public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) { + super.onScrolled(recyclerView, dx, dy); + LinearLayoutManager layoutManager = (LinearLayoutManager) recyclerView.getLayoutManager(); + if (layoutManager != null && layoutManager.findLastVisibleItemPosition() == childList.size() - 1) { + if (requestCount < totalPage && !adapterHome.isLoadingAdded()) { + CommonUtils.LogMsg("------loadmore--"); + adapterHome.addLoadingFooter(); + vmHome.getHomeMore(); + requestCount++; + } + + } + } + }); + + } + + + @Override + 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) { + String pageType = data.getPageType(); + String browseId = data.getBrowseId(); + String twoSubtitle = data.getTwoSubtitle(); + if (pageType.equals(MyValue.PAGE_TYPE_MV)||pageType.equals(MyValue.PAGE_TYPE_MV_LIST)) { + Intent intent = new Intent(activity, PlayActivity.class); + intent.putExtra(MyValue.KEY_PLAY_ACTIVITY_MV, data); + intent.putExtra(MyValue.KEY_ENTER_SOURCE, MyValue.TYPE_ENTER_SOURCE_MV); + startActivity(intent); + } + else { + Intent intent = new Intent(activity, CategoryListActivity.class); + intent.putExtra(MyValue.KEY_CATEGORY_LIST_TYPE, pageType); + intent.putExtra(MyValue.KEY_CATEGORY_LIST_SINGER_NAME, twoSubtitle); + intent.putExtra(MyValue.KEY_CATEGORY_LIST_BROWSER_ID, browseId); + startActivity(intent); + } + + + } + } +} diff --git a/app/src/main/java/com/hi/music/player/ui/fragmnt/ProfileFragment.java b/app/src/main/java/com/hi/music/player/ui/fragmnt/ProfileFragment.java new file mode 100644 index 0000000..9d7ea09 --- /dev/null +++ b/app/src/main/java/com/hi/music/player/ui/fragmnt/ProfileFragment.java @@ -0,0 +1,143 @@ +package com.hi.music.player.ui.fragmnt; + +import android.content.Intent; +import android.util.Pair; +import android.view.View; + +import androidx.annotation.OptIn; +import androidx.lifecycle.Observer; +import androidx.media3.common.util.UnstableApi; +import androidx.media3.exoplayer.offline.Download; + +import com.bumptech.glide.Glide; +import com.hi.music.player.R; +import com.hi.music.player.api.LikeSongListener; +import com.hi.music.player.databinding.FragmentProfileBinding; +import com.hi.music.player.helper.CommonUtils; +import com.hi.music.player.helper.MyValue; +import com.hi.music.player.javabean.BoxDownloadSong; +import com.hi.music.player.javabean.BoxLikeSong; +import com.hi.music.player.media3.MyDownloadService; +import com.hi.music.player.objectbox.ObjectBoxManager; +import com.hi.music.player.ui.activity.LikeSongActivity; +import com.hi.music.player.ui.activity.viewmodel.VMApplication; + +import java.util.List; + +import io.objectbox.reactive.DataSubscription; + + +public class ProfileFragment extends BaseFragment implements View.OnClickListener { + + + private DataSubscription dataSubscription; + + private int likeSongCount = 0; + private int downloadSongCount = 0; + + + + @Override + protected FragmentProfileBinding getFragmentVb() { + return FragmentProfileBinding.inflate(getLayoutInflater()); + } + + + @OptIn(markerClass = UnstableApi.class) + @Override + protected void initView() { + + dataSubscription = ObjectBoxManager.setLikeDataListener(new LikeSongListener() { + @Override + public void onLikeSongChange(List data) { + CommonUtils.LogMsg("------onLikeSongChange data=" + data.size()); + int size = data.size(); + likeSongCount = size; + Vb.tvLikeSize.setText(String.format(getString(R.string.like_song), size)); + if (size == 0) { + Vb.likeCovert.setVisibility(View.GONE); + Vb.likeDefault.setVisibility(View.VISIBLE); + } else { + Vb.likeCovert.setVisibility(View.VISIBLE); + Vb.likeDefault.setVisibility(View.GONE); + BoxLikeSong boxLikeSong = data.get(size - 1); + Glide.with(requireContext()) + .asDrawable() + .load(boxLikeSong.getCovert()) + .placeholder(R.mipmap.im_placeholder) + .error(R.mipmap.im_placeholder) + .into(Vb.likeCovert); + } + } + }); + MyDownloadService.updateDownloadUi(vmApplication); + + vmApplication.downloadData.observe(getViewLifecycleOwner(), new Observer>() { + @Override + public void onChanged(List downloads) { + int size = downloads.size(); + Vb.tvDownloadSize.setText(String.format(getString(R.string.download_song), size)); + downloadSongCount = size; + if (size > 0) { + Download download = downloads.get(downloads.size() - 1); + BoxDownloadSong boxDownloadSong = CommonUtils.downloadToBean(download); + + Vb.downloadCovert.setVisibility(View.VISIBLE); + Vb.downloadDefault.setVisibility(View.GONE); + Glide.with(requireContext()) + .asDrawable() + .load(boxDownloadSong.getCovert()) + .placeholder(R.mipmap.im_placeholder) + .error(R.mipmap.im_placeholder) + .into(Vb.downloadCovert); + } else { + Vb.downloadCovert.setVisibility(View.GONE); + Vb.downloadDefault.setVisibility(View.VISIBLE); + } + } + }); + + Vb.relayoutLike.setOnClickListener(this); + Vb.relayoutDownload.setOnClickListener(this); + } + + @Override + public void onStart() { + super.onStart(); + CommonUtils.LogMsg("------ProfileFragment-onStart"); + + } + + @Override + public void onDestroyView() { + super.onDestroyView(); + if (dataSubscription != null) { + dataSubscription.cancel(); + } + CommonUtils.LogMsg("------ProfileFragment-onDestroyView"); + } + + @Override + public void onDestroy() { + super.onDestroy(); + CommonUtils.LogMsg("------ProfileFragment-onDestroy"); + } + + @Override + public void onClick(View v) { + if (v.equals(Vb.relayoutLike)) { + if (likeSongCount > 0) { + Intent intent = new Intent(requireActivity(), LikeSongActivity.class); + intent.putExtra(MyValue.KEY_ENTER_LIKE_ACTIVITY_TYPE, MyValue.KEY_ENTER_LIKE_ACTIVITY_TYPE_LIKE); + startActivity(intent); + } + } else if (v.equals(Vb.relayoutDownload)) { + if (downloadSongCount > 0) { + Intent intent = new Intent(requireActivity(), LikeSongActivity.class); + intent.putExtra(MyValue.KEY_ENTER_LIKE_ACTIVITY_TYPE, MyValue.KEY_ENTER_LIKE_ACTIVITY_TYPE_DOWNLOAD); + startActivity(intent); + } + } + + } +} \ No newline at end of file diff --git a/app/src/main/java/com/hi/music/player/ui/fragmnt/SearchFragment.java b/app/src/main/java/com/hi/music/player/ui/fragmnt/SearchFragment.java new file mode 100644 index 0000000..37caf28 --- /dev/null +++ b/app/src/main/java/com/hi/music/player/ui/fragmnt/SearchFragment.java @@ -0,0 +1,230 @@ +package com.hi.music.player.ui.fragmnt; + +import android.content.Intent; +import android.text.Editable; +import android.text.TextWatcher; +import android.view.KeyEvent; +import android.view.View; +import android.view.inputmethod.EditorInfo; +import android.widget.TextView; + +import androidx.lifecycle.Observer; +import androidx.recyclerview.widget.LinearLayoutManager; + +import com.hi.music.player.adapter.AdapterSearch; +import com.hi.music.player.adapter.AdapterSuggestion; +import com.hi.music.player.api.HomeItemClickListener; +import com.hi.music.player.databinding.FragmentSearchBinding; +import com.hi.music.player.helper.CommonUtils; +import com.hi.music.player.helper.MyValue; +import com.hi.music.player.javabean.response.ResponseSearch; +import com.hi.music.player.javabean.response.child.ResponseSearchChild; +import com.hi.music.player.javabean.response.child.ResponseSingle; +import com.hi.music.player.ui.activity.CategoryListActivity; +import com.hi.music.player.ui.activity.PlayActivity; +import com.hi.music.player.ui.activity.ResultListActivity; +import com.hi.music.player.ui.fragmnt.viewmodel.VMSearch; + +import java.util.List; + + +public class SearchFragment extends BaseFragment implements HomeItemClickListener, View.OnClickListener { + + + private VMSearch vmSearch; + + @Override + protected FragmentSearchBinding getFragmentVb() { + return FragmentSearchBinding.inflate(getLayoutInflater()); + } + + @Override + protected void initView() { +// Vb.etSearch.requestFocus(); + vmSearch = getFragmentScopeViewModel(VMSearch.class); + + Vb.etSearch.addTextChangedListener(new TextWatcher() { + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) { + + } + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { + + } + + @Override + public void afterTextChanged(Editable s) { + String input = s.toString(); + if (!input.isEmpty()) { + vmSearch.getSuggestion(input); + } + + } + }); + Vb.etSearch.setOnEditorActionListener(new TextView.OnEditorActionListener() { + @Override + public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { + if (actionId == EditorInfo.IME_ACTION_SEARCH) { + String s = v.getText().toString(); + CommonUtils.LogMsg("------------v=" + s); + startQuery(s); + return true; + } else + return false; + } + }); + initSuggestionRecycler(); + initResultRecycler(); + + } + + private void initSuggestionRecycler() { + AdapterSuggestion adapterSuggestion = new AdapterSuggestion(); + Vb.recyclerSuggestion.setLayoutManager(new LinearLayoutManager(requireContext())); + Vb.recyclerSuggestion.setAdapter(adapterSuggestion); + adapterSuggestion.setHomeItemClickListener(this); + Vb.imCancel.setOnClickListener(this); + vmSearch.suggestion.observe(getViewLifecycleOwner(), new Observer>() { + @Override + public void onChanged(List strings) { + if (strings != null) { + adapterSuggestion.setData(strings); + CommonUtils.LogMsg("------------更新="); + } + + } + }); + } + + + private void initResultRecycler(){ + AdapterSearch adapterSearch = new AdapterSearch(); + adapterSearch.setHomeItemClickListener(this); + Vb.recyclerResult.setLayoutManager(new LinearLayoutManager(requireContext())); + Vb.recyclerResult.setAdapter(adapterSearch); + vmSearch.result.observe(getViewLifecycleOwner(), new Observer>() { + @Override + public void onChanged(List responseSearches) { + Vb.pbloading.setVisibility(View.GONE); + if(responseSearches == null){ + Vb.recyclerResult.setVisibility(View.GONE); + Vb.layoutError.setVisibility(View.VISIBLE); + CommonUtils.LogMsg("------------更新结果 null"); + return; + } + Vb.recyclerResult.setVisibility(View.VISIBLE); + Vb.layoutError.setVisibility(View.GONE); + adapterSearch.setData(responseSearches); + } + }); + } + + private void startQuery(String query){ + Vb.pbloading.setVisibility(View.VISIBLE); + Vb.recyclerSuggestion.setVisibility(View.GONE); + Vb.recyclerResult.setVisibility(View.VISIBLE); + vmSearch.getSearchResult(query); + } + + @Override + public void onClickItemSuggestion(boolean isSearch, String data) { + if (isSearch) { + startQuery(data); + } else { + Vb.etSearch.setText(data); + } + } + + + @Override + public void onClick(View v) { + if (v.equals(Vb.imCancel)) { + Vb.etSearch.setText(""); + } + } + + + //点击搜索结果的play按钮 + @Override + public void onClickSearchResultBest(ResponseSearch responseSearch) { + String beastBrowserId = responseSearch.getBeastBrowserId(); + String beastVideoId = responseSearch.getBeastVideoId(); + String beastSongTitle = responseSearch.getBeastSongTitle(); + String beastSongTCovert = responseSearch.getBeastSongTCovert(); + String pageType = responseSearch.getPageType(); + + if(beastVideoId!= null&&!beastVideoId.isEmpty()){ + CommonUtils.LogMsg("---------击搜索结果的play按钮--beastVideoId="+beastVideoId); + + ResponseSingle responseSingle = new ResponseSingle(); + responseSingle.setSongTitle(beastSongTitle); + responseSingle.setSingerHead(beastSongTCovert); + responseSingle.setVideoId(beastVideoId); + responseSingle.setPlaylistId(""); + Intent intent = new Intent(requireActivity(), PlayActivity.class); + intent.putExtra(MyValue.KEY_PLAY_ACTIVITY_SINGER, responseSingle); + startActivity(intent); + }else if(beastBrowserId!= null&&!beastBrowserId.isEmpty()){ + CommonUtils.LogMsg("---------击搜索结果的play按钮--pageType="+pageType); + switch (pageType){ + case "MUSIC_PAGE_TYPE_PLAYLIST": + case "MUSIC_PAGE_TYPE_ALBUM": + Intent intent1 = new Intent(requireActivity(), CategoryListActivity.class); + intent1.putExtra(MyValue.KEY_CATEGORY_LIST_TYPE, pageType); + intent1.putExtra(MyValue.KEY_CATEGORY_LIST_SINGER_NAME, responseSearch.getBeastSongDescribe()); + intent1.putExtra(MyValue.KEY_CATEGORY_LIST_BROWSER_ID, beastBrowserId); + startActivity(intent1); + break; + default: + Intent intent = new Intent(requireActivity(), ResultListActivity.class); + intent.putExtra(MyValue.KEY_SEARCH_RESULT_BROWSER_ID, beastBrowserId); + startActivity(intent); + break; + } + } + } + + + //点击搜索结果普通项 + @Override + public void onClickSearchResult(ResponseSearchChild responseSearchChild) { + String browserId = responseSearchChild.getBrowserId(); + String songVideoId = responseSearchChild.getSongVideoId(); + String songCovert = responseSearchChild.getSongCovert(); + String songTitle = responseSearchChild.getSongTitle(); + String songDescribe = responseSearchChild.getSongDescribe(); + + if(browserId!= null&&!browserId.isEmpty()){ + String pageType = responseSearchChild.getPageType(); + CommonUtils.LogMsg("---------点击搜索结果--pageType="+pageType); + switch (pageType){ + case "MUSIC_PAGE_TYPE_ARTIST": + Intent intent = new Intent(requireActivity(), ResultListActivity.class); + intent.putExtra(MyValue.KEY_SEARCH_RESULT_BROWSER_ID, browserId); + startActivity(intent); + break; + default: + Intent intent1 = new Intent(requireActivity(), CategoryListActivity.class); + intent1.putExtra(MyValue.KEY_CATEGORY_LIST_TYPE, pageType); + intent1.putExtra(MyValue.KEY_CATEGORY_LIST_SINGER_NAME, songDescribe); + intent1.putExtra(MyValue.KEY_CATEGORY_LIST_BROWSER_ID, browserId); + startActivity(intent1); + break; + } + + }else { + CommonUtils.LogMsg("---------点击搜索结果--VideoId="+songVideoId); + + ResponseSingle responseSingle = new ResponseSingle(); + responseSingle.setSongTitle(songTitle); + responseSingle.setSingerHead(songCovert); + responseSingle.setVideoId(songVideoId); + responseSingle.setPlaylistId(""); + Intent intent = new Intent(requireActivity(), PlayActivity.class); + intent.putExtra(MyValue.KEY_PLAY_ACTIVITY_SINGER, responseSingle); + startActivity(intent); + } + } +} \ No newline at end of file diff --git a/app/src/main/res/layout/activity_ahome.xml b/app/src/main/res/layout/activity_ahome.xml index a7840b4..73b98d2 100644 --- a/app/src/main/res/layout/activity_ahome.xml +++ b/app/src/main/res/layout/activity_ahome.xml @@ -60,7 +60,7 @@ android:layout_width="48dp" android:layout_height="48dp" android:layout_gravity="center" - android:src="@mipmap/default_image" /> + android:src="@mipmap/im_placeholder" /> diff --git a/app/src/main/res/layout/activity_category_list.xml b/app/src/main/res/layout/activity_category_list.xml new file mode 100644 index 0000000..a7ace6f --- /dev/null +++ b/app/src/main/res/layout/activity_category_list.xml @@ -0,0 +1,140 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/activity_result_list.xml b/app/src/main/res/layout/activity_result_list.xml new file mode 100644 index 0000000..8eceffa --- /dev/null +++ b/app/src/main/res/layout/activity_result_list.xml @@ -0,0 +1,87 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/dialog_play_list.xml b/app/src/main/res/layout/dialog_play_list.xml new file mode 100644 index 0000000..cdf673a --- /dev/null +++ b/app/src/main/res/layout/dialog_play_list.xml @@ -0,0 +1,116 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_home.xml b/app/src/main/res/layout/fragment_home.xml new file mode 100644 index 0000000..880ca72 --- /dev/null +++ b/app/src/main/res/layout/fragment_home.xml @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/fragment_search.xml b/app/src/main/res/layout/fragment_search.xml new file mode 100644 index 0000000..02dd0c4 --- /dev/null +++ b/app/src/main/res/layout/fragment_search.xml @@ -0,0 +1,94 @@ + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_a_home_1.xml b/app/src/main/res/layout/item_a_home_1.xml index dc59684..76c632c 100644 --- a/app/src/main/res/layout/item_a_home_1.xml +++ b/app/src/main/res/layout/item_a_home_1.xml @@ -18,7 +18,7 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:scaleType="centerCrop" - android:src="@mipmap/default_image" /> + android:src="@mipmap/im_placeholder" /> + android:src="@mipmap/im_placeholder" /> diff --git a/app/src/main/res/layout/item_a_home_3.xml b/app/src/main/res/layout/item_a_home_3.xml index fae54f7..2daa201 100644 --- a/app/src/main/res/layout/item_a_home_3.xml +++ b/app/src/main/res/layout/item_a_home_3.xml @@ -20,7 +20,7 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:scaleType="centerCrop" - android:src="@mipmap/default_image" /> + android:src="@mipmap/im_placeholder" /> diff --git a/app/src/main/res/layout/item_a_import.xml b/app/src/main/res/layout/item_a_import.xml index 9e650cf..cdeecf5 100644 --- a/app/src/main/res/layout/item_a_import.xml +++ b/app/src/main/res/layout/item_a_import.xml @@ -22,7 +22,7 @@ android:layout_height="match_parent" android:scaleType="centerCrop" android:background="@color/black" - android:src="@mipmap/default_image" /> + android:src="@mipmap/im_placeholder" /> diff --git a/app/src/main/res/layout/item_home.xml b/app/src/main/res/layout/item_home.xml new file mode 100644 index 0000000..606ceec --- /dev/null +++ b/app/src/main/res/layout/item_home.xml @@ -0,0 +1,37 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_play_list.xml b/app/src/main/res/layout/item_play_list.xml new file mode 100644 index 0000000..9d25a99 --- /dev/null +++ b/app/src/main/res/layout/item_play_list.xml @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_result_list_album.xml b/app/src/main/res/layout/item_result_list_album.xml new file mode 100644 index 0000000..8316257 --- /dev/null +++ b/app/src/main/res/layout/item_result_list_album.xml @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_singer.xml b/app/src/main/res/layout/item_singer.xml new file mode 100644 index 0000000..3fbb783 --- /dev/null +++ b/app/src/main/res/layout/item_singer.xml @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/layout_panel.xml b/app/src/main/res/layout/layout_panel.xml new file mode 100644 index 0000000..82f9f67 --- /dev/null +++ b/app/src/main/res/layout/layout_panel.xml @@ -0,0 +1,99 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-xxxhdpi/im_logo.jpg b/app/src/main/res/mipmap-xxxhdpi/im_logo.jpg new file mode 100644 index 0000000..38809b2 Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/im_logo.jpg differ diff --git a/app/src/main/res/mipmap-xxxhdpi/im_placeholder.jpg b/app/src/main/res/mipmap-xxxhdpi/im_placeholder.jpg new file mode 100644 index 0000000..92a5d44 Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/im_placeholder.jpg differ diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 68f0f1a..56b12ab 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,10 +1,18 @@ - Offline Music Player + MusicApp + Hello blank fragment + Song Of The Day + Recently Played + Top Artists + Trending in Shorts + New Releases + Musicoo + Library Listen Music Anytime Resource Loading... - Sounds of Serenity - Soothing tunes crafted for deep relaxation and sleep 🌙 + Ready to sleep + We\'ve carefully prepared sounds for you 🔥 White noise Voice of Nature Setting @@ -13,4 +21,24 @@ Share Privacy Policy Terms of Service + Play next + Try again + An error occurred + Search songs, artists… + Play + There is no next song yet + Image loading failed + Song loading failed + Playlist loading failed + %d:%02d:%02d + %d:%02d + Like + Download + Songs %d + Offline %d + Favorite Songs + Offline Songs + It\'s already downloaded + An error occurred in the network request. Please try again. + No results yet \ No newline at end of file