Compare commits

..

12 Commits
main ... AB

Author SHA1 Message Date
litingting
56c0d99b6c Merge branch 'AB' of ssh://git.zhenbs.com:2222/2-group-adbrush-android/Music_Player into AB 2024-11-22 15:30:10 +08:00
litingting
40a897c38b 加载广告 2024-11-22 15:29:57 +08:00
lihongwei
99bde9160e 忽略.safedk下的文件 2024-11-22 15:13:25 +08:00
litingting
6412c21786 Merge branch 'AB' of ssh://git.zhenbs.com:2222/2-group-adbrush-android/Music_Player into AB 2024-11-22 14:19:35 +08:00
litingting
6666abcf17 添加混淆规则 2024-11-22 14:19:24 +08:00
lihongwei
2384b4d09d A面通知栏更新 2024-11-22 13:52:17 +08:00
litingting
8d3921de0d A面进度更新 2024-11-22 10:59:58 +08:00
lihongwei
7c739312e9 A面播放优化bug 2024-11-21 18:32:19 +08:00
litingting
1b27a928b9 接入max插页和原生广告 2024-11-21 17:16:57 +08:00
litingting
872a535cc0 删除无用资源 2024-11-19 14:18:34 +08:00
litingting
7349c58e6d 播放功能优化 2024-11-19 11:58:01 +08:00
lihongwei
c62e38eb7e A面合并 2024-11-18 16:52:58 +08:00
191 changed files with 12932 additions and 150 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

1
.safedk/list.enc Normal file

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,36 @@
-keep class androidx.multidex.** { *; }
-keep class androidx.browser.customtabs.CustomTabsIntent { *; }
-keep class androidx.** {
*** startActivityForResult(***);
*** startActivity(***);
}
-keep class android.support.multidex.** { *; }
-keep class android.support.v4.app.** { *; }
-keep class com.google.android.gms.location.FusedLocationProviderApi { *; }
-keep class com.google.android.gms.location.LocationListener { *; }
-keep class io.fabric.sdk.android.** { *; }
-keep class okio.** { *; }
-keep class retrofit2.** { *; }
-keep class okhttp3.** { *; }
-keep class com.squareup.okhttp.** { *; }
-keep class com.android.volley.** { *; }
-keep class com.flurry.** { *; }
-keep class org.apache.** { *; }
-keep class com.applovin.** { *; }
-keep class com.google.android.gms.ads.** { *; }
-keep class com.ironsource.** { *; }
-keep class com.fyber.inneractive.** { *; }
-keep class com.vungle.** { *; }
-keep class com.unity3d.ads.** { *; }
-keep class com.unity3d.services.** { *; }
-keep class com.mintegral.msdk.** { *; }
-keep class com.mbridge.msdk.** { *; }
-keep class com.adcolony.sdk.** { *; }
-keep class com.inmobi.** { *; }
-keep class com.five_corp.** { *; }
-keep class com.bytedance.** { *; }
-keep class com.smaato.** { *; }
-keep class com.explorestack.** { *; }
-keep class com.safedk.** { *; }
-keep class com.applovin.quality.** { *; }
-keep class com.braze.** { *; }

Binary file not shown.

View File

@ -3,11 +3,17 @@ import java.text.SimpleDateFormat
plugins {
id("com.android.application")
// id("org.jetbrains.kotlin.android")
id("org.jetbrains.kotlin.android")
// id ("kotlin-kapt")
id("io.objectbox")
}
id("com.google.gms.google-services")
id("com.google.firebase.crashlytics")
id("applovin-quality-service")
}
applovin {
apiKey = "4CFHxOfvQvy95EqDxa_eNPe4pmq_KfoZPJeVscTMt2uT3VL2fO3iyZyBFMTOOTRbKw6WelbaDEhNzxOxVrlQTE"
}
val timestamp = SimpleDateFormat("MM_dd_HH_mm").format(Date())
android {
@ -15,18 +21,16 @@ android {
compileSdk = 34
defaultConfig {
//com.hi.music.player
applicationId = "com.offline.music.playermp3"
minSdk = 23
targetSdk = 34
versionCode = 1
versionName = "1.0.0"
versionCode = 2
versionName = "1.0.1"
setProperty(
"archivesBaseName",
"Offline Music Player" + versionName + "(${versionCode})_$timestamp"
)
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}
@ -39,10 +43,17 @@ android {
"proguard-rules.pro"
)
}
debug {
isMinifyEnabled = true
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}
kotlinOptions{
jvmTarget = "1.8"
}
// kotlinOptions{
// jvmTarget = "1.8"
// }
compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
@ -76,25 +87,48 @@ dependencies {
implementation("com.github.bumptech.glide:glide:4.16.0")
// kapt("com.github.bumptech.glide:compiler:4.16.0")
// Glide 的图片变换库,包括高斯模糊
implementation("jp.wasabeef:glide-transformations:4.3.0")
implementation ("jp.wasabeef:glide-transformations:4.3.0")
//提取图片主色
// implementation ("androidx.palette:palette:1.0.0")
implementation ("androidx.palette:palette:1.0.0")
//动画
// implementation ("com.airbnb.android:lottie:6.5.0")
implementation ("com.airbnb.android:lottie:6.5.0")
//----------media3
// implementation("androidx.media3:media3-exoplayer:1.4.1")
// implementation("androidx.media3:media3-exoplayer-dash:1.4.1")
// implementation("androidx.media3:media3-session:1.4.1")
// implementation("androidx.media3:media3-ui:1.4.1")
// implementation ("androidx.media3:media3-database:1.4.1")
implementation("androidx.media3:media3-exoplayer:1.4.1")
implementation("androidx.media3:media3-exoplayer-dash:1.4.1")
implementation("androidx.media3:media3-session:1.4.1")
implementation("androidx.media3:media3-ui:1.4.1")
implementation ("androidx.media3:media3-database:1.4.1")
//----------media3
//------------------firebase
implementation(platform("com.google.firebase:firebase-bom:33.1.1"))
implementation("com.google.firebase:firebase-crashlytics")
implementation("com.google.firebase:firebase-analytics")
implementation("com.google.firebase:firebase-config")
// implementation ("com.geyifeng.immersionbar:immersionbar:3.2.2")
// implementation ("com.geyifeng.immersionbar:immersionbar-components:3.2.2")
//-----------------------applovin
implementation("com.applovin:applovin-sdk:+")
implementation("com.applovin.mediation:bigoads-adapter:+")
implementation("com.applovin.mediation:chartboost-adapter:+")
implementation("com.google.android.gms:play-services-base:16.1.0")
implementation("com.applovin.mediation:fyber-adapter:+")
implementation("com.applovin.mediation:inmobi-adapter:+")
implementation("com.squareup.picasso:picasso:2.71828")
implementation("androidx.recyclerview:recyclerview:1.1.0")
implementation("com.applovin.mediation:ironsource-adapter:+")
implementation("com.applovin.mediation:vungle-adapter:+")
implementation("com.applovin.mediation:facebook-adapter:+")
implementation("com.applovin.mediation:moloco-adapter:+")
implementation("com.applovin.mediation:bytedance-adapter:+")
implementation("com.applovin.mediation:unityads-adapter:+")
//applovin The SDK collects the Google Advertising ID. This requires the Android Advertising ID (AAID) module (com.google.android.gms:play-services-ads-identifier).
implementation("com.google.android.gms:play-services-ads-identifier:18.1.0")
}

29
app/google-services.json Normal file
View File

@ -0,0 +1,29 @@
{
"project_info": {
"project_number": "764712897094",
"project_id": "offline-music-player-176ab",
"storage_bucket": "offline-music-player-176ab.firebasestorage.app"
},
"client": [
{
"client_info": {
"mobilesdk_app_id": "1:764712897094:android:2d533528b5ff4271a45847",
"android_client_info": {
"package_name": "com.offline.music.playermp3"
}
},
"oauth_client": [],
"api_key": [
{
"current_key": "AIzaSyDohnv8AScCd1A7RTR-6LLLtQcE7EuJmIw"
}
],
"services": {
"appinvite_service": {
"other_platform_oauth_client": []
}
}
}
],
"configuration_version": "1"
}

View File

@ -43,3 +43,60 @@
-keep class * extends com.google.gson.reflect.TypeToken
-keepattributes AnnotationDefault,RuntimeVisibleAnnotations
# 使用R8全模式对未保留的类剥离通用签名挂起函数被包装在使用类型参数的continuation中
-keep,allowobfuscation,allowshrinking class kotlin.coroutines.Continuation
# 如果不保留R8完整模式将从返回类型中剥离通用签名
-if interface * { @retrofit2.http.* public *** *(...); }
-keep,allowoptimization,allowshrinking,allowobfuscation class <3>
# 在R8全模式下对未保留的类剥离通用签名
-keep,allowobfuscation,allowshrinking class retrofit2.Response
## ---------Retrofit混淆方法---------------
-dontwarn javax.annotation.**
-dontwarn javax.inject.**
# OkHttp3
-dontwarn okhttp3.logging.**
-keep class okhttp3.internal.**{*;}
-dontwarn okio.**
# Retrofit
-dontwarn retrofit2.**
-keep class retrofit2.** { *; }
-keepattributes Signature
-keepattributes Exceptions
# RxJava RxAndroid
-dontwarn sun.misc.**
-keepclassmembers class rx.internal.util.unsafe.*ArrayQueue*Field* {
long producerIndex;
long consumerIndex;
}
-keepclassmembers class rx.internal.util.unsafe.BaseLinkedQueueProducerNodeRef {
rx.internal.util.atomic.LinkedQueueNode producerNode;
}
-keepclassmembers class rx.internal.util.unsafe.BaseLinkedQueueConsumerNodeRef {
rx.internal.util.atomic.LinkedQueueNode consumerNode;
}
# Gson
-keep class com.google.gson.stream.** { *; }
-keepattributes EnclosingMethod
-keep class com.offline.music.playermp3.javabean.**{*;}
-keep class org.chromium.** { *; }
-keep class androidx.media3.datasource.cronet.** { *; }
-keep class androidx.media3.exoplayer.scheduler.PlatformScheduler$PlatformSchedulerService { *; }

View File

@ -5,13 +5,16 @@
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
<uses-permission
android:name="android.permission.READ_EXTERNAL_STORAGE"
android:maxSdkVersion="32" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<application
android:name=".MusicApplication"
android:allowBackup="true"
@ -23,25 +26,44 @@
android:supportsRtl="true"
android:theme="@style/Theme.MusicApp"
tools:targetApi="31">
<activity
android:name=".ui.activity.LikeSongActivity"
android:exported="false"
android:screenOrientation="portrait" />
<activity
android:name=".ui.activity.ResultListActivity"
android:exported="false"
android:screenOrientation="portrait" />
<activity
android:name=".ui.activity.CategoryListActivity"
android:exported="false"
android:screenOrientation="portrait" />
<activity
android:name=".ui.activity.A_PlayActivity"
android:screenOrientation="portrait"
android:exported="false" />
android:exported="false"
android:screenOrientation="portrait" />
<activity
android:name=".ui.activity.A_SettingActivity"
android:screenOrientation="portrait"
android:exported="false" />
android:exported="false"
android:screenOrientation="portrait" />
<activity
android:name=".ui.activity.PlayActivity"
android:exported="false"
android:screenOrientation="portrait" />
<activity
android:name=".ui.activity.A_HomeActivity"
android:exported="true"
android:screenOrientation="portrait" />
<activity
android:name=".ui.activity.HomeActivity"
android:exported="false"
android:screenOrientation="portrait">
</activity>
<activity
android:name=".ui.activity.A_SplashActivity"
android:screenOrientation="portrait"
android:exported="true" >
android:exported="true"
android:screenOrientation="portrait">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
@ -49,25 +71,25 @@
</intent-filter>
</activity>
<!-- <service-->
<!-- android:name=".media3.PlaybackService"-->
<!-- android:exported="true"-->
<!-- android:foregroundServiceType="mediaPlayback">-->
<!-- <intent-filter>-->
<!-- <action android:name="androidx.media3.session.MediaSessionService" />-->
<!-- </intent-filter>-->
<!-- </service>-->
<service
android:name=".media3.PlaybackService"
android:exported="true"
android:foregroundServiceType="mediaPlayback">
<intent-filter>
<action android:name="androidx.media3.session.MediaSessionService" />
</intent-filter>
</service>
<!-- <service-->
<!-- android:name=".media3.MyDownloadService"-->
<!-- android:exported="false"-->
<!-- android:foregroundServiceType="dataSync"-->
<!-- tools:ignore="ForegroundServicePermission">-->
<!-- <intent-filter>-->
<!-- <action android:name="androidx.media3.exoplayer.downloadService.action.RESTART" />-->
<!-- <category android:name="android.intent.category.DEFAULT" />-->
<!-- </intent-filter>-->
<!-- </service>-->
<service
android:name=".media3.MyDownloadService"
android:exported="false"
android:foregroundServiceType="dataSync"
tools:ignore="ForegroundServicePermission">
<intent-filter>
<action android:name="androidx.media3.exoplayer.downloadService.action.RESTART" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</service>
<service
android:name=".service.MusicPlayerForegroundService"
android:enabled="true"

View File

@ -2,6 +2,24 @@ package com.offline.music.playermp3;
import android.app.Application;
import android.content.Context;
import android.content.Intent;
import androidx.annotation.OptIn;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import androidx.media3.common.util.UnstableApi;
import androidx.media3.database.StandaloneDatabaseProvider;
import com.applovin.sdk.AppLovinMediationProvider;
import com.applovin.sdk.AppLovinSdk;
import com.applovin.sdk.AppLovinSdkConfiguration;
import com.applovin.sdk.AppLovinSdkInitializationConfiguration;
import com.offline.music.playermp3.api.MediaControllerStatusListener;
import com.offline.music.playermp3.firebase.RemoteConfigJava;
import com.offline.music.playermp3.firebase.Sp;
import com.offline.music.playermp3.helper.CommonUtils;
import com.offline.music.playermp3.media3.MyDownloadService;
import com.offline.music.playermp3.media3.MyMediaControllerManager;
import com.offline.music.playermp3.objectbox.ObjectBoxManager;
public class MusicApplication extends Application {
@ -9,7 +27,10 @@ public class MusicApplication extends Application {
public static String visitorData;
private String MAx_SDK_key = "tAh5Z8CtFiG05NfRWh9UuOHCp3h3SaFvlh6Phw5ucaxFMCRs2d97F7lywfBKOEKSipdaGq8vZOaDLZkrL1fDDc";
public static boolean initSDkOK = false;
public static String initAction = "MUSIC_ACTION_INIT";
public static void setVisitorData(String visitorData) {
@ -20,20 +41,42 @@ public class MusicApplication extends Application {
return visitorData;
}
@OptIn(markerClass = UnstableApi.class)
@Override
public void onCreate() {
super.onCreate();
myApplication = this;
// Sp.init(this);
// RemoteConfigJava.getInstance().init(this);
// ObjectBoxManager.init(this);
// MyMediaControllerManager.getInstance().init(new MediaControllerStatusListener() {
// @Override
// public void onMediaControllerComplete(boolean isOk) {
// CommonUtils.LogMsg("=-----mediaController+" + isOk);
// }
// });
InitializeMax();
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);
}
});
}
private void InitializeMax(){
AppLovinSdkInitializationConfiguration initConfig = AppLovinSdkInitializationConfiguration.builder( MAx_SDK_key, this )
.setMediationProvider( AppLovinMediationProvider.MAX )
.build();
// Initialize the SDK with the configuration
AppLovinSdk.getInstance( this ).initialize( initConfig, new AppLovinSdk.SdkInitializationListener()
{
@Override
public void onSdkInitialized(final AppLovinSdkConfiguration sdkConfig)
{
initSDkOK = true;
LocalBroadcastManager.getInstance(MusicApplication.this).sendBroadcast(new Intent(initAction));
}
} );
}

View File

@ -0,0 +1,69 @@
package com.offline.music.playermp3.adapter;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import com.bumptech.glide.Glide;
import com.offline.music.playermp3.MusicApplication;
import com.offline.music.playermp3.R;
import com.offline.music.playermp3.databinding.ItemCategoryBinding;
import com.offline.music.playermp3.helper.CommonUtils;
import com.offline.music.playermp3.javabean.response.child.ResponseCategory;
public class AdapterCategory extends BaseAdapter<ResponseCategory, ItemCategoryBinding>{
@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<ItemCategoryBinding> itemHolder = (VHolder<ItemCategoryBinding>) 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);
}
}
});
}
}
}

View File

@ -0,0 +1,100 @@
package com.offline.music.playermp3.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.request.RequestListener;
import com.bumptech.glide.request.target.Target;
import com.offline.music.playermp3.MusicApplication;
import com.offline.music.playermp3.R;
import com.offline.music.playermp3.databinding.ItemCategoryListBinding;
import com.offline.music.playermp3.databinding.ItemSingerBinding;
import com.offline.music.playermp3.helper.CommonUtils;
import com.offline.music.playermp3.helper.MyValue;
import com.offline.music.playermp3.javabean.response.ResponsePlayListInfo;
public class AdapterCategoryList extends BaseAdapter<ResponsePlayListInfo, ItemCategoryListBinding> {
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<ItemCategoryListBinding> itemHolder = (VHolder<ItemCategoryListBinding>) 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<Drawable>() {
@Override
public boolean onLoadFailed(@Nullable GlideException e, @Nullable Object model, @NonNull Target<Drawable> target, boolean isFirstResource) {
CommonUtils.LogMsg(e.getMessage());
return false;
}
@OptIn(markerClass = UnstableApi.class)
@Override
public boolean onResourceReady(@NonNull Drawable resource, @NonNull Object model, Target<Drawable> target, @NonNull DataSource dataSource, boolean isFirstResource) {
return false;
}
})
.into(vb.image);
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);
}
}
}

View File

@ -0,0 +1,80 @@
package com.offline.music.playermp3.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.offline.music.playermp3.MusicApplication;
import com.offline.music.playermp3.R;
import com.offline.music.playermp3.databinding.ItemLikeSongBinding;
import com.offline.music.playermp3.helper.CommonUtils;
import com.offline.music.playermp3.javabean.BoxDownloadSong;
public class AdapterDownloadSong extends BaseAdapter<Download, ItemLikeSongBinding> {
@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<ItemLikeSongBinding> itemHolder = (VHolder<ItemLikeSongBinding>) 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<Drawable>() {
@Override
public boolean onLoadFailed(@Nullable GlideException e, @Nullable Object model, @NonNull Target<Drawable> target, boolean isFirstResource) {
CommonUtils.LogMsg(e.getMessage());
return false;
}
@Override
public boolean onResourceReady(@NonNull Drawable resource, @NonNull Object model, Target<Drawable> 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);
}
}
});
}
}

View File

@ -0,0 +1,85 @@
package com.offline.music.playermp3.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.offline.music.playermp3.MusicApplication;
import com.offline.music.playermp3.databinding.ItemFooterLoadingBinding;
import com.offline.music.playermp3.databinding.ItemHomeBinding;
import com.offline.music.playermp3.helper.CommonUtils;
import com.offline.music.playermp3.javabean.response.child.ResponseCategory;
import com.offline.music.playermp3.javabean.response.child.ResponseHomeChild;
import com.offline.music.playermp3.javabean.response.child.ResponseSingle;
import java.util.List;
public class AdapterHome extends BaseAdapter<ResponseHomeChild, ItemHomeBinding> {
@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);
CommonUtils.LogMsg("-----------pos="+position+"---itemViewType="+itemViewType);
if (itemViewType == TYPE_ITEM) {
VHolder<ItemHomeBinding> itemHolder = (VHolder<ItemHomeBinding>) holder;
ItemHomeBinding vb = itemHolder.getVb();
ResponseHomeChild responseHomeChild = data.get(position);
vb.headTitle.setText(responseHomeChild.getHeaderTitle());
List<ResponseSingle> singleList = responseHomeChild.getSingleList();
List<ResponseCategory> 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<ItemFooterLoadingBinding> footerHolder = (VHolder<ItemFooterLoadingBinding>) holder;
FrameLayout root = footerHolder.getVb().getRoot();
if (isLoadingAdded) {
root.setVisibility(View.VISIBLE);
} else {
root.setVisibility(View.GONE);
}
}
}
}

View File

@ -0,0 +1,211 @@
package com.offline.music.playermp3.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.media3.common.util.UnstableApi;
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.offline.music.playermp3.MusicApplication;
import com.offline.music.playermp3.R;
import com.offline.music.playermp3.api.onCheckDownload;
import com.offline.music.playermp3.databinding.ItemLikeSongBinding;
import com.offline.music.playermp3.databinding.ItemSingerBinding;
import com.offline.music.playermp3.helper.CommonUtils;
import com.offline.music.playermp3.javabean.BoxDownloadSong;
import com.offline.music.playermp3.javabean.BoxLikeSong;
import com.offline.music.playermp3.javabean.CustomerDownload;
import com.offline.music.playermp3.javabean.response.ResponsePlayUrl;
import com.offline.music.playermp3.media3.MyDownloadService;
import com.offline.music.playermp3.network.JsonHelper;
import com.offline.music.playermp3.network.RetrofitManager;
import com.offline.music.playermp3.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<BoxLikeSong, ItemLikeSongBinding> {
private VMApplication vmApplication;
private List<Pair<Boolean, String>> 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<ItemLikeSongBinding> itemHolder = (VHolder<ItemLikeSongBinding>) 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<Boolean,String> 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.offline.music.playermp3.api.RequestListener<ResponseBody>() {
@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<Drawable>() {
@Override
public boolean onLoadFailed(@Nullable GlideException e, @Nullable Object model, @NonNull Target<Drawable> target, boolean isFirstResource) {
CommonUtils.LogMsg(e.getMessage());
return false;
}
@Override
public boolean onResourceReady(@NonNull Drawable resource, @NonNull Object model, Target<Drawable> 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);
}
}
});
}
}

View File

@ -0,0 +1,112 @@
package com.offline.music.playermp3.adapter;
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.offline.music.playermp3.MusicApplication;
import com.offline.music.playermp3.R;
import com.offline.music.playermp3.databinding.ItemPlayListBinding;
import com.offline.music.playermp3.helper.CommonUtils;
import com.offline.music.playermp3.javabean.response.ResponsePlayListInfo;
import com.offline.music.playermp3.media3.MyMediaControllerManager;
public class AdapterPlayList extends BaseAdapter<ResponsePlayListInfo, ItemPlayListBinding> {
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<ItemPlayListBinding> itemHolder = (VHolder<ItemPlayListBinding>) 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());
}
});
}
}

View File

@ -0,0 +1,68 @@
package com.offline.music.playermp3.adapter;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.offline.music.playermp3.databinding.ItemResultBinding;
import com.offline.music.playermp3.helper.CommonUtils;
import com.offline.music.playermp3.helper.ItemDecoration;
import com.offline.music.playermp3.javabean.response.ResponseResultList;
public class AdapterResult extends BaseAdapter<ResponseResultList, ItemResultBinding>{
public AdapterResult(Context mContext) {
super(mContext);
}
@Override
protected ItemResultBinding getViewBinding(ViewGroup parent) {
return ItemResultBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false);
}
@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
ResponseResultList resultList = data.get(position);
VHolder<ItemResultBinding> itemHolder = (VHolder<ItemResultBinding>) holder;
ItemResultBinding vb = itemHolder.getVb();
String headerTitle = resultList.getHeaderTitle();
vb.tvHeaderTitle.setText(headerTitle);
CommonUtils.LogMsg("---------headerTitle="+headerTitle);
ItemDecoration itemDecoration ;
if(position == 0){
itemDecoration = new ItemDecoration(10,0,0);
AdapterResultListSong adapterResultListSong = new AdapterResultListSong();
adapterResultListSong.setData(resultList.getChildList());
vb.listChild.setAdapter(adapterResultListSong);
vb.listChild.setLayoutManager(new LinearLayoutManager(mContext));
if(homeItemClickListener!= null)
adapterResultListSong.setHomeItemClickListener(homeItemClickListener);
}else {
itemDecoration = new ItemDecoration(0,10,0);
AdapterResultListAlbum adapterResultListAlbum = new AdapterResultListAlbum();
adapterResultListAlbum.setData(resultList.getChildList());
vb.listChild.setAdapter(adapterResultListAlbum);
vb.listChild.setLayoutManager(new LinearLayoutManager(mContext, LinearLayoutManager.HORIZONTAL,false));
if(homeItemClickListener!= null)
adapterResultListAlbum.setHomeItemClickListener(homeItemClickListener);
}
// vb.listChild.addItemDecoration(itemDecoration);
if(position == data.size() - 1){
vb.place.setVisibility(View.VISIBLE);
}else {
vb.place.setVisibility(View.GONE);
}
}
}

View File

@ -0,0 +1,81 @@
package com.offline.music.playermp3.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.offline.music.playermp3.MusicApplication;
import com.offline.music.playermp3.R;
import com.offline.music.playermp3.databinding.ItemResultListAlbumBinding;
import com.offline.music.playermp3.helper.CommonUtils;
import com.offline.music.playermp3.javabean.response.child.ResponseResultListChild;
public class AdapterResultListAlbum extends BaseAdapter<ResponseResultListChild, ItemResultListAlbumBinding>{
@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<ItemResultListAlbumBinding> itemHolder = (VHolder<ItemResultListAlbumBinding>) 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<Drawable>() {
@Override
public boolean onLoadFailed(@Nullable GlideException e, @Nullable Object model, @NonNull Target<Drawable> target, boolean isFirstResource) {
CommonUtils.LogMsg(e.getMessage());
return false;
}
@Override
public boolean onResourceReady(@NonNull Drawable resource, @NonNull Object model, Target<Drawable> 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());
}
}

View File

@ -0,0 +1,83 @@
package com.offline.music.playermp3.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.offline.music.playermp3.MusicApplication;
import com.offline.music.playermp3.R;
import com.offline.music.playermp3.databinding.ItemResultListSongBinding;
import com.offline.music.playermp3.helper.CommonUtils;
import com.offline.music.playermp3.javabean.response.child.ResponseResultListChild;
public class AdapterResultListSong extends BaseAdapter<ResponseResultListChild, ItemResultListSongBinding> {
@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<ItemResultListSongBinding> itemHolder = (VHolder<ItemResultListSongBinding>) 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<Drawable>() {
@Override
public boolean onLoadFailed(@Nullable GlideException e, @Nullable Object model, @NonNull Target<Drawable> target, boolean isFirstResource) {
CommonUtils.LogMsg(e.getMessage());
return false;
}
@Override
public boolean onResourceReady(@NonNull Drawable resource, @NonNull Object model, Target<Drawable> 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());
}
}

View File

@ -0,0 +1,82 @@
package com.offline.music.playermp3.adapter;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.core.content.ContextCompat;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.bumptech.glide.Glide;
import com.bumptech.glide.load.resource.bitmap.CircleCrop;
import com.offline.music.playermp3.MusicApplication;
import com.offline.music.playermp3.R;
import com.offline.music.playermp3.databinding.ItemSearchBinding;
import com.offline.music.playermp3.helper.CommonUtils;
import com.offline.music.playermp3.javabean.response.ResponseSearch;
import com.offline.music.playermp3.javabean.response.child.ResponseSearchChild;
import java.util.List;
public class AdapterSearch extends BaseAdapter<ResponseSearch, ItemSearchBinding> {
@Override
protected ItemSearchBinding getViewBinding(ViewGroup parent) {
return ItemSearchBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false);
}
@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
ResponseSearch responseSearch = data.get(position);
VHolder<ItemSearchBinding> itemHolder = (VHolder<ItemSearchBinding>) holder;
ItemSearchBinding vb = itemHolder.getVb();
vb.tvHeaderTitle.setText(responseSearch.getHeaderTitle());
String beastSongTCovert = responseSearch.getBeastSongTCovert();
if (beastSongTCovert != null && !beastSongTCovert.isEmpty()) {
vb.layoutBest.setVisibility(View.VISIBLE);
Glide.with(MusicApplication.myApplication)
.load(beastSongTCovert)
.transform(new CircleCrop())
.into(vb.imBestCovert);
vb.tvBestTitle.setText(responseSearch.getBeastSongTitle());
vb.tvBestSubtitle.setText(responseSearch.getBeastSongDescribe());
vb.layout.setBackground(ContextCompat.getDrawable(MusicApplication.myApplication, R.drawable.bg_best_bg));
vb.layout.setPadding(CommonUtils.dpToPx(6), CommonUtils.dpToPx(10), 0, 0);
vb.tvPlay.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (homeItemClickListener != null) {
homeItemClickListener.onClickSearchResultBest(responseSearch);
}
}
});
} else {
vb.layoutBest.setVisibility(View.GONE);
vb.layout.setBackground(null);
vb.layout.setPadding(0, 0, 0, 0);
}
List<ResponseSearchChild> list = responseSearch.getList();
if(list!= null&&list.size()>0){
AdapterSearchChild adapterSearchChild = new AdapterSearchChild();
adapterSearchChild.setData(list);
vb.list.setAdapter(adapterSearchChild);
if (homeItemClickListener != null)
adapterSearchChild.setHomeItemClickListener(homeItemClickListener);
vb.list.setLayoutManager(new LinearLayoutManager(MusicApplication.myApplication));
}
if(position == data.size() - 1){
vb.place.setVisibility(View.VISIBLE);
}else {
vb.place.setVisibility(View.GONE);
}
}
}

View File

@ -0,0 +1,70 @@
package com.offline.music.playermp3.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.offline.music.playermp3.MusicApplication;
import com.offline.music.playermp3.R;
import com.offline.music.playermp3.databinding.ItemSearchBinding;
import com.offline.music.playermp3.databinding.ItemSearchChildBinding;
import com.offline.music.playermp3.helper.CommonUtils;
import com.offline.music.playermp3.javabean.response.child.ResponseSearchChild;
public class AdapterSearchChild extends BaseAdapter<ResponseSearchChild, ItemSearchChildBinding> {
@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<ItemSearchChildBinding> itemHolder = (VHolder<ItemSearchChildBinding>) 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<Drawable>() {
@Override
public boolean onLoadFailed(@Nullable GlideException e, @Nullable Object model, @NonNull Target<Drawable> target, boolean isFirstResource) {
CommonUtils.LogMsg(e.getMessage());
return false;
}
@Override
public boolean onResourceReady(@NonNull Drawable resource, @NonNull Object model, Target<Drawable> 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);
}
}
});
}
}

View File

@ -0,0 +1,77 @@
package com.offline.music.playermp3.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.offline.music.playermp3.MusicApplication;
import com.offline.music.playermp3.R;
import com.offline.music.playermp3.databinding.ItemSingerBinding;
import com.offline.music.playermp3.helper.CommonUtils;
import com.offline.music.playermp3.javabean.response.child.ResponseSingle;
public class AdapterSinger extends BaseAdapter<ResponseSingle, ItemSingerBinding> {
@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<ItemSingerBinding> itemHolder = (VHolder<ItemSingerBinding>) 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<Drawable>() {
@Override
public boolean onLoadFailed(@Nullable GlideException e, @Nullable Object model, @NonNull Target<Drawable> target, boolean isFirstResource) {
CommonUtils.LogMsg(e.getMessage());
return false;
}
@Override
public boolean onResourceReady(@NonNull Drawable resource, @NonNull Object model, Target<Drawable> 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);
}
}
});
}
}
}

View File

@ -0,0 +1,46 @@
package com.offline.music.playermp3.adapter;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import com.offline.music.playermp3.databinding.ItemSuggestionBinding;
public class AdapterSuggestion extends BaseAdapter<String, ItemSuggestionBinding> {
@Override
protected ItemSuggestionBinding getViewBinding(ViewGroup parent) {
return ItemSuggestionBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false);
}
@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
VHolder<ItemSuggestionBinding> itemHolder = (VHolder<ItemSuggestionBinding>) holder;
ItemSuggestionBinding vb = itemHolder.getVb();
String text = data.get(position);
vb.tvSuggestion.setText(text);
vb.relayout.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(homeItemClickListener!= null){
homeItemClickListener.onClickItemSuggestion(true,text);
}
}
});
vb.imFillIn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(homeItemClickListener!= null){
homeItemClickListener.onClickItemSuggestion(false,text);
}
}
});
}
}

View File

@ -0,0 +1,129 @@
package com.offline.music.playermp3.adapter;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import androidx.viewbinding.ViewBinding;
import com.offline.music.playermp3.api.HomeItemClickListener;
import com.offline.music.playermp3.databinding.ItemFooterLoadingBinding;
import com.offline.music.playermp3.helper.CommonUtils;
import java.util.ArrayList;
import java.util.List;
abstract public class BaseAdapter<K, T extends ViewBinding> extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
protected List<K> data = new ArrayList<>();
protected Context mContext;
protected static final int TYPE_ITEM = 0;
protected static final int TYPE_FOOTER = 1;
protected boolean isLoadingAdded = false;
public BaseAdapter() {
}
public BaseAdapter(Context mContext) {
this.mContext = mContext;
}
protected HomeItemClickListener homeItemClickListener;
public void setHomeItemClickListener(HomeItemClickListener homeItemClickListener) {
this.homeItemClickListener = homeItemClickListener;
}
public boolean isLoadingAdded() {
return isLoadingAdded;
}
public void addData(List<K> data) {
this.data.addAll(data);
notifyDataSetChanged();
}
public void setData(List<K> data) {
this.data.clear();
this.data.addAll(data);
notifyDataSetChanged();
}
public void addLoadingFooter() {
isLoadingAdded = true;
notifyItemInserted(data.size());
CommonUtils.LogMsg("--------addLoadingFooter---position="+data.size());
}
// Hide loading footer
public void removeLoadingFooter() {
int position = getItemCount();
if (position >= 0)
notifyItemRemoved(position);
isLoadingAdded = false;
CommonUtils.LogMsg("---------removeLoadingFooter--position="+position);
}
@NonNull
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
if (viewType == TYPE_ITEM) {
T viewBinding = getViewBinding(parent);
return new VHolder<>(viewBinding);
} else {
// Inflate footer layout
ItemFooterLoadingBinding inflate = ItemFooterLoadingBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false);
return new VHolder<>(inflate);
}
}
protected abstract T getViewBinding(ViewGroup parent);
@Override
public int getItemViewType(int position) {
return (position == data.size() && isLoadingAdded) ? TYPE_FOOTER : TYPE_ITEM;
}
@Override
public int getItemCount() {
CommonUtils.LogMsg("---------getItemCount=");
return data.size() + (isLoadingAdded ? 1 : 0);
}
public static class VHolder<V extends ViewBinding> extends RecyclerView.ViewHolder {
private V vb;
public V getVb() {
return vb;
}
public VHolder(@NonNull V itemView) {
super(itemView.getRoot());
vb = itemView;
}
}
public static class FooterHolder extends RecyclerView.ViewHolder {
private ItemFooterLoadingBinding vb;
public ItemFooterLoadingBinding getVb() {
return vb;
}
public FooterHolder(@NonNull ItemFooterLoadingBinding itemView) {
super(itemView.getRoot());
vb = itemView;
}
}
}

View File

@ -0,0 +1,36 @@
package com.offline.music.playermp3.adapter;
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import androidx.viewpager2.adapter.FragmentStateAdapter;
import com.offline.music.playermp3.ui.fragmnt.HomeFragment;
import com.offline.music.playermp3.ui.fragmnt.ProfileFragment;
import com.offline.music.playermp3.ui.fragmnt.SearchFragment;
import java.util.ArrayList;
import java.util.List;
public class HomeViewPagerAdapter extends FragmentStateAdapter {
private final List<Fragment> fragments = new ArrayList<>();
public HomeViewPagerAdapter(@NonNull FragmentActivity fragmentActivity) {
super(fragmentActivity);
fragments.add(new HomeFragment());
fragments.add(new SearchFragment());
fragments.add(new ProfileFragment());
}
@NonNull
@Override
public Fragment createFragment(int position) {
return fragments.get(position);
}
@Override
public int getItemCount() {
return fragments.size();
}
}

View File

@ -0,0 +1,100 @@
package com.offline.music.playermp3.api;
import androidx.media3.exoplayer.offline.Download;
import com.offline.music.playermp3.javabean.BoxLikeSong;
import com.offline.music.playermp3.javabean.response.ResponsePlayListInfo;
import com.offline.music.playermp3.javabean.response.ResponseSearch;
import com.offline.music.playermp3.javabean.response.child.ResponseCategory;
import com.offline.music.playermp3.javabean.response.child.ResponseResultListChild;
import com.offline.music.playermp3.javabean.response.child.ResponseSearchChild;
import com.offline.music.playermp3.javabean.response.child.ResponseSingle;
public interface HomeItemClickListener {
//首页点击了单曲
default void onClickItemSinger(ResponseSingle data) {
}
//首页点击了分类合集
default void onClickItemCategory(ResponseCategory data) {
}
//分类合集点击了某一首歌曲
default void onClickItemCategoryList(ResponsePlayListInfo data,int musicIndex) {
}
/**
* 点击了搜索建议
* @param isSearch true 直接搜索 false 输入搜索建议文字
* @param data
*/
default void onClickItemSuggestion(boolean isSearch,String data) {
}
/**
* 点击搜索最佳结果第一个
* @param
*/
default void onClickSearchResultBest(ResponseSearch responseSearch){
}
/**
* 点击搜索结果
* @param
*/
default void onClickSearchResult(ResponseSearchChild responseSearchChild){
}
/**
* 搜索结果详情页面点击单曲
* @param child
* @param index
*/
default void onClickResultSong(ResponseResultListChild child,int index){
}
default void onClickResultAlbum(ResponseResultListChild child,int index){
}
/**
* 点击喜爱歌曲列表的某一首歌曲
* @param boxLikeSong
* @param index
*/
default void onClickLikeSong(BoxLikeSong boxLikeSong, int index){
}
/**
* 开始下载歌曲
* @param boxLikeSong
* @param index
*/
default void onDownloadSong(String videoId, int index){
}
/**
* 点击下载歌曲列表的某一首歌曲
* @param index
*/
default void onClickDownloadSong(Download download, int index){
}
}

View File

@ -0,0 +1,10 @@
package com.offline.music.playermp3.api;
import com.offline.music.playermp3.javabean.BoxLikeSong;
import java.util.List;
public interface LikeSongListener {
void onLikeSongChange(List<BoxLikeSong> data);
}

View File

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

View File

@ -0,0 +1,9 @@
package com.offline.music.playermp3.api;
public interface MediaControllerStatusListener {
void onMediaControllerComplete(boolean isOk);
}

View File

@ -0,0 +1,5 @@
package com.offline.music.playermp3.api;
public interface OnHasUrlAction {
void onHasUrl();
}

View File

@ -0,0 +1,10 @@
package com.offline.music.playermp3.api;
import org.json.JSONObject;
public interface RequestListener<T> {
void onFail(String errorMsg);
void onSuccess(JSONObject data) ;
}

View File

@ -0,0 +1,7 @@
package com.offline.music.playermp3.api;
import com.offline.music.playermp3.javabean.CustomerDownload;
public interface onCheckDownload {
void onHasDownload(CustomerDownload customerDownload);
}

View File

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

View File

@ -0,0 +1,5 @@
package com.offline.music.playermp3.api;
public interface onPlayNextListener {
void onPlayNext(boolean hasNext);
}

View File

@ -0,0 +1,167 @@
package com.offline.music.playermp3.customerview;
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.View;
import android.view.animation.LinearInterpolator;
import com.offline.music.playermp3.helper.CommonUtils;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
public class AnimaPlayingView extends View {
private Paint paint;
private int barColor = Color.GREEN; // 音量条的颜色
private int numBars = 5; // 音量条的数量
private float[] barHeights; // 每个音量条的高度
private int maxHeight = CommonUtils.dpToPx(20); // 音量条的最大高度
private int minHeight = CommonUtils.dpToPx(4); // 音量条的最小高度
// 每个柱子的宽度
private int barWidth = CommonUtils.dpToPx(4);
// 柱子之间的间距
private int barSpacing = CommonUtils.dpToPx(2);
private int cornerRadius = CommonUtils.dpToPx(16);
private Random random;
private ValueAnimator animator;
private List<ValueAnimator> valueAnimatorList;
public AnimaPlayingView(Context context) {
super(context);
init();
}
public AnimaPlayingView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public AnimaPlayingView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
valueAnimatorList = new ArrayList<>();
paint = new Paint();
paint.setColor(barColor);
paint.setStyle(Paint.Style.FILL);
barHeights = new float[numBars];
random = new Random();
// 初始化音量条高度
for (int i = 0; i < numBars; i++) {
barHeights[i] = 0;
}
initAnimating();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int height = getHeight();
int width = getWidth();
int barCount = barHeights.length;
int totalWidth = barCount * barWidth + (barCount - 1) * barSpacing;
int startX = (width - totalWidth) / 2;
for (int i = 0; i < numBars; i++) {
float left = startX + i * (barWidth + barSpacing);
float top = height - barHeights[i];
float right = left + barWidth;
float bottom = height;
canvas.drawRoundRect(left, top, right, bottom, cornerRadius, cornerRadius, paint);
}
}
public void startAnimating() {
for (ValueAnimator valueAnimator : valueAnimatorList) {
valueAnimator.start();
}
}
// 启动固定动画使音量条的高度变化
public void initAnimating() {
for (int i = 0; i < numBars; i++) {
animator = ValueAnimator.ofFloat(0, 1);
animator.setDuration(1000); // 每0.5秒更新一次
animator.setRepeatCount(ValueAnimator.INFINITE);
animator.setRepeatMode(ValueAnimator.REVERSE);
animator.setInterpolator(new LinearInterpolator());
int finalI = i;
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
// 更新每个音量条的高度使其不断变化
float animatedValue = (Float) animation.getAnimatedValue();
float v = animatedValue * maxHeight;
barHeights[finalI] = v;
invalidate(); // 重绘视图
}
});
valueAnimatorList.add(animator);
animator.setStartDelay(i * 200L);
}
}
public void pauseAnimating() {
for (ValueAnimator valueAnimator : valueAnimatorList) {
valueAnimator.pause();
}
for (int i = 0; i < numBars; i++) {
barHeights[i] = minHeight;
}
invalidate();
}
// 随机生成音量条的高度
private float randomHeight() {
return minHeight + random.nextFloat() * (maxHeight - minHeight);
}
// 设置音量条的颜色
public void setBarColor(int color) {
barColor = color;
paint.setColor(barColor);
invalidate();
}
// 设置音量条的最大高度
public void setMaxHeight(int maxHeight) {
this.maxHeight = maxHeight;
invalidate();
}
// 设置音量条的最小高度
public void setMinHeight(int minHeight) {
this.minHeight = minHeight;
invalidate();
}
// 设置音量条数量
public void setNumBars(int numBars) {
this.numBars = numBars;
barHeights = new float[numBars];
invalidate();
}
}

View File

@ -0,0 +1,93 @@
package com.offline.music.playermp3.dialog;
import android.annotation.SuppressLint;
import android.app.Dialog;
import android.graphics.Color;
import android.os.Bundle;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.DialogFragment;
import androidx.viewbinding.ViewBinding;
import com.offline.music.playermp3.R;
public abstract class BaseDialog<T extends ViewBinding> extends DialogFragment {
protected T vb;
protected abstract T getViewBinding(LayoutInflater inflater, ViewGroup container);
protected abstract void initView();
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
vb = getViewBinding(inflater, container);
init();
initView();
return vb.getRoot();
}
@Override
public void onStart() {
super.onStart();
}
@SuppressLint("ResourceType")
private void init() {
Dialog dialog = getDialog();
setCancelable(true);
if (dialog != null) {
Window window = dialog.getWindow();
if (window != null) {
if (isFullScreen()) {
window.setStatusBarColor(Color.YELLOW); // 设置状态栏为透明或其他颜色
// 设置 dialog 占据全屏并延伸到状态栏
// window.setLayout(WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.MATCH_PARENT);
// 允许内容绘制到状态栏
window.setFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS, WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS);
window.setBackgroundDrawableResource(R.color.color_transparent);
window.getDecorView().setPadding(0, 0, 0, 0);
WindowManager.LayoutParams attributes = window.getAttributes();
attributes.gravity = Gravity.BOTTOM;
attributes.width = WindowManager.LayoutParams.MATCH_PARENT;
attributes.height = WindowManager.LayoutParams.MATCH_PARENT;
window.setAttributes(attributes);
} else {
window.setBackgroundDrawableResource(R.color.color_transparent);
window.getDecorView().setPadding(0, 0, 0, 0);
WindowManager.LayoutParams attributes = window.getAttributes();
attributes.gravity = Gravity.BOTTOM;
attributes.width = WindowManager.LayoutParams.MATCH_PARENT;
attributes.height = WindowManager.LayoutParams.WRAP_CONTENT;
window.setAttributes(attributes);
}
}
}
}
public abstract boolean isFullScreen();
public void closeDialog() {
dismiss();
}
}

View File

@ -0,0 +1,125 @@
package com.offline.music.playermp3.dialog;
import android.graphics.drawable.Drawable;
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.offline.music.playermp3.MusicApplication;
import com.offline.music.playermp3.R;
import com.offline.music.playermp3.adapter.AdapterPlayList;
import com.offline.music.playermp3.api.onImageColorListener;
import com.offline.music.playermp3.databinding.DialogPlayListBinding;
import com.offline.music.playermp3.helper.CommonUtils;
import com.offline.music.playermp3.javabean.response.ResponsePlayListInfo;
import com.offline.music.playermp3.media3.MyMediaControllerManager;
import java.util.List;
public class DialogPlayList extends BaseDialog<DialogPlayListBinding> {
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<Drawable>() {
@Override
public boolean onLoadFailed(@Nullable GlideException e, @Nullable Object model, @NonNull Target<Drawable> target, boolean isFirstResource) {
CommonUtils.LogMsg(e.getMessage());
return false;
}
@OptIn(markerClass = UnstableApi.class)
@Override
public boolean onResourceReady(@NonNull Drawable resource, @NonNull Object model, Target<Drawable> target, @NonNull DataSource dataSource, boolean isFirstResource) {
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<ResponsePlayListInfo> playList = instance.getPlayList();
AdapterPlayList adapterPlayList = new AdapterPlayList();
vb.recyclerList.setLayoutManager(new LinearLayoutManager(MusicApplication.myApplication));
adapterPlayList.addData(playList);
vb.recyclerList.setAdapter(adapterPlayList);
}
}

View File

@ -0,0 +1,149 @@
package com.offline.music.playermp3.firebase
import android.app.Application
import android.content.Context
import android.os.Handler
import android.os.Looper
import android.os.Message
import android.text.TextUtils
import com.google.firebase.remoteconfig.ConfigUpdate
import com.google.firebase.remoteconfig.ConfigUpdateListener
import com.google.firebase.remoteconfig.FirebaseRemoteConfig
import com.google.firebase.remoteconfig.FirebaseRemoteConfigException
import com.google.firebase.remoteconfig.FirebaseRemoteConfigSettings
import com.google.firebase.remoteconfig.FirebaseRemoteConfigValue
import com.offline.music.playermp3.BuildConfig
import com.offline.music.playermp3.helper.CommonUtils
import java.lang.ref.WeakReference
class RemoteConfig {
private var ctx: Context? = null
private var mFirebaseRemoteConfig: FirebaseRemoteConfig? = null
//配置是否初始化成功
private var isInit = false
//上次获取数据的时间
private var lastFetchTime: Long = 0
private val handler = MHandler(this)
companion object {
const val key_open_type = "TYPE"
//进入A面
const val value_open_type_0 = "0"
//进入B面
const val value_open_type_1 = "1"
const val MSG_REFRESH_CONFIG = 1
val instance: RemoteConfig by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED) {
RemoteConfig()
}
}
fun init(ctx: Application) {
this.ctx = ctx
initConfig()
fetchConfig()
onConfigUpdate()
}
private fun initConfig() {
var intervalTime = (60 * 10).toLong()
//如果是开发状态,则将提取时间缩短
if (BuildConfig.DEBUG) {
intervalTime = (60 * 5).toLong()
}
mFirebaseRemoteConfig = FirebaseRemoteConfig.getInstance()
val configSettings =
FirebaseRemoteConfigSettings.Builder() //默认值12小时的最短提取间隔如果在间隔内取值则优先取上次的结果
.setMinimumFetchIntervalInSeconds(intervalTime).build()
mFirebaseRemoteConfig!!.setConfigSettingsAsync(configSettings)
}
private fun onConfigUpdate() {
mFirebaseRemoteConfig!!.addOnConfigUpdateListener(object : ConfigUpdateListener {
override fun onUpdate(configUpdate: ConfigUpdate) {
CommonUtils.LogMsg("Updated keys: " + configUpdate.updatedKeys)
try {
mFirebaseRemoteConfig!!.activate().addOnCompleteListener { task ->
if (task.isSuccessful) {
updateData("onConfigUpdate", mFirebaseRemoteConfig!!.all)
}
}
} catch (ignore: Exception) {
}
}
override fun onError(error: FirebaseRemoteConfigException) {
CommonUtils.LogMsg("Config update error with code: " + error.code)
}
})
}
private fun fetchConfig() {
//这里可能会抛出异常 FirebaseRemoteConfigFetchThrottledException
try {
mFirebaseRemoteConfig!!.fetchAndActivate().addOnCompleteListener { task ->
if (task.isSuccessful) {
isInit = true
lastFetchTime = System.currentTimeMillis()
updateData("fetchAndActivate", mFirebaseRemoteConfig!!.all)
//24小时后重新再去获取
handler.removeMessages(MSG_REFRESH_CONFIG)
handler.sendEmptyMessageDelayed(
MSG_REFRESH_CONFIG, (1000 * 60 * 60 * 24).toLong()
)
} else {
//15分钟后重新再去获取
handler.removeMessages(MSG_REFRESH_CONFIG)
handler.sendEmptyMessageDelayed(MSG_REFRESH_CONFIG, (1000 * 60 * 15).toLong())
}
}
} catch (ignore: Exception) {
}
}
private fun updateData(from: String, all: Map<String, FirebaseRemoteConfigValue>) {
for ((key, value) in all) {
try {
CommonUtils.LogMsg( "from = " + from + "Key = " + key + " Value = " + value.asString()
)
if (TextUtils.equals(key_open_type, key)) {
Sp.getInstance()
.putStringValue(key_open_type, value.asString()).commit()
}
} catch (ignore: Exception) {
}
}
}
private class MHandler(remoteConfig: RemoteConfig) : Handler(Looper.getMainLooper()) {
private val weakReference: WeakReference<RemoteConfig>
init {
weakReference = WeakReference(remoteConfig)
}
override fun handleMessage(msg: Message) {
super.handleMessage(msg)
val remoteConfig = weakReference.get()
if (remoteConfig?.ctx != null) {
if (msg.what == MSG_REFRESH_CONFIG) {
remoteConfig.fetchConfig()
}
}
}
}
}

View File

@ -0,0 +1,167 @@
package com.offline.music.playermp3.firebase;
import android.content.Context;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.text.TextUtils;
import androidx.annotation.NonNull;
import com.google.android.gms.tasks.OnCompleteListener;
import com.google.android.gms.tasks.Task;
import com.google.firebase.BuildConfig;
import com.google.firebase.remoteconfig.ConfigUpdate;
import com.google.firebase.remoteconfig.ConfigUpdateListener;
import com.google.firebase.remoteconfig.FirebaseRemoteConfig;
import com.google.firebase.remoteconfig.FirebaseRemoteConfigException;
import com.google.firebase.remoteconfig.FirebaseRemoteConfigSettings;
import com.google.firebase.remoteconfig.FirebaseRemoteConfigValue;
import com.offline.music.playermp3.helper.CommonUtils;
import java.lang.ref.WeakReference;
import java.util.Map;
public class RemoteConfigJava {
private static RemoteConfigJava remoteConfigJava;
public static String key_open_type = "TYPE";
//进入A面
public static String value_open_type_0 = "0";
//进入B面
public static String value_open_type_1 = "1";
private static int MSG_REFRESH_CONFIG = 1;
private Context ctx;
private FirebaseRemoteConfig mFirebaseRemoteConfig;
//配置是否初始化成功
private boolean isInit = false;
//上次获取数据的时间
private long lastFetchTime = 0;
private MHandler handler = new MHandler(this);
private Context getCtx() {
return ctx;
}
public static RemoteConfigJava getInstance(){
if(remoteConfigJava == null){
remoteConfigJava = new RemoteConfigJava();
}
return remoteConfigJava;
}
private static class MHandler extends Handler {
private WeakReference<RemoteConfigJava> weakReference;
public MHandler(RemoteConfigJava remoteConfig) {
super(Looper.getMainLooper());
weakReference = new WeakReference<>(remoteConfig);
}
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
RemoteConfigJava remoteConfig1 = weakReference.get();
if (remoteConfig1.getCtx() != null) {
if (msg.what == MSG_REFRESH_CONFIG) {
remoteConfig1.fetchConfig();
}
}
}
}
public void init(Context context) {
this.ctx = context;
initConfig();
fetchConfig();
onConfigUpdate();
}
private void initConfig() {
long intervalTime = 60L * 10L;
//如果是开发状态则将提取时间缩短
if (BuildConfig.DEBUG) {
intervalTime = 60L * 5L;
}
mFirebaseRemoteConfig = FirebaseRemoteConfig.getInstance();
FirebaseRemoteConfigSettings configSettings = new FirebaseRemoteConfigSettings.Builder() //默认值12小时的最短提取间隔如果在间隔内取值则优先取上次的结果
.setMinimumFetchIntervalInSeconds(intervalTime).build();
mFirebaseRemoteConfig.setConfigSettingsAsync(configSettings);
}
private void onConfigUpdate() {
mFirebaseRemoteConfig.addOnConfigUpdateListener(new ConfigUpdateListener() {
@Override
public void onUpdate(@NonNull ConfigUpdate configUpdate) {
CommonUtils.LogMsg("---FirebaseRemote---Updated keys: " + configUpdate.getUpdatedKeys());
try {
mFirebaseRemoteConfig.activate().addOnCompleteListener(new OnCompleteListener<Boolean>() {
@Override
public void onComplete(@NonNull Task<Boolean> task) {
if (task.isSuccessful()) {
updateData("onConfigUpdate", mFirebaseRemoteConfig.getAll());
}
}
});
} catch (Exception ignore) {
CommonUtils.LogMsg("---FirebaseRemote---onConfigUpdate Exception " + ignore.getMessage());
}
}
@Override
public void onError(@NonNull FirebaseRemoteConfigException error) {
CommonUtils.LogMsg("---FirebaseRemote---onConfigUpdate onError " + error.getMessage());
}
});
}
private void fetchConfig() {
//这里可能会抛出异常 FirebaseRemoteConfigFetchThrottledException
try {
mFirebaseRemoteConfig.fetchAndActivate().addOnCompleteListener(new OnCompleteListener<Boolean>() {
@Override
public void onComplete(@NonNull Task<Boolean> task) {
if (task.isSuccessful()) {
isInit = true;
lastFetchTime = System.currentTimeMillis();
updateData("fetchAndActivate", mFirebaseRemoteConfig.getAll());
//24小时后重新再去获取
handler.removeMessages(MSG_REFRESH_CONFIG);
handler.sendEmptyMessageDelayed(
MSG_REFRESH_CONFIG, 1000 * 60 * 60 * 24L);
} else {
//15分钟后重新再去获取
handler.removeMessages(MSG_REFRESH_CONFIG);
handler.sendEmptyMessageDelayed(MSG_REFRESH_CONFIG, 1000 * 60 * 15L);
}
}
});
} catch (Exception exception) {
CommonUtils.LogMsg("---FirebaseRemote--- exception = " + exception.getMessage());
}
}
private void updateData(String from, Map<String, FirebaseRemoteConfigValue> all) {
for (Map.Entry<String, FirebaseRemoteConfigValue> entry : all.entrySet()) {
FirebaseRemoteConfigValue value = entry.getValue();
String key = entry.getKey();
CommonUtils.LogMsg("---FirebaseRemote---from = " + from + "Key = " + key + " Value = " + value.asString());
if (TextUtils.equals(key_open_type, key)) {
Sp.getInstance()
.putStringValue(key_open_type, value.asString()).commit();
}
}
}
}

View File

@ -0,0 +1,44 @@
package com.offline.music.playermp3.firebase;
import android.content.Context;
import android.content.SharedPreferences;
public class Sp {
private static String spName = "AD_SHOW";
private static Sp instance;
private SharedPreferences.Editor editor;
private SharedPreferences preferences;
private Sp(Context context) {
preferences = context.getSharedPreferences(spName, Context.MODE_PRIVATE);
editor = preferences.edit();
}
public static void init(Context context){
instance = new Sp(context);
}
public static Sp getInstance() {
return instance;
}
public Sp putStringValue(String key, String value) {
editor.putString(key, value);
return this;
}
public boolean commit() {
return editor.commit();
}
public String getStringValue(String key) {
return preferences.getString(key, RemoteConfigJava.value_open_type_0);
}
}

View File

@ -0,0 +1,24 @@
package com.offline.music.playermp3.helper;
import androidx.annotation.NonNull;
import androidx.lifecycle.ViewModelStore;
import androidx.lifecycle.ViewModelStoreOwner;
public class BaseViewModelStoreOwner implements ViewModelStoreOwner {
private final static BaseViewModelStoreOwner sInstance = new BaseViewModelStoreOwner();
private ViewModelStore mAppViewModelStore;
private BaseViewModelStoreOwner() {
}
public static BaseViewModelStoreOwner getInstance() {
return sInstance;
}
@NonNull
@Override
public ViewModelStore getViewModelStore() {
if (mAppViewModelStore == null) mAppViewModelStore = new ViewModelStore();
return mAppViewModelStore;
}
}

View File

@ -0,0 +1,314 @@
package com.offline.music.playermp3.helper;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.GradientDrawable;
import android.net.Uri;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.View;
import androidx.annotation.OptIn;
import androidx.media3.common.MediaItem;
import androidx.media3.common.MediaMetadata;
import androidx.media3.common.util.UnstableApi;
import androidx.media3.exoplayer.offline.Download;
import androidx.palette.graphics.Palette;
import com.google.gson.Gson;
import com.offline.music.playermp3.MusicApplication;
import com.offline.music.playermp3.R;
import com.offline.music.playermp3.api.onImageColorListener;
import com.offline.music.playermp3.javabean.BoxDownloadSong;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import okhttp3.ResponseBody;
public class CommonUtils {
private static String TAG = "----MUSIC---------";
private static String TAG_ERROR = "----MUSIC---------ERROR--";
public static void LogMsg(String msg) {
Log.d(TAG, msg);
}
public static void LogErrorMsg(String msg) {
Log.e(TAG_ERROR, msg);
}
public static JSONObject toJsonObject(ResponseBody body) {
try {
String string = body.string();
return new JSONObject(string);
} catch (IOException | JSONException exception) {
return null;
}
}
public static int dpToPx(int dp) {
return Math.round(dp * (MusicApplication.myApplication.getResources().getDisplayMetrics().xdpi / DisplayMetrics.DENSITY_DEFAULT));
}
public static int pxToDp(int px) {
return Math.round(px / (MusicApplication.myApplication.getResources().getDisplayMetrics().xdpi / DisplayMetrics.DENSITY_DEFAULT));
}
// 获取导航栏高度的方法
public int getNavigationBarHeight(Context context) {
int navigationBarHeight = 0;
int resourceId = context.getResources().getIdentifier("navigation_bar_height", "dimen", "android");
if (resourceId > 0) {
navigationBarHeight = context.getResources().getDimensionPixelSize(resourceId);
}
return navigationBarHeight;
}
public static int getStatusBarHeight(Context context) {
int statusBarHeight = 0;
int resourceId = context.getResources().getIdentifier("status_bar_height", "dimen", "android");
if (resourceId > 0) {
statusBarHeight = context.getResources().getDimensionPixelSize(resourceId);
}
return statusBarHeight;
}
//time 3:45 转换成毫秒
public static long convertToMilliseconds(String time) {
String[] parts = time.split(":");
if (parts.length == 2) {
//3:45
int minutes = Integer.parseInt(parts[0]);
int seconds = Integer.parseInt(parts[1]);
return (minutes * 60L + seconds) * 1000 - 1000;
} else {
//3:4507
int hours = Integer.parseInt(parts[0]);
int minutes = Integer.parseInt(parts[1]);
int seconds = Integer.parseInt(parts[2]);
return (hours * 60L * 60L + minutes * 60L + seconds) * 1000 - 1000;
}
}
public static String convertMillisToTime(long millis) {
long seconds = millis / 1000;
long hours = seconds / 3600;
long minutes = (seconds % 3600) / 60;
long remainingSeconds = seconds % 60;
// long seconds = (millis / 1000) % 60;
// long minutes = (millis / (1000 * 60)) % 60;
if (hours > 0) {
return String.format(MusicApplication.myApplication.getString(R.string.time_format), hours, minutes, remainingSeconds); // 格式化为 mm:ss
} else {
return String.format(MusicApplication.myApplication.getString(R.string.minute_time_format), minutes, remainingSeconds); // 格式化为 mm:ss
}
}
public static int getMyColor(int resId) {
return MusicApplication.myApplication.getColor(resId);
}
// 使用 Palette 提取深色主色调
public static void getDominantDarkColor1(Drawable imDraw, onImageColorListener listener) {
// 异步生成 Palette
BitmapDrawable drawable = (BitmapDrawable) imDraw;
if (drawable != null) {
Bitmap bitmap = drawable.getBitmap();
Palette.from(bitmap)
.generate(new Palette.PaletteAsyncListener() {
@Override
public void onGenerated(Palette palette) {
// 首先尝试获取深色活力色
Palette.Swatch darkVibrantSwatch = palette.getDarkVibrantSwatch();
// 如果没有深色活力色尝试获取深色柔和色
if (darkVibrantSwatch == null) {
darkVibrantSwatch = palette.getDarkMutedSwatch();
}
// 如果存在深色样本则获取其 RGB 颜色值
if (darkVibrantSwatch != null) {
int dominantDarkColor = darkVibrantSwatch.getRgb();
listener.onImageColor(dominantDarkColor);
String dominantColorHex = String.format("#%06X", (0xFFFFFF & dominantDarkColor));
} else {
listener.onImageColor(-1);
}
}
});
} else {
listener.onImageColor(-1);
}
}
// 调整颜色的亮度factor > 1 表示变亮factor < 1 表示变暗
public static int adjustBrightness(int color, float factor) {
// RGB 颜色转换为 HSV
float[] hsv = new float[3];
Color.colorToHSV(color, hsv);
// 调整亮度V
hsv[2] = Math.min(1.0f, hsv[2] * factor); // 亮度值最多是 1.0
// 返回调整后的颜色HSV 转回 RGB
return Color.HSVToColor(hsv);
}
public static void getMainColor(Drawable imDraw, onImageColorListener listener) {
BitmapDrawable drawable = (BitmapDrawable) imDraw;
// 使用 Palette 提取颜色
if (drawable != null) {
Bitmap bitmap = drawable.getBitmap();
Palette.from(bitmap).generate(new Palette.PaletteAsyncListener() {
@Override
public void onGenerated(Palette palette) {
// 获取占比最大的颜色
Palette.Swatch dominantSwatch = palette.getDominantSwatch();
if (dominantSwatch != null) {
int dominantColor = dominantSwatch.getRgb(); // 获取 RGB 颜色
listener.onImageColor(dominantColor);
} else {
listener.onImageColor(-1);
}
}
});
}
}
public static Drawable getNewDrawable(int color, float RadiusTopLeft, float RadiusTopRight, float RadiusBottomRight, float RadiusBottomLeft) {
// 创建 GradientDrawable
GradientDrawable drawable = new GradientDrawable();
// 设置形状为矩形
drawable.setShape(GradientDrawable.RECTANGLE);
// 设置背景颜色
drawable.setColor(color);
// 设置顶部圆角 (top-left and top-right)
float[] radii = new float[]{
RadiusTopLeft, RadiusTopLeft, // top-left radius
RadiusTopRight, RadiusTopRight, // top-right radius
RadiusBottomRight, RadiusBottomRight, // bottom-right radius
RadiusBottomLeft, RadiusBottomLeft // bottom-left radius
};
drawable.setCornerRadii(radii);
return drawable;
}
public static void extractColorsFromImage(Drawable drawable, View view) {
Bitmap bitmap = ((BitmapDrawable) drawable).getBitmap();
Palette.from(bitmap).generate(palette -> {
if (palette != null) {
// 获取主色
int vibrantColor = palette.getVibrantColor(0);
int mutedColor = palette.getMutedColor(0);
// 创建左右渐变色
createGradientBackground(vibrantColor, mutedColor, view);
}
});
}
public static void createGradientBackground(int leftColor, int rightColor, View view) {
GradientDrawable gradientDrawable = new android.graphics.drawable.GradientDrawable(
android.graphics.drawable.GradientDrawable.Orientation.LEFT_RIGHT,
new int[]{leftColor, rightColor}
);
view.setBackground(gradientDrawable);
}
/**
* 当前媒体项是否有有效的播放地址
*
* @param mediaItem
* @return
*/
public static boolean hasValidUri(MediaItem mediaItem) {
MediaItem.LocalConfiguration localConfiguration = mediaItem.localConfiguration;
if (localConfiguration != null) {
if (CommonUtils.isUriFormat(localConfiguration.uri)) {
return true;
}
}
return false;
}
private static boolean isUriFormat(Uri parsedUri) {
// 3. 检查 URI 是否具有正确的格式
try {
if (parsedUri.getScheme() == null) {
return false;
}
} catch (Exception e) {
LogMsg("URI parsing failed: " + e.getMessage());
return false;
}
return true;
}
public static BoxDownloadSong downloadToBean(Download download) {
@OptIn(markerClass = UnstableApi.class) byte[] data = download.request.data;
String additionalData = new String(data, StandardCharsets.UTF_8);
Gson gson = new Gson();
return gson.fromJson(additionalData, BoxDownloadSong.class);
}
@OptIn(markerClass = UnstableApi.class)
public static MediaItem downloadToMediaItem(Download data){
MediaItem mediaItem = data.request.toMediaItem();
BoxDownloadSong boxDownloadSong = CommonUtils.downloadToBean(data);
MediaItem.Builder builder = mediaItem.buildUpon();
MediaMetadata.Builder metadataBuilder = new MediaMetadata.Builder();
metadataBuilder.setArtist(boxDownloadSong.getSingerName());
metadataBuilder.setDescription(boxDownloadSong.getDuration());
metadataBuilder.setDurationMs(boxDownloadSong.getDurationMs());
metadataBuilder.setArtworkUri(Uri.parse(boxDownloadSong.getCovert()));
metadataBuilder.setTitle(boxDownloadSong.getSongName());
builder.setMediaMetadata(metadataBuilder.build());
return builder.build();
}
// @OptIn(markerClass = UnstableApi.class)
// public static boolean isSongCached(SimpleCache cache, String id) {
//
// return cachedFile.exists();
//
// }
}

View File

@ -0,0 +1,130 @@
package com.offline.music.playermp3.helper;
public class MyValue {
// 现场表演
public static final String PAGE_TYPE_MV_LIST="MUSIC_PAGE_TYPE_USER_CHANNEL";
//个人live
public static final String PAGE_TYPE_MV="MUSIC_PAGE_TYPE_ARTIST";
//风格音乐合集乡村音乐
public static final String PAGE_TYPE_LIST="MUSIC_PAGE_TYPE_PLAYLIST";
//专辑
public static final String PAGE_TYPE_ALBUM="MUSIC_PAGE_TYPE_ALBUM";
//-----------------------------PlayActivity
/**
* 歌手单曲进入的数据key
*/
public static String KEY_PLAY_ACTIVITY_SINGER = "click_singer";
/**
* 音乐列表进入的数据key和点击的列表位置key
*/
public static String KEY_PLAY_ACTIVITY_CATEGORY_LIST = "click_category_list";
public static String KEY_PLAY_ACTIVITY_CATEGORY_LIST_INDEX = "click_category_list_index";
/**
* MV进入的数据key
*/
public static String KEY_PLAY_ACTIVITY_MV = "click_mv";
/**
* 进入PlayActivity传递的即将播放的歌曲videoId
*/
public static String KEY_PLAY_VIDEO_ID = "video_id";
/**
* 进入PlayActivity传递的即将播放的歌曲index(针对在进入PlayActivity之前已经调用vmApplication.reSetPlayList
* 重置了播放列表的情况)
*/
public static String KEY_PLAY_INDEX = "play_index";
//播放错误
public final static int PLAY_STATUS_CODE_ERROR = -1;
//正在播放
public final static int PLAY_STATUS_CODE_PLAYING = -2;
//暂停或者停止
public final static int PLAY_STATUS_CODE_PAUSE = -3;
//切歌
public final static int PLAY_STATUS_CHANGE_MUSIC= -4;
/**
* 进入播放页面的来源
* 0--首页单曲进入 1--首页音乐分类合集列表进入 2--首页单个视频mv进入
* 3--控制面板进入
*/
public static String KEY_ENTER_SOURCE = "ENTER_SOURCE";
public final static int TYPE_ENTER_SOURCE_SINGLE = 0;
public final static int TYPE_ENTER_SOURCE_CATEGORY = 1;
public final static int TYPE_ENTER_SOURCE_MV = 2;
public final static int TYPE_ENTER_PANEL = 3;
//从喜爱歌曲进入
public final static int TYPE_ENTER_LIKE= 4;
//从下载歌曲进入
public final static int TYPE_ENTER_DOWNLOAD= 5;
//-----------------------------CategoryListActivity
public static String KEY_PLAY_ACTIVITY_CATEGORY= "click_category";
public static String KEY_CATEGORY_LIST_TYPE= "page_type";
public static String KEY_CATEGORY_LIST_SINGER_NAME= "singer_name";
public static String KEY_CATEGORY_LIST_BROWSER_ID= "browser_id";
//------------------------ResultListActivity
public static String KEY_SEARCH_RESULT_BROWSER_ID= "search_result_browser_id";
//---------------------LikeSongActivity
public static String KEY_ENTER_LIKE_ACTIVITY_TYPE = "song_type";
public final static int KEY_ENTER_LIKE_ACTIVITY_TYPE_LIKE = 0;
public final static int KEY_ENTER_LIKE_ACTIVITY_TYPE_DOWNLOAD = 1;
}

View File

@ -0,0 +1,29 @@
package com.offline.music.playermp3.helper;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModel;
import androidx.lifecycle.ViewModelProvider;
public class ViewModelScope {
private ViewModelProvider mFragmentProvider;
private ViewModelProvider mActivityProvider;
private ViewModelProvider mApplicationProvider;
public <T extends ViewModel> T getFragmentScopeViewModel(@NonNull Fragment fragment, @NonNull Class<T> modelClass) {
if (mFragmentProvider == null) mFragmentProvider = new ViewModelProvider(fragment);
return mFragmentProvider.get(modelClass);
}
public <T extends ViewModel> T getActivityScopeViewModel(@NonNull AppCompatActivity activity, @NonNull Class<T> modelClass) {
if (mActivityProvider == null) mActivityProvider = new ViewModelProvider(activity);
return mActivityProvider.get(modelClass);
}
public <T extends ViewModel> T getApplicationScopeViewModel(@NonNull Class<T> modelClass) {
if (mApplicationProvider == null)
mApplicationProvider = new ViewModelProvider(BaseViewModelStoreOwner.getInstance());
return mApplicationProvider.get(modelClass);
}
}

View File

@ -0,0 +1,88 @@
package com.offline.music.playermp3.javabean;
import io.objectbox.annotation.Entity;
import io.objectbox.annotation.Id;
@Entity
public class BoxDownloadSong {
@Id
public long id;
private String songName;
private String singerName;
private String videoId;
private String covert;
private long durationMs;
private String duration ;
// 音视频文件uri
private String videoUrl ;
public BoxDownloadSong( ) {
}
public BoxDownloadSong(String songName, String singerName, String videoId, String covert, long durationMs, String duration) {
this.songName = songName;
this.singerName = singerName;
this.videoId = videoId;
this.covert = covert;
this.durationMs = durationMs;
this.duration = duration;
}
public String getVideoUrl() {
return videoUrl;
}
public void setVideoUrl(String videoUrl) {
this.videoUrl = videoUrl;
}
public long getDurationMs() {
return durationMs;
}
public String getDuration() {
return duration;
}
public void setDuration(String duration) {
this.duration = duration;
}
public void setDurationMs(long durationMs) {
this.durationMs = durationMs;
}
public String getSingerName() {
return singerName;
}
public void setSingerName(String singerName) {
this.singerName = singerName;
}
public String getVideoId() {
return videoId;
}
public void setVideoId(String videoId) {
this.videoId = videoId;
}
public String getCovert() {
return covert;
}
public void setCovert(String covert) {
this.covert = covert;
}
public String getSongName() {
return songName;
}
public void setSongName(String songName) {
this.songName = songName;
}
}

View File

@ -0,0 +1,80 @@
package com.offline.music.playermp3.javabean;
import io.objectbox.annotation.Entity;
import io.objectbox.annotation.Id;
@Entity
public class BoxLikeSong {
@Id
public long id;
private String songName;
private String singerName;
private String videoId;
private String covert;
private long durationMs;
private String duration ;
public BoxLikeSong( ) {
}
public BoxLikeSong(String songName, String singerName, String videoId, String covert,long durationMs,String duration) {
this.songName = songName;
this.singerName = singerName;
this.videoId = videoId;
this.covert = covert;
this.durationMs = durationMs;
this.duration = duration;
}
public long getDurationMs() {
return durationMs;
}
public String getDuration() {
return duration;
}
public void setDuration(String duration) {
this.duration = duration;
}
public void setDurationMs(long durationMs) {
this.durationMs = durationMs;
}
public String getSingerName() {
return singerName;
}
public void setSingerName(String singerName) {
this.singerName = singerName;
}
public String getVideoId() {
return videoId;
}
public void setVideoId(String videoId) {
this.videoId = videoId;
}
public String getCovert() {
return covert;
}
public void setCovert(String covert) {
this.covert = covert;
}
public String getSongName() {
return songName;
}
public void setSongName(String songName) {
this.songName = songName;
}
}

View File

@ -0,0 +1,24 @@
package com.offline.music.playermp3.javabean;
import androidx.media3.exoplayer.offline.Download;
public class CustomerDownload {
private Download download;
private boolean isDownload;
public CustomerDownload(Download download, boolean isDownload) {
this.download = download;
this.isDownload = isDownload;
}
public Download getDownloadData() {
return download;
}
public boolean isDownload() {
return isDownload;
}
}

View File

@ -0,0 +1,44 @@
package com.offline.music.playermp3.javabean;
import com.offline.music.playermp3.javabean.response.ResponsePlayUrl;
public class CustomerUrlInfo {
private ResponsePlayUrl playUrl;
private int playMusicIndex;
private boolean needPlayNow;
private String videoId;
public String getVideoId() {
return videoId;
}
public void setVideoId(String videoId) {
this.videoId = videoId;
}
public ResponsePlayUrl getPlayUrl() {
return playUrl;
}
public void setPlayUrl(ResponsePlayUrl playUrl) {
this.playUrl = playUrl;
}
public int getPlayMusicIndex() {
return playMusicIndex;
}
public void setPlayMusicIndex(int playMusicIndex) {
this.playMusicIndex = playMusicIndex;
}
public boolean isNeedPlayNow() {
return needPlayNow;
}
public void setNeedPlayNow(boolean needPlayNow) {
this.needPlayNow = needPlayNow;
}
}

View File

@ -0,0 +1,37 @@
package com.offline.music.playermp3.javabean.requestbody;
import com.offline.music.playermp3.javabean.requestbody.child.ContextBody;
import java.io.Serializable;
/**
* 首页接口请求体
*/
public class BodyHome implements Serializable {
private String browseId = "FEmusic_home";
public String getBrowseId() {
return browseId;
}
public void setBrowseId(String browseId) {
this.browseId = browseId;
}
private ContextBody context = new ContextBody();
public ContextBody getContext() {
return context;
}
public void setContext(ContextBody context) {
this.context = context;
}
}

View File

@ -0,0 +1,78 @@
package com.offline.music.playermp3.javabean.requestbody;
import com.offline.music.playermp3.javabean.requestbody.child.ContextBody;
import java.io.Serializable;
/**
* 播放页面接口请求体
*/
public class BodyPlay implements Serializable {
private ContextBody context = new ContextBody();
private String tunerSettingValue = "AUTOMIX_SETTING_NORMAL";
private boolean isAudioOnly = true;
private Configs watchEndpointMusicSupportedConfigs = new Configs();
private int index;
private String playlistId;
private String videoId;
private String params;
private String playlistSetVideoId;
public static class Configs {
private String musicVideoType = "MUSIC_VIDEO_TYPE_ATV";
public String getMusicVideoType() {
return musicVideoType;
}
public void setMusicVideoType(String musicVideoType) {
this.musicVideoType = musicVideoType;
}
}
public ContextBody getContext() {
return context;
}
public void setContext(ContextBody context) {
this.context = context;
}
public void setTunerSettingValue(String tunerSettingValue) {
this.tunerSettingValue = tunerSettingValue;
}
public void setIndex(int index) {
this.index = index;
}
public void setPlaylistId(String playlistId) {
this.playlistId = playlistId;
}
public void setVideoId(String videoId) {
this.videoId = videoId;
}
public void setParams(String params) {
this.params = params;
}
public void setPlaylistSetVideoId(String playlistSetVideoId) {
this.playlistSetVideoId = playlistSetVideoId;
}
public Configs getWatchEndpointMusicSupportedConfigs() {
return watchEndpointMusicSupportedConfigs;
}
}

View File

@ -0,0 +1,40 @@
package com.offline.music.playermp3.javabean.requestbody;
import com.offline.music.playermp3.javabean.requestbody.child.ContextBody;
import java.io.Serializable;
/**
* 首页接口请求体
*/
public class BodyPlayUrl implements Serializable {
private String videoId ;
private ContextBody context = new ContextBody();
// private String key = "AIzaSyC9XL3ZjwddXya6X74dJOCTL-WEYFDNX30" ;
private String params ="CgIQBg";
public String getVideoId() {
return videoId;
}
public void setVideoId(String videoId) {
this.videoId = videoId;
}
public ContextBody getContext() {
return context;
}
public void setContext(ContextBody context) {
this.context = context;
}
}

View File

@ -0,0 +1,33 @@
package com.offline.music.playermp3.javabean.requestbody;
import com.offline.music.playermp3.javabean.requestbody.child.ContextBody;
import java.io.Serializable;
/**
* 搜索建议接口
*/
public class BodySearch implements Serializable {
private String query = "";
private ContextBody context = new ContextBody();
public String getQuery() {
return query;
}
public void setQuery(String query) {
this.query = query;
}
public ContextBody getContext() {
return context;
}
public void setContext(ContextBody context) {
this.context = context;
}
}

View File

@ -0,0 +1,33 @@
package com.offline.music.playermp3.javabean.requestbody;
import com.offline.music.playermp3.javabean.requestbody.child.ContextBody;
import java.io.Serializable;
/**
* 搜索建议接口
*/
public class BodySearchSuggestion implements Serializable {
private String input = "";
public String getInput() {
return input;
}
public void setInput(String input) {
this.input = input;
}
private ContextBody context = new ContextBody();
public ContextBody getContext() {
return context;
}
public void setContext(ContextBody context) {
this.context = context;
}
}

View File

@ -0,0 +1,55 @@
package com.offline.music.playermp3.javabean.requestbody.child;
import java.io.Serializable;
import java.util.Locale;
public class Client implements Serializable {
private String clientName = "WEB_REMIX";
//1.20240506.01.00
private String clientVersion = "1.20220918";
private String hl = Locale.getDefault().getLanguage();
private String gl = "US";
private String platform = "DESKTOP";
private String visitorData;
public String getVisitorData() {
return visitorData;
}
public void setVisitorData(String visitorData) {
this.visitorData = visitorData;
}
public void setClientName(String clientName) {
this.clientName = clientName;
}
public void setClientVersion(String clientVersion) {
this.clientVersion = clientVersion;
}
public void setPlatform(String platform) {
this.platform = platform;
}
public String getGl() {
return gl;
}
public void setGl(String gl) {
this.gl = gl;
}
public String getHl() {
return hl;
}
public void setHl(String hl) {
this.hl = hl;
}
}

View File

@ -0,0 +1,33 @@
package com.offline.music.playermp3.javabean.requestbody.child;
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;
}
}
}

View File

@ -0,0 +1,65 @@
package com.offline.music.playermp3.javabean.response;
import java.io.Serializable;
import java.util.List;
public class ResponseCategoryList implements Serializable {
private String covert;
private String title;
private String description;
private String secondSubtitle;
private String singName;
private List<ResponsePlayListInfo> list;
public String getSingName() {
return singName;
}
public void setSingName(String singName) {
this.singName = singName;
}
public String getCovert() {
return covert;
}
public void setCovert(String covert) {
this.covert = covert;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getSecondSubtitle() {
return secondSubtitle;
}
public void setSecondSubtitle(String secondSubtitle) {
this.secondSubtitle = secondSubtitle;
}
public List<ResponsePlayListInfo> getList() {
return list;
}
public void setList(List<ResponsePlayListInfo> list) {
this.list = list;
}
}

View File

@ -0,0 +1,68 @@
package com.offline.music.playermp3.javabean.response;
import androidx.annotation.Nullable;
import com.offline.music.playermp3.javabean.response.child.ResponseHomeChild;
import java.util.List;
public class ResponseHome {
//用于更多数据请求的cit
private String clickTrackingParams;
//用于更多数据请求的ctoken
private String continuation;
@Nullable
//用于更多数据请求的visitorData(只有第一个接口会返回该值,youtubei/v1/browse?prettyPrint=false)
private String visitorData;
@Nullable
private String backgroundUrl;
private List<ResponseHomeChild> childList;
public List<ResponseHomeChild> getChildList() {
return childList;
}
public void setChildList(List<ResponseHomeChild> childList) {
this.childList = childList;
}
public String getClickTrackingParams() {
return clickTrackingParams;
}
public void setClickTrackingParams(String clickTrackingParams) {
this.clickTrackingParams = clickTrackingParams;
}
public String getContinuation() {
return continuation;
}
public void setContinuation(String continuation) {
this.continuation = continuation;
}
public String getVisitorData() {
return visitorData;
}
public void setVisitorData(String visitorData) {
this.visitorData = visitorData;
}
public String getBackgroundUrl() {
return backgroundUrl;
}
public void setBackgroundUrl(String backgroundUrl) {
this.backgroundUrl = backgroundUrl;
}
}

View File

@ -0,0 +1,177 @@
package com.offline.music.playermp3.javabean.response;
import java.io.Serializable;
/**
* 播放列表
*/
public class ResponsePlayListInfo implements Serializable {
//封面
private String covert;
//小尺寸封面
private String smallCovert;
//歌曲名字
private String SongTitle;
//歌手名字
private String SingerName;
//专辑名称
private String AlbumTitle;
//发行年份
private String Year;
//歌曲时长
private String Duration;
//歌曲时长 毫秒
private long DurationMs;
private String describe;
private String videoId;
private String playlistId;
private String params;
private String musicVideoType;
public ResponsePlayListInfo() {
}
public ResponsePlayListInfo(String covert, String songTitle, String singerName, long durationMs, String videoId,String duration) {
this.covert = covert;
SongTitle = songTitle;
SingerName = singerName;
DurationMs = durationMs;
this.Duration = duration;
this.videoId = videoId;
}
public String getDescribe() {
return describe;
}
public void setDescribe(String describe) {
this.describe = describe;
}
public String getSmallCovert() {
return smallCovert;
}
public void setSmallCovert(String smallCovert) {
this.smallCovert = smallCovert;
}
public long getDurationMs() {
return DurationMs;
}
public void setDurationMs(long durationMs) {
DurationMs = durationMs;
}
public String getDuration() {
return Duration;
}
public void setDuration(String duration) {
Duration = duration;
}
public String getCovert() {
return covert;
}
public void setCovert(String covert) {
this.covert = covert;
}
public String getSongTitle() {
return SongTitle;
}
public void setSongTitle(String songTitle) {
SongTitle = songTitle;
}
public String getSingerName() {
return SingerName;
}
public void setSingerName(String singerName) {
SingerName = singerName;
}
public String getAlbumTitle() {
return AlbumTitle;
}
public void setAlbumTitle(String albumTitle) {
AlbumTitle = albumTitle;
}
public String getYear() {
return Year;
}
public void setYear(String year) {
Year = year;
}
public String getVideoId() {
return videoId;
}
public void setVideoId(String videoId) {
this.videoId = videoId;
}
public String getPlaylistId() {
return playlistId;
}
public void setPlaylistId(String playlistId) {
this.playlistId = playlistId;
}
public String getParams() {
return params;
}
public void setParams(String params) {
this.params = params;
}
public String getMusicVideoType() {
return musicVideoType;
}
public void setMusicVideoType(String musicVideoType) {
this.musicVideoType = musicVideoType;
}
// public String getAudioUrlLow() {
// return audioUrlLow;
// }
//
// public void setAudioUrlLow(String audioUrlLow) {
// this.audioUrlLow = audioUrlLow;
// }
//
// public String getAudioUrlMedium() {
// return audioUrlMedium;
// }
//
// public void setAudioUrlMedium(String audioUrlMedium) {
// this.audioUrlMedium = audioUrlMedium;
// }
}

View File

@ -0,0 +1,79 @@
package com.offline.music.playermp3.javabean.response;
/**
* 音频源地址
*/
public class ResponsePlayUrl {
private String status;
private String audioUrlLow;
private String audioUrlMedium;
private String videoId;
private String BigCovert;
private String videoUrlMedium;
public String getVideoUrlMedium() {
return videoUrlMedium;
}
public void setVideoUrlMedium(String videoUrlMedium) {
this.videoUrlMedium = videoUrlMedium;
}
public String getBigCovert() {
return BigCovert;
}
public void setBigCovert(String bigCovert) {
BigCovert = bigCovert;
}
public String getAudioUrlMedium() {
return audioUrlMedium;
}
public void setAudioUrlMedium(String audioUrlMedium) {
this.audioUrlMedium = audioUrlMedium;
}
///AUDIO_QUALITY_MEDIUMAUDIO_QUALITY_LOW
// private String audioQuality;
//
//
// public String getAudioQuality() {
// return audioQuality;
// }
//
// public void setAudioQuality(String audioQuality) {
// this.audioQuality = audioQuality;
// }
public String getVideoId() {
return videoId;
}
public void setVideoId(String videoId) {
this.videoId = videoId;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
public String getAudioUrlLow() {
return audioUrlLow;
}
public void setAudioUrlLow(String audioUrlLow) {
this.audioUrlLow = audioUrlLow;
}
}

View File

@ -0,0 +1,36 @@
package com.offline.music.playermp3.javabean.response;
import java.util.List;
public class ResponseResult {
private String mainTitle;
private String mainCovert;
private List<ResponseResultList> list;
public String getMainTitle() {
return mainTitle;
}
public void setMainTitle(String mainTitle) {
this.mainTitle = mainTitle;
}
public String getMainCovert() {
return mainCovert;
}
public void setMainCovert(String mainCovert) {
this.mainCovert = mainCovert;
}
public List<ResponseResultList> getList() {
return list;
}
public void setList(List<ResponseResultList> list) {
this.list = list;
}
}

View File

@ -0,0 +1,29 @@
package com.offline.music.playermp3.javabean.response;
import com.offline.music.playermp3.javabean.response.child.ResponseResultListChild;
import java.util.List;
public class ResponseResultList {
private String headerTitle;
private List<ResponseResultListChild> childList;
public String getHeaderTitle() {
return headerTitle;
}
public void setHeaderTitle(String headerTitle) {
this.headerTitle = headerTitle;
}
public List<ResponseResultListChild> getChildList() {
return childList;
}
public void setChildList(List<ResponseResultListChild> childList) {
this.childList = childList;
}
}

View File

@ -0,0 +1,90 @@
package com.offline.music.playermp3.javabean.response;
import com.offline.music.playermp3.javabean.response.child.ResponseSearchChild;
import java.util.List;
public class ResponseSearch {
private String headerTitle;
private String beastSongTitle;
private String beastSongTCovert;
private String beastSongDescribe;
private String beastBrowserId;
private String beastVideoId;
private String pageType;
private List<ResponseSearchChild> list;
public String getBeastVideoId() {
return beastVideoId;
}
public void setBeastVideoId(String beastVideoId) {
this.beastVideoId = beastVideoId;
}
public String getHeaderTitle() {
return headerTitle;
}
public void setHeaderTitle(String headerTitle) {
this.headerTitle = headerTitle;
}
public List<ResponseSearchChild> getList() {
return list;
}
public void setList(List<ResponseSearchChild> list) {
this.list = list;
}
public String getBeastSongTitle() {
return beastSongTitle;
}
public void setBeastSongTitle(String beastSongTitle) {
this.beastSongTitle = beastSongTitle;
}
public String getBeastSongTCovert() {
return beastSongTCovert;
}
public void setBeastSongTCovert(String beastSongTCovert) {
this.beastSongTCovert = beastSongTCovert;
}
public String getBeastSongDescribe() {
return beastSongDescribe;
}
public void setBeastSongDescribe(String beastSongDescribe) {
this.beastSongDescribe = beastSongDescribe;
}
public String getBeastBrowserId() {
return beastBrowserId;
}
public void setBeastBrowserId(String beastBrowserId) {
this.beastBrowserId = beastBrowserId;
}
public String getPageType() {
return pageType;
}
public void setPageType(String pageType) {
this.pageType = pageType;
}
}

View File

@ -0,0 +1,117 @@
package com.offline.music.playermp3.javabean.response.child;
import java.io.Serializable;
public class ResponseCategory implements Serializable {
//类别封面
private String covert;
//类别标题
private String twoTitle;
//类别描述
/**
* PAGE_TYPE_ALBUM : 专辑.歌手名字
* PAGE_TYPE_LIST 多个歌手名字
*/
private String twoSubtitle;
private String browseId;
private String videoId;
private String playListId;
private String params;
private String musicVideoType;
/**
* 视频Mv合集
*MUSIC_PAGE_TYPE_USER_CHANNEL
*
* 视频Mv单个
* MUSIC_PAGE_TYPE_USER_CHANNEL
*
* 音乐合集列表
* MUSIC_PAGE_TYPE_PLAYLIST
*
* 专辑
*MUSIC_PAGE_TYPE_ALBUM
*/
private String pageType;
public String getParams() {
return params;
}
public void setParams(String params) {
this.params = params;
}
public String getMusicVideoType() {
return musicVideoType;
}
public void setMusicVideoType(String musicVideoType) {
this.musicVideoType = musicVideoType;
}
public String getPlayListId() {
return playListId;
}
public void setPlayListId(String playListId) {
this.playListId = playListId;
}
public String getVideoId() {
return videoId;
}
public void setVideoId(String videoId) {
this.videoId = videoId;
}
public String getPageType() {
return pageType;
}
public void setPageType(String pageType) {
this.pageType = pageType;
}
public String getBrowseId() {
return browseId;
}
public void setBrowseId(String browseId) {
this.browseId = browseId;
}
public String getCovert() {
return covert;
}
public void setCovert(String covert) {
this.covert = covert;
}
public String getTwoTitle() {
return twoTitle;
}
public void setTwoTitle(String twoTitle) {
this.twoTitle = twoTitle;
}
public String getTwoSubtitle() {
return twoSubtitle;
}
public void setTwoSubtitle(String twoSubtitle) {
this.twoSubtitle = twoSubtitle;
}
}

View File

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

View File

@ -0,0 +1,36 @@
package com.offline.music.playermp3.javabean.response.child;
import java.util.List;
public class ResponseHomeChild {
private String HeaderTitle;
private List<ResponseSingle> singleList;
private List<ResponseCategory> categoryList;
public String getHeaderTitle() {
return HeaderTitle;
}
public void setHeaderTitle(String headerTitle) {
HeaderTitle = headerTitle;
}
public List<ResponseSingle> getSingleList() {
return singleList;
}
public void setSingleList(List<ResponseSingle> singleList) {
this.singleList = singleList;
}
public List<ResponseCategory> getCategoryList() {
return categoryList;
}
public void setCategoryList(List<ResponseCategory> categoryList) {
this.categoryList = categoryList;
}
}

View File

@ -0,0 +1,85 @@
package com.offline.music.playermp3.javabean.response.child;
public class ResponseResultListChild {
private String thumbnail;
private String songName;
private String subTitle;
private String playCount;
private String videoId;
private String playListId;
private String browserId;
private String pageType;
public String getThumbnail() {
return thumbnail;
}
public void setThumbnail(String thumbnail) {
this.thumbnail = thumbnail;
}
public String getSongName() {
return songName;
}
public void setSongName(String songName) {
this.songName = songName;
}
public String getSubTitle() {
return subTitle;
}
public void setSubTitle(String subTitle) {
this.subTitle = subTitle;
}
public String getBrowserId() {
return browserId;
}
public void setBrowserId(String browserId) {
this.browserId = browserId;
}
public String getPageType() {
return pageType;
}
public void setPageType(String pageType) {
this.pageType = pageType;
}
public String getPlayCount() {
return playCount;
}
public void setPlayCount(String playCount) {
this.playCount = playCount;
}
public String getVideoId() {
return videoId;
}
public void setVideoId(String videoId) {
this.videoId = videoId;
}
public String getPlayListId() {
return playListId;
}
public void setPlayListId(String playListId) {
this.playListId = playListId;
}
}

View File

@ -0,0 +1,75 @@
package com.offline.music.playermp3.javabean.response.child;
public class ResponseSearchChild {
private String songTitle;
private String songCovert;
private String songDescribe;
private String songPlayCount;
private String songVideoId;
private String browserId;
private String pageType;
public String getBrowserId() {
return browserId;
}
public void setBrowserId(String browserId) {
this.browserId = browserId;
}
public String getPageType() {
return pageType;
}
public void setPageType(String pageType) {
this.pageType = pageType;
}
public String getSongTitle() {
return songTitle;
}
public void setSongTitle(String songTitle) {
this.songTitle = songTitle;
}
public String getSongCovert() {
return songCovert;
}
public void setSongCovert(String songCovert) {
this.songCovert = songCovert;
}
public String getSongDescribe() {
return songDescribe;
}
public void setSongDescribe(String songDescribe) {
this.songDescribe = songDescribe;
}
public String getSongPlayCount() {
return songPlayCount;
}
public void setSongPlayCount(String songPlayCount) {
this.songPlayCount = songPlayCount;
}
public String getSongVideoId() {
return songVideoId;
}
public void setSongVideoId(String songVideoId) {
this.songVideoId = songVideoId;
}
}

View File

@ -0,0 +1,91 @@
package com.offline.music.playermp3.javabean.response.child;
import java.io.Serializable;
public class ResponseSingle implements Serializable {
//歌手头像
private String SingerHead;
//歌曲名字
private String SongTitle;
//歌手名字
private String SingerName;
//描述
private String Description;
private String videoId;
private String playlistId;
private String params;
private String musicVideoType;
public String getSingerHead() {
return SingerHead;
}
public void setSingerHead(String singerHead) {
SingerHead = singerHead;
}
public String getSongTitle() {
return SongTitle;
}
public void setSongTitle(String songTitle) {
SongTitle = songTitle;
}
public String getSingerName() {
return SingerName;
}
public void setSingerName(String singerName) {
SingerName = singerName;
}
public String getDescription() {
return Description;
}
public void setDescription(String description) {
Description = description;
}
public void setVideoId(String videoId) {
this.videoId = videoId;
}
public void setPlaylistId(String playlistId) {
this.playlistId = playlistId;
}
public void setParams(String params) {
this.params = params;
}
public void setMusicVideoType(String musicVideoType) {
this.musicVideoType = musicVideoType;
}
public String getVideoId() {
return videoId;
}
public String getPlaylistId() {
return playlistId;
}
public String getParams() {
return params;
}
public String getMusicVideoType() {
return musicVideoType;
}
}

View File

@ -0,0 +1,11 @@
package com.offline.music.playermp3.max;
import com.applovin.mediation.MaxAd;
public interface MaxListener {
void onFail(MaxAd ad);
void onShowSuccess(MaxAd ad);
void onHidden();
}

View File

@ -0,0 +1,219 @@
package com.offline.music.playermp3.max;
import android.app.Activity;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.applovin.mediation.MaxAd;
import com.applovin.mediation.MaxAdListener;
import com.applovin.mediation.MaxError;
import com.applovin.mediation.ads.MaxInterstitialAd;
import com.applovin.mediation.nativeAds.MaxNativeAdListener;
import com.applovin.mediation.nativeAds.MaxNativeAdLoader;
import com.applovin.mediation.nativeAds.MaxNativeAdView;
import com.offline.music.playermp3.MusicApplication;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class MaxManager {
public static final String AD_TAG = "------MAX---------";
/**
* 原生广告
*/
//small
public static final String native_Ad1 = "318a8ed92bb2596d";
//small
public static final String native_Ad2 = "63e3d50c36c627d2";
/**
* 插页广告
*/
private static final String one_AD = "6510769676690c4d";
private static final String two_Ad = "7c6b55091a9aaf39";
private static final String three_ad = "39c6f571b5939dde";
public static final int type_no_cache = 0;
public static final int type_has_cache = 1;
public static final int type_show_success = 2;
public static final int type_show_close = 3;
public static final int type_show_fail = 4;
private static List<MaxInterstitialAd> adList = new ArrayList<>();
public static MaxInterstitialAd getAd(List<MaxInterstitialAd> list) {
Collections.shuffle(list);
for (MaxInterstitialAd ad : list) {
if (ad.isReady()) {
return ad;
}
}
return null;
}
public static void onLoadNativeAd(ViewGroup nativeAdContainer, String adPlace,NativeMaxListener listener) {
MaxNativeAdLoader nativeAdLoader;
nativeAdLoader = new MaxNativeAdLoader(adPlace, MusicApplication.myApplication);
nativeAdLoader.setNativeAdListener(new MaxNativeAdListener() {
MaxAd nativeAd;
@Override
public void onNativeAdLoaded(@Nullable MaxNativeAdView maxNativeAdView, @NonNull MaxAd maxAd) {
// Clean up any pre-existing native ad to prevent memory leaks.
if (nativeAd != null) {
nativeAdLoader.destroy(nativeAd);
}
// Save ad for cleanup.
nativeAd = maxAd;
// Add ad view to view.
nativeAdContainer.removeAllViews();
nativeAdContainer.addView(maxNativeAdView);
nativeAdContainer.setVisibility(View.VISIBLE);
listener.onLoaded(maxAd,nativeAdLoader);
Log.d(AD_TAG, "-------onNativeAdLoaded-----maxAd=" + maxAd.getAdUnitId());
}
@Override
public void onNativeAdLoadFailed(@NonNull String s, @NonNull MaxError maxError) {
super.onNativeAdLoadFailed(s, maxError);
Log.d(AD_TAG, "-------onNativeAdLoadFailed-----maxAd=" + maxError.getMessage());
}
});
nativeAdLoader.loadAd();
}
public static void clearNativeAd(MaxAd nativeAd, MaxNativeAdLoader nativeAdLoader) {
if (nativeAd != null) {
nativeAdLoader.destroy(nativeAd);
}
nativeAdLoader.destroy();
}
public static List<MaxInterstitialAd> onLoadAd() {
if (adList.isEmpty()) {
MaxInterstitialAd AdT = new MaxInterstitialAd(two_Ad, MusicApplication.myApplication);
MaxInterstitialAd AdOne = new MaxInterstitialAd(one_AD, MusicApplication.myApplication);
MaxInterstitialAd AdThree = new MaxInterstitialAd(three_ad, MusicApplication.myApplication);
adList.add(AdOne);
adList.add(AdT);
adList.add(AdThree);
}
for (MaxInterstitialAd ad : adList) {
if (!ad.isReady()) {
setMyListener(ad, new MaxListener() {
@Override
public void onFail(MaxAd ad) {
}
@Override
public void onShowSuccess(MaxAd ad) {
}
@Override
public void onHidden() {
}
});
ad.loadAd();
}
}
return adList;
}
public static void setMyListener(MaxInterstitialAd ad, MaxListener maxListener) {
ad.setListener(new MaxAdListener() {
@Override
public void onAdLoaded(@NonNull MaxAd maxAd) {
Log.d(AD_TAG, "-------onAdLoaded-----maxAd=" + maxAd.getAdUnitId());
}
@Override
public void onAdDisplayed(@NonNull MaxAd maxAd) {
Log.d(AD_TAG, "-------onAdDisplayed-----maxAd=" + maxAd.getAdUnitId());
maxListener.onShowSuccess(maxAd);
}
@Override
public void onAdHidden(@NonNull MaxAd maxAd) {
Log.d(AD_TAG, "-------onAdHidden-----maxAd=" + maxAd.getAdUnitId());
maxListener.onHidden();
ad.loadAd();
}
@Override
public void onAdClicked(@NonNull MaxAd maxAd) {
}
@Override
public void onAdLoadFailed(@NonNull String s, @NonNull MaxError maxError) {
Log.d(AD_TAG, "-------onAdLoadFailed-----s=" + s + "----maxError=" + maxError.getMessage());
}
@Override
public void onAdDisplayFailed(@NonNull MaxAd maxAd, @NonNull MaxError maxError) {
maxListener.onFail(maxAd);
}
});
}
protected static void ShowAd(Activity activity, onAdStatusListener listener) {
MaxInterstitialAd ad = MaxManager.getAd(adList);
if (ad == null) {
listener.onAdStatus(type_no_cache);
} else {
listener.onAdStatus(type_has_cache);
MaxManager.setMyListener(ad, new MaxListener() {
@Override
public void onFail(MaxAd ad) {
listener.onAdStatus(type_show_fail);
}
@Override
public void onShowSuccess(MaxAd ad) {
listener.onAdStatus(type_show_success);
}
@Override
public void onHidden() {
listener.onAdStatus(type_show_close);
}
});
ad.showAd(activity);
}
}
public static void startShowMaxAd(Activity activity, onAdAfterAction listener) {
MaxManager.ShowAd(activity, new onAdStatusListener() {
@Override
public void onAdStatus(int type) {
if (type == MaxManager.type_show_close || type == MaxManager.type_show_fail || type == MaxManager.type_no_cache) {
if (listener != null)
listener.onAction();
}
}
});
}
}

View File

@ -0,0 +1,10 @@
package com.offline.music.playermp3.max;
import com.applovin.mediation.MaxAd;
import com.applovin.mediation.nativeAds.MaxNativeAdLoader;
public interface NativeMaxListener {
void onLoaded(MaxAd ad, MaxNativeAdLoader maxNativeAdLoader);
}

View File

@ -0,0 +1,80 @@
package com.offline.music.playermp3.max
import android.app.Activity
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.os.CountDownTimer
import android.util.Log
import androidx.localbroadcastmanager.content.LocalBroadcastManager
import com.applovin.mediation.ads.MaxInterstitialAd
import com.offline.music.playermp3.MusicApplication
object WelComManager {
private lateinit var timer: CountDownTimer
private var need_Show = true
private lateinit var lists: List<MaxInterstitialAd>
@JvmStatic
fun initTimer(activity: Activity, countTime: Long,countAction: (Long) -> Unit, goMainAction: () -> Unit): CountDownTimer {
need_Show = true
timer = object : CountDownTimer(countTime, 100) {
override fun onTick(millisUntilFinished: Long) {
countAction.invoke(millisUntilFinished)
if (need_Show) {
MaxManager.ShowAd(activity) {
Log.d(MaxManager.AD_TAG, "--onTick----------it=$it")
if (it == MaxManager.type_has_cache) {
need_Show = false
}
if (it == MaxManager.type_show_close || it == MaxManager.type_show_fail) {
Log.d(MaxManager.AD_TAG, "--onTick---------enter")
goMainAction.invoke()
}
}
}
}
override fun onFinish() {
if (need_Show) {
MaxManager.ShowAd(activity) {
if (it == MaxManager.type_show_close || it == MaxManager.type_show_fail || it == MaxManager.type_no_cache) {
Log.d(MaxManager.AD_TAG, "--onFinish---------enter")
goMainAction.invoke()
}
}
}
}
}
startAd(activity)
return timer
}
private fun startAd(activity: Activity) {
if (!MusicApplication.initSDkOK) {
timer.start()
LocalBroadcastManager.getInstance(activity)
.registerReceiver(object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
lists = MaxManager.onLoadAd()
Log.d(MaxManager.AD_TAG, "------------1sucess")
}
}, IntentFilter(MusicApplication.initAction))
} else {
lists = MaxManager.onLoadAd()
timer.start()
Log.d(MaxManager.AD_TAG, "------------2sucess")
}
}
}

View File

@ -0,0 +1,6 @@
package com.offline.music.playermp3.max;
public interface onAdAfterAction {
void onAction();
}

View File

@ -0,0 +1,6 @@
package com.offline.music.playermp3.max;
public interface onAdStatusListener {
void onAdStatus(int type);
}

View File

@ -0,0 +1,132 @@
package com.offline.music.playermp3.media3;
import androidx.media3.common.MediaItem;
import androidx.media3.common.util.UnstableApi;
import androidx.media3.datasource.DataSource;
import androidx.media3.datasource.cache.SimpleCache;
import androidx.media3.exoplayer.drm.DrmSessionManagerProvider;
import androidx.media3.exoplayer.source.MediaSource;
import androidx.media3.exoplayer.source.ProgressiveMediaSource;
import androidx.media3.exoplayer.upstream.LoadErrorHandlingPolicy;
import com.offline.music.playermp3.helper.CommonUtils;
public class DynamicMediaSourceFactory implements MediaSource.Factory {
private DataSource.Factory mediaSourceFactory;
private SimpleCache simpleCache;
public DynamicMediaSourceFactory(DataSource.Factory factory) {
this.mediaSourceFactory = factory;
}
@UnstableApi
@Override
public MediaSource.Factory setDrmSessionManagerProvider(DrmSessionManagerProvider drmSessionManagerProvider) {
return null;
}
@UnstableApi
@Override
public MediaSource.Factory setLoadErrorHandlingPolicy(LoadErrorHandlingPolicy loadErrorHandlingPolicy) {
return null;
}
@UnstableApi
@Override
public int[] getSupportedTypes() {
return new int[0];
}
@UnstableApi
@Override
public MediaSource createMediaSource(MediaItem mediaItem) {
CommonUtils.LogMsg("------createMediaSource----------=" + mediaItem.mediaId + "---------" + mediaItem.mediaMetadata.title);
// DefaultHttpDataSource.Factory httpDataSourceFactory = new DefaultHttpDataSource.Factory().setAllowCrossProtocolRedirects(true);
// 这里的DefaultDataSource同时支持本地和HTTP请求的资源自动实现检测 The DefaultDataSource supports both local and Http sources. It automatically detects which one to use.
//实现缓存
// CacheDataSource.Factory cacheDataSourceFactory = new CacheDataSource.Factory()
// .setCache(simpleCache)
// .setUpstreamDataSourceFactory(mediaSourceFactory)
// .setFlags(CacheDataSource.FLAG_IGNORE_CACHE_ON_ERROR);
MediaSource mediaSource = new ProgressiveMediaSource.Factory(mediaSourceFactory)
.createMediaSource(mediaItem);
return mediaSource;
// return new ProgressiveMediaSource.Factory(cacheDataSourceFactory)
// .createMediaSource(MediaItem.fromUri(responsePlayUrl.getAudioUrlMedium()));
// 可以在这里根据 mediaItem ID 动态生成新的 URI
// String updatedUri = "https://rr1---sn-tt1e7nlz.googlevideo.com/videoplayback?expire=1727628605&ei=3TD5ZuWFEe-yzN0P27WuqQ4&ip=146.19.167.8&id=o-ANGM0PjEvsfYH7TmYV_DFuD-65tipJeeLe2URDOk90sL&itag=140&source=youtube&requiressl=yes&xpc=EgVo2aDSNQ%3D%3D&mh=xj&mm=31%2C29&mn=sn-tt1e7nlz%2Csn-vgqsknsk&ms=au%2Crdu&mv=m&mvi=1&pl=24&pcm2=no&gcr=us&initcwndbps=7610000&vprv=1&svpuc=1&mime=audio%2Fmp4&rqh=1&gir=yes&clen=5440168&dur=335.973&lmt=1709326903801285&mt=1727606690&fvip=5&keepalive=yes&fexp=51299152&c=ANDROID_MUSIC&txp=2318224&sparams=expire%2Cei%2Cip%2Cid%2Citag%2Csource%2Crequiressl%2Cxpc%2Cpcm2%2Cgcr%2Cvprv%2Csvpuc%2Cmime%2Crqh%2Cgir%2Cclen%2Cdur%2Clmt&sig=AJfQdSswRQIhALmM_S8Cmagr60muB3wDOby0OdcjF-x6f7TcEenixH0KAiAnR0-hmA03MeVzSg2wi5ncJ4Ve5FFlpZnlSoRNGWgGhQ%3D%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&lsig=ABPmVW0wRQIhAPv5slfEnf8_E7o6yjEkussQ6JIFFaSY6QtP9HXncTTcAiAjlhMa71t76Wu1R1rcmsHoO6pyxjhGYouio4D0deJqEA%3D%3D";
// Uri updateUri = null;
//
// MediaItem.Builder builder = mediaItem.buildUpon();
// if (mediaItem.localConfiguration != null) {
// updateUri = mediaItem.localConfiguration.uri;
// builder.setUri(updateUri);
// CommonUtils.LogErrorMsg("----updateUri=成功 builder="+mediaItem.mediaId+"--updateUri="+updateUri);
// }else {
// CommonUtils.LogErrorMsg("----updateUri=null builder="+mediaItem.mediaId);
// }
// return mediaSourceFactory.createMediaSource(builder.build());
}
// @UnstableApi
// @Override
// public DataSource createDataSource() {
//
//
//
//
//// RetrofitManager.getInstance().getPlayUrl(mediaItem.mediaId, new RequestListener<ResponseBody>() {
////
//// @Override
//// public void onFail(String errorMsg) {
////
//// }
////
//// @Override
//// public void onSuccess(ResponseBody data) {
//// JSONObject jsonObject = CommonUtils.toJsonObject(data);
//// if (jsonObject != null) {
//// ResponsePlayUrl responsePlayUrl = JsonHelper.ResolvePlayUrlJson(jsonObject);
//// if (responsePlayUrl == null) {
//// // TODO: 2024/9/27
//// return;
//// }
//// String uri = "";
//// if (responsePlayUrl.getAudioUrlMedium() != null) {
//// uri = responsePlayUrl.getAudioUrlMedium();
//// } else {
//// uri = responsePlayUrl.getAudioUrlLow();
//// }
//// CommonUtils.LogMsg("------createMediaSource----------="+mediaItem.mediaId+"---------"+uri);
//// ProgressiveMediaSource mediaSource = new ProgressiveMediaSource.Factory(cacheDataSourceFactory)
//// .createMediaSource(MediaItem.fromUri(uri));
//// player.setMediaSource(mediaSource);
//// player.prepare();
////
//// }
////
////
//// }
//// });
// return null;
// }
}

View File

@ -0,0 +1,13 @@
package com.offline.music.playermp3.media3;
import android.content.Context;
import androidx.media3.common.util.UnstableApi;
import androidx.media3.ui.PlayerControlView;
@UnstableApi
public class MyControllerView extends PlayerControlView{
public MyControllerView(Context context) {
super(context);
}
}

View File

@ -0,0 +1,35 @@
package com.offline.music.playermp3.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.NoOpCacheEvictor;
import androidx.media3.datasource.cache.SimpleCache;
import com.offline.music.playermp3.MusicApplication;
import java.io.File;
/**
* 下载缓存
*/
public class MyDownloadCacheManager {
private static SimpleCache downloadCache;
@OptIn(markerClass = UnstableApi.class)
public static SimpleCache getMyCache(StandaloneDatabaseProvider databaseProvider){
if(downloadCache == null){
Context myApplication = MusicApplication.myApplication;
File musicApp = new File(myApplication.getCacheDir(), "Music_App");
downloadCache = new SimpleCache(musicApp, new NoOpCacheEvictor(), databaseProvider);
}
return downloadCache;
}
}

View File

@ -0,0 +1,200 @@
package com.offline.music.playermp3.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.offline.music.playermp3.R;
import com.offline.music.playermp3.api.onCheckDownload;
import com.offline.music.playermp3.helper.CommonUtils;
import com.offline.music.playermp3.javabean.CustomerDownload;
import com.offline.music.playermp3.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));
updateDownloadUi(vmApplication);
}
}
@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 && download.state == Download.STATE_COMPLETED);
}).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<Download> 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<Download> downloads, int notMetRequirements) {
// 构建用于显示下载进度的通知
return buildNotification(downloads, notMetRequirements);
}
private Notification buildNotification(
List<Download> downloads, int notMetRequirements) {
return new DownloadNotificationHelper(this, CHANNEL_ID)
.buildProgressNotification(this, R.drawable.ic_download, null, null, downloads, notMetRequirements);
}
}

View File

@ -0,0 +1,420 @@
package com.offline.music.playermp3.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.offline.music.playermp3.MusicApplication;
import com.offline.music.playermp3.api.MediaControllerListener;
import com.offline.music.playermp3.api.MediaControllerStatusListener;
import com.offline.music.playermp3.api.OnHasUrlAction;
import com.offline.music.playermp3.api.onCheckDownload;
import com.offline.music.playermp3.api.onPlayNextListener;
import com.offline.music.playermp3.helper.CommonUtils;
import com.offline.music.playermp3.helper.MyValue;
import com.offline.music.playermp3.javabean.CustomerDownload;
import com.offline.music.playermp3.javabean.response.ResponsePlayListInfo;
import com.offline.music.playermp3.javabean.response.ResponsePlayUrl;
import com.offline.music.playermp3.network.RetrofitManager;
import com.offline.music.playermp3.ui.activity.viewmodel.VMApplication;
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<ResponsePlayListInfo> 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<MediaController> 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<ResponsePlayListInfo> playList) {
this.playList = playList;
addMusicPlayList(playList);
}
public List<ResponsePlayListInfo> getPlayList() {
return playList;
}
public void resetPlayList() {
mediaController.clearMediaItems();
}
/**
* 添加播放列表不带音频url
* 注意没有setUri的不会被实际添加到mediaController(mediaController.getMediaItemCount())
* 这里setUri 一个占位
*
* @param listInfo
*/
@OptIn(markerClass = UnstableApi.class)
public void addMusicPlayList(List<ResponsePlayListInfo> 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();
}
}
}

View File

@ -0,0 +1,34 @@
package com.offline.music.playermp3.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;
}
}

View File

@ -0,0 +1,162 @@
package com.offline.music.playermp3.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.cache.CacheDataSource;
import androidx.media3.datasource.cache.SimpleCache;
import androidx.media3.exoplayer.DefaultLoadControl;
import androidx.media3.exoplayer.ExoPlayer;
import androidx.media3.exoplayer.source.MediaSource;
import androidx.media3.exoplayer.source.ProgressiveMediaSource;
import androidx.media3.session.MediaSession;
import androidx.media3.session.MediaSessionService;
import com.offline.music.playermp3.helper.CommonUtils;
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(factory);
// .setUpstreamDataSourceFactory(new DefaultHttpDataSource.Factory()
// .setConnectTimeoutMs(16000)
// .setReadTimeoutMs(8000)
// .setUserAgent("Mozilla/5.0 (Windows NT 10.0; rv:91.0) Gecko/20100101 Firefox/91.0"));
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);
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);
}
});
}
@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();
}
}

View File

@ -0,0 +1,67 @@
package com.offline.music.playermp3.media3;
import androidx.media3.common.util.UnstableApi;
import androidx.media3.datasource.DataSource;
import androidx.media3.exoplayer.source.DefaultMediaSourceFactory;
public class testSourceFactory implements DataSource.Factory {
// private final CacheDataSource.Factory cacheDataSourceFactory;
// private final ExoPlayer player;
private DefaultMediaSourceFactory mediaSourceFactory;
// public DynamicMediaSourceFactory(DefaultMediaSourceFactory factory,CacheDataSource.Factory cacheDataSourceFactory, ExoPlayer exoPlayer) {
// this.cacheDataSourceFactory = cacheDataSourceFactory;
// this.player = exoPlayer;
// this.mediaSourceFactory = factory;
// }
public testSourceFactory(DefaultMediaSourceFactory factory) {
this.mediaSourceFactory = factory;
}
@UnstableApi
@Override
public DataSource createDataSource() {
// RetrofitManager.getInstance().getPlayUrl(mediaItem.mediaId, new RequestListener<ResponseBody>() {
//
// @Override
// public void onFail(String errorMsg) {
//
// }
//
// @Override
// public void onSuccess(ResponseBody data) {
// JSONObject jsonObject = CommonUtils.toJsonObject(data);
// if (jsonObject != null) {
// ResponsePlayUrl responsePlayUrl = JsonHelper.ResolvePlayUrlJson(jsonObject);
// if (responsePlayUrl == null) {
// // TODO: 2024/9/27
// return;
// }
// String uri = "";
// if (responsePlayUrl.getAudioUrlMedium() != null) {
// uri = responsePlayUrl.getAudioUrlMedium();
// } else {
// uri = responsePlayUrl.getAudioUrlLow();
// }
// CommonUtils.LogMsg("------createMediaSource----------="+mediaItem.mediaId+"---------"+uri);
// ProgressiveMediaSource mediaSource = new ProgressiveMediaSource.Factory(cacheDataSourceFactory)
// .createMediaSource(MediaItem.fromUri(uri));
// player.setMediaSource(mediaSource);
// player.prepare();
//
// }
//
//
// }
// });
return null;
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,73 @@
package com.offline.music.playermp3.network;
import io.reactivex.Observable;
import okhttp3.RequestBody;
import okhttp3.ResponseBody;
import retrofit2.http.Body;
import retrofit2.http.Header;
import retrofit2.http.Headers;
import retrofit2.http.POST;
import retrofit2.http.Query;
public interface MusicApi {
//首页数据
@POST("youtubei/v1/browse?prettyPrint=false")
@Headers("X-Goog-Api-Key:AIzaSyAO_FJ2SlqU8Q4STEHLGCilw_Y9_11qcW8")
Observable<ResponseBody> getHomeData(@Body RequestBody requestBody);
//首页更多数据
@POST("youtubei/v1/browse")
@Headers("X-Goog-Api-Key:AIzaSyAO_FJ2SlqU8Q4STEHLGCilw_Y9_11qcW8")
Observable<ResponseBody> getHomeMoreData(@Query("ctoken") String token,
@Query("continuation") String continuation,
@Query("type") String type,
@Query("itct") String itct,
@Query("prettyPrint") boolean prettyPrint,@Body RequestBody requestBody);
// X-Goog-FieldMask: contents.singleColumnMusicWatchNextResultsRenderer.tabbedRenderer.watchNextTabbedResultsRenderer.tabs.tabRenderer.content.musicQueueRenderer.content.playlistPanelRenderer(continuations,contents(automixPreviewVideoRenderer,playlistPanelVideoRenderer(title,navigationEndpoint,longBylineText,shortBylineText,thumbnail,lengthText)))
//获取播放列表(返回的是每首歌曲详情列表)
@POST("youtubei/v1/next?prettyPrint=false")
@Headers("X-Goog-Api-Key:AIzaSyAO_FJ2SlqU8Q4STEHLGCilw_Y9_11qcW8")
Observable<ResponseBody> getMusicPlayPage(@Header("X-Goog-FieldMask") String customHeader, @Body RequestBody requestBody);
// "X-Goog-FieldMask:playabilityStatus.status,playerConfig.audioConfig,streamingData.adaptiveFormats,videoDetails.videoId,videoDetails.thumbnail"
//获取播放音频资源url "X-Goog-Api-Key:AIzaSyC9XL3ZjwddXya6X74dJOCTL-WEYFDNX30"
@POST("youtubei/v1/player?prettyPrint=false")
@Headers({"X-Goog-Api-Key:AIzaSyAO_FJ2SlqU8Q4STEHLGCilw_Y9_11qcW8"})
Observable<ResponseBody> getMusicPlayUrl(@Body RequestBody requestBody);
//首页分类项下的播放列表子页面 (类型1单曲合集2专辑3音乐视频合集 不同类型返回数据结构有区别)
@POST("youtubei/v1/browse?prettyPrint=false")
@Headers({"X-Goog-Api-Key:AIzaSyAO_FJ2SlqU8Q4STEHLGCilw_Y9_11qcW8"})
Observable<ResponseBody> getCategoryList(@Body RequestBody requestBody);
@POST("youtubei/v1/music/get_search_suggestions?prettyPrint=false")
@Headers({"X-Goog-Api-Key:AIzaSyAO_FJ2SlqU8Q4STEHLGCilw_Y9_11qcW8",
"X-Goog-FieldMask:contents.searchSuggestionsSectionRenderer.contents.searchSuggestionRenderer.navigationEndpoint.searchEndpoint.query"})
Observable<ResponseBody> getSearchSuggestion(@Body RequestBody requestBody);
// "X-Goog-FieldMask:contents.tabbedSearchResultsRenderer.tabs.tabRenderer.content.sectionListRenderer.contents.musicShelfRenderer(continuations,contents.musicResponsiveListItemRenderer(flexColumns,fixedColumns,thumbnail,navigationEndpoint))"
@POST("youtubei/v1/search?prettyPrint=false")
@Headers({"X-Goog-Api-Key:AIzaSyAO_FJ2SlqU8Q4STEHLGCilw_Y9_11qcW8"})
Observable<ResponseBody> getSearch(@Body RequestBody requestBody);
}

View File

@ -0,0 +1,60 @@
package com.offline.music.playermp3.network;
import com.offline.music.playermp3.api.RequestListener;
import com.offline.music.playermp3.helper.CommonUtils;
import org.json.JSONException;
import org.json.JSONObject;
import io.reactivex.Observer;
import io.reactivex.disposables.Disposable;
import okhttp3.ResponseBody;
public class ObserverWrapper<T> implements Observer<T> {
private RequestListener<T> requestListener;
public ObserverWrapper(RequestListener<T> requestListener) {
this.requestListener = requestListener;
}
@Override
public void onSubscribe(Disposable d) {
}
@Override
public void onNext(T t) {
ResponseBody responseBody = (ResponseBody) t;
JSONObject jsonObject = CommonUtils.toJsonObject(responseBody);
try {
if (jsonObject != null) {
if (jsonObject.has("playabilityStatus")) {
String status = jsonObject.getJSONObject("playabilityStatus").getString("status");
if (!status.equals("OK")) {
requestListener.onFail("");
return;
}
}
requestListener.onSuccess(jsonObject);
}
} catch (JSONException e) {
requestListener.onFail(e.getMessage());
}
}
@Override
public void onError(Throwable e) {
CommonUtils.LogMsg("----------onError---" + e.getMessage());
requestListener.onFail(e.getMessage());
}
@Override
public void onComplete() {
}
}

View File

@ -0,0 +1,206 @@
package com.offline.music.playermp3.network;
import com.google.gson.Gson;
import com.offline.music.playermp3.MusicApplication;
import com.offline.music.playermp3.api.RequestListener;
import com.offline.music.playermp3.javabean.requestbody.BodyHome;
import com.offline.music.playermp3.javabean.requestbody.BodyPlay;
import com.offline.music.playermp3.javabean.requestbody.BodyPlayUrl;
import com.offline.music.playermp3.javabean.requestbody.BodySearch;
import com.offline.music.playermp3.javabean.requestbody.BodySearchSuggestion;
import com.offline.music.playermp3.javabean.requestbody.child.Client;
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<ResponseBody> 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<ResponseBody>(requestListener));
}
public void getHomeMoreData(String continuation, String itct, String visitorData, RequestListener<ResponseBody> 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<String, String> 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<ResponseBody>(requestListener));
}
public void getPlayList(String params, String playlistId, String videoId, String musicVideoType, RequestListener<ResponseBody> 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<ResponseBody>(requestListener));
}
public void getPlayUrl(String videoId, RequestListener<ResponseBody> 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<ResponseBody>(requestListener));
}
public void getCategoryList(String browserId, RequestListener<ResponseBody> 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<ResponseBody> 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<ResponseBody> 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));
}
}

View File

@ -0,0 +1,109 @@
package com.offline.music.playermp3.objectbox;
import android.content.Context;
import androidx.annotation.NonNull;
import com.offline.music.playermp3.api.LikeSongListener;
import com.offline.music.playermp3.helper.CommonUtils;
import com.offline.music.playermp3.javabean.BoxLikeSong;
import com.offline.music.playermp3.javabean.BoxLikeSong_;
import com.offline.music.playermp3.javabean.MyObjectBox;
import java.util.List;
import io.objectbox.Box;
import io.objectbox.BoxStore;
import io.objectbox.android.AndroidScheduler;
import io.objectbox.query.Query;
import io.objectbox.query.QueryBuilder;
import io.objectbox.reactive.DataObserver;
import io.objectbox.reactive.DataSubscription;
import io.objectbox.reactive.DataSubscriptionList;
public class ObjectBoxManager {
private static BoxStore boxStore;
private static Box<BoxLikeSong> boxLikeSongBox;
private static DataSubscription observer;
public static void init(Context context) {
boxStore = MyObjectBox.builder().androidContext(context).build();
}
public static Box<BoxLikeSong> getObjectBoxLike() {
if (boxLikeSongBox == null) {
return boxStore.boxFor(BoxLikeSong.class);
}
return boxLikeSongBox;
}
public static DataSubscription setLikeDataListener(LikeSongListener listener) {
Query<BoxLikeSong> build = getObjectBoxLike().query().build();
return build.subscribe(new DataSubscriptionList())
.on(AndroidScheduler.mainThread())
.observer(new DataObserver<List<BoxLikeSong>>() {
@Override
public void onData(@NonNull List<BoxLikeSong> data) {
CommonUtils.LogMsg("------ProfileFragment 11data=" + data.size());
listener.onLikeSongChange(data);
}
});
}
public static void insertOrUpdateLike(BoxLikeSong boxLikeSong) {
Box<BoxLikeSong> objectBoxLike = getObjectBoxLike();
String videoId = boxLikeSong.getVideoId();
BoxLikeSong first = objectBoxLike.query()
.equal(BoxLikeSong_.videoId, videoId, QueryBuilder.StringOrder.CASE_SENSITIVE)
.build()
.findFirst();
if (first == null) {
CommonUtils.LogErrorMsg("-----------添加 song=" + boxLikeSong.getSongName() + "---videoId=" + videoId);
objectBoxLike.put(boxLikeSong);
} else {
CommonUtils.LogErrorMsg("-----------已经存在 song=" + boxLikeSong.getSongName() + "---videoId=" + videoId);
}
}
public static void deleteLike(BoxLikeSong boxLikeSong) {
Box<BoxLikeSong> objectBoxLike = getObjectBoxLike();
String videoId = boxLikeSong.getVideoId();
List<BoxLikeSong> boxLikeSongs = objectBoxLike.query()
.equal(BoxLikeSong_.videoId, videoId, QueryBuilder.StringOrder.CASE_SENSITIVE)
.build()
.find();
for (BoxLikeSong song : boxLikeSongs) {
objectBoxLike.remove(song);
CommonUtils.LogErrorMsg("-------onLikeSongChange-deleteLike song=" + song.getSongName());
}
}
public static List<BoxLikeSong> queryAllLike() {
return getObjectBoxLike().query()
.build()
.find();
}
public static boolean queryIsLike(String videoId) {
Box<BoxLikeSong> objectBoxLike = getObjectBoxLike();
BoxLikeSong first = objectBoxLike.query()
.equal(BoxLikeSong_.videoId, videoId, QueryBuilder.StringOrder.CASE_SENSITIVE)
.build()
.findFirst();
if (first == null) {
return false;
} else {
return true;
}
}
}

View File

@ -1,5 +1,6 @@
package com.offline.music.playermp3.service;
import android.Manifest;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
@ -7,6 +8,7 @@ import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.res.AssetFileDescriptor;
import android.media.MediaPlayer;
import android.net.Uri;
@ -20,7 +22,9 @@ import android.util.Log;
import android.widget.Toast;
import androidx.annotation.Nullable;
import androidx.core.app.ActivityCompat;
import androidx.core.app.NotificationCompat;
import androidx.core.app.NotificationManagerCompat;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
@ -69,54 +73,89 @@ public class MusicPlayerForegroundService extends Service {
// 服务启动时调用
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d("MusicPlayerService", "Service started");
Log.d("MusicPlayerService", "=========onStartCommand---------");
audioItem = (AudioItem) intent.getSerializableExtra("Path"); // 获取音频路径
if (audioItem != null) {
String newAudioPath = audioItem.getFile();
// 如果新音频路径不同于当前路径则重新初始化播放器
if (newAudioPath != null && !newAudioPath.equals(currentAudioPath)) {
Log.d("MusicPlayerService", "检测到新的音频路径,初始化播放器");
currentAudioPath = newAudioPath; // 更新路径
stopAndResetMediaPlayer(); // 停止并重置播放器
initializePlayer(newAudioPath); // 初始化播放器
} else {
Log.d("MusicPlayerService", "音频路径相同,无需重新初始化播放器");
// Log.d("MusicPlayerService", "音频路径相同,无需重新初始化播放器");
}
} else {
stopSelf(); // 路径为空停止服务
}
startForeground(1, createNotification()); // 启动前台服务并显示通知
showNotification();
acquireWakeLock(); // 获取WakeLock
return START_NOT_STICKY; // 服务不会在被杀死后自动重启
}
// 创建通知
private Notification createNotification() {
createNotificationChannel(); // 创建通知渠道
Intent notificationIntent = new Intent(this, A_PlayActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent,
Build.VERSION.SDK_INT >= Build.VERSION_CODES.S ? PendingIntent.FLAG_IMMUTABLE : 0); // 适配Android 12
public void showNotification() {
// 获取 Notification 对象
Notification notification = createNotification();
return new NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle("Playing audio") // 通知标题
.setContentText("Your audio is playing") // 通知内容
.setSmallIcon(R.drawable.home_select) // 小图标
.setContentIntent(pendingIntent) // 点击通知的Intent
.setPriority(NotificationCompat.PRIORITY_LOW) // 低优先级
.build();
// 使用 NotificationManagerCompat 来显示通知
NotificationManagerCompat notificationManager = NotificationManagerCompat.from(this);
// 为每个通知提供唯一的 ID以便以后可以更新或移除通知
int notificationId = 1; // 可以是任何唯一的 ID
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) {
// TODO: Consider calling
// ActivityCompat#requestPermissions
// here to request the missing permissions, and then overriding
// public void onRequestPermissionsResult(int requestCode, String[] permissions,
// int[] grantResults)
// to handle the case where the user grants the permission. See the documentation
// for ActivityCompat#requestPermissions for more details.
Log.d("----------", "Have no authority");
return;
}
notificationManager.notify(notificationId, notification);
}
private Notification createNotification() {
createNotificationChannel();
// 创建一个Intent点击通知后跳转到 A_PlayActivity
Intent notificationIntent = new Intent(this, A_PlayActivity.class);
notificationIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
// 创建通知构建器
NotificationCompat.Builder builder = new NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle("Offline Music Player") // 通知标题
.setContentText("Your audio is playing") // 通知内容
.setSmallIcon(R.mipmap.small_logo) // 小图标
.setPriority(NotificationCompat.PRIORITY_HIGH); // 优先级设置为高
// 设置展开样式可选根据需要
builder.setStyle(new NotificationCompat.BigTextStyle()
.bigText("Your audio is playing in the background. Tap to go to the player activity."));
return builder.build();
}
// 创建通知渠道
private void createNotificationChannel() {
// 仅在 Android 8.0API 级别 26及以上版本创建通知渠道
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
// 创建通知渠道设置重要性
NotificationChannel serviceChannel = new NotificationChannel(
CHANNEL_ID, "Music player channel", NotificationManager.IMPORTANCE_LOW);
NotificationManager manager = getSystemService(NotificationManager.class);
if (manager != null) {
manager.createNotificationChannel(serviceChannel); // 创建渠道
CHANNEL_ID, "Offline Music Player", NotificationManager.IMPORTANCE_HIGH); // 设置为高优先级
// 配置通知渠道的详细信息如描述
serviceChannel.setDescription("This is the channel for the music player notifications");
// 注册通知渠道
NotificationManager notificationManager = getSystemService(NotificationManager.class);
if (notificationManager != null) {
notificationManager.createNotificationChannel(serviceChannel);
}
}
}
@ -134,7 +173,7 @@ public class MusicPlayerForegroundService extends Service {
// 初始化播放器
private void initializePlayer(String path) {
try {
Log.d("MusicPlayerService", "正在初始化播放器,音频路径: " + path);
Log.d("MusicPlayerService", "Initializing the player, audio path: " + path);
if (mediaPlayer == null) {
mediaPlayer = new MediaPlayer(); // 创建新的MediaPlayer实例
}
@ -160,15 +199,15 @@ public class MusicPlayerForegroundService extends Service {
});
// 播放错误监听
mediaPlayer.setOnErrorListener((mp, what, extra) -> {
Log.e("MusicPlayerService", "播放时发生错误: " + what + ", 额外信息: " + extra);
isPlaying.postValue(false); // 更新状态
stopSelf(); // 播放出错后停止服务
return true; // 表示错误已处理
});
// mediaPlayer.setOnErrorListener((mp, what, extra) -> {
// Log.e("MusicPlayerService", "播放时发生错误: " + what + ", 额外信息: " + extra);
// isPlaying.postValue(false); // 更新状态
//// stopSelf(); // 播放出错后停止服务
// return true; // 表示错误已处理
// });
} catch (IOException | IllegalArgumentException | IllegalStateException e) {
Log.e("MusicPlayerService", "初始化播放器失败,路径: " + path, e);
Log.e("MusicPlayerService", "Failed to initialize the player, path: " + path, e);
isPlaying.postValue(false); // 更新状态
stopSelf(); // 停止服务
}
@ -193,8 +232,8 @@ public class MusicPlayerForegroundService extends Service {
throw new IOException("Unable to open the content URI: " + path);
}
} catch (SecurityException e) {
Log.e("MusicPlayerService", "权限被拒绝,无法访问内容URI: " + path, e);
throw new IOException("权限被拒绝", e); // 处理权限异常
Log.e("MusicPlayerService", "Permission denied, unable to access content URI: " + path, e);
throw new IOException("Permission denied", e); // 处理权限异常
}
}
@ -224,7 +263,7 @@ public class MusicPlayerForegroundService extends Service {
// 如果找到了点返回不带扩展名的文件名
return (dotIndex > 0) ? fileName.substring(0, dotIndex) : fileName;
} catch (Exception e) {
Log.e("MusicPlayerService", "获取文件名失败", e);
Log.e("MusicPlayerService", "Failed to get file name", e);
return "Unknown file"; // 默认文件名
}
}
@ -347,12 +386,14 @@ public class MusicPlayerForegroundService extends Service {
@Override
public void run() {
if (mediaPlayer != null && mediaPlayer.isPlaying()) {
Log.d("nowtotal", "run222");
currentPosition.postValue(mediaPlayer.getCurrentPosition());
handler.postDelayed(this, 1000); // 每隔1秒更新一次
}
}
};
handler.post(runnable); // 启动更新
Log.d("nowtotal", "service");
}
// 跳转到指定播放位置

View File

@ -1,13 +1,19 @@
package com.offline.music.playermp3.ui.activity;
import android.Manifest;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.IBinder;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.Toast;
import androidx.core.app.ActivityCompat;
import com.google.android.material.tabs.TabLayout;
import com.google.android.material.tabs.TabLayoutMediator;
@ -21,8 +27,9 @@ import com.offline.music.playermp3.service.MusicPlayerForegroundService;
import java.util.Locale;
public class A_HomeActivity extends BaseActivity<ActivityAhomeBinding> {
private static final int PERMISSION_REQUEST_CODE = 1001;
private MusicPlayerForegroundService musicService; // 音乐播放服务
public static MusicPlayerForegroundService musicService; // 音乐播放服务
private boolean isBound = false; // 服务是否绑定
private final int[] defaultIcons = {R.drawable.home_unselect, R.drawable.import_unselect}; // 默认图标
@ -54,10 +61,16 @@ public class A_HomeActivity extends BaseActivity<ActivityAhomeBinding> {
@Override
protected void onCreateInit() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.POST_NOTIFICATIONS}, PERMISSION_REQUEST_CODE);
}
}
Intent serviceIntent = new Intent(this, MusicPlayerForegroundService.class);
startService(serviceIntent); // 启动音乐播放服务
bindService(serviceIntent, serviceConnection, Context.BIND_AUTO_CREATE); // 绑定服务
// 初始化界面
vb.homeContainer.setVisibility(View.GONE);
vb.pause.setOnClickListener(v -> togglePlayPause()); // 设置暂停/播放按钮点击事件
@ -80,6 +93,22 @@ public class A_HomeActivity extends BaseActivity<ActivityAhomeBinding> {
// 处理其他点击事件
}
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == PERMISSION_REQUEST_CODE) {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
Intent serviceIntent = new Intent(this, MusicPlayerForegroundService.class);
startService(serviceIntent); // 启动音乐播放服务
bindService(serviceIntent, serviceConnection, Context.BIND_AUTO_CREATE); // 绑定服务
} else {
Toast.makeText(this, "Notification permission is required to show notifications", Toast.LENGTH_SHORT).show();
}
}
}
@Override
public boolean isFullScreen() {
return true;

View File

@ -55,6 +55,8 @@ public class A_PlayActivity extends BaseActivity<ActivityAplayBinding> {
musicService = ((MusicPlayerForegroundService.MusicBinder) service).getService();
isBound = true;
Log.d("nowtotal","onServiceConnected");
// 设置观察者
musicService.getIsPlaying().observe(A_PlayActivity.this, this::updatePlayButton);
@ -99,10 +101,7 @@ public class A_PlayActivity extends BaseActivity<ActivityAplayBinding> {
loadBackgroundPreference(); // 加载之前保存的背景状态
updateSeekBarAndTime();
if (musicService != null) {
musicService.startUpdatingProgress();
}
// 从Intent获取音频项
AudioItem audioItem = (AudioItem) getIntent().getSerializableExtra("Path");
@ -142,6 +141,13 @@ public class A_PlayActivity extends BaseActivity<ActivityAplayBinding> {
startMusicService(audioItem); // 启动音乐服务
setupPlayButtonClickListener(); // 设置播放按钮的点击事件
updateSeekBarAndTime();
if (musicService != null) {
musicService.startUpdatingProgress();
Log.d("nowtotal","onCreateInit");
}
}
@ -214,21 +220,25 @@ public class A_PlayActivity extends BaseActivity<ActivityAplayBinding> {
// 更新进度条和时间显示
private void updateSeekBarAndTime() {
if (isBound && musicService != null) {
if (isBound && musicService!= null) {
// 观察当前播放进度
musicService.getCurrentPositionLiveData().observe(this, position -> {
if (musicService.getDurationLiveData().getValue() != null) {
int duration = musicService.getDurationLiveData().getValue();
if (duration > 0) {
vb.songSeekbar.setProgress((int) ((position / (float) duration) * 100));
vb.current.setText(formatTime(position));
}
// Integer duration = musicService.getDurationLiveData().getValue();
int duration = musicService.getDuration();
if (duration > 0) {
vb.songSeekbar.setProgress((int) ((position / (float) duration) * 100));
vb.current.setText(formatTime(position));
Log.d("nowtotal","current position="+position);
}
});
// 观察音频总时长
musicService.getDurationLiveData().observe(this, duration -> {
vb.time.setText(formatTime(duration));
int duration1 = musicService.getDuration();
if (duration1>0) {
vb.time.setText(formatTime(duration1));
Log.d("nowtotal","time duration="+duration1);
}
});
}
}

View File

@ -5,11 +5,18 @@ import android.os.CountDownTimer;
import android.view.View;
import com.offline.music.playermp3.databinding.ActivityAsplashBinding;
import com.offline.music.playermp3.firebase.RemoteConfigJava;
import com.offline.music.playermp3.firebase.Sp;
import com.offline.music.playermp3.max.WelComManager;
import kotlin.Unit;
import kotlin.jvm.functions.Function0;
import kotlin.jvm.functions.Function1;
public class A_SplashActivity extends BaseActivity<ActivityAsplashBinding> {
private static final int SPLASH_TIME_OUT = 1500;
private static final int SPLASH_TIME_OUT = 13000;
private CountDownTimer countDownTimer;
@Override
@ -45,26 +52,23 @@ public class A_SplashActivity extends BaseActivity<ActivityAsplashBinding> {
}
public void intData() {
countDownTimer = new CountDownTimer(SPLASH_TIME_OUT, 100) {
countDownTimer = WelComManager.initTimer(this, SPLASH_TIME_OUT, new Function1<Long, Unit>() {
@Override
public void onTick(long millisUntilFinished) {
float v = 100 - (float) millisUntilFinished / SPLASH_TIME_OUT * 100;
public Unit invoke(Long aLong) {
float v = 100 - (float) aLong / SPLASH_TIME_OUT * 100;
int v1 = (int) v;
vb.progressBar.setProgress(v1);
return null;
}
}, new Function0<Unit>() {
@Override
public void onFinish() {
public Unit invoke() {
vb.progressBar.setProgress(100);
enterHome();
return null;
}
};
});
countDownTimer.start();
}
@ -72,11 +76,25 @@ public class A_SplashActivity extends BaseActivity<ActivityAsplashBinding> {
public void onClick(View v) {
}
private void enterHome() {
Intent intent = new Intent(A_SplashActivity.this, A_HomeActivity.class);
String stringValue = Sp.getInstance().getStringValue(RemoteConfigJava.key_open_type);
Intent intent;
if (stringValue.equals(RemoteConfigJava.value_open_type_0)) {
intent = new Intent(A_SplashActivity.this, A_HomeActivity.class);
} else {
intent = new Intent(A_SplashActivity.this, HomeActivity.class);
}
startActivity(intent);
finish();
}
@Override
protected void onDestroy() {
super.onDestroy();
if (countDownTimer != null) {
countDownTimer.cancel();
countDownTimer = null;
}
}
}

View File

@ -1,21 +1,44 @@
package com.offline.music.playermp3.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.offline.music.playermp3.MusicApplication;
import com.offline.music.playermp3.R;
import com.offline.music.playermp3.api.onPlayNextListener;
import com.offline.music.playermp3.databinding.ActivityBaseBinding;
import com.offline.music.playermp3.databinding.LayoutPanelBinding;
import com.offline.music.playermp3.helper.CommonUtils;
import com.offline.music.playermp3.helper.MyValue;
import com.offline.music.playermp3.helper.ViewModelScope;
import com.offline.music.playermp3.media3.MyMediaControllerManager;
import com.offline.music.playermp3.ui.activity.viewmodel.VMApplication;
public abstract class BaseActivity<T extends ViewBinding> extends AppCompatActivity implements View.OnClickListener {
// private final ViewModelScope mViewModelScope = new ViewModelScope();
private final ViewModelScope mViewModelScope = new ViewModelScope();
protected T vb;
@ -24,8 +47,8 @@ public abstract class BaseActivity<T extends ViewBinding> extends AppCompatActiv
protected View mView;
private ActivityBaseBinding rootVb;
// protected VMApplication vmApplication;
// protected MyMediaControllerManager mediaControllerManager;
protected VMApplication vmApplication;
protected MyMediaControllerManager mediaControllerManager;
private Handler mHandler;
private Runnable mRunnable;
@ -41,10 +64,11 @@ public abstract class BaseActivity<T extends ViewBinding> extends AppCompatActiv
window = getWindow();
decorView = window.getDecorView();
mView = decorView.getRootView();
// vmApplication = getApplicationScopeViewModel(VMApplication.class);
// mediaControllerManager = MyMediaControllerManager.getInstance();
vmApplication = getApplicationScopeViewModel(VMApplication.class);
mediaControllerManager = MyMediaControllerManager.getInstance();
if (showPanel())
initPanel();
setStatusBar();
if (isFullScreen()) {
// ImmersionBar.with(this).init();//设置沉浸式效果
@ -70,11 +94,134 @@ public abstract class BaseActivity<T extends ViewBinding> extends AppCompatActiv
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<Integer>() {
@OptIn(markerClass = UnstableApi.class)
@Override
public void onChanged(Integer 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());
@ -88,13 +235,13 @@ public abstract class BaseActivity<T extends ViewBinding> extends AppCompatActiv
mView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
}
// protected <K extends ViewModel> K getActivityScopeViewModel(@NonNull Class<K> modelClass) {
// return mViewModelScope.getActivityScopeViewModel(this, modelClass);
// }
//
// protected <K extends ViewModel> K getApplicationScopeViewModel(@NonNull Class<K> modelClass) {
// return mViewModelScope.getApplicationScopeViewModel(modelClass);
// }
protected <K extends ViewModel> K getActivityScopeViewModel(@NonNull Class<K> modelClass) {
return mViewModelScope.getActivityScopeViewModel(this, modelClass);
}
protected <K extends ViewModel> K getApplicationScopeViewModel(@NonNull Class<K> modelClass) {
return mViewModelScope.getApplicationScopeViewModel(modelClass);
}
@Override
protected void onDestroy() {

View File

@ -0,0 +1,247 @@
package com.offline.music.playermp3.ui.activity;
import static com.bumptech.glide.request.RequestOptions.bitmapTransform;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.OptIn;
import androidx.lifecycle.Observer;
import androidx.media3.common.util.UnstableApi;
import androidx.recyclerview.widget.LinearLayoutManager;
import android.content.Intent;
import android.graphics.drawable.Drawable;
import android.view.View;
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.offline.music.playermp3.MusicApplication;
import com.offline.music.playermp3.R;
import com.offline.music.playermp3.adapter.AdapterCategoryList;
import com.offline.music.playermp3.api.HomeItemClickListener;
import com.offline.music.playermp3.databinding.ActivityCategoryListBinding;
import com.offline.music.playermp3.databinding.ActivityHomeBinding;
import com.offline.music.playermp3.helper.CommonUtils;
import com.offline.music.playermp3.helper.MyValue;
import com.offline.music.playermp3.javabean.response.ResponseCategoryList;
import com.offline.music.playermp3.javabean.response.ResponsePlayListInfo;
import com.offline.music.playermp3.max.MaxManager;
import com.offline.music.playermp3.ui.fragmnt.viewmodel.VMCategoryList;
import jp.wasabeef.glide.transformations.BlurTransformation;
/**
* 音乐合集或者专辑列表页面
*/
public class CategoryListActivity extends BaseActivity<ActivityCategoryListBinding> 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() {
MaxManager.onLoadAd();
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<ResponseCategoryList>() {
@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<Drawable>() {
@Override
public boolean onLoadFailed(@Nullable GlideException e, @Nullable Object model, @NonNull Target<Drawable> target, boolean isFirstResource) {
return false;
}
@Override
public boolean onResourceReady(@NonNull Drawable resource, @NonNull Object model, Target<Drawable> target, @NonNull DataSource dataSource, boolean isFirstResource) {
CommonUtils.extractColorsFromImage(resource,vb.imBg);
return false;
}
})
.preload();
Glide.with(MusicApplication.myApplication)
.asDrawable()
// .apply(bitmapTransform(new RoundedCorners(CommonUtils.dpToPx(10))))
.load(url)
.placeholder(R.mipmap.im_placeholder)
.listener(new RequestListener<Drawable>() {
@Override
public boolean onLoadFailed(@Nullable GlideException e, @Nullable Object model, @NonNull Target<Drawable> target, boolean isFirstResource) {
CommonUtils.LogMsg(e.getMessage());
vb.pbLoading.setVisibility(View.GONE);
return false;
}
@OptIn(markerClass = UnstableApi.class)
@Override
public boolean onResourceReady(@NonNull Drawable resource, @NonNull Object model, Target<Drawable> target, @NonNull DataSource dataSource, boolean isFirstResource) {
// CommonUtils.getDominantDarkColor1(resource, new onImageColorListener() {
// @Override
// public void onImageColor(int color) {
// if (color == -1) {
// return;
// }
//
// }
// });
vb.pbLoading.setVisibility(View.GONE);
return false;
}
})
.into(vb.imCovert);
}
@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();
}
}

View File

@ -0,0 +1,130 @@
package com.offline.music.playermp3.ui.activity;
import android.view.LayoutInflater;
import android.view.View;
import androidx.core.content.ContextCompat;
import com.google.android.material.tabs.TabLayout;
import com.google.android.material.tabs.TabLayoutMediator;
import com.offline.music.playermp3.MusicApplication;
import com.offline.music.playermp3.R;
import com.offline.music.playermp3.adapter.HomeViewPagerAdapter;
import com.offline.music.playermp3.databinding.ActivityHomeBinding;
import com.offline.music.playermp3.databinding.HomeTabCustomBinding;
import com.offline.music.playermp3.helper.CommonUtils;
public class HomeActivity extends BaseActivity<ActivityHomeBinding> {
// 图标数组定义为类成员避免重复
private final int[] defaultIcons = {
R.drawable.home_unselect,
R.drawable.search_unselect,
R.drawable.profile_unselect
};
private final int[] selectedIcons = {
R.drawable.home_select,
R.drawable.search_select,
R.drawable.profile_select
};
@Override
protected ActivityHomeBinding getViewBinding() {
return ActivityHomeBinding.inflate(getLayoutInflater());
}
@Override
protected void onCreateInit() {
initView(); // 可以在这里初始化视图
}
@Override
protected void onInitClick() {
}
@Override
public boolean isFullScreen() {
return true;
}
@Override
public boolean statusBarLight() {
return false;
}
@Override
public boolean showPanel() {
return true;
}
public void initView() {
int statusBarHeight = CommonUtils.getStatusBarHeight(MusicApplication.myApplication);
View root = vb.getRoot();
root.setPadding(0,statusBarHeight,0,0);
HomeViewPagerAdapter adapter = new HomeViewPagerAdapter(this);
vb.homeViewPager.setAdapter(adapter);
vb.homeViewPager.setUserInputEnabled(false);
// 设置TabLayout的图标
new TabLayoutMediator(vb.homeTabLayout, vb.homeViewPager, (tab, position) -> {
HomeTabCustomBinding tabBinding = HomeTabCustomBinding.inflate(LayoutInflater.from(this));
tab.setCustomView(tabBinding.getRoot());
tabBinding.homeIcon.setImageResource(defaultIcons[position]); // 默认图标
}).attach();
// 添加Tab选中与未选中事件监听器
vb.homeTabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
@Override
public void onTabSelected(TabLayout.Tab tab) {
updateTabIcon(tab, true); // 更新选中的图标
int position = tab.getPosition();
switch (position){
case 0:
vb.view.setVisibility(View.VISIBLE);
vb.homeLayout.setBackgroundColor(ContextCompat.getColor(HomeActivity.this,R.color.black));
break;
case 1:
vb.view.setVisibility(View.GONE);
vb.homeLayout.setBackgroundColor(ContextCompat.getColor(HomeActivity.this,R.color.default_play_list_color));
break;
case 2:
vb.view.setVisibility(View.GONE);
vb.homeLayout.setBackground(ContextCompat.getDrawable(HomeActivity.this,R.mipmap.bg_library));
break;
}
}
@Override
public void onTabUnselected(TabLayout.Tab tab) {
updateTabIcon(tab, false); // 恢复未选中图标
}
@Override
public void onTabReselected(TabLayout.Tab tab) {
// 可选重复选择Tab时的操作
}
});
// 设置默认选中第一个
TabLayout.Tab firstTab = vb.homeTabLayout.getTabAt(0);
if (firstTab != null) {
firstTab.select();
updateTabIcon(firstTab, true); // 设置选中的图标
}
}
private void updateTabIcon(TabLayout.Tab tab, boolean isSelected) {
HomeTabCustomBinding tabBinding = HomeTabCustomBinding.bind(tab.getCustomView());
int position = tab.getPosition();
tabBinding.homeIcon.setImageResource(isSelected ? selectedIcons[position] : defaultIcons[position]);
}
@Override
public void onClick(View v) {
}
}

View File

@ -0,0 +1,192 @@
package com.offline.music.playermp3.ui.activity;
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 androidx.recyclerview.widget.LinearLayoutManager;
import com.applovin.mediation.MaxAd;
import com.applovin.mediation.nativeAds.MaxNativeAdLoader;
import com.offline.music.playermp3.R;
import com.offline.music.playermp3.adapter.AdapterDownloadSong;
import com.offline.music.playermp3.adapter.AdapterLikeSong;
import com.offline.music.playermp3.api.HomeItemClickListener;
import com.offline.music.playermp3.databinding.ActivityLikeSongBinding;
import com.offline.music.playermp3.helper.CommonUtils;
import com.offline.music.playermp3.helper.MyValue;
import com.offline.music.playermp3.javabean.BoxLikeSong;
import com.offline.music.playermp3.javabean.response.ResponsePlayListInfo;
import com.offline.music.playermp3.max.MaxManager;
import com.offline.music.playermp3.max.NativeMaxListener;
import com.offline.music.playermp3.media3.MyDownloadService;
import com.offline.music.playermp3.objectbox.ObjectBoxManager;
import java.util.ArrayList;
import java.util.List;
public class LikeSongActivity extends BaseActivity<ActivityLikeSongBinding> implements HomeItemClickListener {
private List<BoxLikeSong> boxLikeSongs;
private int mType;
private MaxAd nativeAd;
private MaxNativeAdLoader nativeAdLoader;
private List<Pair<Integer, String>> downloadList = new ArrayList<>();
@Override
protected ActivityLikeSongBinding getViewBinding() {
return ActivityLikeSongBinding.inflate(getLayoutInflater());
}
@OptIn(markerClass = UnstableApi.class)
@Override
protected void onCreateInit() {
MaxManager.onLoadAd();
Intent intent = getIntent();
mType = intent.getIntExtra(MyValue.KEY_ENTER_LIKE_ACTIVITY_TYPE, MyValue.KEY_ENTER_LIKE_ACTIVITY_TYPE_LIKE);
MaxManager.onLoadNativeAd(vb.adLayout, MaxManager.native_Ad2, new NativeMaxListener() {
@Override
public void onLoaded(MaxAd ad, MaxNativeAdLoader maxNativeAdLoader) {
nativeAd = ad;
nativeAdLoader = maxNativeAdLoader;
}
});
switch (mType) {
case MyValue.KEY_ENTER_LIKE_ACTIVITY_TYPE_LIKE:
vb.tvTitle.setText(getString(R.string.text_like_song));
boxLikeSongs = ObjectBoxManager.queryAllLike();
AdapterLikeSong adapterLikeSong = new AdapterLikeSong(this, vmApplication);
adapterLikeSong.setHomeItemClickListener(this);
adapterLikeSong.setData(boxLikeSongs);
vb.recycler.setLayoutManager(new LinearLayoutManager(this));
vb.recycler.setAdapter(adapterLikeSong);
vb.tvSongSize.setText(String.format(getString(R.string.like_song), boxLikeSongs.size()));
MyDownloadService.addDownloadListener(vmApplication);
vmApplication.downloadChange.observe(this, new Observer<Pair<Boolean, Download>>() {
@Override
public void onChanged(Pair<Boolean, Download> booleanDownloadPair) {
Boolean first = booleanDownloadPair.first;
Download downloads = booleanDownloadPair.second;
String id = downloads.request.id;
for (Pair<Integer,String> pair:downloadList){
Integer position = pair.first;
String videoId = pair.second;
if(id.equals(videoId)){
adapterLikeSong.updateDownloadStatus(first,position,videoId);
}
}
}
});
break;
case MyValue.KEY_ENTER_LIKE_ACTIVITY_TYPE_DOWNLOAD:
vb.tvTitle.setText(getString(R.string.text_offline_song));
MyDownloadService.updateDownloadUi(vmApplication);
vmApplication.downloadData.observe(this, new Observer<List<Download>>() {
@Override
public void onChanged(List<Download> downloads) {
AdapterDownloadSong adapterDownloadSong = new AdapterDownloadSong();
adapterDownloadSong.setHomeItemClickListener(LikeSongActivity.this);
adapterDownloadSong.setData(downloads);
vb.recycler.setLayoutManager(new LinearLayoutManager(LikeSongActivity.this));
vb.recycler.setAdapter(adapterDownloadSong);
vb.tvSongSize.setText(String.format(getString(R.string.download_song), downloads.size()));
}
});
break;
}
}
@Override
protected void onDestroy() {
if(nativeAd!= null&&nativeAdLoader!= null){
MaxManager.clearNativeAd(nativeAd,nativeAdLoader);
}
super.onDestroy();
}
@Override
protected void onInitClick() {
vb.imBack.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();
}
}
@Override
public void onClickLikeSong(BoxLikeSong boxLikeSong, int index) {
List<ResponsePlayListInfo> playList = new ArrayList<>();
for (BoxLikeSong song : boxLikeSongs) {
String videoId1 = song.getVideoId();
String songName = song.getSongName();
String covert = song.getCovert();
String singerName = song.getSingerName();
long durationMs = song.getDurationMs();
String duration = song.getDuration();
ResponsePlayListInfo playListInfo = new ResponsePlayListInfo(covert, songName, singerName, durationMs, videoId1, duration);
playList.add(playListInfo);
}
vmApplication.reSetPlayList(playList);
String videoId = boxLikeSong.getVideoId();
Intent intent = new Intent(this, PlayActivity.class);
intent.putExtra(MyValue.KEY_PLAY_VIDEO_ID, videoId);
intent.putExtra(MyValue.KEY_PLAY_INDEX, index);
intent.putExtra(MyValue.KEY_ENTER_SOURCE, MyValue.TYPE_ENTER_LIKE);
startActivity(intent);
}
@OptIn(markerClass = UnstableApi.class)
@Override
public void onClickDownloadSong(Download download, int index) {
// MyDownloadService.updateDownloadUi(vmApplication)
String videoId = download.request.id;
Intent intent = new Intent(this, PlayActivity.class);
intent.putExtra(MyValue.KEY_PLAY_VIDEO_ID, videoId);
intent.putExtra(MyValue.KEY_PLAY_INDEX, index);
intent.putExtra(MyValue.KEY_ENTER_SOURCE, MyValue.TYPE_ENTER_DOWNLOAD);
startActivity(intent);
}
@Override
public void onDownloadSong(String videoId, int index) {
Pair<Integer, String> integerStringPair = new Pair<>(index, videoId);
downloadList.add(integerStringPair);
}
}

View File

@ -0,0 +1,894 @@
package com.offline.music.playermp3.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.util.Pair;
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.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.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.request.RequestListener;
import com.bumptech.glide.request.target.Target;
import com.google.gson.Gson;
import com.offline.music.playermp3.MusicApplication;
import com.offline.music.playermp3.R;
import com.offline.music.playermp3.adapter.AdapterPlayList;
import com.offline.music.playermp3.api.MediaControllerListener;
import com.offline.music.playermp3.api.onCheckDownload;
import com.offline.music.playermp3.api.onImageColorListener;
import com.offline.music.playermp3.api.onPlayNextListener;
import com.offline.music.playermp3.databinding.ActivityPlayBinding;
import com.offline.music.playermp3.helper.CommonUtils;
import com.offline.music.playermp3.helper.MyValue;
import com.offline.music.playermp3.javabean.BoxDownloadSong;
import com.offline.music.playermp3.javabean.BoxLikeSong;
import com.offline.music.playermp3.javabean.CustomerDownload;
import com.offline.music.playermp3.javabean.CustomerUrlInfo;
import com.offline.music.playermp3.javabean.response.ResponsePlayListInfo;
import com.offline.music.playermp3.javabean.response.child.ResponseCategory;
import com.offline.music.playermp3.javabean.response.child.ResponseSingle;
import com.offline.music.playermp3.max.MaxManager;
import com.offline.music.playermp3.max.onAdAfterAction;
import com.offline.music.playermp3.media3.MyDownloadService;
import com.offline.music.playermp3.media3.MyMediaControllerManager;
import com.offline.music.playermp3.media3.MyPlayCacheManager;
import com.offline.music.playermp3.objectbox.ObjectBoxManager;
import com.offline.music.playermp3.ui.activity.viewmodel.VMPlay;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
public class PlayActivity extends BaseActivity<ActivityPlayBinding> 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<BoxDownloadSong> downloadSongList = new ArrayList<>();
@Override
protected ActivityPlayBinding getViewBinding() {
return ActivityPlayBinding.inflate(getLayoutInflater());
}
@Override
protected void onCreateInit() {
MaxManager.startShowMaxAd(this, new onAdAfterAction() {
@Override
public void onAction() {
}
});
MaxManager.onLoadAd();
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);
// 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<List<ResponsePlayListInfo>>() {
@Override
public void onChanged(List<ResponsePlayListInfo> 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<List<ResponsePlayListInfo>>() {
@Override
public void onChanged(List<ResponsePlayListInfo> 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<CustomerUrlInfo>() {
@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<List<ResponsePlayListInfo>>() {
@Override
public void onChanged(List<ResponsePlayListInfo> 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<ResponsePlayListInfo> 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);
vmApplication.downloadChange.observe(this, new Observer<Pair<Boolean, Download>>() {
@Override
public void onChanged(Pair<Boolean, Download> booleanDownloadPair) {
}
});
}
/**
* 更新播放进度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<Drawable>() {
@Override
public boolean onLoadFailed(@Nullable GlideException e, @Nullable Object model, @NonNull Target<Drawable> 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<Drawable> 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)) {
MaxManager.startShowMaxAd(this, new onAdAfterAction() {
@Override
public void onAction() {
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)) {
MaxManager.startShowMaxAd(this, new onAdAfterAction() {
@Override
public void onAction() {
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)) {
MaxManager.startShowMaxAd(this, new onAdAfterAction() {
@Override
public void onAction() {
if (vb.imDownload.isSelected()) {
//已经下载
Toast.makeText(PlayActivity.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(
PlayActivity.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<ResponsePlayListInfo> 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<Drawable>() {
@Override
public boolean onLoadFailed(@Nullable GlideException e, @Nullable Object model, @NonNull Target<Drawable> target, boolean isFirstResource) {
CommonUtils.LogMsg(e.getMessage());
return false;
}
@OptIn(markerClass = UnstableApi.class)
@Override
public boolean onResourceReady(@NonNull Drawable resource, @NonNull Object model, Target<Drawable> target, @NonNull DataSource dataSource, boolean isFirstResource) {
return false;
}
})
.into(vb.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<List<Download>>() {
@Override
public void onChanged(List<Download> downloads) {
if (downloads == null || downloads.size() == 0) {
vb.downloadPb.setVisibility(View.GONE);
vb.imDownload.setVisibility(View.VISIBLE);
vb.imDownload.setSelected(false);
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);
}else {
vb.downloadPb.setVisibility(View.GONE);
vb.imDownload.setVisibility(View.VISIBLE);
vb.imDownload.setSelected(false);
}
}
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();
}
}
});
}
}

View File

@ -0,0 +1,169 @@
package com.offline.music.playermp3.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.offline.music.playermp3.R;
import com.offline.music.playermp3.adapter.AdapterResult;
import com.offline.music.playermp3.api.HomeItemClickListener;
import com.offline.music.playermp3.databinding.ActivityResultListBinding;
import com.offline.music.playermp3.helper.CommonUtils;
import com.offline.music.playermp3.helper.MyValue;
import com.offline.music.playermp3.javabean.response.ResponseResult;
import com.offline.music.playermp3.javabean.response.child.ResponseResultListChild;
import com.offline.music.playermp3.javabean.response.child.ResponseSingle;
import com.offline.music.playermp3.max.MaxManager;
import com.offline.music.playermp3.ui.fragmnt.viewmodel.VMResultList;
public class ResultListActivity extends BaseActivity<ActivityResultListBinding> implements HomeItemClickListener {
private VMResultList vm;
private String key;
@Override
protected ActivityResultListBinding getViewBinding() {
return ActivityResultListBinding.inflate(getLayoutInflater());
}
@Override
protected void onCreateInit() {
MaxManager.onLoadAd();
vm = getActivityScopeViewModel(VMResultList.class);
key = getIntent().getStringExtra(MyValue.KEY_SEARCH_RESULT_BROWSER_ID);
vm.getList(key);
vm.data.observe(this, new Observer<ResponseResult>() {
@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<Drawable>() {
@Override
public boolean onLoadFailed(@Nullable GlideException e, @Nullable Object model, @NonNull Target<Drawable> target, boolean isFirstResource) {
CommonUtils.LogMsg(e.getMessage());
return false;
}
@Override
public boolean onResourceReady(@NonNull Drawable resource, @NonNull Object model, Target<Drawable> 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;
}
}
}
}

View File

@ -0,0 +1,60 @@
package com.offline.music.playermp3.ui.activity.viewmodel;
import android.util.Pair;
import androidx.annotation.OptIn;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModel;
import androidx.media3.common.util.UnstableApi;
import androidx.media3.exoplayer.offline.Download;
import com.offline.music.playermp3.javabean.response.ResponsePlayListInfo;
import java.util.List;
public class VMApplication extends ViewModel {
private MutableLiveData<List<ResponsePlayListInfo>> _playList = new MutableLiveData<List<ResponsePlayListInfo>>();
public LiveData<List<ResponsePlayListInfo>> playList = _playList;
private MutableLiveData<Integer> _playStatus = new MutableLiveData<Integer>();
public LiveData<Integer> playStatus = _playStatus;
private MutableLiveData<List<Download>> _downloadData = new MutableLiveData<>();
public LiveData<List<Download>> downloadData = _downloadData;
private MutableLiveData<Pair<Boolean,Download>> _downloadChange = new MutableLiveData<>();
public LiveData<Pair<Boolean,Download>> downloadChange = _downloadChange;
/**
* 重置播放列表
*
* @param list
*/
public void reSetPlayList(List<ResponsePlayListInfo> list) {
_playList.setValue(list);
}
public void setPlayStatus(int status) {
_playStatus.setValue(status);
}
@OptIn(markerClass = UnstableApi.class)
public void setDownloadData(List<Download> downloadList) {
_downloadData.setValue(downloadList);
}
@OptIn(markerClass = UnstableApi.class)
public void setDownloadChange(Pair<Boolean,Download> download) {
_downloadChange.setValue(download);
}
}

View File

@ -0,0 +1,90 @@
package com.offline.music.playermp3.ui.activity.viewmodel;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModel;
import com.offline.music.playermp3.api.RequestListener;
import com.offline.music.playermp3.helper.CommonUtils;
import com.offline.music.playermp3.javabean.CustomerUrlInfo;
import com.offline.music.playermp3.javabean.response.ResponsePlayListInfo;
import com.offline.music.playermp3.javabean.response.ResponsePlayUrl;
import com.offline.music.playermp3.media3.MyMediaControllerManager;
import com.offline.music.playermp3.network.JsonHelper;
import com.offline.music.playermp3.network.RetrofitManager;
import org.json.JSONObject;
import java.util.List;
import okhttp3.ResponseBody;
public class VMPlay extends ViewModel {
private MutableLiveData<List<ResponsePlayListInfo>> _playList = new MutableLiveData<List<ResponsePlayListInfo>>();
public LiveData<List<ResponsePlayListInfo>> playList = _playList;
private MutableLiveData<CustomerUrlInfo> _playUrlMutableLiveData = new MutableLiveData<CustomerUrlInfo>();
public LiveData<CustomerUrlInfo> playUrlLiveData = _playUrlMutableLiveData;
public void getPlayMusicList(String playlistId, String videoId, String params, String musicVideoType) {
RetrofitManager.getInstance().getPlayList(params, playlistId, videoId, musicVideoType, new RequestListener<ResponseBody>() {
@Override
public void onFail(String errorMsg) {
// TODO: 2024/10/8 播放列表拉取失败
_playList.setValue(null);
}
@Override
public void onSuccess(JSONObject data) {
// JSONObject jsonObject = CommonUtils.toJsonObject(data);
if (data != null) {
List<ResponsePlayListInfo> responsePlayListInfos = JsonHelper.ResolvePlayListJson(data);
_playList.setValue(responsePlayListInfos);
} else {
_playList.setValue(null);
}
}
});
}
public void getPlayUrl(String videoId, int playListIndex,boolean playNow) {
CustomerUrlInfo customerUrlInfo = new CustomerUrlInfo();
customerUrlInfo.setNeedPlayNow(playNow);
customerUrlInfo.setVideoId(videoId);
customerUrlInfo.setPlayMusicIndex(playListIndex);
RetrofitManager.getInstance().getPlayUrl(videoId, new RequestListener<ResponseBody>() {
@Override
public void onFail(String errorMsg) {
// CommonUtils.LogMsg("-------------此次网络请求失败 playNow="+playNow +"--playListIndex="+playListIndex);
_playUrlMutableLiveData.setValue(customerUrlInfo);
}
@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;
}
MyMediaControllerManager.getInstance().UpdateAudioUrl(responsePlayUrl,playListIndex);
customerUrlInfo.setPlayUrl(responsePlayUrl);
}
_playUrlMutableLiveData.setValue(customerUrlInfo);
}
});
}
}

Some files were not shown because too many files have changed in this diff Show More