Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
56c0d99b6c | ||
|
|
40a897c38b | ||
|
|
99bde9160e | ||
|
|
6412c21786 | ||
|
|
6666abcf17 | ||
|
|
2384b4d09d | ||
|
|
8d3921de0d | ||
|
|
7c739312e9 | ||
|
|
1b27a928b9 | ||
|
|
872a535cc0 | ||
|
|
7349c58e6d | ||
|
|
c62e38eb7e |
BIN
.safedk/api/SafeDKAndroid-6.6.2.jar
Normal file
BIN
.safedk/api/SafeDKAndroid-6.6.2.jar
Normal file
Binary file not shown.
BIN
.safedk/dex/SafeDKAndroid-6.6.2.dex
Normal file
BIN
.safedk/dex/SafeDKAndroid-6.6.2.dex
Normal file
Binary file not shown.
BIN
.safedk/dex/android-support-multidex.dex
Normal file
BIN
.safedk/dex/android-support-multidex.dex
Normal file
Binary file not shown.
1
.safedk/list.enc
Normal file
1
.safedk/list.enc
Normal file
File diff suppressed because one or more lines are too long
36
.safedk/proguard-safedk.pro
Normal file
36
.safedk/proguard-safedk.pro
Normal 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.
@ -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
29
app/google-services.json
Normal 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"
|
||||
}
|
||||
57
app/proguard-rules.pro
vendored
57
app/proguard-rules.pro
vendored
@ -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 { *; }
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@ -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"
|
||||
|
||||
@ -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));
|
||||
}
|
||||
} );
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -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());
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@ -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());
|
||||
}
|
||||
}
|
||||
@ -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());
|
||||
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
@ -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){
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
@ -0,0 +1,9 @@
|
||||
package com.offline.music.playermp3.api;
|
||||
|
||||
public interface MediaControllerStatusListener {
|
||||
|
||||
|
||||
void onMediaControllerComplete(boolean isOk);
|
||||
|
||||
|
||||
}
|
||||
@ -0,0 +1,5 @@
|
||||
package com.offline.music.playermp3.api;
|
||||
|
||||
public interface OnHasUrlAction {
|
||||
void onHasUrl();
|
||||
}
|
||||
@ -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) ;
|
||||
}
|
||||
@ -0,0 +1,7 @@
|
||||
package com.offline.music.playermp3.api;
|
||||
|
||||
import com.offline.music.playermp3.javabean.CustomerDownload;
|
||||
|
||||
public interface onCheckDownload {
|
||||
void onHasDownload(CustomerDownload customerDownload);
|
||||
}
|
||||
@ -0,0 +1,5 @@
|
||||
package com.offline.music.playermp3.api;
|
||||
|
||||
public interface onImageColorListener {
|
||||
void onImageColor(int color);
|
||||
}
|
||||
@ -0,0 +1,5 @@
|
||||
package com.offline.music.playermp3.api;
|
||||
|
||||
public interface onPlayNextListener {
|
||||
void onPlayNext(boolean hasNext);
|
||||
}
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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:45:07
|
||||
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();
|
||||
//
|
||||
// }
|
||||
}
|
||||
@ -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;
|
||||
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -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;
|
||||
// }
|
||||
}
|
||||
@ -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_MEDIUM、AUDIO_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;
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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();
|
||||
}
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@ -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);
|
||||
|
||||
|
||||
}
|
||||
@ -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")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -0,0 +1,6 @@
|
||||
package com.offline.music.playermp3.max;
|
||||
|
||||
public interface onAdAfterAction {
|
||||
|
||||
void onAction();
|
||||
}
|
||||
@ -0,0 +1,6 @@
|
||||
package com.offline.music.playermp3.max;
|
||||
|
||||
public interface onAdStatusListener {
|
||||
|
||||
void onAdStatus(int type);
|
||||
}
|
||||
@ -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;
|
||||
// }
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
@ -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
@ -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);
|
||||
|
||||
}
|
||||
@ -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() {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -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));
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@ -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.0(API 级别 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");
|
||||
}
|
||||
|
||||
// 跳转到指定播放位置
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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() {
|
||||
|
||||
@ -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();
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@ -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) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
@ -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
Loading…
Reference in New Issue
Block a user