V5 Push
Here's a list of changes/features: https://github.com/RetroMusicPlayer/RetroMusicPlayer/releases/tag/v5.0 Internal Changes: 1) Migrated to ViewBinding 2) Migrated to Glide V4 3) Migrated to kotlin version of Material Dialogs
This commit is contained in:
parent
fc42767031
commit
bce6dbfa27
421 changed files with 13285 additions and 5757 deletions
|
@ -1,31 +1,29 @@
|
||||||
apply plugin: 'com.android.application'
|
apply plugin: 'com.android.application'
|
||||||
apply plugin: 'kotlin-android'
|
apply plugin: 'kotlin-android'
|
||||||
apply plugin: 'kotlin-android-extensions'
|
|
||||||
apply plugin: 'kotlin-kapt'
|
apply plugin: 'kotlin-kapt'
|
||||||
apply plugin: "androidx.navigation.safeargs.kotlin"
|
apply plugin: "androidx.navigation.safeargs.kotlin"
|
||||||
|
apply plugin: 'kotlin-parcelize'
|
||||||
|
|
||||||
android {
|
android {
|
||||||
compileSdkVersion 29
|
compileSdkVersion 31
|
||||||
buildToolsVersion = '29.0.3'
|
buildToolsVersion = '29.0.3'
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
minSdkVersion 21
|
minSdkVersion 21
|
||||||
targetSdkVersion 29
|
targetSdkVersion 29
|
||||||
|
|
||||||
renderscriptTargetApi 29 //must match target sdk and build tools
|
renderscriptTargetApi 29//must match target sdk and build tools
|
||||||
vectorDrawables.useSupportLibrary = true
|
vectorDrawables.useSupportLibrary = true
|
||||||
|
|
||||||
applicationId "code.name.monkey.retromusic"
|
applicationId "code.name.monkey.retromusic"
|
||||||
versionCode 10503
|
versionCode 10519
|
||||||
versionName '4.0.010' + "_" + getDate()
|
versionName '5.0.0' + "_" + getDate()
|
||||||
|
|
||||||
multiDexEnabled true
|
|
||||||
|
|
||||||
buildConfigField("String", "GOOGLE_PLAY_LICENSING_KEY", "\"${getProperty(getProperties('../public.properties'), 'GOOGLE_PLAY_LICENSE_KEY')}\"")
|
buildConfigField("String", "GOOGLE_PLAY_LICENSING_KEY", "\"${getProperty(getProperties('../public.properties'), 'GOOGLE_PLAY_LICENSE_KEY')}\"")
|
||||||
}
|
}
|
||||||
signingConfigs {
|
signingConfigs {
|
||||||
release {
|
release {
|
||||||
Properties properties = getProperties('/Users/apple/Documents/Github/music.jks')
|
Properties properties = getProperties('retro.properties')
|
||||||
storeFile file(getProperty(properties, 'storeFile'))
|
storeFile file(getProperty(properties, 'storeFile'))
|
||||||
keyAlias getProperty(properties, 'keyAlias')
|
keyAlias getProperty(properties, 'keyAlias')
|
||||||
storePassword getProperty(properties, 'storePassword')
|
storePassword getProperty(properties, 'storePassword')
|
||||||
|
@ -36,7 +34,6 @@ android {
|
||||||
release {
|
release {
|
||||||
//debuggable true
|
//debuggable true
|
||||||
minifyEnabled true
|
minifyEnabled true
|
||||||
//shrinkResources true
|
|
||||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
||||||
signingConfig signingConfigs.release
|
signingConfig signingConfigs.release
|
||||||
}
|
}
|
||||||
|
@ -46,6 +43,10 @@ android {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
buildFeatures{
|
||||||
|
viewBinding true
|
||||||
|
}
|
||||||
|
|
||||||
packagingOptions {
|
packagingOptions {
|
||||||
exclude 'META-INF/LICENSE'
|
exclude 'META-INF/LICENSE'
|
||||||
exclude 'META-INF/NOTICE'
|
exclude 'META-INF/NOTICE'
|
||||||
|
@ -67,17 +68,11 @@ android {
|
||||||
configurations.all {
|
configurations.all {
|
||||||
resolutionStrategy.force 'com.google.code.findbugs:jsr305:1.3.9'
|
resolutionStrategy.force 'com.google.code.findbugs:jsr305:1.3.9'
|
||||||
}
|
}
|
||||||
androidExtensions {
|
|
||||||
experimental = true
|
|
||||||
}
|
|
||||||
kapt {
|
|
||||||
generateStubs = true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def getProperties(String fileName) {
|
def getProperties(String fileName) {
|
||||||
final Properties properties = new Properties()
|
final Properties properties = new Properties()
|
||||||
def file = file(fileName)
|
def file = rootProject.file(fileName)
|
||||||
if (file.exists()) {
|
if (file.exists()) {
|
||||||
file.withInputStream { stream -> properties.load(stream) }
|
file.withInputStream { stream -> properties.load(stream) }
|
||||||
}
|
}
|
||||||
|
@ -95,76 +90,76 @@ static def getDate() {
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation project(':appthemehelper')
|
implementation project(':appthemehelper')
|
||||||
implementation 'androidx.multidex:multidex:2.0.1'
|
|
||||||
implementation "androidx.gridlayout:gridlayout:1.0.0"
|
implementation "androidx.gridlayout:gridlayout:1.0.0"
|
||||||
implementation "androidx.cardview:cardview:1.0.0"
|
implementation "androidx.cardview:cardview:1.0.0"
|
||||||
implementation 'androidx.appcompat:appcompat:1.2.0'
|
implementation 'androidx.appcompat:appcompat:1.3.1'
|
||||||
implementation 'androidx.annotation:annotation:1.1.0'
|
implementation 'androidx.annotation:annotation:1.2.0'
|
||||||
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
|
implementation 'androidx.constraintlayout:constraintlayout:2.1.0'
|
||||||
implementation 'androidx.recyclerview:recyclerview:1.1.0'
|
implementation 'androidx.recyclerview:recyclerview:1.2.1'
|
||||||
implementation 'androidx.preference:preference-ktx:1.1.1'
|
implementation 'androidx.preference:preference-ktx:1.1.1'
|
||||||
implementation 'androidx.core:core-ktx:1.3.2'
|
implementation 'androidx.core:core-ktx:1.6.0'
|
||||||
implementation 'androidx.fragment:fragment-ktx:1.2.5'
|
|
||||||
implementation 'androidx.palette:palette-ktx:1.0.0'
|
implementation 'androidx.palette:palette-ktx:1.0.0'
|
||||||
|
|
||||||
def nav_version = "2.3.2"
|
//Cast Dependencies
|
||||||
|
implementation 'androidx.mediarouter:mediarouter:1.2.5'
|
||||||
|
implementation 'com.google.android.gms:play-services-cast-framework:20.0.0'
|
||||||
|
//WebServer by NanoHttpd
|
||||||
|
implementation "org.nanohttpd:nanohttpd:2.3.1"
|
||||||
|
|
||||||
|
def nav_version = "2.4.0-alpha07"
|
||||||
implementation "androidx.navigation:navigation-runtime-ktx:$nav_version"
|
implementation "androidx.navigation:navigation-runtime-ktx:$nav_version"
|
||||||
implementation "androidx.navigation:navigation-fragment-ktx:$nav_version"
|
implementation "androidx.navigation:navigation-fragment-ktx:$nav_version"
|
||||||
implementation "androidx.navigation:navigation-ui-ktx:$nav_version"
|
implementation "androidx.navigation:navigation-ui-ktx:$nav_version"
|
||||||
|
|
||||||
def room_version = "2.2.5"
|
def room_version = "2.3.0"
|
||||||
implementation "androidx.room:room-runtime:$room_version"
|
implementation "androidx.room:room-runtime:$room_version"
|
||||||
implementation "androidx.room:room-ktx:$room_version"
|
implementation "androidx.room:room-ktx:$room_version"
|
||||||
kapt "androidx.room:room-compiler:$room_version"
|
kapt "androidx.room:room-compiler:$room_version"
|
||||||
|
|
||||||
def lifecycle_version = "2.2.0"
|
def lifecycle_version = "2.3.1"
|
||||||
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"
|
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"
|
||||||
implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version"
|
implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version"
|
||||||
implementation "androidx.lifecycle:lifecycle-common-java8:$lifecycle_version"
|
implementation "androidx.lifecycle:lifecycle-common-java8:$lifecycle_version"
|
||||||
|
|
||||||
implementation 'com.google.android.play:core-ktx:1.8.1'
|
implementation 'com.google.android.play:core-ktx:1.8.1'
|
||||||
implementation 'com.google.android.material:material:1.3.0-alpha04'
|
implementation 'com.google.android.material:material:1.5.0-alpha03'
|
||||||
|
|
||||||
def retrofit_version = '2.9.0'
|
def retrofit_version = '2.9.0'
|
||||||
implementation "com.squareup.retrofit2:retrofit:$retrofit_version"
|
implementation "com.squareup.retrofit2:retrofit:$retrofit_version"
|
||||||
implementation "com.squareup.retrofit2:converter-gson:$retrofit_version"
|
implementation "com.squareup.retrofit2:converter-gson:$retrofit_version"
|
||||||
implementation 'com.squareup.okhttp3:logging-interceptor:3.6.0'
|
implementation 'com.squareup.okhttp3:logging-interceptor:5.0.0-alpha.2'
|
||||||
|
|
||||||
def material_dialog_version = "0.9.6.0"
|
def material_dialog_version = "3.3.0"
|
||||||
implementation "com.afollestad.material-dialogs:core:$material_dialog_version"
|
implementation "com.afollestad.material-dialogs:core:$material_dialog_version"
|
||||||
implementation "com.afollestad.material-dialogs:commons:$material_dialog_version"
|
implementation "com.afollestad.material-dialogs:input:$material_dialog_version"
|
||||||
|
implementation "com.afollestad.material-dialogs:color:$material_dialog_version"
|
||||||
|
implementation "com.afollestad.material-dialogs:bottomsheets:$material_dialog_version"
|
||||||
|
//noinspection GradleDependency
|
||||||
implementation 'com.afollestad:material-cab:0.1.12'
|
implementation 'com.afollestad:material-cab:0.1.12'
|
||||||
|
|
||||||
def kotlin_coroutines_version = "1.3.8"
|
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
|
||||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.4.10"
|
|
||||||
|
def kotlin_coroutines_version = "1.5.1"
|
||||||
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$kotlin_coroutines_version"
|
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$kotlin_coroutines_version"
|
||||||
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$kotlin_coroutines_version"
|
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$kotlin_coroutines_version"
|
||||||
|
|
||||||
def koin_version = "2.1.5"
|
def koin_version = "2.1.5"
|
||||||
implementation "org.koin:koin-core:$koin_version"
|
implementation "org.koin:koin-core:$koin_version"
|
||||||
implementation "org.koin:koin-core-ext:$koin_version"
|
|
||||||
implementation "org.koin:koin-androidx-scope:$koin_version"
|
|
||||||
implementation "org.koin:koin-androidx-viewmodel:$koin_version"
|
implementation "org.koin:koin-androidx-viewmodel:$koin_version"
|
||||||
implementation "org.koin:koin-androidx-fragment:$koin_version"
|
|
||||||
implementation "org.koin:koin-androidx-ext:$koin_version"
|
|
||||||
|
|
||||||
implementation 'com.github.bumptech.glide:glide:3.8.0'
|
implementation 'com.github.bumptech.glide:glide:4.12.0'
|
||||||
implementation 'com.github.bumptech.glide:okhttp3-integration:1.5.0'
|
kapt 'com.github.bumptech.glide:compiler:4.12.0'
|
||||||
|
implementation 'com.github.bumptech.glide:okhttp3-integration:4.12.0'
|
||||||
|
|
||||||
implementation 'com.h6ah4i.android.widget.advrecyclerview:advrecyclerview:1.0.0'
|
implementation 'com.h6ah4i.android.widget.advrecyclerview:advrecyclerview:1.0.0'
|
||||||
|
|
||||||
implementation 'org.eclipse.mylyn.github:org.eclipse.egit.github.core:3.4.0.201406110918-r'
|
implementation 'org.eclipse.mylyn.github:org.eclipse.egit.github.core:3.4.0.201406110918-r'
|
||||||
implementation 'com.github.ksoichiro:android-observablescrollview:1.6.0'
|
implementation 'org.bitbucket.ijabz:jaudiotagger:2.2.5'
|
||||||
implementation 'com.github.kabouzeid:recyclerview-fastscroll:1.9-kmod'
|
|
||||||
implementation 'com.github.AdrienPoupa:jaudiotagger:2.2.3'
|
|
||||||
implementation 'com.anjlab.android.iab.v3:library:1.1.0'
|
implementation 'com.anjlab.android.iab.v3:library:1.1.0'
|
||||||
implementation 'com.r0adkll:slidableactivity:2.1.0'
|
implementation 'com.r0adkll:slidableactivity:2.1.0'
|
||||||
implementation 'com.heinrichreimersoftware:material-intro:1.6'
|
implementation 'com.heinrichreimersoftware:material-intro:2.0.0'
|
||||||
implementation 'com.github.dhaval2404:imagepicker:1.7.1'
|
implementation 'com.github.dhaval2404:imagepicker:1.7.1'
|
||||||
implementation 'org.jsoup:jsoup:1.11.1'
|
implementation 'me.zhanghai.android.fastscroll:library:1.1.7'
|
||||||
implementation 'me.zhanghai.android.fastscroll:library:1.1.0'
|
|
||||||
implementation 'me.jorgecastillo:androidcolorx:0.2.0'
|
|
||||||
implementation 'org.jsoup:jsoup:1.11.1'
|
|
||||||
debugImplementation 'com.amitshekhar.android:debug-db:1.0.6'
|
debugImplementation 'com.amitshekhar.android:debug-db:1.0.6'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
16
app/proguard-rules.pro
vendored
16
app/proguard-rules.pro
vendored
|
@ -23,6 +23,9 @@
|
||||||
# If you keep the line number information, uncomment this to
|
# If you keep the line number information, uncomment this to
|
||||||
# hide the original source file name.
|
# hide the original source file name.
|
||||||
#-renamesourcefileattribute SourceFile
|
#-renamesourcefileattribute SourceFile
|
||||||
|
-keepnames class **
|
||||||
|
-keepnames class ** { *; }
|
||||||
|
-keepattributes SourceFile,LineNumberTable
|
||||||
|
|
||||||
-dontwarn java.lang.invoke.*
|
-dontwarn java.lang.invoke.*
|
||||||
-dontwarn **$$Lambda$*
|
-dontwarn **$$Lambda$*
|
||||||
|
@ -34,9 +37,10 @@
|
||||||
|
|
||||||
# Glide
|
# Glide
|
||||||
-keep public class * implements com.bumptech.glide.module.GlideModule
|
-keep public class * implements com.bumptech.glide.module.GlideModule
|
||||||
-keep public enum com.bumptech.glide.load.resource.bitmap.ImageHeaderParser$** {
|
-keep public class * extends com.bumptech.glide.module.AppGlideModule
|
||||||
**[] $VALUES;
|
-keep public enum com.bumptech.glide.load.ImageHeaderParser$** {
|
||||||
public *;
|
**[] $VALUES;
|
||||||
|
public *;
|
||||||
}
|
}
|
||||||
|
|
||||||
# OkHttp
|
# OkHttp
|
||||||
|
@ -47,8 +51,6 @@
|
||||||
|
|
||||||
#-dontwarn
|
#-dontwarn
|
||||||
#-ignorewarnings
|
#-ignorewarnings
|
||||||
-dontshrink
|
|
||||||
-dontobfuscate
|
|
||||||
|
|
||||||
-dontwarn org.jaudiotagger.**
|
-dontwarn org.jaudiotagger.**
|
||||||
-keep class org.jaudiotagger.** { *; }
|
-keep class org.jaudiotagger.** { *; }
|
||||||
|
@ -60,3 +62,7 @@
|
||||||
-keep class * extends androidx.fragment.app.Fragment{}
|
-keep class * extends androidx.fragment.app.Fragment{}
|
||||||
-keepnames class * extends android.os.Parcelable
|
-keepnames class * extends android.os.Parcelable
|
||||||
-keepnames class * extends java.io.Serializable
|
-keepnames class * extends java.io.Serializable
|
||||||
|
-keep class code.name.monkey.retromusic.network.model.** { *; }
|
||||||
|
-keep class code.name.monkey.retromusic.model.CategoryInfo { *; }
|
||||||
|
-keep class com.google.android.material.bottomsheet.** { *; }
|
||||||
|
-keep class code.name.monkey.retromusic.Constants { *; }
|
|
@ -6,6 +6,11 @@
|
||||||
<item name="fontFamily">@font/sans</item>
|
<item name="fontFamily">@font/sans</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
<style name="TextViewNormalCompress" parent="TextAppearance.MaterialComponents.Caption">
|
||||||
|
<item name="android:textSize">14sp</item>
|
||||||
|
<item name="fontFamily">@font/sans</item>
|
||||||
|
</style>
|
||||||
|
|
||||||
<style name="TextViewHeadline4" parent="TextAppearance.MaterialComponents.Headline4">
|
<style name="TextViewHeadline4" parent="TextAppearance.MaterialComponents.Headline4">
|
||||||
<item name="fontFamily">@font/sans</item>
|
<item name="fontFamily">@font/sans</item>
|
||||||
</style>
|
</style>
|
||||||
|
@ -91,6 +96,11 @@
|
||||||
|
|
||||||
<style name="circleImageView" parent="">
|
<style name="circleImageView" parent="">
|
||||||
<item name="cornerFamily">rounded</item>
|
<item name="cornerFamily">rounded</item>
|
||||||
<item name="cornerSize">5%</item>
|
<item name="cornerSize">16dp</item>
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<style name="BottomSheetItemTextAppearance" parent="Widget.MaterialComponents.BottomNavigationView.Colored">
|
||||||
|
<item name="android:textSize">13sp</item>
|
||||||
|
<item name="fontFamily">@font/sans</item>
|
||||||
</style>
|
</style>
|
||||||
</resources>
|
</resources>
|
|
@ -5,9 +5,10 @@
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.BLUETOOTH" />
|
<uses-permission android:name="android.permission.BLUETOOTH" />
|
||||||
<uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
|
<uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
|
||||||
|
<uses-permission android:name="android.permission.VIBRATE" />
|
||||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
|
||||||
<uses-permission android:name="android.permission.INTERNET" />
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||||
<uses-permission android:name="android.permission.BROADCAST_STICKY" />
|
<uses-permission android:name="android.permission.BROADCAST_STICKY" />
|
||||||
|
@ -29,7 +30,7 @@
|
||||||
android:roundIcon="@mipmap/ic_launcher_round"
|
android:roundIcon="@mipmap/ic_launcher_round"
|
||||||
android:supportsRtl="true"
|
android:supportsRtl="true"
|
||||||
android:theme="@style/Theme.RetroMusic.FollowSystem"
|
android:theme="@style/Theme.RetroMusic.FollowSystem"
|
||||||
android:usesCleartextTraffic="false"
|
android:usesCleartextTraffic="true"
|
||||||
tools:ignore="AllowBackup,GoogleAppIndexingWarning"
|
tools:ignore="AllowBackup,GoogleAppIndexingWarning"
|
||||||
tools:targetApi="m">
|
tools:targetApi="m">
|
||||||
<activity
|
<activity
|
||||||
|
@ -131,6 +132,20 @@
|
||||||
android:name=".activities.saf.SAFGuideActivity"
|
android:name=".activities.saf.SAFGuideActivity"
|
||||||
android:theme="@style/Theme.Intro" />
|
android:theme="@style/Theme.Intro" />
|
||||||
|
|
||||||
|
<activity
|
||||||
|
android:name=".cast.ExpandedControlsActivity"
|
||||||
|
android:label="@string/app_name"
|
||||||
|
android:launchMode="singleTask"
|
||||||
|
android:theme="@style/Theme.AppCompat.NoActionBar"
|
||||||
|
android:screenOrientation="portrait">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.MAIN"/>
|
||||||
|
</intent-filter>
|
||||||
|
<meta-data
|
||||||
|
android:name="android.support.PARENT_ACTIVITY"
|
||||||
|
android:value=".activities.MainActivity"/>
|
||||||
|
</activity>
|
||||||
|
|
||||||
<provider
|
<provider
|
||||||
android:name=".misc.GenericFileProvider"
|
android:name=".misc.GenericFileProvider"
|
||||||
android:authorities="${applicationId}.provider"
|
android:authorities="${applicationId}.provider"
|
||||||
|
@ -246,16 +261,24 @@
|
||||||
android:name="com.lge.support.SPLIT_WINDOW"
|
android:name="com.lge.support.SPLIT_WINDOW"
|
||||||
android:value="true" />
|
android:value="true" />
|
||||||
|
|
||||||
<meta-data
|
|
||||||
android:name="code.name.monkey.retromusic.glide.RetroMusicGlideModule"
|
|
||||||
android:value="GlideModule" />
|
|
||||||
|
|
||||||
<meta-data
|
|
||||||
android:name="com.bumptech.glide.integration.okhttp3.OkHttpGlideModule"
|
|
||||||
android:value="GlideModule" />
|
|
||||||
|
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="com.android.vending.splits.required"
|
android:name="com.android.vending.splits.required"
|
||||||
android:value="true" />
|
android:value="true" />
|
||||||
|
|
||||||
|
<!-- Android Auto -->
|
||||||
|
<!-- <meta-data
|
||||||
|
android:name="com.google.android.gms.car.application"
|
||||||
|
android:resource="@xml/automotive_app_desc"/>
|
||||||
|
<meta-data
|
||||||
|
android:name="com.google.android.gms.car.application.theme"
|
||||||
|
android:resource="@style/CarTheme" />
|
||||||
|
<meta-data
|
||||||
|
android:name="com.google.android.gms.car.notification.SmallIcon"
|
||||||
|
android:resource="@drawable/ic_notification"/>
|
||||||
|
-->
|
||||||
|
<!-- ChromeCast -->
|
||||||
|
<meta-data
|
||||||
|
android:name="com.google.android.gms.cast.framework.OPTIONS_PROVIDER_CLASS_NAME"
|
||||||
|
android:value="code.name.monkey.retromusic.cast.CastOptionsProvider" />
|
||||||
</application>
|
</application>
|
||||||
</manifest>
|
</manifest>
|
||||||
|
|
|
@ -6,14 +6,14 @@
|
||||||
"image": "https://i.imgur.com/AoVs9oj.jpg"
|
"image": "https://i.imgur.com/AoVs9oj.jpg"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Lennart Glamann",
|
"name": "Prathamesh More",
|
||||||
"summary": "Play Store Banner & Images",
|
"summary": "Developer",
|
||||||
"link": "https://t.me/FlixbusLennart",
|
"link": "https://prathameshmm02.github.io",
|
||||||
"image": "https://i.imgur.com/Q5Nsx1R.jpg"
|
"image": "https://i.imgur.com/ZHoOrHx.jpg"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Daksh P. Jain",
|
"name": "Daksh P. Jain",
|
||||||
"summary": "Support Representative & Moderator",
|
"summary": "Website & GitHub Maintainer",
|
||||||
"link": "https://daksh.eu.org",
|
"link": "https://daksh.eu.org",
|
||||||
"image": "https://i.imgur.com/fnYpg65.jpg"
|
"image": "https://i.imgur.com/fnYpg65.jpg"
|
||||||
},
|
},
|
||||||
|
@ -23,9 +23,15 @@
|
||||||
"link": "https://t.me/MilindGoel15",
|
"link": "https://t.me/MilindGoel15",
|
||||||
"image": "https://i.imgur.com/Bz4De21_d.jpg"
|
"image": "https://i.imgur.com/Bz4De21_d.jpg"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"name": "Lennart Glamann",
|
||||||
|
"summary": "Play Store Banner & Images",
|
||||||
|
"link": "https://t.me/FlixbusLennart",
|
||||||
|
"image": "https://i.imgur.com/Q5Nsx1R.jpg"
|
||||||
|
},
|
||||||
|
{
|
||||||
"name": "Haythem Gataa",
|
"name": "Haythem Gataa",
|
||||||
"summary": "App Logo Designer",
|
"summary": "App Logo & Banners",
|
||||||
"link": "https://dribbble.com/haythemgataa",
|
"link": "https://dribbble.com/haythemgataa",
|
||||||
"image": "https://i.imgur.com/g5RuIZq.jpg"
|
"image": "https://i.imgur.com/g5RuIZq.jpg"
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,9 +6,11 @@
|
||||||
word-wrap: break-word;
|
word-wrap: break-word;
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
div{
|
||||||
padding-left: 1rem;
|
margin: 20px 10px;
|
||||||
padding-right: 1rem;
|
padding: 10px;
|
||||||
|
border-radius: 10px;
|
||||||
|
box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.2);
|
||||||
}
|
}
|
||||||
|
|
||||||
h2 {
|
h2 {
|
||||||
|
@ -18,10 +20,7 @@
|
||||||
|
|
||||||
li {
|
li {
|
||||||
font-size: 0.85rem;
|
font-size: 0.85rem;
|
||||||
padding-top: 0.5rem;
|
padding: 0.5rem 0;
|
||||||
padding-left: 0;
|
|
||||||
padding-right: 0;
|
|
||||||
color: rgba(0, 0, 0, 0.8);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ul {
|
ul {
|
||||||
|
@ -44,39 +43,62 @@
|
||||||
margin-block-end: 0.5rem;
|
margin-block-end: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
h3 span {
|
h3 {
|
||||||
border-radius: 0.2rem;
|
margin: 10px 0px;
|
||||||
padding-left: 0.5rem;
|
|
||||||
padding-right: 0.5rem;
|
|
||||||
padding-top: 0.3rem;
|
|
||||||
padding-bottom: 0.3rem;
|
|
||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
}
|
}
|
||||||
{style-placeholder}
|
{style-placeholder}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<h5>April 30, 2020</h5>
|
<h2>
|
||||||
<h2>v3.5.110</h2>
|
<b>Clear the app if it crashes after updating</b>
|
||||||
<span class="tag"><i>Beta version</i></span>
|
</h2>
|
||||||
<h3><span class="colorHeader">What's New</span></h3>
|
<div>
|
||||||
<ul>
|
|
||||||
<li>Changed profile form image to icon</li>
|
<h5>September 06, 2021</h5>
|
||||||
<li>New what's new screen</li>
|
<h2>v5.0.0</h2>
|
||||||
<li>Added In-App language changer, where you can select language</li>
|
<h3>What's New</h3>
|
||||||
</ul>
|
<ul>
|
||||||
<h3><span class="colorHeader">Improved</span></h3>
|
<li>Added Chromecast support</li>
|
||||||
<ul>
|
<li>Added animated icons</li>
|
||||||
<li>Improved loading of Songs, Albums, Artists, Genres, Playlists</li>
|
<li>Added cross-fade (experimental)</li>
|
||||||
</ul>
|
<li>Added ability to remember the last tab</li>
|
||||||
|
<li>Added whitelisting songs</li>
|
||||||
|
<li>Added support for embedded synced lyrics</li>
|
||||||
|
<li>Added lyrics editor for normal and synced lyrics</li>
|
||||||
|
<li>Added playlist ordering</li>
|
||||||
|
<li>Added search filters</li>
|
||||||
|
<li>Added audio fade</li>
|
||||||
|
<li>Added Multi-select in album and artist details</li>
|
||||||
|
<li>Added SD card from folders tab</li>
|
||||||
|
<li>Added Synced lyrics in all themes</li>
|
||||||
|
<li>Added Swipe anywhere to change the song</li>
|
||||||
|
<li>Added album artist</li>
|
||||||
|
<li> Albums now show album artists instead of artists of the first song</li>
|
||||||
|
</ul>
|
||||||
|
<h3>Fixed</h3>
|
||||||
|
<ul>
|
||||||
|
<li> Fixed playlist preview images</li>
|
||||||
|
<li> Fixed language switching</li>
|
||||||
|
</ul>
|
||||||
|
<h3>Improved</h3>
|
||||||
|
<ul>
|
||||||
|
<li>Improved playlists tab</li>
|
||||||
|
<li> Improved genres tab</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
<!--<h3><span class="colorHeader">Bug fixes</span></h3>
|
<!--<h3><span class="colorHeader">Bug fixes</span></h3>
|
||||||
<ul>
|
<ul>
|
||||||
<li></li>
|
<li></li>
|
||||||
</ul>-->
|
</ul>-->
|
||||||
<p>*If you face any UI related issues you clear app data and cache, if itsnot working try to
|
|
||||||
uninstall and install
|
|
||||||
again. </p>
|
|
||||||
</body>
|
</body>
|
|
@ -14,8 +14,8 @@
|
||||||
*/
|
*/
|
||||||
package code.name.monkey.retromusic
|
package code.name.monkey.retromusic
|
||||||
|
|
||||||
|
import android.app.Application
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.multidex.MultiDexApplication
|
|
||||||
import code.name.monkey.appthemehelper.ThemeStore
|
import code.name.monkey.appthemehelper.ThemeStore
|
||||||
import code.name.monkey.appthemehelper.util.VersionUtils
|
import code.name.monkey.appthemehelper.util.VersionUtils
|
||||||
import code.name.monkey.retromusic.Constants.PRO_VERSION_PRODUCT_ID
|
import code.name.monkey.retromusic.Constants.PRO_VERSION_PRODUCT_ID
|
||||||
|
@ -25,7 +25,7 @@ import com.anjlab.android.iab.v3.TransactionDetails
|
||||||
import org.koin.android.ext.koin.androidContext
|
import org.koin.android.ext.koin.androidContext
|
||||||
import org.koin.core.context.startKoin
|
import org.koin.core.context.startKoin
|
||||||
|
|
||||||
class App : MultiDexApplication() {
|
class App : Application() {
|
||||||
|
|
||||||
lateinit var billingProcessor: BillingProcessor
|
lateinit var billingProcessor: BillingProcessor
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,8 @@ object Constants {
|
||||||
const val PRO_VERSION_PRODUCT_ID = "pro_version"
|
const val PRO_VERSION_PRODUCT_ID = "pro_version"
|
||||||
const val RATE_ON_GOOGLE_PLAY =
|
const val RATE_ON_GOOGLE_PLAY =
|
||||||
"https://play.google.com/store/apps/details?id=code.name.monkey.retromusic"
|
"https://play.google.com/store/apps/details?id=code.name.monkey.retromusic"
|
||||||
const val TRANSLATE = "https://github.com/RetroMusicPlayer/RetroMusicPlayer"
|
const val TRANSLATE = "https://crowdin.com/project/retromusicplayer"
|
||||||
|
const val WEBSITE = "https://retromusic.app"
|
||||||
const val GITHUB_PROJECT = "https://github.com/RetroMusicPlayer/RetroMusicPlayer"
|
const val GITHUB_PROJECT = "https://github.com/RetroMusicPlayer/RetroMusicPlayer"
|
||||||
const val TELEGRAM_CHANGE_LOG = "https://t.me/retromusiclog"
|
const val TELEGRAM_CHANGE_LOG = "https://t.me/retromusiclog"
|
||||||
const val USER_PROFILE = "profile.jpg"
|
const val USER_PROFILE = "profile.jpg"
|
||||||
|
@ -49,7 +50,7 @@ object Constants {
|
||||||
MediaStore.Audio.AudioColumns.ARTIST_ID, // 9
|
MediaStore.Audio.AudioColumns.ARTIST_ID, // 9
|
||||||
MediaStore.Audio.AudioColumns.ARTIST, // 10
|
MediaStore.Audio.AudioColumns.ARTIST, // 10
|
||||||
MediaStore.Audio.AudioColumns.COMPOSER, // 11
|
MediaStore.Audio.AudioColumns.COMPOSER, // 11
|
||||||
"album_artist" // 12
|
ALBUM_ARTIST // 12
|
||||||
)
|
)
|
||||||
const val NUMBER_OF_TOP_TRACKS = 99
|
const val NUMBER_OF_TOP_TRACKS = 99
|
||||||
}
|
}
|
||||||
|
@ -66,7 +67,7 @@ const val EXTRA_SONG_INFO = "extra_song_info"
|
||||||
const val DESATURATED_COLOR = "desaturated_color"
|
const val DESATURATED_COLOR = "desaturated_color"
|
||||||
const val BLACK_THEME = "black_theme"
|
const val BLACK_THEME = "black_theme"
|
||||||
const val KEEP_SCREEN_ON = "keep_screen_on"
|
const val KEEP_SCREEN_ON = "keep_screen_on"
|
||||||
const val TYPE_HOME_BANNER = "type_home_banner"
|
const val TOGGLE_HOME_BANNER = "toggle_home_banner"
|
||||||
const val NOW_PLAYING_SCREEN_ID = "now_playing_screen_id"
|
const val NOW_PLAYING_SCREEN_ID = "now_playing_screen_id"
|
||||||
const val CAROUSEL_EFFECT = "carousel_effect"
|
const val CAROUSEL_EFFECT = "carousel_effect"
|
||||||
const val COLORED_NOTIFICATION = "colored_notification"
|
const val COLORED_NOTIFICATION = "colored_notification"
|
||||||
|
@ -129,6 +130,7 @@ const val START_DIRECTORY = "start_directory"
|
||||||
const val RECENTLY_PLAYED_CUTOFF = "recently_played_interval"
|
const val RECENTLY_PLAYED_CUTOFF = "recently_played_interval"
|
||||||
const val LOCK_SCREEN = "lock_screen"
|
const val LOCK_SCREEN = "lock_screen"
|
||||||
const val ALBUM_ARTISTS_ONLY = "album_artists_only"
|
const val ALBUM_ARTISTS_ONLY = "album_artists_only"
|
||||||
|
const val ALBUM_ARTIST = "album_artist"
|
||||||
const val ALBUM_DETAIL_SONG_SORT_ORDER = "album_detail_song_sort_order"
|
const val ALBUM_DETAIL_SONG_SORT_ORDER = "album_detail_song_sort_order"
|
||||||
const val LYRICS_OPTIONS = "lyrics_tab_position"
|
const val LYRICS_OPTIONS = "lyrics_tab_position"
|
||||||
const val CHOOSE_EQUALIZER = "choose_equalizer"
|
const val CHOOSE_EQUALIZER = "choose_equalizer"
|
||||||
|
@ -138,3 +140,11 @@ const val SONG_GRID_STYLE = "song_grid_style"
|
||||||
const val PAUSE_ON_ZERO_VOLUME = "pause_on_zero_volume"
|
const val PAUSE_ON_ZERO_VOLUME = "pause_on_zero_volume"
|
||||||
const val FILTER_SONG = "filter_song"
|
const val FILTER_SONG = "filter_song"
|
||||||
const val EXPAND_NOW_PLAYING_PANEL = "expand_now_playing_panel"
|
const val EXPAND_NOW_PLAYING_PANEL = "expand_now_playing_panel"
|
||||||
|
const val EXTRA_ARTIST_NAME = "extra_artist_name"
|
||||||
|
const val TOGGLE_SUGGESTIONS = "toggle_suggestions"
|
||||||
|
const val AUDIO_FADE_DURATION = "audio_fade_duration"
|
||||||
|
const val CROSS_FADE_DURATION = "cross_fade_duration"
|
||||||
|
const val SHOW_LYRICS = "show_lyrics"
|
||||||
|
const val REMEMBER_LAST_TAB = "remember_last_tab"
|
||||||
|
const val LAST_USED_TAB = "last_used_tab"
|
||||||
|
const val WHITELIST_MUSIC = "whitelist_music"
|
||||||
|
|
|
@ -5,15 +5,20 @@ import android.content.ContextWrapper;
|
||||||
import android.content.res.Configuration;
|
import android.content.res.Configuration;
|
||||||
import android.content.res.Resources;
|
import android.content.res.Resources;
|
||||||
import android.os.LocaleList;
|
import android.os.LocaleList;
|
||||||
import code.name.monkey.appthemehelper.util.VersionUtils;
|
|
||||||
|
import com.google.android.gms.common.annotation.KeepName;
|
||||||
|
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
|
||||||
|
import code.name.monkey.appthemehelper.util.VersionUtils;
|
||||||
|
|
||||||
public class LanguageContextWrapper extends ContextWrapper {
|
public class LanguageContextWrapper extends ContextWrapper {
|
||||||
|
|
||||||
public LanguageContextWrapper(Context base) {
|
public LanguageContextWrapper(Context base) {
|
||||||
super(base);
|
super(base);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@KeepName
|
||||||
public static LanguageContextWrapper wrap(Context context, Locale newLocale) {
|
public static LanguageContextWrapper wrap(Context context, Locale newLocale) {
|
||||||
Resources res = context.getResources();
|
Resources res = context.getResources();
|
||||||
Configuration configuration = res.getConfiguration();
|
Configuration configuration = res.getConfiguration();
|
||||||
|
|
|
@ -3,6 +3,7 @@ package code.name.monkey.retromusic
|
||||||
import androidx.room.Room
|
import androidx.room.Room
|
||||||
import androidx.room.RoomDatabase
|
import androidx.room.RoomDatabase
|
||||||
import androidx.sqlite.db.SupportSQLiteDatabase
|
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||||
|
import code.name.monkey.retromusic.auto.AutoMusicProvider
|
||||||
import code.name.monkey.retromusic.db.BlackListStoreDao
|
import code.name.monkey.retromusic.db.BlackListStoreDao
|
||||||
import code.name.monkey.retromusic.db.BlackListStoreEntity
|
import code.name.monkey.retromusic.db.BlackListStoreEntity
|
||||||
import code.name.monkey.retromusic.db.PlaylistWithSongs
|
import code.name.monkey.retromusic.db.PlaylistWithSongs
|
||||||
|
@ -85,6 +86,18 @@ private val roomModule = module {
|
||||||
RealRoomRepository(get(), get(), get(), get(), get())
|
RealRoomRepository(get(), get(), get(), get(), get())
|
||||||
} bind RoomRepository::class
|
} bind RoomRepository::class
|
||||||
}
|
}
|
||||||
|
private val autoModule = module {
|
||||||
|
single {
|
||||||
|
AutoMusicProvider(androidContext(),
|
||||||
|
get(),
|
||||||
|
get(),
|
||||||
|
get(),
|
||||||
|
get(),
|
||||||
|
get(),
|
||||||
|
get()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
private val mainModule = module {
|
private val mainModule = module {
|
||||||
single {
|
single {
|
||||||
androidContext().contentResolver
|
androidContext().contentResolver
|
||||||
|
@ -167,10 +180,11 @@ private val viewModules = module {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
viewModel { (artistId: Long) ->
|
viewModel { (artistId: Long?, artistName: String?) ->
|
||||||
ArtistDetailsViewModel(
|
ArtistDetailsViewModel(
|
||||||
get(),
|
get(),
|
||||||
artistId
|
artistId,
|
||||||
|
artistName
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -189,4 +203,4 @@ private val viewModules = module {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val appModules = listOf(mainModule, dataModule, viewModules, networkModule, roomModule)
|
val appModules = listOf(mainModule, dataModule, autoModule, viewModules, networkModule, roomModule)
|
|
@ -4,8 +4,11 @@ import android.content.Context;
|
||||||
import android.util.AttributeSet;
|
import android.util.AttributeSet;
|
||||||
import android.view.MotionEvent;
|
import android.view.MotionEvent;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
|
||||||
import androidx.coordinatorlayout.widget.CoordinatorLayout;
|
import androidx.coordinatorlayout.widget.CoordinatorLayout;
|
||||||
|
|
||||||
import com.google.android.material.bottomsheet.BottomSheetBehavior;
|
import com.google.android.material.bottomsheet.BottomSheetBehavior;
|
||||||
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
public class RetroBottomSheetBehavior<V extends View> extends BottomSheetBehavior<V> {
|
public class RetroBottomSheetBehavior<V extends View> extends BottomSheetBehavior<V> {
|
||||||
|
|
|
@ -23,10 +23,12 @@ import android.widget.SeekBar
|
||||||
import code.name.monkey.appthemehelper.ThemeStore
|
import code.name.monkey.appthemehelper.ThemeStore
|
||||||
import code.name.monkey.retromusic.R
|
import code.name.monkey.retromusic.R
|
||||||
import code.name.monkey.retromusic.activities.base.AbsMusicServiceActivity
|
import code.name.monkey.retromusic.activities.base.AbsMusicServiceActivity
|
||||||
|
import code.name.monkey.retromusic.databinding.ActivityDriveModeBinding
|
||||||
import code.name.monkey.retromusic.fragments.base.AbsPlayerControlsFragment
|
import code.name.monkey.retromusic.fragments.base.AbsPlayerControlsFragment
|
||||||
import code.name.monkey.retromusic.glide.BlurTransformation
|
import code.name.monkey.retromusic.glide.BlurTransformation
|
||||||
|
import code.name.monkey.retromusic.glide.GlideApp
|
||||||
|
import code.name.monkey.retromusic.glide.RetroGlideExtension
|
||||||
import code.name.monkey.retromusic.glide.RetroMusicColoredTarget
|
import code.name.monkey.retromusic.glide.RetroMusicColoredTarget
|
||||||
import code.name.monkey.retromusic.glide.SongGlideRequest
|
|
||||||
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
||||||
import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper
|
import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper
|
||||||
import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper.Callback
|
import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper.Callback
|
||||||
|
@ -35,19 +37,19 @@ import code.name.monkey.retromusic.misc.SimpleOnSeekbarChangeListener
|
||||||
import code.name.monkey.retromusic.service.MusicService
|
import code.name.monkey.retromusic.service.MusicService
|
||||||
import code.name.monkey.retromusic.util.MusicUtil
|
import code.name.monkey.retromusic.util.MusicUtil
|
||||||
import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
|
import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
|
||||||
import com.bumptech.glide.Glide
|
|
||||||
import kotlinx.android.synthetic.main.activity_drive_mode.*
|
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by hemanths on 2020-02-02.
|
* Created by hemanths on 2020-02-02.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
class DriveModeActivity : AbsMusicServiceActivity(), Callback {
|
class DriveModeActivity : AbsMusicServiceActivity(), Callback {
|
||||||
|
|
||||||
|
private lateinit var binding: ActivityDriveModeBinding
|
||||||
private var lastPlaybackControlsColor: Int = Color.GRAY
|
private var lastPlaybackControlsColor: Int = Color.GRAY
|
||||||
private var lastDisabledPlaybackControlsColor: Int = Color.GRAY
|
private var lastDisabledPlaybackControlsColor: Int = Color.GRAY
|
||||||
private lateinit var progressViewUpdateHelper: MusicProgressViewUpdateHelper
|
private lateinit var progressViewUpdateHelper: MusicProgressViewUpdateHelper
|
||||||
|
@ -55,12 +57,13 @@ class DriveModeActivity : AbsMusicServiceActivity(), Callback {
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
setDrawUnderStatusBar()
|
setDrawUnderStatusBar()
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
setContentView(R.layout.activity_drive_mode)
|
binding = ActivityDriveModeBinding.inflate(layoutInflater)
|
||||||
|
setContentView(binding.root)
|
||||||
setUpMusicControllers()
|
setUpMusicControllers()
|
||||||
|
|
||||||
progressViewUpdateHelper = MusicProgressViewUpdateHelper(this)
|
progressViewUpdateHelper = MusicProgressViewUpdateHelper(this)
|
||||||
lastPlaybackControlsColor = ThemeStore.accentColor(this)
|
lastPlaybackControlsColor = ThemeStore.accentColor(this)
|
||||||
close.setOnClickListener {
|
binding.close.setOnClickListener {
|
||||||
onBackPressed()
|
onBackPressed()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -75,7 +78,7 @@ class DriveModeActivity : AbsMusicServiceActivity(), Callback {
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setupFavouriteToggle() {
|
private fun setupFavouriteToggle() {
|
||||||
songFavourite.setOnClickListener {
|
binding.songFavourite.setOnClickListener {
|
||||||
MusicUtil.toggleFavorite(
|
MusicUtil.toggleFavorite(
|
||||||
this@DriveModeActivity,
|
this@DriveModeActivity,
|
||||||
MusicPlayerRemote.currentSong
|
MusicPlayerRemote.currentSong
|
||||||
|
@ -88,13 +91,13 @@ class DriveModeActivity : AbsMusicServiceActivity(), Callback {
|
||||||
val isFavourite =
|
val isFavourite =
|
||||||
MusicUtil.isFavorite(this@DriveModeActivity, MusicPlayerRemote.currentSong)
|
MusicUtil.isFavorite(this@DriveModeActivity, MusicPlayerRemote.currentSong)
|
||||||
withContext(Dispatchers.Main) {
|
withContext(Dispatchers.Main) {
|
||||||
songFavourite.setImageResource(if (isFavourite) R.drawable.ic_favorite else R.drawable.ic_favorite_border)
|
binding.songFavourite.setImageResource(if (isFavourite) R.drawable.ic_favorite else R.drawable.ic_favorite_border)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setUpProgressSlider() {
|
private fun setUpProgressSlider() {
|
||||||
progressSlider.setOnSeekBarChangeListener(object : SimpleOnSeekbarChangeListener() {
|
binding.progressSlider.setOnSeekBarChangeListener(object : SimpleOnSeekbarChangeListener() {
|
||||||
override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
|
override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
|
||||||
if (fromUser) {
|
if (fromUser) {
|
||||||
MusicPlayerRemote.seekTo(progress)
|
MusicPlayerRemote.seekTo(progress)
|
||||||
|
@ -119,20 +122,20 @@ class DriveModeActivity : AbsMusicServiceActivity(), Callback {
|
||||||
|
|
||||||
private fun setUpPrevNext() {
|
private fun setUpPrevNext() {
|
||||||
|
|
||||||
nextButton.setOnClickListener { MusicPlayerRemote.playNextSong() }
|
binding.nextButton.setOnClickListener { MusicPlayerRemote.playNextSong() }
|
||||||
previousButton.setOnClickListener { MusicPlayerRemote.back() }
|
binding.previousButton.setOnClickListener { MusicPlayerRemote.back() }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setUpShuffleButton() {
|
private fun setUpShuffleButton() {
|
||||||
shuffleButton.setOnClickListener { MusicPlayerRemote.toggleShuffleMode() }
|
binding.shuffleButton.setOnClickListener { MusicPlayerRemote.toggleShuffleMode() }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setUpRepeatButton() {
|
private fun setUpRepeatButton() {
|
||||||
repeatButton.setOnClickListener { MusicPlayerRemote.cycleRepeatMode() }
|
binding.repeatButton.setOnClickListener { MusicPlayerRemote.cycleRepeatMode() }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setUpPlayPauseFab() {
|
private fun setUpPlayPauseFab() {
|
||||||
playPauseButton.setOnClickListener(PlayPauseButtonOnClickHandler())
|
binding.playPauseButton.setOnClickListener(PlayPauseButtonOnClickHandler())
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onRepeatModeChanged() {
|
override fun onRepeatModeChanged() {
|
||||||
|
@ -161,19 +164,19 @@ class DriveModeActivity : AbsMusicServiceActivity(), Callback {
|
||||||
|
|
||||||
private fun updatePlayPauseDrawableState() {
|
private fun updatePlayPauseDrawableState() {
|
||||||
if (MusicPlayerRemote.isPlaying) {
|
if (MusicPlayerRemote.isPlaying) {
|
||||||
playPauseButton.setImageResource(R.drawable.ic_pause)
|
binding.playPauseButton.setImageResource(R.drawable.ic_pause)
|
||||||
} else {
|
} else {
|
||||||
playPauseButton.setImageResource(R.drawable.ic_play_arrow)
|
binding.playPauseButton.setImageResource(R.drawable.ic_play_arrow)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun updateShuffleState() {
|
fun updateShuffleState() {
|
||||||
when (MusicPlayerRemote.shuffleMode) {
|
when (MusicPlayerRemote.shuffleMode) {
|
||||||
MusicService.SHUFFLE_MODE_SHUFFLE -> shuffleButton.setColorFilter(
|
MusicService.SHUFFLE_MODE_SHUFFLE -> binding.shuffleButton.setColorFilter(
|
||||||
lastPlaybackControlsColor,
|
lastPlaybackControlsColor,
|
||||||
PorterDuff.Mode.SRC_IN
|
PorterDuff.Mode.SRC_IN
|
||||||
)
|
)
|
||||||
else -> shuffleButton.setColorFilter(
|
else -> binding.shuffleButton.setColorFilter(
|
||||||
lastDisabledPlaybackControlsColor,
|
lastDisabledPlaybackControlsColor,
|
||||||
PorterDuff.Mode.SRC_IN
|
PorterDuff.Mode.SRC_IN
|
||||||
)
|
)
|
||||||
|
@ -183,19 +186,25 @@ class DriveModeActivity : AbsMusicServiceActivity(), Callback {
|
||||||
private fun updateRepeatState() {
|
private fun updateRepeatState() {
|
||||||
when (MusicPlayerRemote.repeatMode) {
|
when (MusicPlayerRemote.repeatMode) {
|
||||||
MusicService.REPEAT_MODE_NONE -> {
|
MusicService.REPEAT_MODE_NONE -> {
|
||||||
repeatButton.setImageResource(R.drawable.ic_repeat)
|
binding.repeatButton.setImageResource(R.drawable.ic_repeat)
|
||||||
repeatButton.setColorFilter(
|
binding.repeatButton.setColorFilter(
|
||||||
lastDisabledPlaybackControlsColor,
|
lastDisabledPlaybackControlsColor,
|
||||||
PorterDuff.Mode.SRC_IN
|
PorterDuff.Mode.SRC_IN
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
MusicService.REPEAT_MODE_ALL -> {
|
MusicService.REPEAT_MODE_ALL -> {
|
||||||
repeatButton.setImageResource(R.drawable.ic_repeat)
|
binding.repeatButton.setImageResource(R.drawable.ic_repeat)
|
||||||
repeatButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN)
|
binding.repeatButton.setColorFilter(
|
||||||
|
lastPlaybackControlsColor,
|
||||||
|
PorterDuff.Mode.SRC_IN
|
||||||
|
)
|
||||||
}
|
}
|
||||||
MusicService.REPEAT_MODE_THIS -> {
|
MusicService.REPEAT_MODE_THIS -> {
|
||||||
repeatButton.setImageResource(R.drawable.ic_repeat_one)
|
binding.repeatButton.setImageResource(R.drawable.ic_repeat_one)
|
||||||
repeatButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN)
|
binding.repeatButton.setColorFilter(
|
||||||
|
lastPlaybackControlsColor,
|
||||||
|
PorterDuff.Mode.SRC_IN
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -209,29 +218,29 @@ class DriveModeActivity : AbsMusicServiceActivity(), Callback {
|
||||||
private fun updateSong() {
|
private fun updateSong() {
|
||||||
val song = MusicPlayerRemote.currentSong
|
val song = MusicPlayerRemote.currentSong
|
||||||
|
|
||||||
songTitle.text = song.title
|
binding.songTitle.text = song.title
|
||||||
songText.text = song.artistName
|
binding.songText.text = song.artistName
|
||||||
|
|
||||||
SongGlideRequest.Builder.from(Glide.with(this), song)
|
GlideApp.with(this)
|
||||||
.checkIgnoreMediaStore(this)
|
.asBitmapPalette()
|
||||||
.generatePalette(this)
|
.songCoverOptions(song)
|
||||||
.build()
|
.load(RetroGlideExtension.getSongModel(song))
|
||||||
.transform(BlurTransformation.Builder(this).build())
|
.transform(BlurTransformation.Builder(this).build())
|
||||||
.into(object : RetroMusicColoredTarget(image) {
|
.into(object : RetroMusicColoredTarget(binding.image) {
|
||||||
override fun onColorReady(colors: MediaNotificationProcessor) {
|
override fun onColorReady(colors: MediaNotificationProcessor) {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onUpdateProgressViews(progress: Int, total: Int) {
|
override fun onUpdateProgressViews(progress: Int, total: Int) {
|
||||||
progressSlider.max = total
|
binding.progressSlider.max = total
|
||||||
|
|
||||||
val animator = ObjectAnimator.ofInt(progressSlider, "progress", progress)
|
val animator = ObjectAnimator.ofInt(binding.progressSlider, "progress", progress)
|
||||||
animator.duration = AbsPlayerControlsFragment.SLIDER_ANIMATION_TIME
|
animator.duration = AbsPlayerControlsFragment.SLIDER_ANIMATION_TIME
|
||||||
animator.interpolator = LinearInterpolator()
|
animator.interpolator = LinearInterpolator()
|
||||||
animator.start()
|
animator.start()
|
||||||
|
|
||||||
songTotalTime.text = MusicUtil.getReadableDurationString(total.toLong())
|
binding.songTotalTime.text = MusicUtil.getReadableDurationString(total.toLong())
|
||||||
songCurrentProgress.text = MusicUtil.getReadableDurationString(progress.toLong())
|
binding.songCurrentProgress.text = MusicUtil.getReadableDurationString(progress.toLong())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,19 +18,23 @@ import android.graphics.Color;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
import android.webkit.WebView;
|
import android.webkit.WebView;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.appcompat.widget.Toolbar;
|
import androidx.appcompat.widget.Toolbar;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
|
||||||
import code.name.monkey.appthemehelper.ThemeStore;
|
import code.name.monkey.appthemehelper.ThemeStore;
|
||||||
import code.name.monkey.appthemehelper.util.ATHUtil;
|
import code.name.monkey.appthemehelper.util.ATHUtil;
|
||||||
import code.name.monkey.appthemehelper.util.ColorUtil;
|
import code.name.monkey.appthemehelper.util.ColorUtil;
|
||||||
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper;
|
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper;
|
||||||
import code.name.monkey.retromusic.R;
|
import code.name.monkey.retromusic.R;
|
||||||
import code.name.monkey.retromusic.activities.base.AbsBaseActivity;
|
import code.name.monkey.retromusic.activities.base.AbsBaseActivity;
|
||||||
import java.io.BufferedReader;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.InputStreamReader;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
/** Created by hemanths on 2019-09-27. */
|
/** Created by hemanths on 2019-09-27. */
|
||||||
public class LicenseActivity extends AbsBaseActivity {
|
public class LicenseActivity extends AbsBaseActivity {
|
||||||
|
|
|
@ -23,27 +23,29 @@ import android.view.WindowManager
|
||||||
import androidx.core.view.ViewCompat
|
import androidx.core.view.ViewCompat
|
||||||
import code.name.monkey.retromusic.R
|
import code.name.monkey.retromusic.R
|
||||||
import code.name.monkey.retromusic.activities.base.AbsMusicServiceActivity
|
import code.name.monkey.retromusic.activities.base.AbsMusicServiceActivity
|
||||||
|
import code.name.monkey.retromusic.databinding.ActivityLockScreenBinding
|
||||||
import code.name.monkey.retromusic.extensions.whichFragment
|
import code.name.monkey.retromusic.extensions.whichFragment
|
||||||
import code.name.monkey.retromusic.fragments.player.lockscreen.LockScreenControlsFragment
|
import code.name.monkey.retromusic.fragments.player.lockscreen.LockScreenControlsFragment
|
||||||
|
import code.name.monkey.retromusic.glide.GlideApp
|
||||||
|
import code.name.monkey.retromusic.glide.RetroGlideExtension
|
||||||
import code.name.monkey.retromusic.glide.RetroMusicColoredTarget
|
import code.name.monkey.retromusic.glide.RetroMusicColoredTarget
|
||||||
import code.name.monkey.retromusic.glide.SongGlideRequest
|
|
||||||
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
||||||
import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
|
import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
|
||||||
import com.bumptech.glide.Glide
|
|
||||||
import com.r0adkll.slidr.Slidr
|
import com.r0adkll.slidr.Slidr
|
||||||
import com.r0adkll.slidr.model.SlidrConfig
|
import com.r0adkll.slidr.model.SlidrConfig
|
||||||
import com.r0adkll.slidr.model.SlidrListener
|
import com.r0adkll.slidr.model.SlidrListener
|
||||||
import com.r0adkll.slidr.model.SlidrPosition
|
import com.r0adkll.slidr.model.SlidrPosition
|
||||||
import kotlinx.android.synthetic.main.activity_lock_screen.*
|
|
||||||
|
|
||||||
class LockScreenActivity : AbsMusicServiceActivity() {
|
class LockScreenActivity : AbsMusicServiceActivity() {
|
||||||
|
private lateinit var binding: ActivityLockScreenBinding
|
||||||
private var fragment: LockScreenControlsFragment? = null
|
private var fragment: LockScreenControlsFragment? = null
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
setDrawUnderStatusBar()
|
setDrawUnderStatusBar()
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
lockScreenInit()
|
lockScreenInit()
|
||||||
setContentView(R.layout.activity_lock_screen)
|
binding = ActivityLockScreenBinding.inflate(layoutInflater)
|
||||||
|
setContentView(binding.root)
|
||||||
hideStatusBar()
|
hideStatusBar()
|
||||||
setStatusbarColorAuto()
|
setStatusbarColorAuto()
|
||||||
setNavigationbarColorAuto()
|
setNavigationbarColorAuto()
|
||||||
|
@ -107,9 +109,12 @@ class LockScreenActivity : AbsMusicServiceActivity() {
|
||||||
|
|
||||||
private fun updateSongs() {
|
private fun updateSongs() {
|
||||||
val song = MusicPlayerRemote.currentSong
|
val song = MusicPlayerRemote.currentSong
|
||||||
SongGlideRequest.Builder.from(Glide.with(this), song).checkIgnoreMediaStore(this)
|
GlideApp.with(this)
|
||||||
.generatePalette(this).build().dontAnimate()
|
.asBitmapPalette()
|
||||||
.into(object : RetroMusicColoredTarget(image) {
|
.songCoverOptions(song)
|
||||||
|
.load(RetroGlideExtension.getSongModel(song))
|
||||||
|
.dontAnimate()
|
||||||
|
.into(object : RetroMusicColoredTarget(binding.image) {
|
||||||
override fun onColorReady(colors: MediaNotificationProcessor) {
|
override fun onColorReady(colors: MediaNotificationProcessor) {
|
||||||
fragment?.setColor(colors)
|
fragment?.setColor(colors)
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,38 +15,64 @@
|
||||||
package code.name.monkey.retromusic.activities
|
package code.name.monkey.retromusic.activities
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.Menu
|
import android.text.InputType
|
||||||
import android.view.MenuItem
|
import android.view.*
|
||||||
import android.view.WindowManager
|
|
||||||
import androidx.core.view.ViewCompat
|
import androidx.core.view.ViewCompat
|
||||||
import androidx.interpolator.view.animation.FastOutSlowInInterpolator
|
import androidx.fragment.app.Fragment
|
||||||
|
import androidx.fragment.app.FragmentActivity
|
||||||
|
import androidx.viewpager2.adapter.FragmentStateAdapter
|
||||||
import code.name.monkey.appthemehelper.ThemeStore
|
import code.name.monkey.appthemehelper.ThemeStore
|
||||||
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
|
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
|
||||||
import code.name.monkey.retromusic.R
|
import code.name.monkey.retromusic.R
|
||||||
import code.name.monkey.retromusic.activities.base.AbsMusicServiceActivity
|
import code.name.monkey.retromusic.activities.base.AbsMusicServiceActivity
|
||||||
|
import code.name.monkey.retromusic.activities.tageditor.WriteTagsAsyncTask
|
||||||
|
import code.name.monkey.retromusic.databinding.ActivityLyricsBinding
|
||||||
|
import code.name.monkey.retromusic.databinding.FragmentNormalLyricsBinding
|
||||||
|
import code.name.monkey.retromusic.databinding.FragmentSyncedLyricsBinding
|
||||||
|
import code.name.monkey.retromusic.extensions.accentColor
|
||||||
import code.name.monkey.retromusic.extensions.surfaceColor
|
import code.name.monkey.retromusic.extensions.surfaceColor
|
||||||
|
import code.name.monkey.retromusic.extensions.textColorSecondary
|
||||||
|
import code.name.monkey.retromusic.fragments.base.AbsMusicServiceFragment
|
||||||
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
||||||
import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper
|
import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper
|
||||||
import code.name.monkey.retromusic.lyrics.LrcView
|
import code.name.monkey.retromusic.lyrics.LrcView
|
||||||
|
import code.name.monkey.retromusic.model.LoadingInfo
|
||||||
import code.name.monkey.retromusic.model.Song
|
import code.name.monkey.retromusic.model.Song
|
||||||
import code.name.monkey.retromusic.util.LyricUtil
|
import code.name.monkey.retromusic.util.LyricUtil
|
||||||
import code.name.monkey.retromusic.util.RetroUtil
|
import code.name.monkey.retromusic.util.RetroUtil
|
||||||
|
import com.afollestad.materialdialogs.LayoutMode
|
||||||
|
import com.afollestad.materialdialogs.MaterialDialog
|
||||||
|
import com.afollestad.materialdialogs.bottomsheets.BottomSheet
|
||||||
|
import com.afollestad.materialdialogs.input.input
|
||||||
import com.google.android.material.color.MaterialColors
|
import com.google.android.material.color.MaterialColors
|
||||||
import com.google.android.material.transition.platform.MaterialArcMotion
|
import com.google.android.material.tabs.TabLayoutMediator
|
||||||
import com.google.android.material.transition.platform.MaterialContainerTransform
|
import com.google.android.material.transition.platform.MaterialContainerTransform
|
||||||
import com.google.android.material.transition.platform.MaterialContainerTransformSharedElementCallback
|
import kotlinx.coroutines.*
|
||||||
import kotlinx.android.synthetic.main.activity_lyrics.*
|
import org.jaudiotagger.audio.AudioFileIO
|
||||||
|
import org.jaudiotagger.tag.FieldKey
|
||||||
|
import java.io.File
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
class LyricsActivity : AbsMusicServiceActivity(), MusicProgressViewUpdateHelper.Callback {
|
class LyricsActivity : AbsMusicServiceActivity() {
|
||||||
private lateinit var updateHelper: MusicProgressViewUpdateHelper
|
|
||||||
|
|
||||||
|
private lateinit var binding: ActivityLyricsBinding
|
||||||
private lateinit var song: Song
|
private lateinit var song: Song
|
||||||
|
|
||||||
|
private val lyricsSectionsAdapter = LyricsSectionsAdapter(this)
|
||||||
|
|
||||||
private val googleSearchLrcUrl: String
|
private val googleSearchLrcUrl: String
|
||||||
get() {
|
get() {
|
||||||
var baseUrl = "http://www.google.com/search?"
|
var baseUrl = "http://www.google.com/search?"
|
||||||
var query = song.title + "+" + song.artistName
|
var query = song.title + "+" + song.artistName
|
||||||
query = "q=" + query.replace(" ", "+") + " .lrc"
|
query = "q=" + query.replace(" ", "+") + " lyrics"
|
||||||
|
baseUrl += query
|
||||||
|
return baseUrl
|
||||||
|
}
|
||||||
|
private val syairSearchLrcUrl: String
|
||||||
|
get() {
|
||||||
|
var baseUrl = "https://www.syair.info/search?"
|
||||||
|
var query = song.title + "+" + song.artistName
|
||||||
|
query = "q=" + query.replace(" ", "+")
|
||||||
baseUrl += query
|
baseUrl += query
|
||||||
return baseUrl
|
return baseUrl
|
||||||
}
|
}
|
||||||
|
@ -63,75 +89,55 @@ class LyricsActivity : AbsMusicServiceActivity(), MusicProgressViewUpdateHelper.
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
setContentView(R.layout.activity_lyrics)
|
binding = ActivityLyricsBinding.inflate(layoutInflater)
|
||||||
ViewCompat.setTransitionName(container, "lyrics")
|
setContentView(binding.root)
|
||||||
|
ViewCompat.setTransitionName(binding.container, "lyrics")
|
||||||
setStatusbarColorAuto()
|
setStatusbarColorAuto()
|
||||||
setTaskDescriptionColorAuto()
|
setTaskDescriptionColorAuto()
|
||||||
setNavigationbarColorAuto()
|
setNavigationbarColorAuto()
|
||||||
|
|
||||||
setupWakelock()
|
setupWakelock()
|
||||||
|
|
||||||
toolbar.setBackgroundColor(surfaceColor())
|
binding.toolbar.setBackgroundColor(surfaceColor())
|
||||||
ToolbarContentTintHelper.colorBackButton(toolbar)
|
binding.tabLyrics.setBackgroundColor(surfaceColor())
|
||||||
setSupportActionBar(toolbar)
|
binding.container.setBackgroundColor(surfaceColor())
|
||||||
|
ToolbarContentTintHelper.colorBackButton(binding.toolbar)
|
||||||
|
setSupportActionBar(binding.toolbar)
|
||||||
|
setupViews()
|
||||||
|
|
||||||
updateHelper = MusicProgressViewUpdateHelper(this, 500, 1000)
|
|
||||||
setupLyricsView()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setupLyricsView() {
|
|
||||||
lyricsView.apply {
|
private fun setupViews() {
|
||||||
setCurrentColor(ThemeStore.accentColor(context))
|
binding.lyricsPager.adapter = lyricsSectionsAdapter
|
||||||
setTimeTextColor(ThemeStore.accentColor(context))
|
TabLayoutMediator(binding.tabLyrics, binding.lyricsPager) { tab, position ->
|
||||||
setTimelineColor(ThemeStore.accentColor(context))
|
tab.text = when (position) {
|
||||||
setTimelineTextColor(ThemeStore.accentColor(context))
|
0 -> "Synced Lyrics"
|
||||||
setDraggable(true, LrcView.OnPlayClickListener {
|
1 -> "Normal Lyrics"
|
||||||
MusicPlayerRemote.seekTo(it.toInt())
|
else -> ""
|
||||||
return@OnPlayClickListener true
|
}
|
||||||
})
|
}.attach()
|
||||||
}
|
// lyricsPager.isUserInputEnabled = false
|
||||||
|
|
||||||
|
binding.tabLyrics.setSelectedTabIndicatorColor(ThemeStore.accentColor(this))
|
||||||
|
binding.tabLyrics.setTabTextColors(textColorSecondary(), accentColor())
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onResume() {
|
|
||||||
super.onResume()
|
|
||||||
updateHelper.start()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onPause() {
|
|
||||||
super.onPause()
|
|
||||||
updateHelper.stop()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onUpdateProgressViews(progress: Int, total: Int) {
|
|
||||||
lyricsView.updateTime(progress.toLong())
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun loadLRCLyrics() {
|
|
||||||
lyricsView.setLabel("Empty")
|
|
||||||
val song = MusicPlayerRemote.currentSong
|
|
||||||
if (LyricUtil.isLrcOriginalFileExist(song.data)) {
|
|
||||||
lyricsView.loadLrc(LyricUtil.getLocalLyricOriginalFile(song.data))
|
|
||||||
} else if (LyricUtil.isLrcFileExist(song.title, song.artistName)) {
|
|
||||||
lyricsView.loadLrc(LyricUtil.getLocalLyricFile(song.title, song.artistName))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onPlayingMetaChanged() {
|
override fun onPlayingMetaChanged() {
|
||||||
super.onPlayingMetaChanged()
|
super.onPlayingMetaChanged()
|
||||||
updateTitleSong()
|
updateTitleSong()
|
||||||
loadLRCLyrics()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onServiceConnected() {
|
override fun onServiceConnected() {
|
||||||
super.onServiceConnected()
|
super.onServiceConnected()
|
||||||
updateTitleSong()
|
updateTitleSong()
|
||||||
loadLRCLyrics()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateTitleSong() {
|
private fun updateTitleSong() {
|
||||||
song = MusicPlayerRemote.currentSong
|
song = MusicPlayerRemote.currentSong
|
||||||
toolbar.title = song.title
|
binding.toolbar.title = song.title
|
||||||
toolbar.subtitle = song.artistName
|
binding.toolbar.subtitle = song.artistName
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setupWakelock() {
|
private fun setupWakelock() {
|
||||||
|
@ -149,8 +155,206 @@ class LyricsActivity : AbsMusicServiceActivity(), MusicProgressViewUpdateHelper.
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
if (item.itemId == R.id.action_search) {
|
if (item.itemId == R.id.action_search) {
|
||||||
RetroUtil.openUrl(this, googleSearchLrcUrl)
|
RetroUtil.openUrl(
|
||||||
|
this, when (binding.lyricsPager.currentItem) {
|
||||||
|
0 -> syairSearchLrcUrl
|
||||||
|
1 -> googleSearchLrcUrl
|
||||||
|
else -> googleSearchLrcUrl
|
||||||
|
}
|
||||||
|
)
|
||||||
|
} else if (item.itemId == R.id.action_edit) {
|
||||||
|
when (binding.lyricsPager.currentItem) {
|
||||||
|
0 -> {
|
||||||
|
editSyncedLyrics()
|
||||||
|
}
|
||||||
|
1 -> {
|
||||||
|
editNormalLyrics()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return super.onOptionsItemSelected(item)
|
return super.onOptionsItemSelected(item)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun editNormalLyrics() {
|
||||||
|
var content = ""
|
||||||
|
val file = File(MusicPlayerRemote.currentSong.data)
|
||||||
|
try {
|
||||||
|
content = AudioFileIO.read(file).tagOrCreateDefault.getFirst(FieldKey.LYRICS)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
e.printStackTrace()
|
||||||
|
}
|
||||||
|
|
||||||
|
MaterialDialog(this, BottomSheet(LayoutMode.WRAP_CONTENT)).show {
|
||||||
|
title(res = R.string.edit_normal_lyrics)
|
||||||
|
input(
|
||||||
|
hintRes = R.string.paste_lyrics_here,
|
||||||
|
prefill = content,
|
||||||
|
inputType = InputType.TYPE_TEXT_FLAG_MULTI_LINE or InputType.TYPE_CLASS_TEXT
|
||||||
|
) { _, input ->
|
||||||
|
val fieldKeyValueMap = EnumMap<FieldKey, String>(FieldKey::class.java)
|
||||||
|
fieldKeyValueMap[FieldKey.LYRICS] = input.toString()
|
||||||
|
WriteTagsAsyncTask(this@LyricsActivity).execute(
|
||||||
|
LoadingInfo(
|
||||||
|
listOf(song.data), fieldKeyValueMap, null
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
positiveButton(res = R.string.save) {
|
||||||
|
(lyricsSectionsAdapter.fragments[1].first as NormalLyrics).loadNormalLyrics()
|
||||||
|
}
|
||||||
|
negativeButton(res = android.R.string.cancel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun editSyncedLyrics() {
|
||||||
|
var lrcFile: File? = null
|
||||||
|
if (LyricUtil.isLrcOriginalFileExist(song.data)) {
|
||||||
|
lrcFile = LyricUtil.getLocalLyricOriginalFile(song.data)
|
||||||
|
} else if (LyricUtil.isLrcFileExist(song.title, song.artistName)) {
|
||||||
|
lrcFile = LyricUtil.getLocalLyricFile(song.title, song.artistName)
|
||||||
|
}
|
||||||
|
val content: String = LyricUtil.getStringFromLrc(lrcFile)
|
||||||
|
|
||||||
|
MaterialDialog(this, BottomSheet(LayoutMode.WRAP_CONTENT)).show {
|
||||||
|
title(res = R.string.edit_synced_lyrics)
|
||||||
|
input(
|
||||||
|
hintRes = R.string.paste_timeframe_lyrics_here,
|
||||||
|
prefill = content,
|
||||||
|
inputType = InputType.TYPE_TEXT_FLAG_MULTI_LINE or InputType.TYPE_CLASS_TEXT
|
||||||
|
) { _, input ->
|
||||||
|
LyricUtil.writeLrc(song, input.toString())
|
||||||
|
}
|
||||||
|
positiveButton(res = R.string.save) {
|
||||||
|
(lyricsSectionsAdapter.fragments[0].first as SyncedLyrics).loadLRCLyrics()
|
||||||
|
}
|
||||||
|
negativeButton(res = android.R.string.cancel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class LyricsSectionsAdapter(fragmentActivity: FragmentActivity) :
|
||||||
|
FragmentStateAdapter(fragmentActivity) {
|
||||||
|
val fragments = listOf(
|
||||||
|
Pair(SyncedLyrics(), R.string.synced_lyrics),
|
||||||
|
Pair(NormalLyrics(), R.string.normal_lyrics)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
override fun getItemCount(): Int {
|
||||||
|
return fragments.size
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun createFragment(position: Int): Fragment {
|
||||||
|
return fragments[position].first
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class NormalLyrics : AbsMusicServiceFragment(R.layout.fragment_normal_lyrics) {
|
||||||
|
|
||||||
|
private var _binding: FragmentNormalLyricsBinding? = null
|
||||||
|
private val binding get() = _binding!!
|
||||||
|
|
||||||
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
_binding = FragmentNormalLyricsBinding.bind(view)
|
||||||
|
loadNormalLyrics()
|
||||||
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun loadNormalLyrics() {
|
||||||
|
var lyrics: String? = null
|
||||||
|
val file = File(MusicPlayerRemote.currentSong.data)
|
||||||
|
try {
|
||||||
|
lyrics = AudioFileIO.read(file).tagOrCreateDefault.getFirst(FieldKey.LYRICS)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
e.printStackTrace()
|
||||||
|
}
|
||||||
|
if (lyrics.isNullOrEmpty()) {
|
||||||
|
binding.noLyricsFound.visibility = View.VISIBLE
|
||||||
|
} else {
|
||||||
|
binding.noLyricsFound.visibility = View.GONE
|
||||||
|
}
|
||||||
|
binding.normalLyrics.text = lyrics
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onPlayingMetaChanged() {
|
||||||
|
super.onPlayingMetaChanged()
|
||||||
|
loadNormalLyrics()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onServiceConnected() {
|
||||||
|
super.onServiceConnected()
|
||||||
|
loadNormalLyrics()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroyView() {
|
||||||
|
super.onDestroyView()
|
||||||
|
_binding = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SyncedLyrics : AbsMusicServiceFragment(R.layout.fragment_synced_lyrics),
|
||||||
|
MusicProgressViewUpdateHelper.Callback {
|
||||||
|
|
||||||
|
private var _binding: FragmentSyncedLyricsBinding? = null
|
||||||
|
private val binding get() = _binding!!
|
||||||
|
private lateinit var updateHelper: MusicProgressViewUpdateHelper
|
||||||
|
|
||||||
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
updateHelper = MusicProgressViewUpdateHelper(this, 500, 1000)
|
||||||
|
_binding = FragmentSyncedLyricsBinding.bind(view)
|
||||||
|
setupLyricsView()
|
||||||
|
loadLRCLyrics()
|
||||||
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun loadLRCLyrics() {
|
||||||
|
binding.lyricsView.setLabel("Empty")
|
||||||
|
val song = MusicPlayerRemote.currentSong
|
||||||
|
if (LyricUtil.isLrcOriginalFileExist(song.data)) {
|
||||||
|
binding.lyricsView.loadLrc(LyricUtil.getLocalLyricOriginalFile(song.data))
|
||||||
|
} else if (LyricUtil.isLrcFileExist(song.title, song.artistName)) {
|
||||||
|
binding.lyricsView.loadLrc(LyricUtil.getLocalLyricFile(song.title, song.artistName))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setupLyricsView() {
|
||||||
|
binding.lyricsView.apply {
|
||||||
|
setCurrentColor(ThemeStore.accentColor(context))
|
||||||
|
setTimeTextColor(ThemeStore.accentColor(context))
|
||||||
|
setTimelineColor(ThemeStore.accentColor(context))
|
||||||
|
setTimelineTextColor(ThemeStore.accentColor(context))
|
||||||
|
setDraggable(true, LrcView.OnPlayClickListener {
|
||||||
|
MusicPlayerRemote.seekTo(it.toInt())
|
||||||
|
return@OnPlayClickListener true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onUpdateProgressViews(progress: Int, total: Int) {
|
||||||
|
binding.lyricsView.updateTime(progress.toLong())
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onPlayingMetaChanged() {
|
||||||
|
super.onPlayingMetaChanged()
|
||||||
|
loadLRCLyrics()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onServiceConnected() {
|
||||||
|
super.onServiceConnected()
|
||||||
|
loadLRCLyrics()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onResume() {
|
||||||
|
super.onResume()
|
||||||
|
updateHelper.start()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onPause() {
|
||||||
|
super.onPause()
|
||||||
|
updateHelper.stop()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,38 +20,15 @@ import android.content.SharedPreferences.OnSharedPreferenceChangeListener
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.provider.MediaStore
|
import android.provider.MediaStore
|
||||||
import android.view.View
|
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.navigation.ui.NavigationUI
|
import androidx.navigation.ui.setupWithNavController
|
||||||
import code.name.monkey.retromusic.ADAPTIVE_COLOR_APP
|
import code.name.monkey.retromusic.*
|
||||||
import code.name.monkey.retromusic.ALBUM_COVER_STYLE
|
import code.name.monkey.retromusic.activities.base.AbsCastActivity
|
||||||
import code.name.monkey.retromusic.ALBUM_COVER_TRANSFORM
|
import code.name.monkey.retromusic.databinding.SlidingMusicPanelLayoutBinding
|
||||||
import code.name.monkey.retromusic.BANNER_IMAGE_PATH
|
|
||||||
import code.name.monkey.retromusic.BLACK_THEME
|
|
||||||
import code.name.monkey.retromusic.CAROUSEL_EFFECT
|
|
||||||
import code.name.monkey.retromusic.CIRCULAR_ALBUM_ART
|
|
||||||
import code.name.monkey.retromusic.DESATURATED_COLOR
|
|
||||||
import code.name.monkey.retromusic.EXTRA_SONG_INFO
|
|
||||||
import code.name.monkey.retromusic.GENERAL_THEME
|
|
||||||
import code.name.monkey.retromusic.HOME_ARTIST_GRID_STYLE
|
|
||||||
import code.name.monkey.retromusic.KEEP_SCREEN_ON
|
|
||||||
import code.name.monkey.retromusic.LANGUAGE_NAME
|
|
||||||
import code.name.monkey.retromusic.LIBRARY_CATEGORIES
|
|
||||||
import code.name.monkey.retromusic.NOW_PLAYING_SCREEN_ID
|
|
||||||
import code.name.monkey.retromusic.PROFILE_IMAGE_PATH
|
|
||||||
import code.name.monkey.retromusic.R
|
|
||||||
import code.name.monkey.retromusic.ROUND_CORNERS
|
|
||||||
import code.name.monkey.retromusic.TAB_TEXT_MODE
|
|
||||||
import code.name.monkey.retromusic.TOGGLE_ADD_CONTROLS
|
|
||||||
import code.name.monkey.retromusic.TOGGLE_FULL_SCREEN
|
|
||||||
import code.name.monkey.retromusic.TOGGLE_GENRE
|
|
||||||
import code.name.monkey.retromusic.TYPE_HOME_BANNER
|
|
||||||
import code.name.monkey.retromusic.TOGGLE_SEPARATE_LINE
|
|
||||||
import code.name.monkey.retromusic.TOGGLE_VOLUME
|
|
||||||
import code.name.monkey.retromusic.USER_NAME
|
|
||||||
import code.name.monkey.retromusic.activities.base.AbsSlidingMusicPanelActivity
|
|
||||||
import code.name.monkey.retromusic.extensions.extra
|
import code.name.monkey.retromusic.extensions.extra
|
||||||
import code.name.monkey.retromusic.extensions.findNavController
|
import code.name.monkey.retromusic.extensions.findNavController
|
||||||
|
import code.name.monkey.retromusic.fragments.base.AbsRecyclerViewFragment
|
||||||
|
import code.name.monkey.retromusic.fragments.home.HomeFragment
|
||||||
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
||||||
import code.name.monkey.retromusic.helper.SearchQueryHelper.getSongs
|
import code.name.monkey.retromusic.helper.SearchQueryHelper.getSongs
|
||||||
import code.name.monkey.retromusic.model.CategoryInfo
|
import code.name.monkey.retromusic.model.CategoryInfo
|
||||||
|
@ -64,13 +41,13 @@ import kotlinx.coroutines.Dispatchers.IO
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import org.koin.android.ext.android.get
|
import org.koin.android.ext.android.get
|
||||||
|
|
||||||
class MainActivity : AbsSlidingMusicPanelActivity(), OnSharedPreferenceChangeListener {
|
class MainActivity : AbsCastActivity(), OnSharedPreferenceChangeListener {
|
||||||
companion object {
|
companion object {
|
||||||
const val TAG = "MainActivity"
|
const val TAG = "MainActivity"
|
||||||
const val EXPAND_PANEL = "expand_panel"
|
const val EXPAND_PANEL = "expand_panel"
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun createContentView(): View {
|
override fun createContentView(): SlidingMusicPanelLayoutBinding {
|
||||||
return wrapSlidingMusicPanel()
|
return wrapSlidingMusicPanel()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -98,10 +75,50 @@ class MainActivity : AbsSlidingMusicPanelActivity(), OnSharedPreferenceChangeLis
|
||||||
|
|
||||||
val categoryInfo: CategoryInfo = PreferenceUtil.libraryCategory.first { it.visible }
|
val categoryInfo: CategoryInfo = PreferenceUtil.libraryCategory.first { it.visible }
|
||||||
if (categoryInfo.visible) {
|
if (categoryInfo.visible) {
|
||||||
navGraph.startDestination = categoryInfo.category.id
|
navGraph.setStartDestination(
|
||||||
|
if (PreferenceUtil.rememberLastTab) {
|
||||||
|
PreferenceUtil.lastTab.let {
|
||||||
|
if (it == 0) {
|
||||||
|
categoryInfo.category.id
|
||||||
|
} else {
|
||||||
|
it
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else categoryInfo.category.id
|
||||||
|
)
|
||||||
}
|
}
|
||||||
navController.graph = navGraph
|
navController.graph = navGraph
|
||||||
NavigationUI.setupWithNavController(getBottomNavigationView(), navController)
|
getBottomNavigationView().setupWithNavController(navController)
|
||||||
|
// Scroll Fragment to top
|
||||||
|
getBottomNavigationView().setOnItemReselectedListener {
|
||||||
|
supportFragmentManager.findFragmentById(R.id.fragment_container)?.childFragmentManager?.fragments?.get(0)
|
||||||
|
.also {
|
||||||
|
if (it is AbsRecyclerViewFragment<*, *>) {
|
||||||
|
it.scrollToTop()
|
||||||
|
}
|
||||||
|
if (it is HomeFragment) {
|
||||||
|
it.scrollToTop()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
navController.addOnDestinationChangedListener { _, destination, _ ->
|
||||||
|
when (destination.id) {
|
||||||
|
R.id.action_home, R.id.action_song, R.id.action_album, R.id.action_artist, R.id.action_folder, R.id.action_playlist, R.id.action_genre -> {
|
||||||
|
// Save the last tab
|
||||||
|
if (PreferenceUtil.rememberLastTab) {
|
||||||
|
saveTab(destination.id)
|
||||||
|
}
|
||||||
|
// Show Bottom Navigation Bar
|
||||||
|
setBottomBarVisibility(true)
|
||||||
|
}
|
||||||
|
else -> setBottomBarVisibility(false) // Hide Bottom Navigation Bar
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun saveTab(id: Int) {
|
||||||
|
PreferenceUtil.lastTab = id
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onSupportNavigateUp(): Boolean =
|
override fun onSupportNavigateUp(): Boolean =
|
||||||
|
@ -112,6 +129,8 @@ class MainActivity : AbsSlidingMusicPanelActivity(), OnSharedPreferenceChangeLis
|
||||||
PreferenceUtil.registerOnSharedPreferenceChangedListener(this)
|
PreferenceUtil.registerOnSharedPreferenceChangedListener(this)
|
||||||
val expand = extra<Boolean>(EXPAND_PANEL).value ?: false
|
val expand = extra<Boolean>(EXPAND_PANEL).value ?: false
|
||||||
if (expand && PreferenceUtil.isExpandPanel) {
|
if (expand && PreferenceUtil.isExpandPanel) {
|
||||||
|
setBottomBarVisibility(false)
|
||||||
|
fromNotification = true
|
||||||
expandPanel()
|
expandPanel()
|
||||||
intent.removeExtra(EXPAND_PANEL)
|
intent.removeExtra(EXPAND_PANEL)
|
||||||
}
|
}
|
||||||
|
@ -123,7 +142,7 @@ class MainActivity : AbsSlidingMusicPanelActivity(), OnSharedPreferenceChangeLis
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) {
|
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) {
|
||||||
if (key == GENERAL_THEME || key == BLACK_THEME || key == ADAPTIVE_COLOR_APP || key == USER_NAME || key == TOGGLE_FULL_SCREEN || key == TOGGLE_VOLUME || key == ROUND_CORNERS || key == CAROUSEL_EFFECT || key == NOW_PLAYING_SCREEN_ID || key == TOGGLE_GENRE || key == BANNER_IMAGE_PATH || key == PROFILE_IMAGE_PATH || key == CIRCULAR_ALBUM_ART || key == KEEP_SCREEN_ON || key == TOGGLE_SEPARATE_LINE || key == TYPE_HOME_BANNER || key == TOGGLE_ADD_CONTROLS || key == ALBUM_COVER_STYLE || key == HOME_ARTIST_GRID_STYLE || key == ALBUM_COVER_TRANSFORM || key == DESATURATED_COLOR || key == EXTRA_SONG_INFO || key == TAB_TEXT_MODE || key == LANGUAGE_NAME || key == LIBRARY_CATEGORIES) {
|
if (key == GENERAL_THEME || key == BLACK_THEME || key == ADAPTIVE_COLOR_APP || key == USER_NAME || key == TOGGLE_FULL_SCREEN || key == TOGGLE_VOLUME || key == ROUND_CORNERS || key == CAROUSEL_EFFECT || key == NOW_PLAYING_SCREEN_ID || key == TOGGLE_GENRE || key == BANNER_IMAGE_PATH || key == PROFILE_IMAGE_PATH || key == CIRCULAR_ALBUM_ART || key == KEEP_SCREEN_ON || key == TOGGLE_SEPARATE_LINE || key == TOGGLE_HOME_BANNER || key == TOGGLE_ADD_CONTROLS || key == ALBUM_COVER_STYLE || key == HOME_ARTIST_GRID_STYLE || key == ALBUM_COVER_TRANSFORM || key == DESATURATED_COLOR || key == EXTRA_SONG_INFO || key == TAB_TEXT_MODE || key == LANGUAGE_NAME || key == LIBRARY_CATEGORIES) {
|
||||||
postRecreate()
|
postRecreate()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,44 +14,51 @@
|
||||||
*/
|
*/
|
||||||
package code.name.monkey.retromusic.activities
|
package code.name.monkey.retromusic.activities
|
||||||
|
|
||||||
|
import android.Manifest
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
|
import android.content.pm.PackageManager
|
||||||
|
import android.content.res.ColorStateList
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.provider.Settings
|
import android.provider.Settings
|
||||||
|
import android.view.View
|
||||||
|
import androidx.annotation.RequiresApi
|
||||||
import androidx.core.text.HtmlCompat
|
import androidx.core.text.HtmlCompat
|
||||||
import code.name.monkey.appthemehelper.ThemeStore
|
import code.name.monkey.appthemehelper.ThemeStore
|
||||||
import code.name.monkey.appthemehelper.util.VersionUtils
|
import code.name.monkey.appthemehelper.util.VersionUtils
|
||||||
import code.name.monkey.retromusic.R
|
|
||||||
import code.name.monkey.retromusic.activities.base.AbsMusicServiceActivity
|
import code.name.monkey.retromusic.activities.base.AbsMusicServiceActivity
|
||||||
|
import code.name.monkey.retromusic.databinding.ActivityPermissionBinding
|
||||||
import code.name.monkey.retromusic.extensions.accentBackgroundColor
|
import code.name.monkey.retromusic.extensions.accentBackgroundColor
|
||||||
import code.name.monkey.retromusic.extensions.show
|
import code.name.monkey.retromusic.extensions.show
|
||||||
import code.name.monkey.retromusic.util.RingtoneManager
|
import code.name.monkey.retromusic.util.RingtoneManager
|
||||||
import kotlinx.android.synthetic.main.activity_permission.*
|
|
||||||
import kotlinx.android.synthetic.main.fragment_library.appNameText
|
|
||||||
|
|
||||||
class PermissionActivity : AbsMusicServiceActivity() {
|
class PermissionActivity : AbsMusicServiceActivity() {
|
||||||
|
private lateinit var binding: ActivityPermissionBinding
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
setContentView((R.layout.activity_permission))
|
binding = ActivityPermissionBinding.inflate(layoutInflater)
|
||||||
|
setContentView(binding.root)
|
||||||
setStatusbarColorAuto()
|
setStatusbarColorAuto()
|
||||||
setNavigationbarColorAuto()
|
setNavigationbarColorAuto()
|
||||||
setLightNavigationBar(true)
|
setLightNavigationBar(true)
|
||||||
setTaskDescriptionColorAuto()
|
setTaskDescriptionColorAuto()
|
||||||
setupTitle()
|
setupTitle()
|
||||||
|
|
||||||
storagePermission.setButtonClick {
|
binding.storagePermission.setButtonClick {
|
||||||
requestPermissions()
|
requestPermissions()
|
||||||
}
|
}
|
||||||
if (VersionUtils.hasMarshmallow()) audioPermission.show()
|
if (VersionUtils.hasMarshmallow()) binding.audioPermission.show()
|
||||||
audioPermission.setButtonClick {
|
binding.audioPermission.setButtonClick {
|
||||||
if (RingtoneManager.requiresDialog(this@PermissionActivity)) {
|
if (RingtoneManager.requiresDialog(this@PermissionActivity)) {
|
||||||
val intent = Intent(Settings.ACTION_MANAGE_WRITE_SETTINGS)
|
val intent = Intent(Settings.ACTION_MANAGE_WRITE_SETTINGS)
|
||||||
intent.data = Uri.parse("package:" + applicationContext.packageName)
|
intent.data = Uri.parse("package:" + applicationContext.packageName)
|
||||||
startActivity(intent)
|
startActivity(intent)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
finish.accentBackgroundColor()
|
binding.finish.accentBackgroundColor()
|
||||||
finish.setOnClickListener {
|
binding.finish.setOnClickListener {
|
||||||
if (hasPermissions()) {
|
if (hasPermissions()) {
|
||||||
startActivity(
|
startActivity(
|
||||||
Intent(this, MainActivity::class.java).addFlags(
|
Intent(this, MainActivity::class.java).addFlags(
|
||||||
|
@ -71,6 +78,32 @@ class PermissionActivity : AbsMusicServiceActivity() {
|
||||||
"Hello there! <br>Welcome to <b>Retro <span style='color:$hexColor';>Music</span></b>",
|
"Hello there! <br>Welcome to <b>Retro <span style='color:$hexColor';>Music</span></b>",
|
||||||
HtmlCompat.FROM_HTML_MODE_COMPACT
|
HtmlCompat.FROM_HTML_MODE_COMPACT
|
||||||
)
|
)
|
||||||
appNameText.text = appName
|
binding.appNameText.text = appName
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequiresApi(Build.VERSION_CODES.M)
|
||||||
|
override fun onResume() {
|
||||||
|
if (hasStoragePermission()) {
|
||||||
|
binding.storagePermission.checkImage.visibility = View.VISIBLE
|
||||||
|
binding.storagePermission.checkImage.imageTintList =
|
||||||
|
ColorStateList.valueOf(ThemeStore.accentColor(this))
|
||||||
|
}
|
||||||
|
if (hasAudioPermission()) {
|
||||||
|
binding.audioPermission.checkImage.visibility = View.VISIBLE
|
||||||
|
binding.audioPermission.checkImage.imageTintList =
|
||||||
|
ColorStateList.valueOf(ThemeStore.accentColor(this))
|
||||||
|
}
|
||||||
|
|
||||||
|
super.onResume()
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequiresApi(Build.VERSION_CODES.M)
|
||||||
|
private fun hasStoragePermission(): Boolean {
|
||||||
|
return checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequiresApi(Build.VERSION_CODES.M)
|
||||||
|
private fun hasAudioPermission(): Boolean {
|
||||||
|
return Settings.System.canWrite(this)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,7 @@ import code.name.monkey.appthemehelper.util.MaterialValueHelper
|
||||||
import code.name.monkey.retromusic.R
|
import code.name.monkey.retromusic.R
|
||||||
import code.name.monkey.retromusic.activities.base.AbsMusicServiceActivity
|
import code.name.monkey.retromusic.activities.base.AbsMusicServiceActivity
|
||||||
import code.name.monkey.retromusic.adapter.song.PlayingQueueAdapter
|
import code.name.monkey.retromusic.adapter.song.PlayingQueueAdapter
|
||||||
|
import code.name.monkey.retromusic.databinding.ActivityPlayingQueueBinding
|
||||||
import code.name.monkey.retromusic.extensions.accentColor
|
import code.name.monkey.retromusic.extensions.accentColor
|
||||||
import code.name.monkey.retromusic.extensions.surfaceColor
|
import code.name.monkey.retromusic.extensions.surfaceColor
|
||||||
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
||||||
|
@ -34,10 +35,10 @@ import com.h6ah4i.android.widget.advrecyclerview.draggable.RecyclerViewDragDropM
|
||||||
import com.h6ah4i.android.widget.advrecyclerview.swipeable.RecyclerViewSwipeManager
|
import com.h6ah4i.android.widget.advrecyclerview.swipeable.RecyclerViewSwipeManager
|
||||||
import com.h6ah4i.android.widget.advrecyclerview.touchguard.RecyclerViewTouchActionGuardManager
|
import com.h6ah4i.android.widget.advrecyclerview.touchguard.RecyclerViewTouchActionGuardManager
|
||||||
import com.h6ah4i.android.widget.advrecyclerview.utils.WrapperAdapterUtils
|
import com.h6ah4i.android.widget.advrecyclerview.utils.WrapperAdapterUtils
|
||||||
import kotlinx.android.synthetic.main.activity_playing_queue.*
|
|
||||||
|
|
||||||
open class PlayingQueueActivity : AbsMusicServiceActivity() {
|
open class PlayingQueueActivity : AbsMusicServiceActivity() {
|
||||||
|
|
||||||
|
private lateinit var binding: ActivityPlayingQueueBinding
|
||||||
private var wrappedAdapter: RecyclerView.Adapter<*>? = null
|
private var wrappedAdapter: RecyclerView.Adapter<*>? = null
|
||||||
private var recyclerViewDragDropManager: RecyclerViewDragDropManager? = null
|
private var recyclerViewDragDropManager: RecyclerViewDragDropManager? = null
|
||||||
private var recyclerViewSwipeManager: RecyclerViewSwipeManager? = null
|
private var recyclerViewSwipeManager: RecyclerViewSwipeManager? = null
|
||||||
|
@ -56,7 +57,8 @@ open class PlayingQueueActivity : AbsMusicServiceActivity() {
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
setDrawUnderStatusBar()
|
setDrawUnderStatusBar()
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
setContentView(R.layout.activity_playing_queue)
|
binding = ActivityPlayingQueueBinding.inflate(layoutInflater)
|
||||||
|
setContentView(binding.root)
|
||||||
setStatusbarColorAuto()
|
setStatusbarColorAuto()
|
||||||
setNavigationbarColorAuto()
|
setNavigationbarColorAuto()
|
||||||
setTaskDescriptionColorAuto()
|
setTaskDescriptionColorAuto()
|
||||||
|
@ -65,7 +67,7 @@ open class PlayingQueueActivity : AbsMusicServiceActivity() {
|
||||||
setupToolbar()
|
setupToolbar()
|
||||||
setUpRecyclerView()
|
setUpRecyclerView()
|
||||||
|
|
||||||
clearQueue.setOnClickListener {
|
binding.clearQueue.setOnClickListener {
|
||||||
MusicPlayerRemote.clearQueue()
|
MusicPlayerRemote.clearQueue()
|
||||||
}
|
}
|
||||||
checkForPadding()
|
checkForPadding()
|
||||||
|
@ -100,25 +102,25 @@ open class PlayingQueueActivity : AbsMusicServiceActivity() {
|
||||||
|
|
||||||
linearLayoutManager = LinearLayoutManager(this)
|
linearLayoutManager = LinearLayoutManager(this)
|
||||||
|
|
||||||
recyclerView.layoutManager = linearLayoutManager
|
binding.recyclerView.layoutManager = linearLayoutManager
|
||||||
recyclerView.adapter = wrappedAdapter
|
binding.recyclerView.adapter = wrappedAdapter
|
||||||
recyclerView.itemAnimator = animator
|
binding.recyclerView.itemAnimator = animator
|
||||||
recyclerViewTouchActionGuardManager?.attachRecyclerView(recyclerView)
|
recyclerViewTouchActionGuardManager?.attachRecyclerView(binding.recyclerView)
|
||||||
recyclerViewDragDropManager?.attachRecyclerView(recyclerView)
|
recyclerViewDragDropManager?.attachRecyclerView(binding.recyclerView)
|
||||||
recyclerViewSwipeManager?.attachRecyclerView(recyclerView)
|
recyclerViewSwipeManager?.attachRecyclerView(binding.recyclerView)
|
||||||
linearLayoutManager.scrollToPositionWithOffset(MusicPlayerRemote.position + 1, 0)
|
linearLayoutManager.scrollToPositionWithOffset(MusicPlayerRemote.position + 1, 0)
|
||||||
|
|
||||||
recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
|
binding.recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
|
||||||
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
|
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
|
||||||
super.onScrolled(recyclerView, dx, dy)
|
super.onScrolled(recyclerView, dx, dy)
|
||||||
if (dy > 0) {
|
if (dy > 0) {
|
||||||
clearQueue.shrink()
|
binding.clearQueue.shrink()
|
||||||
} else if (dy < 0) {
|
} else if (dy < 0) {
|
||||||
clearQueue.extend()
|
binding.clearQueue.extend()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
ThemedFastScroller.create(recyclerView)
|
ThemedFastScroller.create(binding.recyclerView)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun checkForPadding() {
|
private fun checkForPadding() {
|
||||||
|
@ -140,7 +142,7 @@ open class PlayingQueueActivity : AbsMusicServiceActivity() {
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateCurrentSong() {
|
private fun updateCurrentSong() {
|
||||||
toolbar.subtitle = getUpNextAndQueueTime()
|
binding.toolbar.subtitle = getUpNextAndQueueTime()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onPlayingMetaChanged() {
|
override fun onPlayingMetaChanged() {
|
||||||
|
@ -150,7 +152,7 @@ open class PlayingQueueActivity : AbsMusicServiceActivity() {
|
||||||
private fun updateQueuePosition() {
|
private fun updateQueuePosition() {
|
||||||
playingQueueAdapter?.setCurrent(MusicPlayerRemote.position)
|
playingQueueAdapter?.setCurrent(MusicPlayerRemote.position)
|
||||||
resetToCurrentPosition()
|
resetToCurrentPosition()
|
||||||
toolbar.subtitle = getUpNextAndQueueTime()
|
binding.toolbar.subtitle = getUpNextAndQueueTime()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateQueue() {
|
private fun updateQueue() {
|
||||||
|
@ -159,7 +161,7 @@ open class PlayingQueueActivity : AbsMusicServiceActivity() {
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun resetToCurrentPosition() {
|
private fun resetToCurrentPosition() {
|
||||||
recyclerView.stopScroll()
|
binding.recyclerView.stopScroll()
|
||||||
linearLayoutManager.scrollToPositionWithOffset(MusicPlayerRemote.position + 1, 0)
|
linearLayoutManager.scrollToPositionWithOffset(MusicPlayerRemote.position + 1, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -188,15 +190,15 @@ open class PlayingQueueActivity : AbsMusicServiceActivity() {
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setupToolbar() {
|
private fun setupToolbar() {
|
||||||
toolbar.subtitle = getUpNextAndQueueTime()
|
binding.toolbar.subtitle = getUpNextAndQueueTime()
|
||||||
toolbar.setBackgroundColor(surfaceColor())
|
binding.toolbar.setBackgroundColor(surfaceColor())
|
||||||
setSupportActionBar(toolbar)
|
setSupportActionBar(binding.toolbar)
|
||||||
clearQueue.backgroundTintList = ColorStateList.valueOf(accentColor())
|
binding.clearQueue.backgroundTintList = ColorStateList.valueOf(accentColor())
|
||||||
ColorStateList.valueOf(
|
ColorStateList.valueOf(
|
||||||
MaterialValueHelper.getPrimaryTextColor(this, ColorUtil.isColorLight(accentColor()))
|
MaterialValueHelper.getPrimaryTextColor(this, ColorUtil.isColorLight(accentColor()))
|
||||||
).apply {
|
).apply {
|
||||||
clearQueue.setTextColor(this)
|
binding.clearQueue.setTextColor(this)
|
||||||
clearQueue.iconTint = this
|
binding.clearQueue.iconTint = this
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,43 +29,45 @@ import code.name.monkey.retromusic.BuildConfig
|
||||||
import code.name.monkey.retromusic.Constants.PRO_VERSION_PRODUCT_ID
|
import code.name.monkey.retromusic.Constants.PRO_VERSION_PRODUCT_ID
|
||||||
import code.name.monkey.retromusic.R
|
import code.name.monkey.retromusic.R
|
||||||
import code.name.monkey.retromusic.activities.base.AbsBaseActivity
|
import code.name.monkey.retromusic.activities.base.AbsBaseActivity
|
||||||
|
import code.name.monkey.retromusic.databinding.ActivityProVersionBinding
|
||||||
import com.anjlab.android.iab.v3.BillingProcessor
|
import com.anjlab.android.iab.v3.BillingProcessor
|
||||||
import com.anjlab.android.iab.v3.TransactionDetails
|
import com.anjlab.android.iab.v3.TransactionDetails
|
||||||
import java.lang.ref.WeakReference
|
import java.lang.ref.WeakReference
|
||||||
import kotlinx.android.synthetic.main.activity_pro_version.*
|
|
||||||
|
|
||||||
class PurchaseActivity : AbsBaseActivity(), BillingProcessor.IBillingHandler {
|
class PurchaseActivity : AbsBaseActivity(), BillingProcessor.IBillingHandler {
|
||||||
|
|
||||||
|
private lateinit var binding: ActivityProVersionBinding
|
||||||
private lateinit var billingProcessor: BillingProcessor
|
private lateinit var billingProcessor: BillingProcessor
|
||||||
private var restorePurchaseAsyncTask: AsyncTask<*, *, *>? = null
|
private var restorePurchaseAsyncTask: AsyncTask<*, *, *>? = null
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
setDrawUnderStatusBar()
|
setDrawUnderStatusBar()
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
setContentView(R.layout.activity_pro_version)
|
binding = ActivityProVersionBinding.inflate(layoutInflater)
|
||||||
|
setContentView(binding.root)
|
||||||
setStatusbarColor(Color.TRANSPARENT)
|
setStatusbarColor(Color.TRANSPARENT)
|
||||||
setLightStatusbar(false)
|
setLightStatusbar(false)
|
||||||
setNavigationbarColor(Color.BLACK)
|
setNavigationbarColor(Color.BLACK)
|
||||||
setLightNavigationBar(false)
|
setLightNavigationBar(false)
|
||||||
toolbar.navigationIcon?.setTint(Color.WHITE)
|
binding.toolbar.navigationIcon?.setTint(Color.WHITE)
|
||||||
toolbar.setNavigationOnClickListener { onBackPressed() }
|
binding.toolbar.setNavigationOnClickListener { onBackPressed() }
|
||||||
|
|
||||||
restoreButton.isEnabled = false
|
binding.restoreButton.isEnabled = false
|
||||||
purchaseButton.isEnabled = false
|
binding.purchaseButton.isEnabled = false
|
||||||
|
|
||||||
billingProcessor = BillingProcessor(this, BuildConfig.GOOGLE_PLAY_LICENSING_KEY, this)
|
billingProcessor = BillingProcessor(this, BuildConfig.GOOGLE_PLAY_LICENSING_KEY, this)
|
||||||
|
|
||||||
MaterialUtil.setTint(purchaseButton, true)
|
MaterialUtil.setTint(binding.purchaseButton, true)
|
||||||
|
|
||||||
restoreButton.setOnClickListener {
|
binding.restoreButton.setOnClickListener {
|
||||||
if (restorePurchaseAsyncTask == null || restorePurchaseAsyncTask!!.status != AsyncTask.Status.RUNNING) {
|
if (restorePurchaseAsyncTask == null || restorePurchaseAsyncTask!!.status != AsyncTask.Status.RUNNING) {
|
||||||
restorePurchase()
|
restorePurchase()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
purchaseButton.setOnClickListener {
|
binding.purchaseButton.setOnClickListener {
|
||||||
billingProcessor.purchase(this@PurchaseActivity, PRO_VERSION_PRODUCT_ID)
|
billingProcessor.purchase(this@PurchaseActivity, PRO_VERSION_PRODUCT_ID)
|
||||||
}
|
}
|
||||||
bannerContainer.backgroundTintList =
|
binding.bannerContainer.backgroundTintList =
|
||||||
ColorStateList.valueOf(ThemeStore.accentColor(this))
|
ColorStateList.valueOf(ThemeStore.accentColor(this))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -99,8 +101,8 @@ class PurchaseActivity : AbsBaseActivity(), BillingProcessor.IBillingHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onBillingInitialized() {
|
override fun onBillingInitialized() {
|
||||||
restoreButton.isEnabled = true
|
binding.restoreButton.isEnabled = true
|
||||||
purchaseButton.isEnabled = true
|
binding.purchaseButton.isEnabled = true
|
||||||
}
|
}
|
||||||
|
|
||||||
public override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
public override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||||
|
|
|
@ -23,16 +23,19 @@ import code.name.monkey.appthemehelper.util.VersionUtils
|
||||||
import code.name.monkey.retromusic.R
|
import code.name.monkey.retromusic.R
|
||||||
import code.name.monkey.retromusic.activities.base.AbsBaseActivity
|
import code.name.monkey.retromusic.activities.base.AbsBaseActivity
|
||||||
import code.name.monkey.retromusic.appshortcuts.DynamicShortcutManager
|
import code.name.monkey.retromusic.appshortcuts.DynamicShortcutManager
|
||||||
|
import code.name.monkey.retromusic.databinding.ActivitySettingsBinding
|
||||||
import code.name.monkey.retromusic.extensions.applyToolbar
|
import code.name.monkey.retromusic.extensions.applyToolbar
|
||||||
import code.name.monkey.retromusic.extensions.findNavController
|
import code.name.monkey.retromusic.extensions.findNavController
|
||||||
import com.afollestad.materialdialogs.color.ColorChooserDialog
|
import com.afollestad.materialdialogs.MaterialDialog
|
||||||
import kotlinx.android.synthetic.main.activity_settings.*
|
import com.afollestad.materialdialogs.color.ColorCallback
|
||||||
|
|
||||||
class SettingsActivity : AbsBaseActivity(), ColorChooserDialog.ColorCallback {
|
class SettingsActivity : AbsBaseActivity(), ColorCallback {
|
||||||
|
private lateinit var binding: ActivitySettingsBinding
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
setDrawUnderStatusBar()
|
setDrawUnderStatusBar()
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
setContentView(R.layout.activity_settings)
|
binding = ActivitySettingsBinding.inflate(layoutInflater)
|
||||||
|
setContentView(binding.root)
|
||||||
setStatusbarColorAuto()
|
setStatusbarColorAuto()
|
||||||
setNavigationbarColorAuto()
|
setNavigationbarColorAuto()
|
||||||
setLightNavigationBar(true)
|
setLightNavigationBar(true)
|
||||||
|
@ -41,10 +44,11 @@ class SettingsActivity : AbsBaseActivity(), ColorChooserDialog.ColorCallback {
|
||||||
|
|
||||||
private fun setupToolbar() {
|
private fun setupToolbar() {
|
||||||
setTitle(R.string.action_settings)
|
setTitle(R.string.action_settings)
|
||||||
applyToolbar(toolbar)
|
applyToolbar(binding.toolbar)
|
||||||
val navController: NavController = findNavController(R.id.contentFrame)
|
val navController: NavController = findNavController(R.id.contentFrame)
|
||||||
navController.addOnDestinationChangedListener { _, _, _ ->
|
navController.addOnDestinationChangedListener { _, _, _ ->
|
||||||
toolbar.title = navController.currentDestination?.let { getStringFromDestination(it) }
|
binding.toolbar.title =
|
||||||
|
navController.currentDestination?.let { getStringFromDestination(it) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -68,24 +72,18 @@ class SettingsActivity : AbsBaseActivity(), ColorChooserDialog.ColorCallback {
|
||||||
return findNavController(R.id.contentFrame).navigateUp() || super.onSupportNavigateUp()
|
return findNavController(R.id.contentFrame).navigateUp() || super.onSupportNavigateUp()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onColorSelection(dialog: ColorChooserDialog, selectedColor: Int) {
|
|
||||||
when (dialog.title) {
|
|
||||||
R.string.accent_color -> {
|
|
||||||
ThemeStore.editTheme(this).accentColor(selectedColor).commit()
|
|
||||||
if (VersionUtils.hasNougatMR())
|
|
||||||
DynamicShortcutManager(this).updateDynamicShortcuts()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
recreate()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onColorChooserDismissed(dialog: ColorChooserDialog) {
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||||
if (item.itemId == android.R.id.home) {
|
if (item.itemId == android.R.id.home) {
|
||||||
onBackPressed()
|
onBackPressed()
|
||||||
}
|
}
|
||||||
return super.onOptionsItemSelected(item)
|
return super.onOptionsItemSelected(item)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun invoke(dialog: MaterialDialog, color: Int) {
|
||||||
|
ThemeStore.editTheme(this).accentColor(color).commit()
|
||||||
|
if (VersionUtils.hasNougatMR())
|
||||||
|
DynamicShortcutManager(this).updateDynamicShortcuts()
|
||||||
|
|
||||||
|
recreate()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,15 +26,14 @@ import androidx.core.view.drawToBitmap
|
||||||
import code.name.monkey.appthemehelper.ThemeStore
|
import code.name.monkey.appthemehelper.ThemeStore
|
||||||
import code.name.monkey.appthemehelper.util.ColorUtil
|
import code.name.monkey.appthemehelper.util.ColorUtil
|
||||||
import code.name.monkey.appthemehelper.util.MaterialValueHelper
|
import code.name.monkey.appthemehelper.util.MaterialValueHelper
|
||||||
import code.name.monkey.retromusic.R
|
|
||||||
import code.name.monkey.retromusic.activities.base.AbsBaseActivity
|
import code.name.monkey.retromusic.activities.base.AbsBaseActivity
|
||||||
|
import code.name.monkey.retromusic.databinding.ActivityShareInstagramBinding
|
||||||
|
import code.name.monkey.retromusic.glide.GlideApp
|
||||||
|
import code.name.monkey.retromusic.glide.RetroGlideExtension
|
||||||
import code.name.monkey.retromusic.glide.RetroMusicColoredTarget
|
import code.name.monkey.retromusic.glide.RetroMusicColoredTarget
|
||||||
import code.name.monkey.retromusic.glide.SongGlideRequest
|
|
||||||
import code.name.monkey.retromusic.model.Song
|
import code.name.monkey.retromusic.model.Song
|
||||||
import code.name.monkey.retromusic.util.Share
|
import code.name.monkey.retromusic.util.Share
|
||||||
import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
|
import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
|
||||||
import com.bumptech.glide.Glide
|
|
||||||
import kotlinx.android.synthetic.main.activity_share_instagram.*
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by hemanths on 2020-02-02.
|
* Created by hemanths on 2020-02-02.
|
||||||
|
@ -42,6 +41,8 @@ import kotlinx.android.synthetic.main.activity_share_instagram.*
|
||||||
|
|
||||||
class ShareInstagramStory : AbsBaseActivity() {
|
class ShareInstagramStory : AbsBaseActivity() {
|
||||||
|
|
||||||
|
private lateinit var binding: ActivityShareInstagramBinding
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val EXTRA_SONG = "extra_song"
|
const val EXTRA_SONG = "extra_song"
|
||||||
}
|
}
|
||||||
|
@ -57,32 +58,33 @@ class ShareInstagramStory : AbsBaseActivity() {
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
setDrawUnderStatusBar()
|
setDrawUnderStatusBar()
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
setContentView(R.layout.activity_share_instagram)
|
binding = ActivityShareInstagramBinding.inflate(layoutInflater)
|
||||||
|
setContentView(binding.root)
|
||||||
setStatusbarColor(Color.TRANSPARENT)
|
setStatusbarColor(Color.TRANSPARENT)
|
||||||
setNavigationbarColor(Color.BLACK)
|
setNavigationbarColor(Color.BLACK)
|
||||||
|
|
||||||
toolbar.setBackgroundColor(Color.TRANSPARENT)
|
binding.toolbar.setBackgroundColor(Color.TRANSPARENT)
|
||||||
setSupportActionBar(toolbar)
|
setSupportActionBar(binding.toolbar)
|
||||||
|
|
||||||
val song = intent.extras?.getParcelable<Song>(EXTRA_SONG)
|
val song = intent.extras?.getParcelable<Song>(EXTRA_SONG)
|
||||||
song?.let { songFinal ->
|
song?.let { songFinal ->
|
||||||
SongGlideRequest.Builder.from(Glide.with(this), songFinal)
|
GlideApp.with(this)
|
||||||
.checkIgnoreMediaStore(this@ShareInstagramStory)
|
.asBitmapPalette()
|
||||||
.generatePalette(this@ShareInstagramStory)
|
.songCoverOptions(songFinal)
|
||||||
.build()
|
.load(RetroGlideExtension.getSongModel(songFinal))
|
||||||
.into(object : RetroMusicColoredTarget(image) {
|
.into(object : RetroMusicColoredTarget(binding.image) {
|
||||||
override fun onColorReady(colors: MediaNotificationProcessor) {
|
override fun onColorReady(colors: MediaNotificationProcessor) {
|
||||||
val isColorLight = ColorUtil.isColorLight(colors.backgroundColor)
|
val isColorLight = ColorUtil.isColorLight(colors.backgroundColor)
|
||||||
setColors(isColorLight, colors.backgroundColor)
|
setColors(isColorLight, colors.backgroundColor)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
shareTitle.text = songFinal.title
|
binding.shareTitle.text = songFinal.title
|
||||||
shareText.text = songFinal.artistName
|
binding.shareText.text = songFinal.artistName
|
||||||
shareButton.setOnClickListener {
|
binding.shareButton.setOnClickListener {
|
||||||
val path: String = Media.insertImage(
|
val path: String = Media.insertImage(
|
||||||
contentResolver,
|
contentResolver,
|
||||||
mainContent.drawToBitmap(Bitmap.Config.ARGB_8888),
|
binding.mainContent.drawToBitmap(Bitmap.Config.ARGB_8888),
|
||||||
"Design", null
|
"Design", null
|
||||||
)
|
)
|
||||||
val uri = Uri.parse(path)
|
val uri = Uri.parse(path)
|
||||||
|
@ -92,24 +94,25 @@ class ShareInstagramStory : AbsBaseActivity() {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
shareButton.setTextColor(
|
binding.shareButton.setTextColor(
|
||||||
MaterialValueHelper.getPrimaryTextColor(
|
MaterialValueHelper.getPrimaryTextColor(
|
||||||
this,
|
this,
|
||||||
ColorUtil.isColorLight(ThemeStore.accentColor(this))
|
ColorUtil.isColorLight(ThemeStore.accentColor(this))
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
shareButton.backgroundTintList = ColorStateList.valueOf(ThemeStore.accentColor(this))
|
binding.shareButton.backgroundTintList =
|
||||||
|
ColorStateList.valueOf(ThemeStore.accentColor(this))
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setColors(colorLight: Boolean, color: Int) {
|
private fun setColors(colorLight: Boolean, color: Int) {
|
||||||
setLightStatusbar(colorLight)
|
setLightStatusbar(colorLight)
|
||||||
toolbar.setTitleTextColor(
|
binding.toolbar.setTitleTextColor(
|
||||||
MaterialValueHelper.getPrimaryTextColor(
|
MaterialValueHelper.getPrimaryTextColor(
|
||||||
this@ShareInstagramStory,
|
this@ShareInstagramStory,
|
||||||
colorLight
|
colorLight
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
toolbar.navigationIcon?.setTintList(
|
binding.toolbar.navigationIcon?.setTintList(
|
||||||
ColorStateList.valueOf(
|
ColorStateList.valueOf(
|
||||||
MaterialValueHelper.getPrimaryTextColor(
|
MaterialValueHelper.getPrimaryTextColor(
|
||||||
this@ShareInstagramStory,
|
this@ShareInstagramStory,
|
||||||
|
@ -117,7 +120,7 @@ class ShareInstagramStory : AbsBaseActivity() {
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
mainContent.background =
|
binding.mainContent.background =
|
||||||
GradientDrawable(
|
GradientDrawable(
|
||||||
GradientDrawable.Orientation.TOP_BOTTOM,
|
GradientDrawable.Orientation.TOP_BOTTOM,
|
||||||
intArrayOf(color, Color.BLACK)
|
intArrayOf(color, Color.BLACK)
|
||||||
|
|
|
@ -37,6 +37,7 @@ import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
|
||||||
import code.name.monkey.retromusic.BuildConfig
|
import code.name.monkey.retromusic.BuildConfig
|
||||||
import code.name.monkey.retromusic.R
|
import code.name.monkey.retromusic.R
|
||||||
import code.name.monkey.retromusic.activities.base.AbsBaseActivity
|
import code.name.monkey.retromusic.activities.base.AbsBaseActivity
|
||||||
|
import code.name.monkey.retromusic.databinding.ActivityDonationBinding
|
||||||
import code.name.monkey.retromusic.extensions.textColorPrimary
|
import code.name.monkey.retromusic.extensions.textColorPrimary
|
||||||
import code.name.monkey.retromusic.extensions.textColorSecondary
|
import code.name.monkey.retromusic.extensions.textColorSecondary
|
||||||
import com.anjlab.android.iab.v3.BillingProcessor
|
import com.anjlab.android.iab.v3.BillingProcessor
|
||||||
|
@ -44,10 +45,11 @@ import com.anjlab.android.iab.v3.SkuDetails
|
||||||
import com.anjlab.android.iab.v3.TransactionDetails
|
import com.anjlab.android.iab.v3.TransactionDetails
|
||||||
import java.lang.ref.WeakReference
|
import java.lang.ref.WeakReference
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import kotlinx.android.synthetic.main.activity_donation.*
|
|
||||||
|
|
||||||
class SupportDevelopmentActivity : AbsBaseActivity(), BillingProcessor.IBillingHandler {
|
class SupportDevelopmentActivity : AbsBaseActivity(), BillingProcessor.IBillingHandler {
|
||||||
|
|
||||||
|
lateinit var binding: ActivityDonationBinding
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
val TAG: String = SupportDevelopmentActivity::class.java.simpleName
|
val TAG: String = SupportDevelopmentActivity::class.java.simpleName
|
||||||
const val DONATION_PRODUCT_IDS = R.array.donation_ids
|
const val DONATION_PRODUCT_IDS = R.array.donation_ids
|
||||||
|
@ -72,6 +74,7 @@ class SupportDevelopmentActivity : AbsBaseActivity(), BillingProcessor.IBillingH
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
binding = ActivityDonationBinding.inflate(layoutInflater)
|
||||||
setContentView(R.layout.activity_donation)
|
setContentView(R.layout.activity_donation)
|
||||||
|
|
||||||
setStatusbarColorAuto()
|
setStatusbarColorAuto()
|
||||||
|
@ -82,15 +85,15 @@ class SupportDevelopmentActivity : AbsBaseActivity(), BillingProcessor.IBillingH
|
||||||
setupToolbar()
|
setupToolbar()
|
||||||
|
|
||||||
billingProcessor = BillingProcessor(this, BuildConfig.GOOGLE_PLAY_LICENSING_KEY, this)
|
billingProcessor = BillingProcessor(this, BuildConfig.GOOGLE_PLAY_LICENSING_KEY, this)
|
||||||
TintHelper.setTint(progress, ThemeStore.accentColor(this))
|
TintHelper.setTint(binding.progress, ThemeStore.accentColor(this))
|
||||||
donation.setTextColor(ThemeStore.accentColor(this))
|
binding.donation.setTextColor(ThemeStore.accentColor(this))
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setupToolbar() {
|
private fun setupToolbar() {
|
||||||
val toolbarColor = ATHUtil.resolveColor(this, R.attr.colorSurface)
|
val toolbarColor = ATHUtil.resolveColor(this, R.attr.colorSurface)
|
||||||
toolbar.setBackgroundColor(toolbarColor)
|
binding.toolbar.setBackgroundColor(toolbarColor)
|
||||||
ToolbarContentTintHelper.colorBackButton(toolbar)
|
ToolbarContentTintHelper.colorBackButton(binding.toolbar)
|
||||||
setSupportActionBar(toolbar)
|
setSupportActionBar(binding.toolbar)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onBillingInitialized() {
|
override fun onBillingInitialized() {
|
||||||
|
@ -146,8 +149,8 @@ private class SkuDetailsLoadAsyncTask(supportDevelopmentActivity: SupportDevelop
|
||||||
super.onPreExecute()
|
super.onPreExecute()
|
||||||
val supportDevelopmentActivity = weakReference.get() ?: return
|
val supportDevelopmentActivity = weakReference.get() ?: return
|
||||||
|
|
||||||
supportDevelopmentActivity.progressContainer.visibility = View.VISIBLE
|
supportDevelopmentActivity.binding.progressContainer.visibility = View.VISIBLE
|
||||||
supportDevelopmentActivity.recyclerView.visibility = View.GONE
|
supportDevelopmentActivity.binding.recyclerView.visibility = View.GONE
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun doInBackground(vararg params: Void): List<SkuDetails>? {
|
override fun doInBackground(vararg params: Void): List<SkuDetails>? {
|
||||||
|
@ -166,15 +169,17 @@ private class SkuDetailsLoadAsyncTask(supportDevelopmentActivity: SupportDevelop
|
||||||
val dialog = weakReference.get() ?: return
|
val dialog = weakReference.get() ?: return
|
||||||
|
|
||||||
if (skuDetails == null || skuDetails.isEmpty()) {
|
if (skuDetails == null || skuDetails.isEmpty()) {
|
||||||
dialog.progressContainer.visibility = View.GONE
|
dialog.binding.progressContainer.visibility = View.GONE
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
dialog.progressContainer.visibility = View.GONE
|
dialog.binding.progressContainer.visibility = View.GONE
|
||||||
dialog.recyclerView.itemAnimator = DefaultItemAnimator()
|
dialog.binding.recyclerView.apply {
|
||||||
dialog.recyclerView.layoutManager = GridLayoutManager(dialog, 2)
|
itemAnimator = DefaultItemAnimator()
|
||||||
dialog.recyclerView.adapter = SkuDetailsAdapter(dialog, skuDetails)
|
layoutManager = GridLayoutManager(dialog, 2)
|
||||||
dialog.recyclerView.visibility = View.VISIBLE
|
adapter = SkuDetailsAdapter(dialog, skuDetails)
|
||||||
|
visibility = View.VISIBLE
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -27,54 +27,58 @@ import code.name.monkey.appthemehelper.util.ColorUtil
|
||||||
import code.name.monkey.appthemehelper.util.MaterialValueHelper
|
import code.name.monkey.appthemehelper.util.MaterialValueHelper
|
||||||
import code.name.monkey.retromusic.Constants.USER_BANNER
|
import code.name.monkey.retromusic.Constants.USER_BANNER
|
||||||
import code.name.monkey.retromusic.Constants.USER_PROFILE
|
import code.name.monkey.retromusic.Constants.USER_PROFILE
|
||||||
import code.name.monkey.retromusic.R
|
|
||||||
import code.name.monkey.retromusic.activities.base.AbsBaseActivity
|
import code.name.monkey.retromusic.activities.base.AbsBaseActivity
|
||||||
|
import code.name.monkey.retromusic.databinding.ActivityUserInfoBinding
|
||||||
import code.name.monkey.retromusic.extensions.accentColor
|
import code.name.monkey.retromusic.extensions.accentColor
|
||||||
import code.name.monkey.retromusic.extensions.applyToolbar
|
import code.name.monkey.retromusic.extensions.applyToolbar
|
||||||
import code.name.monkey.retromusic.glide.ProfileBannerGlideRequest
|
import code.name.monkey.retromusic.glide.GlideApp
|
||||||
import code.name.monkey.retromusic.glide.UserProfileGlideRequest
|
import code.name.monkey.retromusic.glide.RetroGlideExtension
|
||||||
import code.name.monkey.retromusic.util.ImageUtil
|
import code.name.monkey.retromusic.util.ImageUtil
|
||||||
import code.name.monkey.retromusic.util.PreferenceUtil
|
import code.name.monkey.retromusic.util.PreferenceUtil
|
||||||
import com.bumptech.glide.Glide
|
import com.bumptech.glide.Glide
|
||||||
|
import com.bumptech.glide.load.DataSource
|
||||||
import com.bumptech.glide.load.engine.DiskCacheStrategy
|
import com.bumptech.glide.load.engine.DiskCacheStrategy
|
||||||
|
import com.bumptech.glide.load.engine.GlideException
|
||||||
import com.bumptech.glide.request.RequestListener
|
import com.bumptech.glide.request.RequestListener
|
||||||
import com.bumptech.glide.request.target.Target
|
import com.bumptech.glide.request.target.Target
|
||||||
import com.github.dhaval2404.imagepicker.ImagePicker
|
import com.github.dhaval2404.imagepicker.ImagePicker
|
||||||
import com.github.dhaval2404.imagepicker.constant.ImageProvider
|
import com.github.dhaval2404.imagepicker.constant.ImageProvider
|
||||||
import java.io.BufferedOutputStream
|
|
||||||
import java.io.File
|
|
||||||
import java.io.FileOutputStream
|
|
||||||
import java.io.IOException
|
|
||||||
import kotlinx.android.synthetic.main.activity_user_info.*
|
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
import java.io.BufferedOutputStream
|
||||||
|
import java.io.File
|
||||||
|
import java.io.FileOutputStream
|
||||||
|
import java.io.IOException
|
||||||
|
|
||||||
class UserInfoActivity : AbsBaseActivity() {
|
class UserInfoActivity : AbsBaseActivity() {
|
||||||
|
|
||||||
|
private lateinit var binding: ActivityUserInfoBinding
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
setContentView(R.layout.activity_user_info)
|
binding = ActivityUserInfoBinding.inflate(layoutInflater)
|
||||||
|
setContentView(binding.root)
|
||||||
setStatusbarColorAuto()
|
setStatusbarColorAuto()
|
||||||
setNavigationbarColorAuto()
|
setNavigationbarColorAuto()
|
||||||
setTaskDescriptionColorAuto()
|
setTaskDescriptionColorAuto()
|
||||||
setLightNavigationBar(true)
|
setLightNavigationBar(true)
|
||||||
applyToolbar(toolbar)
|
applyToolbar(binding.toolbar)
|
||||||
|
|
||||||
nameContainer.accentColor()
|
binding.nameContainer.accentColor()
|
||||||
name.setText(PreferenceUtil.userName)
|
binding.name.setText(PreferenceUtil.userName)
|
||||||
|
|
||||||
userImage.setOnClickListener {
|
binding.userImage.setOnClickListener {
|
||||||
pickNewPhoto()
|
pickNewPhoto()
|
||||||
}
|
}
|
||||||
|
|
||||||
bannerImage.setOnClickListener {
|
binding.bannerImage.setOnClickListener {
|
||||||
selectBannerImage()
|
selectBannerImage()
|
||||||
}
|
}
|
||||||
|
|
||||||
next.setOnClickListener {
|
binding.next.setOnClickListener {
|
||||||
val nameString = name.text.toString().trim { it <= ' ' }
|
val nameString = binding.name.text.toString().trim { it <= ' ' }
|
||||||
if (TextUtils.isEmpty(nameString)) {
|
if (TextUtils.isEmpty(nameString)) {
|
||||||
Toast.makeText(this, "Umm you're name can't be empty!", Toast.LENGTH_SHORT).show()
|
Toast.makeText(this, "Umm you're name can't be empty!", Toast.LENGTH_SHORT).show()
|
||||||
return@setOnClickListener
|
return@setOnClickListener
|
||||||
|
@ -86,23 +90,24 @@ class UserInfoActivity : AbsBaseActivity() {
|
||||||
|
|
||||||
val textColor =
|
val textColor =
|
||||||
MaterialValueHelper.getPrimaryTextColor(this, ColorUtil.isColorLight(accentColor()))
|
MaterialValueHelper.getPrimaryTextColor(this, ColorUtil.isColorLight(accentColor()))
|
||||||
next.backgroundTintList = ColorStateList.valueOf(accentColor())
|
binding.next.backgroundTintList = ColorStateList.valueOf(accentColor())
|
||||||
next.iconTint = ColorStateList.valueOf(textColor)
|
binding.next.iconTint = ColorStateList.valueOf(textColor)
|
||||||
next.setTextColor(textColor)
|
binding.next.setTextColor(textColor)
|
||||||
loadProfile()
|
loadProfile()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun loadProfile() {
|
private fun loadProfile() {
|
||||||
bannerImage?.let {
|
binding.bannerImage.let {
|
||||||
ProfileBannerGlideRequest.Builder.from(
|
GlideApp.with(this)
|
||||||
Glide.with(this),
|
.asBitmap()
|
||||||
ProfileBannerGlideRequest.getBannerModel()
|
.load(RetroGlideExtension.getBannerModel())
|
||||||
).build().into(it)
|
.profileBannerOptions(RetroGlideExtension.getBannerModel())
|
||||||
|
.into(it)
|
||||||
}
|
}
|
||||||
UserProfileGlideRequest.Builder.from(
|
GlideApp.with(this).asBitmap()
|
||||||
Glide.with(this),
|
.load(RetroGlideExtension.getUserModel())
|
||||||
UserProfileGlideRequest.getUserModel()
|
.userProfileOptions(RetroGlideExtension.getUserModel())
|
||||||
).build().into(userImage)
|
.into(binding.userImage)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||||
|
@ -145,31 +150,31 @@ class UserInfoActivity : AbsBaseActivity() {
|
||||||
|
|
||||||
private fun setAndSaveBannerImage(fileUri: Uri) {
|
private fun setAndSaveBannerImage(fileUri: Uri) {
|
||||||
Glide.with(this)
|
Glide.with(this)
|
||||||
.load(fileUri)
|
|
||||||
.asBitmap()
|
.asBitmap()
|
||||||
|
.load(fileUri)
|
||||||
.diskCacheStrategy(DiskCacheStrategy.NONE)
|
.diskCacheStrategy(DiskCacheStrategy.NONE)
|
||||||
.listener(object : RequestListener<Any, Bitmap> {
|
.listener(object : RequestListener<Bitmap> {
|
||||||
override fun onException(
|
|
||||||
e: java.lang.Exception?,
|
|
||||||
model: Any?,
|
|
||||||
target: Target<Bitmap>?,
|
|
||||||
isFirstResource: Boolean
|
|
||||||
): Boolean {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onResourceReady(
|
override fun onResourceReady(
|
||||||
resource: Bitmap?,
|
resource: Bitmap?,
|
||||||
model: Any?,
|
model: Any?,
|
||||||
target: Target<Bitmap>?,
|
target: Target<Bitmap>?,
|
||||||
isFromMemoryCache: Boolean,
|
dataSource: DataSource?,
|
||||||
isFirstResource: Boolean
|
isFirstResource: Boolean
|
||||||
): Boolean {
|
): Boolean {
|
||||||
resource?.let { saveImage(it, USER_BANNER) }
|
resource?.let { saveImage(it, USER_BANNER) }
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onLoadFailed(
|
||||||
|
e: GlideException?,
|
||||||
|
model: Any?,
|
||||||
|
target: Target<Bitmap>?,
|
||||||
|
isFirstResource: Boolean
|
||||||
|
): Boolean {
|
||||||
|
return false
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.into(bannerImage)
|
.into(binding.bannerImage)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun saveImage(bitmap: Bitmap, fileName: String) {
|
private fun saveImage(bitmap: Bitmap, fileName: String) {
|
||||||
|
@ -195,31 +200,31 @@ class UserInfoActivity : AbsBaseActivity() {
|
||||||
|
|
||||||
private fun setAndSaveUserImage(fileUri: Uri) {
|
private fun setAndSaveUserImage(fileUri: Uri) {
|
||||||
Glide.with(this)
|
Glide.with(this)
|
||||||
.load(fileUri)
|
|
||||||
.asBitmap()
|
.asBitmap()
|
||||||
|
.load(fileUri)
|
||||||
.diskCacheStrategy(DiskCacheStrategy.NONE)
|
.diskCacheStrategy(DiskCacheStrategy.NONE)
|
||||||
.listener(object : RequestListener<Any, Bitmap> {
|
.listener(object : RequestListener<Bitmap> {
|
||||||
override fun onException(
|
|
||||||
e: java.lang.Exception?,
|
|
||||||
model: Any?,
|
|
||||||
target: Target<Bitmap>?,
|
|
||||||
isFirstResource: Boolean
|
|
||||||
): Boolean {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onResourceReady(
|
override fun onResourceReady(
|
||||||
resource: Bitmap?,
|
resource: Bitmap?,
|
||||||
model: Any?,
|
model: Any?,
|
||||||
target: Target<Bitmap>?,
|
target: Target<Bitmap>?,
|
||||||
isFromMemoryCache: Boolean,
|
dataSource: DataSource?,
|
||||||
isFirstResource: Boolean
|
isFirstResource: Boolean
|
||||||
): Boolean {
|
): Boolean {
|
||||||
resource?.let { saveImage(it, USER_PROFILE) }
|
resource?.let { saveImage(it, USER_PROFILE) }
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onLoadFailed(
|
||||||
|
e: GlideException?,
|
||||||
|
model: Any?,
|
||||||
|
target: Target<Bitmap>?,
|
||||||
|
isFirstResource: Boolean
|
||||||
|
): Boolean {
|
||||||
|
return false
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.into(userImage)
|
.into(binding.userImage)
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
|
@ -5,34 +5,40 @@ import android.content.pm.PackageInfo;
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
import android.graphics.Color;
|
import android.graphics.Color;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.webkit.WebView;
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.appcompat.widget.Toolbar;
|
import androidx.core.widget.NestedScrollView;
|
||||||
import code.name.monkey.appthemehelper.ThemeStore;
|
|
||||||
import code.name.monkey.appthemehelper.util.ATHUtil;
|
|
||||||
import code.name.monkey.appthemehelper.util.ColorUtil;
|
|
||||||
import code.name.monkey.appthemehelper.util.MaterialValueHelper;
|
|
||||||
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper;
|
|
||||||
import code.name.monkey.retromusic.R;
|
|
||||||
import code.name.monkey.retromusic.activities.base.AbsBaseActivity;
|
|
||||||
import code.name.monkey.retromusic.util.PreferenceUtil;
|
|
||||||
import java.io.BufferedReader;
|
import java.io.BufferedReader;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
|
||||||
|
import code.name.monkey.appthemehelper.ThemeStore;
|
||||||
|
import code.name.monkey.appthemehelper.util.ATHUtil;
|
||||||
|
import code.name.monkey.appthemehelper.util.ColorUtil;
|
||||||
|
import code.name.monkey.appthemehelper.util.MaterialValueHelper;
|
||||||
|
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper;
|
||||||
|
import code.name.monkey.retromusic.Constants;
|
||||||
|
import code.name.monkey.retromusic.R;
|
||||||
|
import code.name.monkey.retromusic.activities.base.AbsBaseActivity;
|
||||||
|
import code.name.monkey.retromusic.databinding.ActivityWhatsNewBinding;
|
||||||
|
import code.name.monkey.retromusic.extensions.ColorExtKt;
|
||||||
|
import code.name.monkey.retromusic.util.PreferenceUtil;
|
||||||
|
import code.name.monkey.retromusic.util.RetroUtil;
|
||||||
|
|
||||||
public class WhatsNewActivity extends AbsBaseActivity {
|
public class WhatsNewActivity extends AbsBaseActivity {
|
||||||
|
|
||||||
private static String colorToCSS(int color) {
|
private static String colorToCSS(int color) {
|
||||||
return String.format(
|
return String.format(
|
||||||
Locale.getDefault(),
|
Locale.getDefault(),
|
||||||
"rgba(%d, %d, %d, %d)",
|
"rgba(%d, %d, %d, %d)",
|
||||||
Color.red(color),
|
Color.red(color),
|
||||||
Color.green(color),
|
Color.green(color),
|
||||||
Color.blue(color),
|
Color.blue(color),
|
||||||
Color.alpha(color)); // on API 29, WebView doesn't load with hex colors
|
Color.alpha(color)); // on API 29, WebView doesn't load with hex colors
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void setChangelogRead(@NonNull Context context) {
|
private static void setChangelogRead(@NonNull Context context) {
|
||||||
|
@ -49,16 +55,15 @@ public class WhatsNewActivity extends AbsBaseActivity {
|
||||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||||
setDrawUnderStatusBar();
|
setDrawUnderStatusBar();
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
setContentView(R.layout.activity_whats_new);
|
ActivityWhatsNewBinding binding = ActivityWhatsNewBinding.inflate(getLayoutInflater());
|
||||||
|
setContentView(binding.getRoot());
|
||||||
setStatusbarColorAuto();
|
setStatusbarColorAuto();
|
||||||
setNavigationbarColorAuto();
|
setNavigationbarColorAuto();
|
||||||
setTaskDescriptionColorAuto();
|
setTaskDescriptionColorAuto();
|
||||||
|
|
||||||
WebView webView = findViewById(R.id.webView);
|
binding.toolbar.setBackgroundColor(ATHUtil.INSTANCE.resolveColor(this, R.attr.colorSurface));
|
||||||
Toolbar toolbar = findViewById(R.id.toolbar);
|
binding.toolbar.setNavigationOnClickListener(v -> onBackPressed());
|
||||||
toolbar.setBackgroundColor(ATHUtil.INSTANCE.resolveColor(this, R.attr.colorSurface));
|
ToolbarContentTintHelper.colorBackButton(binding.toolbar);
|
||||||
toolbar.setNavigationOnClickListener(v -> onBackPressed());
|
|
||||||
ToolbarContentTintHelper.colorBackButton(toolbar);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
StringBuilder buf = new StringBuilder();
|
StringBuilder buf = new StringBuilder();
|
||||||
|
@ -74,38 +79,50 @@ public class WhatsNewActivity extends AbsBaseActivity {
|
||||||
final boolean isDark = ATHUtil.INSTANCE.isWindowBackgroundDark(this);
|
final boolean isDark = ATHUtil.INSTANCE.isWindowBackgroundDark(this);
|
||||||
final int accentColor = ThemeStore.Companion.accentColor(this);
|
final int accentColor = ThemeStore.Companion.accentColor(this);
|
||||||
final String backgroundColor =
|
final String backgroundColor =
|
||||||
colorToCSS(
|
colorToCSS(
|
||||||
ATHUtil.INSTANCE.resolveColor(
|
ATHUtil.INSTANCE.resolveColor(
|
||||||
this, R.attr.colorSurface, Color.parseColor(isDark ? "#424242" : "#ffffff")));
|
this, R.attr.colorSurface, Color.parseColor(isDark ? "#424242" : "#ffffff")));
|
||||||
final String contentColor = colorToCSS(Color.parseColor(isDark ? "#ffffff" : "#000000"));
|
final String contentColor = colorToCSS(Color.parseColor(isDark ? "#ffffff" : "#000000"));
|
||||||
final String textColor = colorToCSS(Color.parseColor(isDark ? "#60FFFFFF" : "#80000000"));
|
final String textColor = colorToCSS(Color.parseColor(isDark ? "#60FFFFFF" : "#80000000"));
|
||||||
final String accentColorString = colorToCSS(ThemeStore.Companion.accentColor(this));
|
final String accentColorString = colorToCSS(ThemeStore.Companion.accentColor(this));
|
||||||
|
final String cardBackgroundColor = colorToCSS(Color.parseColor(isDark ? "#353535" : "#ffffff"));
|
||||||
final String accentTextColor =
|
final String accentTextColor =
|
||||||
colorToCSS(
|
colorToCSS(
|
||||||
MaterialValueHelper.getPrimaryTextColor(
|
MaterialValueHelper.getPrimaryTextColor(
|
||||||
this, ColorUtil.INSTANCE.isColorLight(accentColor)));
|
this, ColorUtil.INSTANCE.isColorLight(accentColor)));
|
||||||
final String changeLog =
|
final String changeLog =
|
||||||
buf.toString()
|
buf.toString()
|
||||||
.replace(
|
.replace(
|
||||||
"{style-placeholder}",
|
"{style-placeholder}",
|
||||||
String.format(
|
String.format(
|
||||||
"body { background-color: %s; color: %s; } li {color: %s;} .colorHeader {background-color: %s; color: %s;} .tag {color: %s;}",
|
"body { background-color: %s; color: %s; } li {color: %s;} h3 {color: %s;} .tag {color: %s;} div{background-color: %s;}",
|
||||||
backgroundColor,
|
backgroundColor,
|
||||||
contentColor,
|
contentColor,
|
||||||
textColor,
|
textColor,
|
||||||
accentColorString,
|
accentColorString,
|
||||||
accentTextColor,
|
accentColorString,
|
||||||
accentColorString))
|
cardBackgroundColor))
|
||||||
.replace("{link-color}", colorToCSS(ThemeStore.Companion.accentColor(this)))
|
.replace("{link-color}", colorToCSS(ThemeStore.Companion.accentColor(this)))
|
||||||
.replace(
|
.replace(
|
||||||
"{link-color-active}",
|
"{link-color-active}",
|
||||||
colorToCSS(
|
colorToCSS(
|
||||||
ColorUtil.INSTANCE.lightenColor(ThemeStore.Companion.accentColor(this))));
|
ColorUtil.INSTANCE.lightenColor(ThemeStore.Companion.accentColor(this))));
|
||||||
webView.loadData(changeLog, "text/html", "UTF-8");
|
binding.webView.loadData(changeLog, "text/html", "UTF-8");
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
webView.loadData(
|
binding.webView.loadData(
|
||||||
"<h1>Unable to load</h1><p>" + e.getLocalizedMessage() + "</p>", "text/html", "UTF-8");
|
"<h1>Unable to load</h1><p>" + e.getLocalizedMessage() + "</p>", "text/html", "UTF-8");
|
||||||
}
|
}
|
||||||
setChangelogRead(this);
|
setChangelogRead(this);
|
||||||
|
binding.tgFab.setOnClickListener(v -> RetroUtil.openUrl(this, Constants.TELEGRAM_CHANGE_LOG));
|
||||||
|
ColorExtKt.accentColor(binding.tgFab);
|
||||||
|
binding.tgFab.shrink();
|
||||||
|
binding.container.setOnScrollChangeListener((NestedScrollView.OnScrollChangeListener) (v, scrollX, scrollY, oldScrollX, oldScrollY) -> {
|
||||||
|
int dy = scrollY - oldScrollY;
|
||||||
|
if (dy > 0) {
|
||||||
|
binding.tgFab.shrink();
|
||||||
|
} else if (dy < 0) {
|
||||||
|
binding.tgFab.extend();
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,141 @@
|
||||||
|
package code.name.monkey.retromusic.activities.base
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.ViewStub
|
||||||
|
import code.name.monkey.retromusic.R
|
||||||
|
import code.name.monkey.retromusic.cast.CastHelper
|
||||||
|
import code.name.monkey.retromusic.cast.RetroSessionManager
|
||||||
|
import code.name.monkey.retromusic.cast.RetroWebServer
|
||||||
|
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
||||||
|
import com.google.android.gms.cast.framework.CastContext
|
||||||
|
import com.google.android.gms.cast.framework.CastSession
|
||||||
|
import com.google.android.gms.common.ConnectionResult
|
||||||
|
import com.google.android.gms.common.GoogleApiAvailability
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
|
||||||
|
abstract class AbsCastActivity : AbsSlidingMusicPanelActivity() {
|
||||||
|
|
||||||
|
private var mCastSession: CastSession? = null
|
||||||
|
private lateinit var castContext: CastContext
|
||||||
|
private var webServer: RetroWebServer? = null
|
||||||
|
private var playServicesAvailable: Boolean = false
|
||||||
|
|
||||||
|
private val sessionManagerListener by lazy {
|
||||||
|
object : RetroSessionManager {
|
||||||
|
override fun onSessionStarting(castSession: CastSession) {
|
||||||
|
invalidateOptionsMenu()
|
||||||
|
webServer = RetroWebServer.getInstance(this@AbsCastActivity)
|
||||||
|
webServer?.start()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onSessionStarted(castSession: CastSession, p1: String) {
|
||||||
|
invalidateOptionsMenu()
|
||||||
|
mCastSession = castSession
|
||||||
|
loadCastQueue(MusicPlayerRemote.position)
|
||||||
|
inflateCastController()
|
||||||
|
MusicPlayerRemote.isCasting = true
|
||||||
|
setAllowDragging(false)
|
||||||
|
collapsePanel()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onSessionEnding(p0: CastSession) {
|
||||||
|
invalidateOptionsMenu()
|
||||||
|
webServer?.stop()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onSessionEnded(castSession: CastSession, p1: Int) {
|
||||||
|
invalidateOptionsMenu()
|
||||||
|
if (mCastSession == castSession) {
|
||||||
|
mCastSession = null
|
||||||
|
}
|
||||||
|
MusicPlayerRemote.isCasting = false
|
||||||
|
setAllowDragging(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onSessionResumed(castSession: CastSession, p1: Boolean) {
|
||||||
|
invalidateOptionsMenu()
|
||||||
|
mCastSession = castSession
|
||||||
|
loadCastQueue(MusicPlayerRemote.position)
|
||||||
|
inflateCastController()
|
||||||
|
MusicPlayerRemote.isCasting = true
|
||||||
|
setAllowDragging(false)
|
||||||
|
collapsePanel()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onSessionSuspended(castSession: CastSession, p1: Int) {
|
||||||
|
invalidateOptionsMenu()
|
||||||
|
if (mCastSession == castSession) {
|
||||||
|
mCastSession = null
|
||||||
|
}
|
||||||
|
MusicPlayerRemote.isCasting = false
|
||||||
|
setAllowDragging(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
playServicesAvailable = try {
|
||||||
|
GoogleApiAvailability
|
||||||
|
.getInstance().isGooglePlayServicesAvailable(this) == ConnectionResult.SUCCESS
|
||||||
|
} catch (e: Exception) {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
if (playServicesAvailable) {
|
||||||
|
setupCast()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setupCast() {
|
||||||
|
castContext = CastContext.getSharedInstance(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onResume() {
|
||||||
|
if (playServicesAvailable) {
|
||||||
|
castContext.sessionManager.addSessionManagerListener(
|
||||||
|
sessionManagerListener,
|
||||||
|
CastSession::class.java
|
||||||
|
)
|
||||||
|
if (mCastSession == null) {
|
||||||
|
mCastSession = castContext.sessionManager.currentCastSession
|
||||||
|
}
|
||||||
|
}
|
||||||
|
super.onResume()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onStop() {
|
||||||
|
super.onStop()
|
||||||
|
mCastSession = null
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun songChanged(position: Int) {
|
||||||
|
loadCastQueue(position)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun loadCastQueue(position: Int) {
|
||||||
|
if (!MusicPlayerRemote.playingQueue.isNullOrEmpty()) {
|
||||||
|
mCastSession?.let {
|
||||||
|
CastHelper.castQueue(
|
||||||
|
it,
|
||||||
|
MusicPlayerRemote.playingQueue,
|
||||||
|
position,
|
||||||
|
MusicPlayerRemote.songProgressMillis.toLong()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
mCastSession?.let { CastHelper.castSong(it, MusicPlayerRemote.currentSong) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onPlayingMetaChanged() {
|
||||||
|
super.onPlayingMetaChanged()
|
||||||
|
if (playServicesAvailable) {
|
||||||
|
songChanged(MusicPlayerRemote.position)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun inflateCastController() {
|
||||||
|
findViewById<ViewStub>(R.id.cast_stub)?.inflate()
|
||||||
|
}
|
||||||
|
}
|
|
@ -15,12 +15,7 @@
|
||||||
package code.name.monkey.retromusic.activities.base
|
package code.name.monkey.retromusic.activities.base
|
||||||
|
|
||||||
import android.Manifest
|
import android.Manifest
|
||||||
import android.content.BroadcastReceiver
|
import android.content.*
|
||||||
import android.content.ComponentName
|
|
||||||
import android.content.Context
|
|
||||||
import android.content.Intent
|
|
||||||
import android.content.IntentFilter
|
|
||||||
import android.content.ServiceConnection
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.os.IBinder
|
import android.os.IBinder
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
@ -30,11 +25,11 @@ import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
||||||
import code.name.monkey.retromusic.interfaces.IMusicServiceEventListener
|
import code.name.monkey.retromusic.interfaces.IMusicServiceEventListener
|
||||||
import code.name.monkey.retromusic.repository.RealRepository
|
import code.name.monkey.retromusic.repository.RealRepository
|
||||||
import code.name.monkey.retromusic.service.MusicService.*
|
import code.name.monkey.retromusic.service.MusicService.*
|
||||||
import java.lang.ref.WeakReference
|
|
||||||
import java.util.*
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import org.koin.android.ext.android.inject
|
import org.koin.android.ext.android.inject
|
||||||
|
import java.lang.ref.WeakReference
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
abstract class AbsMusicServiceActivity : AbsBaseActivity(), IMusicServiceEventListener {
|
abstract class AbsMusicServiceActivity : AbsBaseActivity(), IMusicServiceEventListener {
|
||||||
|
|
||||||
|
@ -166,6 +161,12 @@ abstract class AbsMusicServiceActivity : AbsBaseActivity(), IMusicServiceEventLi
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onFavoriteStateChanged() {
|
||||||
|
for (listener in mMusicServiceEventListeners) {
|
||||||
|
listener.onFavoriteStateChanged()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun onHasPermissionsChanged(hasPermissions: Boolean) {
|
override fun onHasPermissionsChanged(hasPermissions: Boolean) {
|
||||||
super.onHasPermissionsChanged(hasPermissions)
|
super.onHasPermissionsChanged(hasPermissions)
|
||||||
val intent = Intent(MEDIA_STORE_CHANGED)
|
val intent = Intent(MEDIA_STORE_CHANGED)
|
||||||
|
@ -194,7 +195,8 @@ abstract class AbsMusicServiceActivity : AbsBaseActivity(), IMusicServiceEventLi
|
||||||
val activity = reference.get()
|
val activity = reference.get()
|
||||||
if (activity != null && action != null) {
|
if (activity != null && action != null) {
|
||||||
when (action) {
|
when (action) {
|
||||||
FAVORITE_STATE_CHANGED, META_CHANGED -> activity.onPlayingMetaChanged()
|
FAVORITE_STATE_CHANGED -> activity.onFavoriteStateChanged()
|
||||||
|
META_CHANGED -> activity.onPlayingMetaChanged()
|
||||||
QUEUE_CHANGED -> activity.onQueueChanged()
|
QUEUE_CHANGED -> activity.onQueueChanged()
|
||||||
PLAY_STATE_CHANGED -> activity.onPlayStateChanged()
|
PLAY_STATE_CHANGED -> activity.onPlayStateChanged()
|
||||||
REPEAT_MODE_CHANGED -> activity.onRepeatModeChanged()
|
REPEAT_MODE_CHANGED -> activity.onRepeatModeChanged()
|
||||||
|
|
|
@ -14,7 +14,6 @@
|
||||||
*/
|
*/
|
||||||
package code.name.monkey.retromusic.activities.base
|
package code.name.monkey.retromusic.activities.base
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
|
||||||
import android.graphics.Color
|
import android.graphics.Color
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.View
|
import android.view.View
|
||||||
|
@ -29,6 +28,8 @@ import code.name.monkey.appthemehelper.util.ATHUtil
|
||||||
import code.name.monkey.appthemehelper.util.ColorUtil
|
import code.name.monkey.appthemehelper.util.ColorUtil
|
||||||
import code.name.monkey.retromusic.R
|
import code.name.monkey.retromusic.R
|
||||||
import code.name.monkey.retromusic.RetroBottomSheetBehavior
|
import code.name.monkey.retromusic.RetroBottomSheetBehavior
|
||||||
|
import code.name.monkey.retromusic.databinding.ActivityMainContentBinding
|
||||||
|
import code.name.monkey.retromusic.databinding.SlidingMusicPanelLayoutBinding
|
||||||
import code.name.monkey.retromusic.extensions.*
|
import code.name.monkey.retromusic.extensions.*
|
||||||
import code.name.monkey.retromusic.fragments.LibraryViewModel
|
import code.name.monkey.retromusic.fragments.LibraryViewModel
|
||||||
import code.name.monkey.retromusic.fragments.MiniPlayerFragment
|
import code.name.monkey.retromusic.fragments.MiniPlayerFragment
|
||||||
|
@ -57,12 +58,12 @@ import code.name.monkey.retromusic.model.CategoryInfo
|
||||||
import code.name.monkey.retromusic.util.PreferenceUtil
|
import code.name.monkey.retromusic.util.PreferenceUtil
|
||||||
import code.name.monkey.retromusic.views.BottomNavigationBarTinted
|
import code.name.monkey.retromusic.views.BottomNavigationBarTinted
|
||||||
import com.google.android.material.bottomsheet.BottomSheetBehavior.*
|
import com.google.android.material.bottomsheet.BottomSheetBehavior.*
|
||||||
import kotlinx.android.synthetic.main.sliding_music_panel_layout.*
|
|
||||||
import org.koin.androidx.viewmodel.ext.android.viewModel
|
import org.koin.androidx.viewmodel.ext.android.viewModel
|
||||||
|
|
||||||
abstract class AbsSlidingMusicPanelActivity : AbsMusicServiceActivity() {
|
abstract class AbsSlidingMusicPanelActivity : AbsMusicServiceActivity() {
|
||||||
companion object {
|
companion object {
|
||||||
val TAG: String = AbsSlidingMusicPanelActivity::class.java.simpleName
|
val TAG: String = AbsSlidingMusicPanelActivity::class.java.simpleName
|
||||||
|
var fromNotification: Boolean = false
|
||||||
}
|
}
|
||||||
|
|
||||||
protected val libraryViewModel by viewModel<LibraryViewModel>()
|
protected val libraryViewModel by viewModel<LibraryViewModel>()
|
||||||
|
@ -75,16 +76,17 @@ abstract class AbsSlidingMusicPanelActivity : AbsMusicServiceActivity() {
|
||||||
private var lightStatusBar: Boolean = false
|
private var lightStatusBar: Boolean = false
|
||||||
private var lightNavigationBar: Boolean = false
|
private var lightNavigationBar: Boolean = false
|
||||||
private var paletteColor: Int = Color.WHITE
|
private var paletteColor: Int = Color.WHITE
|
||||||
protected abstract fun createContentView(): View
|
protected abstract fun createContentView(): SlidingMusicPanelLayoutBinding
|
||||||
private val panelState: Int
|
private val panelState: Int
|
||||||
get() = bottomSheetBehavior.state
|
get() = bottomSheetBehavior.state
|
||||||
|
private lateinit var binding: SlidingMusicPanelLayoutBinding
|
||||||
private val bottomSheetCallbackList = object : BottomSheetCallback() {
|
private val bottomSheetCallbackList = object : BottomSheetCallback() {
|
||||||
|
|
||||||
|
|
||||||
override fun onSlide(bottomSheet: View, slideOffset: Float) {
|
override fun onSlide(bottomSheet: View, slideOffset: Float) {
|
||||||
setMiniPlayerAlphaProgress(slideOffset)
|
setMiniPlayerAlphaProgress(slideOffset)
|
||||||
dimBackground.show()
|
binding.dimBackground.show()
|
||||||
dimBackground.alpha = slideOffset
|
binding.dimBackground.alpha = slideOffset
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onStateChanged(bottomSheet: View, newState: Int) {
|
override fun onStateChanged(bottomSheet: View, newState: Int) {
|
||||||
|
@ -94,9 +96,17 @@ abstract class AbsSlidingMusicPanelActivity : AbsMusicServiceActivity() {
|
||||||
}
|
}
|
||||||
STATE_COLLAPSED -> {
|
STATE_COLLAPSED -> {
|
||||||
onPanelCollapsed()
|
onPanelCollapsed()
|
||||||
dimBackground.hide()
|
binding.dimBackground.hide()
|
||||||
|
if (fromNotification) {
|
||||||
|
hideBottomBar(MusicPlayerRemote.playingQueue.isEmpty())
|
||||||
|
fromNotification = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
STATE_SETTLING, STATE_DRAGGING -> {
|
||||||
|
if (fromNotification) {
|
||||||
|
getBottomNavigationView().isVisible = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> {
|
else -> {
|
||||||
println("Do something")
|
println("Do something")
|
||||||
}
|
}
|
||||||
|
@ -108,23 +118,25 @@ abstract class AbsSlidingMusicPanelActivity : AbsMusicServiceActivity() {
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
setContentView(createContentView())
|
binding = createContentView()
|
||||||
|
setContentView(binding.root)
|
||||||
chooseFragmentForTheme()
|
chooseFragmentForTheme()
|
||||||
setupSlidingUpPanel()
|
setupSlidingUpPanel()
|
||||||
setupBottomSheet()
|
setupBottomSheet()
|
||||||
updateColor()
|
updateColor()
|
||||||
|
|
||||||
val themeColor = resolveColor(android.R.attr.windowBackground, Color.GRAY)
|
val themeColor = resolveColor(android.R.attr.windowBackground, Color.GRAY)
|
||||||
dimBackground.setBackgroundColor(ColorUtil.withAlpha(themeColor, 0.5f))
|
binding.dimBackground.setBackgroundColor(ColorUtil.withAlpha(themeColor, 0.5f))
|
||||||
dimBackground.setOnClickListener {
|
binding.dimBackground.setOnClickListener {
|
||||||
println("dimBackground")
|
println("dimBackground")
|
||||||
collapsePanel()
|
collapsePanel()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setupBottomSheet() {
|
private fun setupBottomSheet() {
|
||||||
bottomSheetBehavior = from(slidingPanel) as RetroBottomSheetBehavior
|
bottomSheetBehavior = from(binding.slidingPanel) as RetroBottomSheetBehavior
|
||||||
bottomSheetBehavior.addBottomSheetCallback(bottomSheetCallbackList)
|
bottomSheetBehavior.addBottomSheetCallback(bottomSheetCallbackList)
|
||||||
|
bottomSheetBehavior.maxWidth = resources.displayMetrics.widthPixels
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onResume() {
|
override fun onResume() {
|
||||||
|
@ -142,14 +154,13 @@ abstract class AbsSlidingMusicPanelActivity : AbsMusicServiceActivity() {
|
||||||
bottomSheetBehavior.removeBottomSheetCallback(bottomSheetCallbackList)
|
bottomSheetBehavior.removeBottomSheetCallback(bottomSheetCallbackList)
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("InflateParams")
|
protected fun wrapSlidingMusicPanel(): SlidingMusicPanelLayoutBinding {
|
||||||
protected fun wrapSlidingMusicPanel(): View {
|
val slidingMusicPanelLayoutBinding =
|
||||||
val slidingMusicPanelLayout =
|
SlidingMusicPanelLayoutBinding.inflate(layoutInflater)
|
||||||
layoutInflater.inflate(R.layout.sliding_music_panel_layout, null)
|
|
||||||
val contentContainer: ViewGroup =
|
val contentContainer: ViewGroup =
|
||||||
slidingMusicPanelLayout.findViewById(R.id.mainContentFrame)
|
slidingMusicPanelLayoutBinding.mainContentFrame
|
||||||
layoutInflater.inflate(R.layout.activity_main_content, contentContainer)
|
ActivityMainContentBinding.inflate(layoutInflater, contentContainer, true)
|
||||||
return slidingMusicPanelLayout
|
return slidingMusicPanelLayoutBinding
|
||||||
}
|
}
|
||||||
|
|
||||||
fun collapsePanel() {
|
fun collapsePanel() {
|
||||||
|
@ -166,8 +177,8 @@ abstract class AbsSlidingMusicPanelActivity : AbsMusicServiceActivity() {
|
||||||
val alpha = 1 - progress
|
val alpha = 1 - progress
|
||||||
miniPlayerFragment?.view?.alpha = alpha
|
miniPlayerFragment?.view?.alpha = alpha
|
||||||
miniPlayerFragment?.view?.visibility = if (alpha == 0f) View.GONE else View.VISIBLE
|
miniPlayerFragment?.view?.visibility = if (alpha == 0f) View.GONE else View.VISIBLE
|
||||||
bottomNavigationView.translationY = progress * 500
|
binding.bottomNavigationView.translationY = progress * 500
|
||||||
bottomNavigationView.alpha = alpha
|
binding.bottomNavigationView.alpha = alpha
|
||||||
}
|
}
|
||||||
|
|
||||||
open fun onPanelCollapsed() {
|
open fun onPanelCollapsed() {
|
||||||
|
@ -183,14 +194,14 @@ abstract class AbsSlidingMusicPanelActivity : AbsMusicServiceActivity() {
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setupSlidingUpPanel() {
|
private fun setupSlidingUpPanel() {
|
||||||
slidingPanel.viewTreeObserver.addOnGlobalLayoutListener(object :
|
binding.slidingPanel.viewTreeObserver.addOnGlobalLayoutListener(object :
|
||||||
ViewTreeObserver.OnGlobalLayoutListener {
|
ViewTreeObserver.OnGlobalLayoutListener {
|
||||||
override fun onGlobalLayout() {
|
override fun onGlobalLayout() {
|
||||||
slidingPanel.viewTreeObserver.removeOnGlobalLayoutListener(this)
|
binding.slidingPanel.viewTreeObserver.removeOnGlobalLayoutListener(this)
|
||||||
if (nowPlayingScreen != Peak) {
|
if (nowPlayingScreen != Peak) {
|
||||||
val params = slidingPanel.layoutParams as ViewGroup.LayoutParams
|
val params = binding.slidingPanel.layoutParams as ViewGroup.LayoutParams
|
||||||
params.height = ViewGroup.LayoutParams.MATCH_PARENT
|
params.height = ViewGroup.LayoutParams.MATCH_PARENT
|
||||||
slidingPanel.layoutParams = params
|
binding.slidingPanel.layoutParams = params
|
||||||
}
|
}
|
||||||
when (panelState) {
|
when (panelState) {
|
||||||
STATE_EXPANDED -> onPanelExpanded()
|
STATE_EXPANDED -> onPanelExpanded()
|
||||||
|
@ -204,16 +215,16 @@ abstract class AbsSlidingMusicPanelActivity : AbsMusicServiceActivity() {
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getBottomNavigationView(): BottomNavigationBarTinted {
|
fun getBottomNavigationView(): BottomNavigationBarTinted {
|
||||||
return bottomNavigationView
|
return binding.bottomNavigationView
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onServiceConnected() {
|
override fun onServiceConnected() {
|
||||||
super.onServiceConnected()
|
super.onServiceConnected()
|
||||||
if (MusicPlayerRemote.playingQueue.isNotEmpty()) {
|
if (MusicPlayerRemote.playingQueue.isNotEmpty()) {
|
||||||
slidingPanel.viewTreeObserver.addOnGlobalLayoutListener(object :
|
binding.slidingPanel.viewTreeObserver.addOnGlobalLayoutListener(object :
|
||||||
ViewTreeObserver.OnGlobalLayoutListener {
|
ViewTreeObserver.OnGlobalLayoutListener {
|
||||||
override fun onGlobalLayout() {
|
override fun onGlobalLayout() {
|
||||||
slidingPanel.viewTreeObserver.removeOnGlobalLayoutListener(this)
|
binding.slidingPanel.viewTreeObserver.removeOnGlobalLayoutListener(this)
|
||||||
hideBottomBar(false)
|
hideBottomBar(false)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -305,16 +316,17 @@ abstract class AbsSlidingMusicPanelActivity : AbsMusicServiceActivity() {
|
||||||
}
|
}
|
||||||
|
|
||||||
fun updateTabs() {
|
fun updateTabs() {
|
||||||
bottomNavigationView.menu.clear()
|
binding.bottomNavigationView.menu.clear()
|
||||||
val currentTabs: List<CategoryInfo> = PreferenceUtil.libraryCategory
|
val currentTabs: List<CategoryInfo> = PreferenceUtil.libraryCategory
|
||||||
for (tab in currentTabs) {
|
for (tab in currentTabs) {
|
||||||
if (tab.visible) {
|
if (tab.visible) {
|
||||||
val menu = tab.category
|
val menu = tab.category
|
||||||
bottomNavigationView.menu.add(0, menu.id, 0, menu.stringRes).setIcon(menu.icon)
|
binding.bottomNavigationView.menu.add(0, menu.id, 0, menu.stringRes)
|
||||||
|
.setIcon(menu.icon)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (bottomNavigationView.menu.size() == 1) {
|
if (binding.bottomNavigationView.menu.size() == 1) {
|
||||||
bottomNavigationView.hide()
|
binding.bottomNavigationView.hide()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -326,28 +338,34 @@ abstract class AbsSlidingMusicPanelActivity : AbsMusicServiceActivity() {
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setBottomBarVisibility(visible: Boolean) {
|
fun setBottomBarVisibility(visible: Boolean) {
|
||||||
bottomNavigationView.isVisible = visible
|
binding.bottomNavigationView.isVisible = visible
|
||||||
hideBottomBar(MusicPlayerRemote.playingQueue.isEmpty())
|
hideBottomBar(MusicPlayerRemote.playingQueue.isEmpty())
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun hideBottomBar(hide: Boolean) {
|
private fun hideBottomBar(hide: Boolean) {
|
||||||
val heightOfBar = dip(R.dimen.mini_player_height)
|
val heightOfBar =
|
||||||
val heightOfBarWithTabs = heightOfBar * 2
|
if (MusicPlayerRemote.isCasting) dip(R.dimen.cast_mini_player_height) else dip(R.dimen.mini_player_height)
|
||||||
val isVisible = bottomNavigationView.isVisible
|
val heightOfBarWithTabs =
|
||||||
|
if (MusicPlayerRemote.isCasting) dip(R.dimen.mini_cast_player_height_expanded) else dip(
|
||||||
|
R.dimen.mini_player_height_expanded
|
||||||
|
)
|
||||||
|
val isVisible = binding.bottomNavigationView.isVisible
|
||||||
if (hide) {
|
if (hide) {
|
||||||
bottomSheetBehavior.isHideable = true
|
bottomSheetBehavior.isHideable = true
|
||||||
bottomSheetBehavior.peekHeight = 0
|
bottomSheetBehavior.peekHeight = 0
|
||||||
ViewCompat.setElevation(slidingPanel, 0f)
|
ViewCompat.setElevation(binding.slidingPanel, 0f)
|
||||||
ViewCompat.setElevation(bottomNavigationView, 10f)
|
ViewCompat.setElevation(binding.bottomNavigationView, 10f)
|
||||||
collapsePanel()
|
collapsePanel()
|
||||||
} else {
|
} else {
|
||||||
if (MusicPlayerRemote.playingQueue.isNotEmpty()) {
|
if (MusicPlayerRemote.playingQueue.isNotEmpty()) {
|
||||||
bottomSheetBehavior.isHideable = false
|
bottomSheetBehavior.isHideable = false
|
||||||
ViewCompat.setElevation(slidingPanel, 10f)
|
ViewCompat.setElevation(binding.slidingPanel, 10f)
|
||||||
ViewCompat.setElevation(bottomNavigationView, 10f)
|
ViewCompat.setElevation(binding.bottomNavigationView, 10f)
|
||||||
if (isVisible) {
|
if (isVisible) {
|
||||||
println("List")
|
println("List")
|
||||||
bottomSheetBehavior.peekHeight = heightOfBarWithTabs - 22
|
if (bottomSheetBehavior.state != STATE_EXPANDED)
|
||||||
|
getBottomNavigationView().translateYAnimate(0F)
|
||||||
|
bottomSheetBehavior.peekHeightAnimate(heightOfBarWithTabs)
|
||||||
} else {
|
} else {
|
||||||
println("Details")
|
println("Details")
|
||||||
bottomSheetBehavior.peekHeight = heightOfBar
|
bottomSheetBehavior.peekHeight = heightOfBar
|
||||||
|
@ -356,6 +374,11 @@ abstract class AbsSlidingMusicPanelActivity : AbsMusicServiceActivity() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun setAllowDragging(allowDragging: Boolean) {
|
||||||
|
bottomSheetBehavior.setAllowDragging(allowDragging)
|
||||||
|
hideBottomBar(false)
|
||||||
|
}
|
||||||
|
|
||||||
private fun chooseFragmentForTheme() {
|
private fun chooseFragmentForTheme() {
|
||||||
nowPlayingScreen = PreferenceUtil.nowPlayingScreen
|
nowPlayingScreen = PreferenceUtil.nowPlayingScreen
|
||||||
|
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
package code.name.monkey.retromusic.activities.base
|
package code.name.monkey.retromusic.activities.base
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.content.res.Resources
|
||||||
import android.graphics.Color
|
import android.graphics.Color
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.os.Handler
|
import android.os.Handler
|
||||||
|
@ -23,12 +24,12 @@ import android.view.View
|
||||||
import android.view.WindowManager
|
import android.view.WindowManager
|
||||||
import androidx.annotation.ColorInt
|
import androidx.annotation.ColorInt
|
||||||
import androidx.appcompat.app.AppCompatDelegate.setDefaultNightMode
|
import androidx.appcompat.app.AppCompatDelegate.setDefaultNightMode
|
||||||
|
import androidx.core.os.ConfigurationCompat
|
||||||
import code.name.monkey.appthemehelper.ATH
|
import code.name.monkey.appthemehelper.ATH
|
||||||
import code.name.monkey.appthemehelper.ThemeStore
|
import code.name.monkey.appthemehelper.ThemeStore
|
||||||
import code.name.monkey.appthemehelper.common.ATHToolbarActivity
|
import code.name.monkey.appthemehelper.common.ATHToolbarActivity
|
||||||
import code.name.monkey.appthemehelper.util.ATHUtil
|
import code.name.monkey.appthemehelper.util.ATHUtil
|
||||||
import code.name.monkey.appthemehelper.util.ColorUtil
|
import code.name.monkey.appthemehelper.util.ColorUtil
|
||||||
import code.name.monkey.appthemehelper.util.MaterialDialogsUtil
|
|
||||||
import code.name.monkey.appthemehelper.util.VersionUtils
|
import code.name.monkey.appthemehelper.util.VersionUtils
|
||||||
import code.name.monkey.retromusic.LanguageContextWrapper
|
import code.name.monkey.retromusic.LanguageContextWrapper
|
||||||
import code.name.monkey.retromusic.R
|
import code.name.monkey.retromusic.R
|
||||||
|
@ -48,7 +49,7 @@ abstract class AbsThemeActivity : ATHToolbarActivity(), Runnable {
|
||||||
setImmersiveFullscreen()
|
setImmersiveFullscreen()
|
||||||
registerSystemUiVisibility()
|
registerSystemUiVisibility()
|
||||||
toggleScreenOn()
|
toggleScreenOn()
|
||||||
MaterialDialogsUtil.updateMaterialDialogsThemeSingleton(this)
|
//MaterialDialogsUtil.updateMaterialDialogsThemeSingleton(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateTheme() {
|
private fun updateTheme() {
|
||||||
|
@ -213,8 +214,12 @@ abstract class AbsThemeActivity : ATHToolbarActivity(), Runnable {
|
||||||
|
|
||||||
override fun attachBaseContext(newBase: Context?) {
|
override fun attachBaseContext(newBase: Context?) {
|
||||||
val code = PreferenceUtil.languageCode
|
val code = PreferenceUtil.languageCode
|
||||||
if (code != "auto") {
|
val locale = if (code == "auto") {
|
||||||
super.attachBaseContext(LanguageContextWrapper.wrap(newBase, Locale(code)))
|
// Get the device default locale
|
||||||
} else super.attachBaseContext(newBase)
|
ConfigurationCompat.getLocales(Resources.getSystem().configuration)[0]
|
||||||
|
} else {
|
||||||
|
Locale.forLanguageTag(code)
|
||||||
|
}
|
||||||
|
super.attachBaseContext(LanguageContextWrapper.wrap(newBase, locale))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,18 +41,16 @@ import code.name.monkey.retromusic.activities.bugreport.model.Report
|
||||||
import code.name.monkey.retromusic.activities.bugreport.model.github.ExtraInfo
|
import code.name.monkey.retromusic.activities.bugreport.model.github.ExtraInfo
|
||||||
import code.name.monkey.retromusic.activities.bugreport.model.github.GithubLogin
|
import code.name.monkey.retromusic.activities.bugreport.model.github.GithubLogin
|
||||||
import code.name.monkey.retromusic.activities.bugreport.model.github.GithubTarget
|
import code.name.monkey.retromusic.activities.bugreport.model.github.GithubTarget
|
||||||
|
import code.name.monkey.retromusic.databinding.ActivityBugReportBinding
|
||||||
import code.name.monkey.retromusic.misc.DialogAsyncTask
|
import code.name.monkey.retromusic.misc.DialogAsyncTask
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
import com.google.android.material.floatingactionbutton.FloatingActionButton
|
import com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||||
import com.google.android.material.textfield.TextInputLayout
|
import com.google.android.material.textfield.TextInputLayout
|
||||||
import java.io.IOException
|
|
||||||
import kotlinx.android.synthetic.main.activity_bug_report.*
|
|
||||||
import kotlinx.android.synthetic.main.bug_report_card_device_info.*
|
|
||||||
import kotlinx.android.synthetic.main.bug_report_card_report.*
|
|
||||||
import org.eclipse.egit.github.core.Issue
|
import org.eclipse.egit.github.core.Issue
|
||||||
import org.eclipse.egit.github.core.client.GitHubClient
|
import org.eclipse.egit.github.core.client.GitHubClient
|
||||||
import org.eclipse.egit.github.core.client.RequestException
|
import org.eclipse.egit.github.core.client.RequestException
|
||||||
import org.eclipse.egit.github.core.service.IssueService
|
import org.eclipse.egit.github.core.service.IssueService
|
||||||
|
import java.io.IOException
|
||||||
|
|
||||||
private const val RESULT_SUCCESS = "RESULT_OK"
|
private const val RESULT_SUCCESS = "RESULT_OK"
|
||||||
private const val RESULT_BAD_CREDENTIALS = "RESULT_BAD_CREDENTIALS"
|
private const val RESULT_BAD_CREDENTIALS = "RESULT_BAD_CREDENTIALS"
|
||||||
|
@ -72,12 +70,14 @@ private annotation class Result
|
||||||
|
|
||||||
open class BugReportActivity : AbsThemeActivity() {
|
open class BugReportActivity : AbsThemeActivity() {
|
||||||
|
|
||||||
|
private lateinit var binding: ActivityBugReportBinding
|
||||||
private var deviceInfo: DeviceInfo? = null
|
private var deviceInfo: DeviceInfo? = null
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
setDrawUnderStatusBar()
|
setDrawUnderStatusBar()
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
setContentView(R.layout.activity_bug_report)
|
binding = ActivityBugReportBinding.inflate(layoutInflater)
|
||||||
|
setContentView(binding.root)
|
||||||
setStatusbarColorAuto()
|
setStatusbarColorAuto()
|
||||||
setNavigationbarColorAuto()
|
setNavigationbarColorAuto()
|
||||||
setTaskDescriptionColorAuto()
|
setTaskDescriptionColorAuto()
|
||||||
|
@ -87,50 +87,50 @@ open class BugReportActivity : AbsThemeActivity() {
|
||||||
if (TextUtils.isEmpty(title)) setTitle(R.string.report_an_issue)
|
if (TextUtils.isEmpty(title)) setTitle(R.string.report_an_issue)
|
||||||
|
|
||||||
deviceInfo = DeviceInfo(this)
|
deviceInfo = DeviceInfo(this)
|
||||||
airTextDeviceInfo.text = deviceInfo.toString()
|
binding.cardDeviceInfo.airTextDeviceInfo.text = deviceInfo.toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun initViews() {
|
private fun initViews() {
|
||||||
val accentColor = ThemeStore.accentColor(this)
|
val accentColor = ThemeStore.accentColor(this)
|
||||||
val primaryColor = ATHUtil.resolveColor(this, R.attr.colorSurface)
|
val primaryColor = ATHUtil.resolveColor(this, R.attr.colorSurface)
|
||||||
toolbar.setBackgroundColor(primaryColor)
|
binding.toolbar.setBackgroundColor(primaryColor)
|
||||||
setSupportActionBar(toolbar)
|
setSupportActionBar(binding.toolbar)
|
||||||
ToolbarContentTintHelper.colorBackButton(toolbar)
|
ToolbarContentTintHelper.colorBackButton(binding.toolbar)
|
||||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||||
TintHelper.setTintAuto(optionUseAccount, accentColor, false)
|
TintHelper.setTintAuto(binding.cardReport.optionUseAccount, accentColor, false)
|
||||||
optionUseAccount?.setOnClickListener {
|
binding.cardReport.optionUseAccount.setOnClickListener {
|
||||||
inputTitle.isEnabled = true
|
binding.cardReport.inputTitle.isEnabled = true
|
||||||
inputDescription.isEnabled = true
|
binding.cardReport.inputDescription.isEnabled = true
|
||||||
inputUsername.isEnabled = true
|
binding.cardReport.inputUsername.isEnabled = true
|
||||||
inputPassword.isEnabled = true
|
binding.cardReport.inputPassword.isEnabled = true
|
||||||
|
|
||||||
optionAnonymous.isChecked = false
|
binding.cardReport.optionAnonymous.isChecked = false
|
||||||
sendFab.hide(object : FloatingActionButton.OnVisibilityChangedListener() {
|
binding.sendFab.hide(object : FloatingActionButton.OnVisibilityChangedListener() {
|
||||||
override fun onHidden(fab: FloatingActionButton?) {
|
override fun onHidden(fab: FloatingActionButton?) {
|
||||||
super.onHidden(fab)
|
super.onHidden(fab)
|
||||||
sendFab.setImageResource(R.drawable.ic_send)
|
binding.sendFab.setImageResource(R.drawable.ic_send)
|
||||||
sendFab.show()
|
binding.sendFab.show()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
TintHelper.setTintAuto(optionAnonymous, accentColor, false)
|
TintHelper.setTintAuto(binding.cardReport.optionAnonymous, accentColor, false)
|
||||||
optionAnonymous.setOnClickListener {
|
binding.cardReport.optionAnonymous.setOnClickListener {
|
||||||
inputTitle.isEnabled = false
|
binding.cardReport.inputTitle.isEnabled = false
|
||||||
inputDescription.isEnabled = false
|
binding.cardReport.inputDescription.isEnabled = false
|
||||||
inputUsername.isEnabled = false
|
binding.cardReport.inputUsername.isEnabled = false
|
||||||
inputPassword.isEnabled = false
|
binding.cardReport.inputPassword.isEnabled = false
|
||||||
|
|
||||||
optionUseAccount.isChecked = false
|
binding.cardReport.optionUseAccount.isChecked = false
|
||||||
sendFab.hide(object : FloatingActionButton.OnVisibilityChangedListener() {
|
binding.sendFab.hide(object : FloatingActionButton.OnVisibilityChangedListener() {
|
||||||
override fun onHidden(fab: FloatingActionButton?) {
|
override fun onHidden(fab: FloatingActionButton?) {
|
||||||
super.onHidden(fab)
|
super.onHidden(fab)
|
||||||
sendFab.setImageResource(R.drawable.ic_open_in_browser)
|
binding.sendFab.setImageResource(R.drawable.ic_open_in_browser)
|
||||||
sendFab.show()
|
binding.sendFab.show()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
inputPassword.setOnEditorActionListener { _, actionId, _ ->
|
binding.cardReport.inputPassword.setOnEditorActionListener { _, actionId, _ ->
|
||||||
if (actionId == EditorInfo.IME_ACTION_SEND) {
|
if (actionId == EditorInfo.IME_ACTION_SEND) {
|
||||||
reportIssue()
|
reportIssue()
|
||||||
return@setOnEditorActionListener true
|
return@setOnEditorActionListener true
|
||||||
|
@ -138,22 +138,22 @@ open class BugReportActivity : AbsThemeActivity() {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
airTextDeviceInfo.setOnClickListener { copyDeviceInfoToClipBoard() }
|
binding.cardDeviceInfo.airTextDeviceInfo.setOnClickListener { copyDeviceInfoToClipBoard() }
|
||||||
|
|
||||||
TintHelper.setTintAuto(sendFab, accentColor, true)
|
TintHelper.setTintAuto(binding.sendFab, accentColor, true)
|
||||||
sendFab.setOnClickListener { reportIssue() }
|
binding.sendFab.setOnClickListener { reportIssue() }
|
||||||
|
|
||||||
MaterialUtil.setTint(inputLayoutTitle, false)
|
MaterialUtil.setTint(binding.cardReport.inputLayoutTitle, false)
|
||||||
MaterialUtil.setTint(inputLayoutDescription, false)
|
MaterialUtil.setTint(binding.cardReport.inputLayoutDescription, false)
|
||||||
MaterialUtil.setTint(inputLayoutUsername, false)
|
MaterialUtil.setTint(binding.cardReport.inputLayoutUsername, false)
|
||||||
MaterialUtil.setTint(inputLayoutPassword, false)
|
MaterialUtil.setTint(binding.cardReport.inputLayoutPassword, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun reportIssue() {
|
private fun reportIssue() {
|
||||||
if (optionUseAccount.isChecked) {
|
if (binding.cardReport.optionUseAccount.isChecked) {
|
||||||
if (!validateInput()) return
|
if (!validateInput()) return
|
||||||
val username = inputUsername.text.toString()
|
val username = binding.cardReport.inputUsername.text.toString()
|
||||||
val password = inputPassword.text.toString()
|
val password = binding.cardReport.inputPassword.text.toString()
|
||||||
sendBugReport(GithubLogin(username, password))
|
sendBugReport(GithubLogin(username, password))
|
||||||
} else {
|
} else {
|
||||||
copyDeviceInfoToClipBoard()
|
copyDeviceInfoToClipBoard()
|
||||||
|
@ -179,34 +179,34 @@ open class BugReportActivity : AbsThemeActivity() {
|
||||||
private fun validateInput(): Boolean {
|
private fun validateInput(): Boolean {
|
||||||
var hasErrors = false
|
var hasErrors = false
|
||||||
|
|
||||||
if (optionUseAccount.isChecked) {
|
if (binding.cardReport.optionUseAccount.isChecked) {
|
||||||
if (TextUtils.isEmpty(inputUsername.text)) {
|
if (TextUtils.isEmpty(binding.cardReport.inputUsername.text)) {
|
||||||
setError(inputLayoutUsername, R.string.bug_report_no_username)
|
setError(binding.cardReport.inputLayoutUsername, R.string.bug_report_no_username)
|
||||||
hasErrors = true
|
hasErrors = true
|
||||||
} else {
|
} else {
|
||||||
removeError(inputLayoutUsername)
|
removeError(binding.cardReport.inputLayoutUsername)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (TextUtils.isEmpty(inputPassword.text)) {
|
if (TextUtils.isEmpty(binding.cardReport.inputPassword.text)) {
|
||||||
setError(inputLayoutPassword, R.string.bug_report_no_password)
|
setError(binding.cardReport.inputLayoutPassword, R.string.bug_report_no_password)
|
||||||
hasErrors = true
|
hasErrors = true
|
||||||
} else {
|
} else {
|
||||||
removeError(inputLayoutPassword)
|
removeError(binding.cardReport.inputLayoutPassword)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (TextUtils.isEmpty(inputTitle.text)) {
|
if (TextUtils.isEmpty(binding.cardReport.inputTitle.text)) {
|
||||||
setError(inputLayoutTitle, R.string.bug_report_no_title)
|
setError(binding.cardReport.inputLayoutTitle, R.string.bug_report_no_title)
|
||||||
hasErrors = true
|
hasErrors = true
|
||||||
} else {
|
} else {
|
||||||
removeError(inputLayoutTitle)
|
removeError(binding.cardReport.inputLayoutTitle)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (TextUtils.isEmpty(inputDescription.text)) {
|
if (TextUtils.isEmpty(binding.cardReport.inputDescription.text)) {
|
||||||
setError(inputLayoutDescription, R.string.bug_report_no_description)
|
setError(binding.cardReport.inputLayoutDescription, R.string.bug_report_no_description)
|
||||||
hasErrors = true
|
hasErrors = true
|
||||||
} else {
|
} else {
|
||||||
removeError(inputLayoutDescription)
|
removeError(binding.cardReport.inputLayoutDescription)
|
||||||
}
|
}
|
||||||
|
|
||||||
return !hasErrors
|
return !hasErrors
|
||||||
|
@ -223,8 +223,8 @@ open class BugReportActivity : AbsThemeActivity() {
|
||||||
private fun sendBugReport(login: GithubLogin) {
|
private fun sendBugReport(login: GithubLogin) {
|
||||||
if (!validateInput()) return
|
if (!validateInput()) return
|
||||||
|
|
||||||
val bugTitle = inputTitle.text.toString()
|
val bugTitle = binding.cardReport.inputTitle.text.toString()
|
||||||
val bugDescription = inputDescription.text.toString()
|
val bugDescription = binding.cardReport.inputDescription.text.toString()
|
||||||
|
|
||||||
val extraInfo = ExtraInfo()
|
val extraInfo = ExtraInfo()
|
||||||
onSaveExtraInfo()
|
onSaveExtraInfo()
|
||||||
|
|
|
@ -5,11 +5,14 @@ import android.content.Context;
|
||||||
import android.content.pm.PackageInfo;
|
import android.content.pm.PackageInfo;
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
|
|
||||||
import androidx.annotation.IntRange;
|
import androidx.annotation.IntRange;
|
||||||
import code.name.monkey.retromusic.util.PreferenceUtil;
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
|
||||||
|
import code.name.monkey.retromusic.util.PreferenceUtil;
|
||||||
|
|
||||||
public class DeviceInfo {
|
public class DeviceInfo {
|
||||||
|
|
||||||
@SuppressLint("NewApi")
|
@SuppressLint("NewApi")
|
||||||
|
|
|
@ -16,11 +16,14 @@ package code.name.monkey.retromusic.activities.saf;
|
||||||
|
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import code.name.monkey.retromusic.R;
|
|
||||||
import com.heinrichreimersoftware.materialintro.app.IntroActivity;
|
import com.heinrichreimersoftware.materialintro.app.IntroActivity;
|
||||||
import com.heinrichreimersoftware.materialintro.slide.SimpleSlide;
|
import com.heinrichreimersoftware.materialintro.slide.SimpleSlide;
|
||||||
|
|
||||||
|
import code.name.monkey.retromusic.R;
|
||||||
|
|
||||||
/** Created by hemanths on 2019-07-31. */
|
/** Created by hemanths on 2019-07-31. */
|
||||||
public class SAFGuideActivity extends IntroActivity {
|
public class SAFGuideActivity extends IntroActivity {
|
||||||
|
|
||||||
|
|
|
@ -22,10 +22,12 @@ import android.graphics.BitmapFactory
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
|
import android.view.LayoutInflater
|
||||||
import android.view.MenuItem
|
import android.view.MenuItem
|
||||||
import android.view.View
|
|
||||||
import android.view.animation.OvershootInterpolator
|
import android.view.animation.OvershootInterpolator
|
||||||
|
import android.widget.ImageView
|
||||||
import androidx.appcompat.app.AlertDialog
|
import androidx.appcompat.app.AlertDialog
|
||||||
|
import androidx.viewbinding.ViewBinding
|
||||||
import code.name.monkey.appthemehelper.ThemeStore
|
import code.name.monkey.appthemehelper.ThemeStore
|
||||||
import code.name.monkey.appthemehelper.util.ATHUtil
|
import code.name.monkey.appthemehelper.util.ATHUtil
|
||||||
import code.name.monkey.appthemehelper.util.TintHelper
|
import code.name.monkey.appthemehelper.util.TintHelper
|
||||||
|
@ -41,7 +43,6 @@ import code.name.monkey.retromusic.util.RetroUtil
|
||||||
import code.name.monkey.retromusic.util.SAFUtil
|
import code.name.monkey.retromusic.util.SAFUtil
|
||||||
import com.google.android.material.button.MaterialButton
|
import com.google.android.material.button.MaterialButton
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
import kotlinx.android.synthetic.main.activity_album_tag_editor.*
|
|
||||||
import org.jaudiotagger.audio.AudioFile
|
import org.jaudiotagger.audio.AudioFile
|
||||||
import org.jaudiotagger.audio.AudioFileIO
|
import org.jaudiotagger.audio.AudioFileIO
|
||||||
import org.jaudiotagger.tag.FieldKey
|
import org.jaudiotagger.tag.FieldKey
|
||||||
|
@ -49,7 +50,8 @@ import org.koin.android.ext.android.inject
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
abstract class AbsTagEditorActivity : AbsBaseActivity() {
|
abstract class AbsTagEditorActivity<VB : ViewBinding> : AbsBaseActivity() {
|
||||||
|
abstract val editorImage: ImageView?
|
||||||
val repository by inject<Repository>()
|
val repository by inject<Repository>()
|
||||||
|
|
||||||
lateinit var saveFab: MaterialButton
|
lateinit var saveFab: MaterialButton
|
||||||
|
@ -62,7 +64,11 @@ abstract class AbsTagEditorActivity : AbsBaseActivity() {
|
||||||
private val currentSongPath: String? = null
|
private val currentSongPath: String? = null
|
||||||
private var savedTags: Map<FieldKey, String>? = null
|
private var savedTags: Map<FieldKey, String>? = null
|
||||||
private var savedArtworkInfo: ArtworkInfo? = null
|
private var savedArtworkInfo: ArtworkInfo? = null
|
||||||
protected abstract val contentViewLayout: Int
|
private var _binding: VB? = null
|
||||||
|
protected val binding: VB get() = _binding!!
|
||||||
|
|
||||||
|
abstract val bindingInflater: (LayoutInflater) -> VB
|
||||||
|
|
||||||
protected abstract fun loadImageFromFile(selectedFile: Uri?)
|
protected abstract fun loadImageFromFile(selectedFile: Uri?)
|
||||||
|
|
||||||
protected val show: AlertDialog
|
protected val show: AlertDialog
|
||||||
|
@ -187,7 +193,8 @@ abstract class AbsTagEditorActivity : AbsBaseActivity() {
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
setContentView(contentViewLayout)
|
_binding = bindingInflater.invoke(layoutInflater)
|
||||||
|
setContentView(binding.root)
|
||||||
setStatusbarColorAuto()
|
setStatusbarColorAuto()
|
||||||
setNavigationbarColorAuto()
|
setNavigationbarColorAuto()
|
||||||
setTaskDescriptionColorAuto()
|
setTaskDescriptionColorAuto()
|
||||||
|
@ -284,10 +291,6 @@ abstract class AbsTagEditorActivity : AbsBaseActivity() {
|
||||||
|
|
||||||
protected fun setNoImageMode() {
|
protected fun setNoImageMode() {
|
||||||
isInNoImageMode = true
|
isInNoImageMode = true
|
||||||
imageContainer?.visibility = View.GONE
|
|
||||||
editorImage?.visibility = View.GONE
|
|
||||||
editorImage?.isEnabled = false
|
|
||||||
|
|
||||||
setColors(
|
setColors(
|
||||||
intent.getIntExtra(
|
intent.getIntExtra(
|
||||||
EXTRA_PALETTE,
|
EXTRA_PALETTE,
|
||||||
|
@ -296,6 +299,7 @@ abstract class AbsTagEditorActivity : AbsBaseActivity() {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected fun dataChanged() {
|
protected fun dataChanged() {
|
||||||
showFab()
|
showFab()
|
||||||
}
|
}
|
||||||
|
@ -314,9 +318,9 @@ abstract class AbsTagEditorActivity : AbsBaseActivity() {
|
||||||
|
|
||||||
protected fun setImageBitmap(bitmap: Bitmap?, bgColor: Int) {
|
protected fun setImageBitmap(bitmap: Bitmap?, bgColor: Int) {
|
||||||
if (bitmap == null) {
|
if (bitmap == null) {
|
||||||
editorImage.setImageResource(drawable.default_audio_art)
|
editorImage?.setImageResource(drawable.default_audio_art)
|
||||||
} else {
|
} else {
|
||||||
editorImage.setImageBitmap(bitmap)
|
editorImage?.setImageBitmap(bitmap)
|
||||||
}
|
}
|
||||||
setColors(bgColor)
|
setColors(bgColor)
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,30 +25,31 @@ import android.os.Bundle
|
||||||
import android.text.Editable
|
import android.text.Editable
|
||||||
import android.text.TextWatcher
|
import android.text.TextWatcher
|
||||||
import android.transition.Slide
|
import android.transition.Slide
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.widget.ImageView
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import code.name.monkey.appthemehelper.util.ATHUtil
|
import code.name.monkey.appthemehelper.util.ATHUtil
|
||||||
import code.name.monkey.appthemehelper.util.MaterialUtil
|
import code.name.monkey.appthemehelper.util.MaterialUtil
|
||||||
import code.name.monkey.retromusic.R
|
import code.name.monkey.retromusic.R
|
||||||
|
import code.name.monkey.retromusic.databinding.ActivityAlbumTagEditorBinding
|
||||||
import code.name.monkey.retromusic.extensions.appHandleColor
|
import code.name.monkey.retromusic.extensions.appHandleColor
|
||||||
import code.name.monkey.retromusic.glide.palette.BitmapPaletteTranscoder
|
import code.name.monkey.retromusic.glide.GlideApp
|
||||||
import code.name.monkey.retromusic.glide.palette.BitmapPaletteWrapper
|
import code.name.monkey.retromusic.glide.palette.BitmapPaletteWrapper
|
||||||
import code.name.monkey.retromusic.model.ArtworkInfo
|
import code.name.monkey.retromusic.model.ArtworkInfo
|
||||||
import code.name.monkey.retromusic.model.Song
|
import code.name.monkey.retromusic.model.Song
|
||||||
import code.name.monkey.retromusic.util.ImageUtil
|
import code.name.monkey.retromusic.util.ImageUtil
|
||||||
import code.name.monkey.retromusic.util.RetroColorUtil.generatePalette
|
import code.name.monkey.retromusic.util.RetroColorUtil.generatePalette
|
||||||
import code.name.monkey.retromusic.util.RetroColorUtil.getColor
|
import code.name.monkey.retromusic.util.RetroColorUtil.getColor
|
||||||
import com.bumptech.glide.Glide
|
|
||||||
import com.bumptech.glide.load.engine.DiskCacheStrategy
|
import com.bumptech.glide.load.engine.DiskCacheStrategy
|
||||||
import com.bumptech.glide.request.animation.GlideAnimation
|
import com.bumptech.glide.request.target.ImageViewTarget
|
||||||
import com.bumptech.glide.request.target.SimpleTarget
|
import com.bumptech.glide.request.transition.Transition
|
||||||
import java.util.*
|
|
||||||
import kotlinx.android.synthetic.main.activity_album_tag_editor.*
|
|
||||||
import org.jaudiotagger.tag.FieldKey
|
import org.jaudiotagger.tag.FieldKey
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
class AlbumTagEditorActivity : AbsTagEditorActivity(), TextWatcher {
|
class AlbumTagEditorActivity : AbsTagEditorActivity<ActivityAlbumTagEditorBinding>(), TextWatcher {
|
||||||
|
|
||||||
override val contentViewLayout: Int
|
override val bindingInflater: (LayoutInflater) -> ActivityAlbumTagEditorBinding =
|
||||||
get() = R.layout.activity_album_tag_editor
|
ActivityAlbumTagEditorBinding::inflate
|
||||||
|
|
||||||
private fun windowEnterTransition() {
|
private fun windowEnterTransition() {
|
||||||
val slide = Slide()
|
val slide = Slide()
|
||||||
|
@ -62,20 +63,20 @@ class AlbumTagEditorActivity : AbsTagEditorActivity(), TextWatcher {
|
||||||
|
|
||||||
override fun loadImageFromFile(selectedFile: Uri?) {
|
override fun loadImageFromFile(selectedFile: Uri?) {
|
||||||
|
|
||||||
Glide.with(this@AlbumTagEditorActivity).load(selectedFile).asBitmap()
|
|
||||||
.transcode(BitmapPaletteTranscoder(this), BitmapPaletteWrapper::class.java)
|
GlideApp.with(this@AlbumTagEditorActivity).asBitmapPalette().load(selectedFile)
|
||||||
.diskCacheStrategy(DiskCacheStrategy.NONE).skipMemoryCache(true)
|
.diskCacheStrategy(DiskCacheStrategy.NONE).skipMemoryCache(true)
|
||||||
.into(object : SimpleTarget<BitmapPaletteWrapper>() {
|
.into(object : ImageViewTarget<BitmapPaletteWrapper>(binding.editorImage) {
|
||||||
override fun onResourceReady(
|
override fun onResourceReady(
|
||||||
resource: BitmapPaletteWrapper?,
|
resource: BitmapPaletteWrapper,
|
||||||
glideAnimation: GlideAnimation<in BitmapPaletteWrapper>?
|
transition: Transition<in BitmapPaletteWrapper>?
|
||||||
) {
|
) {
|
||||||
getColor(resource?.palette, Color.TRANSPARENT)
|
getColor(resource.palette, Color.TRANSPARENT)
|
||||||
albumArtBitmap = resource?.bitmap?.let { ImageUtil.resizeBitmap(it, 2048) }
|
albumArtBitmap = resource.bitmap?.let { ImageUtil.resizeBitmap(it, 2048) }
|
||||||
setImageBitmap(
|
setImageBitmap(
|
||||||
albumArtBitmap,
|
albumArtBitmap,
|
||||||
getColor(
|
getColor(
|
||||||
resource?.palette,
|
resource.palette,
|
||||||
ATHUtil.resolveColor(
|
ATHUtil.resolveColor(
|
||||||
this@AlbumTagEditorActivity,
|
this@AlbumTagEditorActivity,
|
||||||
R.attr.defaultFooterColor
|
R.attr.defaultFooterColor
|
||||||
|
@ -87,11 +88,13 @@ class AlbumTagEditorActivity : AbsTagEditorActivity(), TextWatcher {
|
||||||
setResult(Activity.RESULT_OK)
|
setResult(Activity.RESULT_OK)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onLoadFailed(e: Exception?, errorDrawable: Drawable?) {
|
override fun onLoadFailed(errorDrawable: Drawable?) {
|
||||||
super.onLoadFailed(e, errorDrawable)
|
super.onLoadFailed(errorDrawable)
|
||||||
Toast.makeText(this@AlbumTagEditorActivity, e.toString(), Toast.LENGTH_LONG)
|
Toast.makeText(this@AlbumTagEditorActivity, "Load Failed", Toast.LENGTH_LONG)
|
||||||
.show()
|
.show()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun setResource(resource: BitmapPaletteWrapper?) {}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -99,15 +102,15 @@ class AlbumTagEditorActivity : AbsTagEditorActivity(), TextWatcher {
|
||||||
private var deleteAlbumArt: Boolean = false
|
private var deleteAlbumArt: Boolean = false
|
||||||
|
|
||||||
private fun setupToolbar() {
|
private fun setupToolbar() {
|
||||||
toolbar.setBackgroundColor(ATHUtil.resolveColor(this, R.attr.colorSurface))
|
binding.toolbar.setBackgroundColor(ATHUtil.resolveColor(this, R.attr.colorSurface))
|
||||||
setSupportActionBar(toolbar)
|
setSupportActionBar(binding.toolbar)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
setDrawUnderStatusBar()
|
setDrawUnderStatusBar()
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
window.sharedElementsUseOverlay = true
|
window.sharedElementsUseOverlay = true
|
||||||
imageContainer?.transitionName = getString(R.string.transition_album_art)
|
binding.imageContainer.transitionName = getString(R.string.transition_album_art)
|
||||||
windowEnterTransition()
|
windowEnterTransition()
|
||||||
setUpViews()
|
setUpViews()
|
||||||
setupToolbar()
|
setupToolbar()
|
||||||
|
@ -116,22 +119,23 @@ class AlbumTagEditorActivity : AbsTagEditorActivity(), TextWatcher {
|
||||||
private fun setUpViews() {
|
private fun setUpViews() {
|
||||||
fillViewsWithFileTags()
|
fillViewsWithFileTags()
|
||||||
|
|
||||||
MaterialUtil.setTint(yearContainer, false)
|
MaterialUtil.setTint(binding.yearContainer, false)
|
||||||
MaterialUtil.setTint(genreContainer, false)
|
MaterialUtil.setTint(binding.genreContainer, false)
|
||||||
MaterialUtil.setTint(albumTitleContainer, false)
|
MaterialUtil.setTint(binding.albumTitleContainer, false)
|
||||||
MaterialUtil.setTint(albumArtistContainer, false)
|
MaterialUtil.setTint(binding.albumArtistContainer, false)
|
||||||
|
|
||||||
albumText.appHandleColor().addTextChangedListener(this)
|
binding.albumText.appHandleColor().addTextChangedListener(this)
|
||||||
albumArtistText.appHandleColor().addTextChangedListener(this)
|
binding.albumArtistText.appHandleColor().addTextChangedListener(this)
|
||||||
genreTitle.appHandleColor().addTextChangedListener(this)
|
binding.genreTitle.appHandleColor().addTextChangedListener(this)
|
||||||
yearTitle.appHandleColor().addTextChangedListener(this)
|
binding.yearTitle.appHandleColor().addTextChangedListener(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun fillViewsWithFileTags() {
|
private fun fillViewsWithFileTags() {
|
||||||
albumText.setText(albumTitle)
|
binding.albumText.setText(albumTitle)
|
||||||
albumArtistText.setText(albumArtistName)
|
binding.albumArtistText.setText(albumArtistName)
|
||||||
genreTitle.setText(genreName)
|
binding.genreTitle.setText(genreName)
|
||||||
yearTitle.setText(songYear)
|
binding.yearTitle.setText(songYear)
|
||||||
|
println(albumTitle + albumArtistName)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun loadCurrentImage() {
|
override fun loadCurrentImage() {
|
||||||
|
@ -155,7 +159,7 @@ class AlbumTagEditorActivity : AbsTagEditorActivity(), TextWatcher {
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun searchImageOnWeb() {
|
override fun searchImageOnWeb() {
|
||||||
searchWebFor(albumText.text.toString(), albumArtistText.text.toString())
|
searchWebFor(binding.albumText.text.toString(), binding.albumArtistText.text.toString())
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun deleteImage() {
|
override fun deleteImage() {
|
||||||
|
@ -169,12 +173,12 @@ class AlbumTagEditorActivity : AbsTagEditorActivity(), TextWatcher {
|
||||||
|
|
||||||
override fun save() {
|
override fun save() {
|
||||||
val fieldKeyValueMap = EnumMap<FieldKey, String>(FieldKey::class.java)
|
val fieldKeyValueMap = EnumMap<FieldKey, String>(FieldKey::class.java)
|
||||||
fieldKeyValueMap[FieldKey.ALBUM] = albumText.text.toString()
|
fieldKeyValueMap[FieldKey.ALBUM] = binding.albumText.text.toString()
|
||||||
// android seems not to recognize album_artist field so we additionally write the normal artist field
|
// android seems not to recognize album_artist field so we additionally write the normal artist field
|
||||||
fieldKeyValueMap[FieldKey.ARTIST] = albumArtistText.text.toString()
|
fieldKeyValueMap[FieldKey.ARTIST] = binding.albumArtistText.text.toString()
|
||||||
fieldKeyValueMap[FieldKey.ALBUM_ARTIST] = albumArtistText.text.toString()
|
fieldKeyValueMap[FieldKey.ALBUM_ARTIST] = binding.albumArtistText.text.toString()
|
||||||
fieldKeyValueMap[FieldKey.GENRE] = genreTitle.text.toString()
|
fieldKeyValueMap[FieldKey.GENRE] = binding.genreTitle.text.toString()
|
||||||
fieldKeyValueMap[FieldKey.YEAR] = yearTitle.text.toString()
|
fieldKeyValueMap[FieldKey.YEAR] = binding.yearTitle.text.toString()
|
||||||
|
|
||||||
writeValuesToFiles(
|
writeValuesToFiles(
|
||||||
fieldKeyValueMap,
|
fieldKeyValueMap,
|
||||||
|
@ -206,6 +210,10 @@ class AlbumTagEditorActivity : AbsTagEditorActivity(), TextWatcher {
|
||||||
saveFab.backgroundTintList = ColorStateList.valueOf(color)
|
saveFab.backgroundTintList = ColorStateList.valueOf(color)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
override val editorImage: ImageView
|
||||||
|
get() = binding.editorImage
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
val TAG: String = AlbumTagEditorActivity::class.java.simpleName
|
val TAG: String = AlbumTagEditorActivity::class.java.simpleName
|
||||||
|
|
|
@ -14,91 +14,98 @@
|
||||||
*/
|
*/
|
||||||
package code.name.monkey.retromusic.activities.tageditor
|
package code.name.monkey.retromusic.activities.tageditor
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.text.Editable
|
import android.text.Editable
|
||||||
import android.text.TextWatcher
|
import android.text.TextWatcher
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.widget.ImageView
|
||||||
import code.name.monkey.appthemehelper.util.ATHUtil
|
import code.name.monkey.appthemehelper.util.ATHUtil
|
||||||
import code.name.monkey.appthemehelper.util.MaterialUtil
|
import code.name.monkey.appthemehelper.util.MaterialUtil
|
||||||
import code.name.monkey.retromusic.R
|
import code.name.monkey.retromusic.R
|
||||||
|
import code.name.monkey.retromusic.databinding.ActivitySongTagEditorBinding
|
||||||
import code.name.monkey.retromusic.extensions.appHandleColor
|
import code.name.monkey.retromusic.extensions.appHandleColor
|
||||||
import code.name.monkey.retromusic.repository.SongRepository
|
import code.name.monkey.retromusic.repository.SongRepository
|
||||||
import kotlinx.android.synthetic.main.activity_song_tag_editor.*
|
|
||||||
import org.jaudiotagger.tag.FieldKey
|
import org.jaudiotagger.tag.FieldKey
|
||||||
import org.koin.android.ext.android.inject
|
import org.koin.android.ext.android.inject
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
class SongTagEditorActivity : AbsTagEditorActivity(), TextWatcher {
|
class SongTagEditorActivity : AbsTagEditorActivity<ActivitySongTagEditorBinding>(), TextWatcher {
|
||||||
|
|
||||||
|
override val bindingInflater: (LayoutInflater) -> ActivitySongTagEditorBinding =
|
||||||
|
ActivitySongTagEditorBinding::inflate
|
||||||
|
|
||||||
override val contentViewLayout: Int
|
|
||||||
get() = R.layout.activity_song_tag_editor
|
|
||||||
|
|
||||||
private val songRepository by inject<SongRepository>()
|
private val songRepository by inject<SongRepository>()
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
setNoImageMode()
|
|
||||||
setUpViews()
|
setUpViews()
|
||||||
toolbar.setBackgroundColor(ATHUtil.resolveColor(this, R.attr.colorSurface))
|
setNoImageMode()
|
||||||
setSupportActionBar(toolbar)
|
binding.toolbar.setBackgroundColor(ATHUtil.resolveColor(this, R.attr.colorSurface))
|
||||||
|
setSupportActionBar(binding.toolbar)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressLint("ClickableViewAccessibility")
|
||||||
private fun setUpViews() {
|
private fun setUpViews() {
|
||||||
fillViewsWithFileTags()
|
fillViewsWithFileTags()
|
||||||
MaterialUtil.setTint(songTextContainer, false)
|
MaterialUtil.setTint(binding.songTextContainer, false)
|
||||||
MaterialUtil.setTint(composerContainer, false)
|
MaterialUtil.setTint(binding.composerContainer, false)
|
||||||
MaterialUtil.setTint(albumTextContainer, false)
|
MaterialUtil.setTint(binding.albumTextContainer, false)
|
||||||
MaterialUtil.setTint(artistContainer, false)
|
MaterialUtil.setTint(binding.artistContainer, false)
|
||||||
MaterialUtil.setTint(albumArtistContainer, false)
|
MaterialUtil.setTint(binding.albumArtistContainer, false)
|
||||||
MaterialUtil.setTint(yearContainer, false)
|
MaterialUtil.setTint(binding.yearContainer, false)
|
||||||
MaterialUtil.setTint(genreContainer, false)
|
MaterialUtil.setTint(binding.genreContainer, false)
|
||||||
MaterialUtil.setTint(trackNumberContainer, false)
|
MaterialUtil.setTint(binding.trackNumberContainer, false)
|
||||||
MaterialUtil.setTint(lyricsContainer, false)
|
MaterialUtil.setTint(binding.lyricsContainer, false)
|
||||||
|
|
||||||
songText.appHandleColor().addTextChangedListener(this)
|
binding.songText.appHandleColor().addTextChangedListener(this)
|
||||||
albumText.appHandleColor().addTextChangedListener(this)
|
binding.albumText.appHandleColor().addTextChangedListener(this)
|
||||||
albumArtistText.appHandleColor().addTextChangedListener(this)
|
binding.albumArtistText.appHandleColor().addTextChangedListener(this)
|
||||||
artistText.appHandleColor().addTextChangedListener(this)
|
binding.artistText.appHandleColor().addTextChangedListener(this)
|
||||||
genreText.appHandleColor().addTextChangedListener(this)
|
binding.genreText.appHandleColor().addTextChangedListener(this)
|
||||||
yearText.appHandleColor().addTextChangedListener(this)
|
binding.yearText.appHandleColor().addTextChangedListener(this)
|
||||||
trackNumberText.appHandleColor().addTextChangedListener(this)
|
binding.trackNumberText.appHandleColor().addTextChangedListener(this)
|
||||||
lyricsText.appHandleColor().addTextChangedListener(this)
|
binding.lyricsText.appHandleColor().addTextChangedListener(this)
|
||||||
songComposerText.appHandleColor().addTextChangedListener(this)
|
binding.songComposerText.appHandleColor().addTextChangedListener(this)
|
||||||
|
|
||||||
|
binding.lyricsText.setOnTouchListener { view, _ ->
|
||||||
|
view.parent.requestDisallowInterceptTouchEvent(true)
|
||||||
|
return@setOnTouchListener false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun fillViewsWithFileTags() {
|
private fun fillViewsWithFileTags() {
|
||||||
songText.setText(songTitle)
|
binding.songText.setText(songTitle)
|
||||||
albumArtistText.setText(albumArtist)
|
binding.albumArtistText.setText(albumArtist)
|
||||||
albumText.setText(albumTitle)
|
binding.albumText.setText(albumTitle)
|
||||||
artistText.setText(artistName)
|
binding.artistText.setText(artistName)
|
||||||
genreText.setText(genreName)
|
binding.genreText.setText(genreName)
|
||||||
yearText.setText(songYear)
|
binding.yearText.setText(songYear)
|
||||||
trackNumberText.setText(trackNumber)
|
binding.trackNumberText.setText(trackNumber)
|
||||||
lyricsText.setText(lyrics)
|
binding.lyricsText.setText(lyrics)
|
||||||
songComposerText.setText(composer)
|
binding.songComposerText.setText(composer)
|
||||||
|
println(songTitle + songYear)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun loadCurrentImage() {
|
override fun loadCurrentImage() {}
|
||||||
}
|
|
||||||
|
|
||||||
override fun searchImageOnWeb() {
|
override fun searchImageOnWeb() {}
|
||||||
}
|
|
||||||
|
|
||||||
override fun deleteImage() {
|
override fun deleteImage() {}
|
||||||
}
|
|
||||||
|
|
||||||
override fun save() {
|
override fun save() {
|
||||||
val fieldKeyValueMap = EnumMap<FieldKey, String>(FieldKey::class.java)
|
val fieldKeyValueMap = EnumMap<FieldKey, String>(FieldKey::class.java)
|
||||||
fieldKeyValueMap[FieldKey.TITLE] = songText.text.toString()
|
fieldKeyValueMap[FieldKey.TITLE] = binding.songText.text.toString()
|
||||||
fieldKeyValueMap[FieldKey.ALBUM] = albumText.text.toString()
|
fieldKeyValueMap[FieldKey.ALBUM] = binding.albumText.text.toString()
|
||||||
fieldKeyValueMap[FieldKey.ARTIST] = artistText.text.toString()
|
fieldKeyValueMap[FieldKey.ARTIST] = binding.artistText.text.toString()
|
||||||
fieldKeyValueMap[FieldKey.GENRE] = genreText.text.toString()
|
fieldKeyValueMap[FieldKey.GENRE] = binding.genreText.text.toString()
|
||||||
fieldKeyValueMap[FieldKey.YEAR] = yearText.text.toString()
|
fieldKeyValueMap[FieldKey.YEAR] = binding.yearText.text.toString()
|
||||||
fieldKeyValueMap[FieldKey.TRACK] = trackNumberText.text.toString()
|
fieldKeyValueMap[FieldKey.TRACK] = binding.trackNumberText.text.toString()
|
||||||
fieldKeyValueMap[FieldKey.LYRICS] = lyricsText.text.toString()
|
fieldKeyValueMap[FieldKey.LYRICS] = binding.lyricsText.text.toString()
|
||||||
fieldKeyValueMap[FieldKey.ALBUM_ARTIST] = albumArtistText.text.toString()
|
fieldKeyValueMap[FieldKey.ALBUM_ARTIST] = binding.albumArtistText.text.toString()
|
||||||
fieldKeyValueMap[FieldKey.COMPOSER] = songComposerText.text.toString()
|
fieldKeyValueMap[FieldKey.COMPOSER] = binding.songComposerText.text.toString()
|
||||||
writeValuesToFiles(fieldKeyValueMap, null)
|
writeValuesToFiles(fieldKeyValueMap, null)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -120,4 +127,7 @@ class SongTagEditorActivity : AbsTagEditorActivity(), TextWatcher {
|
||||||
companion object {
|
companion object {
|
||||||
val TAG: String = SongTagEditorActivity::class.java.simpleName
|
val TAG: String = SongTagEditorActivity::class.java.simpleName
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override val editorImage: ImageView?
|
||||||
|
get() = null
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,7 @@ import android.widget.Toast;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
import com.afollestad.materialdialogs.MaterialDialog;
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||||
|
|
||||||
import org.jaudiotagger.audio.AudioFile;
|
import org.jaudiotagger.audio.AudioFile;
|
||||||
import org.jaudiotagger.audio.AudioFileIO;
|
import org.jaudiotagger.audio.AudioFileIO;
|
||||||
|
@ -136,17 +136,17 @@ public class WriteTagsAsyncTask extends DialogAsyncTask<LoadingInfo, Integer, Li
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Dialog createDialog(@NonNull Context context) {
|
protected Dialog createDialog(@NonNull Context context) {
|
||||||
return new MaterialDialog.Builder(context)
|
return new MaterialAlertDialogBuilder(context)
|
||||||
.title(R.string.saving_changes)
|
.setTitle(R.string.saving_changes)
|
||||||
.cancelable(false)
|
.setCancelable(false)
|
||||||
.progress(false, 0)
|
.setView(R.layout.loading)
|
||||||
.build();
|
.create();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onProgressUpdate(@NonNull Dialog dialog, Integer... values) {
|
protected void onProgressUpdate(@NonNull Dialog dialog, Integer... values) {
|
||||||
super.onProgressUpdate(dialog, values);
|
super.onProgressUpdate(dialog, values);
|
||||||
((MaterialDialog) dialog).setMaxProgress(values[1]);
|
// ((MaterialDialog) dialog).setMaxProgress(values[1]);
|
||||||
((MaterialDialog) dialog).setProgress(values[0]);
|
// ((MaterialDialog) dialog).setProgress(values[0]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,15 +22,19 @@ import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.recyclerview.widget.ItemTouchHelper;
|
import androidx.recyclerview.widget.ItemTouchHelper;
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
|
import com.google.android.material.checkbox.MaterialCheckBox;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import code.name.monkey.appthemehelper.ThemeStore;
|
import code.name.monkey.appthemehelper.ThemeStore;
|
||||||
import code.name.monkey.retromusic.R;
|
import code.name.monkey.retromusic.R;
|
||||||
import code.name.monkey.retromusic.model.CategoryInfo;
|
import code.name.monkey.retromusic.model.CategoryInfo;
|
||||||
import code.name.monkey.retromusic.util.SwipeAndDragHelper;
|
import code.name.monkey.retromusic.util.SwipeAndDragHelper;
|
||||||
import com.google.android.material.checkbox.MaterialCheckBox;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class CategoryInfoAdapter extends RecyclerView.Adapter<CategoryInfoAdapter.ViewHolder>
|
public class CategoryInfoAdapter extends RecyclerView.Adapter<CategoryInfoAdapter.ViewHolder>
|
||||||
implements SwipeAndDragHelper.ActionCompletionContract {
|
implements SwipeAndDragHelper.ActionCompletionContract {
|
||||||
|
|
|
@ -17,13 +17,18 @@ package code.name.monkey.retromusic.adapter
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import androidx.core.view.ViewCompat
|
import android.view.ViewOutlineProvider
|
||||||
import androidx.fragment.app.FragmentActivity
|
import androidx.fragment.app.FragmentActivity
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import code.name.monkey.retromusic.R
|
import code.name.monkey.retromusic.R
|
||||||
import code.name.monkey.retromusic.adapter.base.MediaEntryViewHolder
|
import code.name.monkey.retromusic.adapter.base.MediaEntryViewHolder
|
||||||
|
import code.name.monkey.retromusic.glide.GlideApp
|
||||||
|
import code.name.monkey.retromusic.glide.RetroGlideExtension
|
||||||
|
import code.name.monkey.retromusic.glide.RetroMusicColoredTarget
|
||||||
import code.name.monkey.retromusic.interfaces.IGenreClickListener
|
import code.name.monkey.retromusic.interfaces.IGenreClickListener
|
||||||
import code.name.monkey.retromusic.model.Genre
|
import code.name.monkey.retromusic.model.Genre
|
||||||
|
import code.name.monkey.retromusic.util.MusicUtil
|
||||||
|
import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -36,6 +41,15 @@ class GenreAdapter(
|
||||||
private val mItemLayoutRes: Int,
|
private val mItemLayoutRes: Int,
|
||||||
private val listener: IGenreClickListener
|
private val listener: IGenreClickListener
|
||||||
) : RecyclerView.Adapter<GenreAdapter.ViewHolder>() {
|
) : RecyclerView.Adapter<GenreAdapter.ViewHolder>() {
|
||||||
|
|
||||||
|
init {
|
||||||
|
this.setHasStableIds(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getItemId(position: Int): Long {
|
||||||
|
return dataSet[position].id
|
||||||
|
}
|
||||||
|
|
||||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
||||||
return ViewHolder(LayoutInflater.from(activity).inflate(mItemLayoutRes, parent, false))
|
return ViewHolder(LayoutInflater.from(activity).inflate(mItemLayoutRes, parent, false))
|
||||||
}
|
}
|
||||||
|
@ -49,6 +63,28 @@ class GenreAdapter(
|
||||||
genre.songCount,
|
genre.songCount,
|
||||||
if (genre.songCount > 1) activity.getString(R.string.songs) else activity.getString(R.string.song)
|
if (genre.songCount > 1) activity.getString(R.string.songs) else activity.getString(R.string.song)
|
||||||
)
|
)
|
||||||
|
loadGenreImage(genre, holder)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun loadGenreImage(genre: Genre, holder: GenreAdapter.ViewHolder) {
|
||||||
|
val genreSong = MusicUtil.songByGenre(genre.id)
|
||||||
|
GlideApp.with(activity)
|
||||||
|
.asBitmapPalette()
|
||||||
|
.load(RetroGlideExtension.getSongModel(genreSong))
|
||||||
|
.songCoverOptions(genreSong)
|
||||||
|
.into(object : RetroMusicColoredTarget(holder.image!!) {
|
||||||
|
override fun onColorReady(colors: MediaNotificationProcessor) {
|
||||||
|
setColors(holder, colors)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
// Just for a bit of shadow around image
|
||||||
|
holder.image?.outlineProvider = ViewOutlineProvider.BOUNDS
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setColors(holder: ViewHolder, color: MediaNotificationProcessor) {
|
||||||
|
holder.imageContainerCard?.setCardBackgroundColor(color.backgroundColor)
|
||||||
|
holder.title?.setTextColor(color.primaryTextColor)
|
||||||
|
holder.text?.setTextColor(color.secondaryTextColor)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getItemCount(): Int {
|
override fun getItemCount(): Int {
|
||||||
|
@ -62,7 +98,6 @@ class GenreAdapter(
|
||||||
|
|
||||||
inner class ViewHolder(itemView: View) : MediaEntryViewHolder(itemView) {
|
inner class ViewHolder(itemView: View) : MediaEntryViewHolder(itemView) {
|
||||||
override fun onClick(v: View?) {
|
override fun onClick(v: View?) {
|
||||||
ViewCompat.setTransitionName(itemView, "genre")
|
|
||||||
listener.onClickGenre(dataSet[layoutPosition], itemView)
|
listener.onClickGenre(dataSet[layoutPosition], itemView)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,7 @@ import android.widget.TextView
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.appcompat.widget.AppCompatTextView
|
import androidx.appcompat.widget.AppCompatTextView
|
||||||
import androidx.core.os.bundleOf
|
import androidx.core.os.bundleOf
|
||||||
|
import androidx.fragment.app.findFragment
|
||||||
import androidx.navigation.findNavController
|
import androidx.navigation.findNavController
|
||||||
import androidx.navigation.fragment.FragmentNavigatorExtras
|
import androidx.navigation.fragment.FragmentNavigatorExtras
|
||||||
import androidx.recyclerview.widget.GridLayoutManager
|
import androidx.recyclerview.widget.GridLayoutManager
|
||||||
|
@ -34,14 +35,15 @@ import code.name.monkey.retromusic.adapter.album.AlbumAdapter
|
||||||
import code.name.monkey.retromusic.adapter.artist.ArtistAdapter
|
import code.name.monkey.retromusic.adapter.artist.ArtistAdapter
|
||||||
import code.name.monkey.retromusic.adapter.song.SongAdapter
|
import code.name.monkey.retromusic.adapter.song.SongAdapter
|
||||||
import code.name.monkey.retromusic.extensions.hide
|
import code.name.monkey.retromusic.extensions.hide
|
||||||
import code.name.monkey.retromusic.glide.SongGlideRequest
|
import code.name.monkey.retromusic.fragments.home.HomeFragment
|
||||||
|
import code.name.monkey.retromusic.glide.GlideApp
|
||||||
|
import code.name.monkey.retromusic.glide.RetroGlideExtension
|
||||||
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
||||||
import code.name.monkey.retromusic.interfaces.IAlbumClickListener
|
import code.name.monkey.retromusic.interfaces.IAlbumClickListener
|
||||||
import code.name.monkey.retromusic.interfaces.IArtistClickListener
|
import code.name.monkey.retromusic.interfaces.IArtistClickListener
|
||||||
import code.name.monkey.retromusic.interfaces.IGenreClickListener
|
import code.name.monkey.retromusic.interfaces.IGenreClickListener
|
||||||
import code.name.monkey.retromusic.model.*
|
import code.name.monkey.retromusic.model.*
|
||||||
import code.name.monkey.retromusic.util.PreferenceUtil
|
import code.name.monkey.retromusic.util.PreferenceUtil
|
||||||
import com.bumptech.glide.Glide
|
|
||||||
import com.google.android.material.card.MaterialCardView
|
import com.google.android.material.card.MaterialCardView
|
||||||
|
|
||||||
class HomeAdapter(
|
class HomeAdapter(
|
||||||
|
@ -82,6 +84,7 @@ class HomeAdapter(
|
||||||
val viewHolder = holder as AlbumViewHolder
|
val viewHolder = holder as AlbumViewHolder
|
||||||
viewHolder.bindView(home)
|
viewHolder.bindView(home)
|
||||||
viewHolder.clickableArea.setOnClickListener {
|
viewHolder.clickableArea.setOnClickListener {
|
||||||
|
it.findFragment<HomeFragment>().setSharedAxisXTransitions()
|
||||||
activity.findNavController(R.id.fragment_container).navigate(
|
activity.findNavController(R.id.fragment_container).navigate(
|
||||||
R.id.detailListFragment,
|
R.id.detailListFragment,
|
||||||
bundleOf("type" to RECENT_ALBUMS)
|
bundleOf("type" to RECENT_ALBUMS)
|
||||||
|
@ -92,6 +95,7 @@ class HomeAdapter(
|
||||||
val viewHolder = holder as AlbumViewHolder
|
val viewHolder = holder as AlbumViewHolder
|
||||||
viewHolder.bindView(home)
|
viewHolder.bindView(home)
|
||||||
viewHolder.clickableArea.setOnClickListener {
|
viewHolder.clickableArea.setOnClickListener {
|
||||||
|
it.findFragment<HomeFragment>().setSharedAxisXTransitions()
|
||||||
activity.findNavController(R.id.fragment_container).navigate(
|
activity.findNavController(R.id.fragment_container).navigate(
|
||||||
R.id.detailListFragment,
|
R.id.detailListFragment,
|
||||||
bundleOf("type" to TOP_ALBUMS)
|
bundleOf("type" to TOP_ALBUMS)
|
||||||
|
@ -102,6 +106,7 @@ class HomeAdapter(
|
||||||
val viewHolder = holder as ArtistViewHolder
|
val viewHolder = holder as ArtistViewHolder
|
||||||
viewHolder.bindView(home)
|
viewHolder.bindView(home)
|
||||||
viewHolder.clickableArea.setOnClickListener {
|
viewHolder.clickableArea.setOnClickListener {
|
||||||
|
it.findFragment<HomeFragment>().setSharedAxisXTransitions()
|
||||||
activity.findNavController(R.id.fragment_container).navigate(
|
activity.findNavController(R.id.fragment_container).navigate(
|
||||||
R.id.detailListFragment,
|
R.id.detailListFragment,
|
||||||
bundleOf("type" to RECENT_ARTISTS)
|
bundleOf("type" to RECENT_ARTISTS)
|
||||||
|
@ -112,6 +117,7 @@ class HomeAdapter(
|
||||||
val viewHolder = holder as ArtistViewHolder
|
val viewHolder = holder as ArtistViewHolder
|
||||||
viewHolder.bindView(home)
|
viewHolder.bindView(home)
|
||||||
viewHolder.clickableArea.setOnClickListener {
|
viewHolder.clickableArea.setOnClickListener {
|
||||||
|
it.findFragment<HomeFragment>().setSharedAxisXTransitions()
|
||||||
activity.findNavController(R.id.fragment_container).navigate(
|
activity.findNavController(R.id.fragment_container).navigate(
|
||||||
R.id.detailListFragment,
|
R.id.detailListFragment,
|
||||||
bundleOf("type" to TOP_ARTISTS)
|
bundleOf("type" to TOP_ARTISTS)
|
||||||
|
@ -126,6 +132,7 @@ class HomeAdapter(
|
||||||
val viewHolder = holder as PlaylistViewHolder
|
val viewHolder = holder as PlaylistViewHolder
|
||||||
viewHolder.bindView(home)
|
viewHolder.bindView(home)
|
||||||
viewHolder.clickableArea.setOnClickListener {
|
viewHolder.clickableArea.setOnClickListener {
|
||||||
|
it.findFragment<HomeFragment>().setSharedAxisXTransitions()
|
||||||
activity.findNavController(R.id.fragment_container).navigate(
|
activity.findNavController(R.id.fragment_container).navigate(
|
||||||
R.id.detailListFragment,
|
R.id.detailListFragment,
|
||||||
bundleOf("type" to FAVOURITES)
|
bundleOf("type" to FAVOURITES)
|
||||||
|
@ -184,17 +191,29 @@ class HomeAdapter(
|
||||||
|
|
||||||
fun bindView(home: Home) {
|
fun bindView(home: Home) {
|
||||||
val color = ThemeStore.accentColor(activity)
|
val color = ThemeStore.accentColor(activity)
|
||||||
itemView.findViewById<TextView>(R.id.message).setTextColor(color)
|
itemView.findViewById<TextView>(R.id.message).apply {
|
||||||
|
setTextColor(color)
|
||||||
|
setOnClickListener {
|
||||||
|
MusicPlayerRemote.playNext((home.arrayList as List<Song>).subList(0, 8))
|
||||||
|
if (!MusicPlayerRemote.isPlaying) {
|
||||||
|
MusicPlayerRemote.playNextSong()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
itemView.findViewById<MaterialCardView>(R.id.card6).apply {
|
itemView.findViewById<MaterialCardView>(R.id.card6).apply {
|
||||||
setCardBackgroundColor(ColorUtil.withAlpha(color, 0.12f))
|
setCardBackgroundColor(ColorUtil.withAlpha(color, 0.12f))
|
||||||
}
|
}
|
||||||
images.forEachIndexed { index, id ->
|
images.forEachIndexed { index, id ->
|
||||||
itemView.findViewById<View>(id).setOnClickListener {
|
itemView.findViewById<View>(id).setOnClickListener {
|
||||||
MusicPlayerRemote.playNext(home.arrayList[index] as Song)
|
MusicPlayerRemote.playNext(home.arrayList[index] as Song)
|
||||||
|
if (!MusicPlayerRemote.isPlaying) {
|
||||||
|
MusicPlayerRemote.playNextSong()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
SongGlideRequest.Builder.from(Glide.with(activity), home.arrayList[index] as Song)
|
GlideApp.with(activity)
|
||||||
.asBitmap()
|
.asBitmap()
|
||||||
.build()
|
.songCoverOptions(home.arrayList[index] as Song)
|
||||||
|
.load(RetroGlideExtension.getSongModel(home.arrayList[index] as Song))
|
||||||
.into(itemView.findViewById(id))
|
.into(itemView.findViewById(id))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -207,7 +226,7 @@ class HomeAdapter(
|
||||||
val songAdapter = SongAdapter(
|
val songAdapter = SongAdapter(
|
||||||
activity,
|
activity,
|
||||||
home.arrayList as MutableList<Song>,
|
home.arrayList as MutableList<Song>,
|
||||||
R.layout.item_album_card, null
|
R.layout.item_favourite_card, null
|
||||||
)
|
)
|
||||||
layoutManager = linearLayoutManager()
|
layoutManager = linearLayoutManager()
|
||||||
adapter = songAdapter
|
adapter = songAdapter
|
||||||
|
@ -257,7 +276,7 @@ class HomeAdapter(
|
||||||
bundleOf(EXTRA_ARTIST_ID to artistId),
|
bundleOf(EXTRA_ARTIST_ID to artistId),
|
||||||
null,
|
null,
|
||||||
FragmentNavigatorExtras(
|
FragmentNavigatorExtras(
|
||||||
view to "artist"
|
view to artistId.toString()
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -268,7 +287,7 @@ class HomeAdapter(
|
||||||
bundleOf(EXTRA_ALBUM_ID to albumId),
|
bundleOf(EXTRA_ALBUM_ID to albumId),
|
||||||
null,
|
null,
|
||||||
FragmentNavigatorExtras(
|
FragmentNavigatorExtras(
|
||||||
view to "album"
|
view to albumId.toString()
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,15 +29,14 @@ import code.name.monkey.retromusic.*
|
||||||
import code.name.monkey.retromusic.adapter.base.MediaEntryViewHolder
|
import code.name.monkey.retromusic.adapter.base.MediaEntryViewHolder
|
||||||
import code.name.monkey.retromusic.db.PlaylistEntity
|
import code.name.monkey.retromusic.db.PlaylistEntity
|
||||||
import code.name.monkey.retromusic.db.PlaylistWithSongs
|
import code.name.monkey.retromusic.db.PlaylistWithSongs
|
||||||
import code.name.monkey.retromusic.glide.AlbumGlideRequest
|
import code.name.monkey.retromusic.glide.GlideApp
|
||||||
import code.name.monkey.retromusic.glide.ArtistGlideRequest
|
import code.name.monkey.retromusic.glide.RetroGlideExtension
|
||||||
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
||||||
import code.name.monkey.retromusic.helper.menu.SongMenuHelper
|
import code.name.monkey.retromusic.helper.menu.SongMenuHelper
|
||||||
import code.name.monkey.retromusic.model.*
|
import code.name.monkey.retromusic.model.*
|
||||||
import code.name.monkey.retromusic.model.smartplaylist.AbsSmartPlaylist
|
import code.name.monkey.retromusic.model.smartplaylist.AbsSmartPlaylist
|
||||||
import code.name.monkey.retromusic.repository.PlaylistSongsLoader
|
import code.name.monkey.retromusic.repository.PlaylistSongsLoader
|
||||||
import code.name.monkey.retromusic.util.MusicUtil
|
import code.name.monkey.retromusic.util.MusicUtil
|
||||||
import com.bumptech.glide.Glide
|
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
class SearchAdapter(
|
class SearchAdapter(
|
||||||
|
@ -52,7 +51,7 @@ class SearchAdapter(
|
||||||
|
|
||||||
override fun getItemViewType(position: Int): Int {
|
override fun getItemViewType(position: Int): Int {
|
||||||
if (dataSet[position] is Album) return ALBUM
|
if (dataSet[position] is Album) return ALBUM
|
||||||
if (dataSet[position] is Artist) return ARTIST
|
if (dataSet[position] is Artist) return if ((dataSet[position] as Artist).isAlbumArtist) ALBUM_ARTIST else ARTIST
|
||||||
if (dataSet[position] is Genre) return GENRE
|
if (dataSet[position] is Genre) return GENRE
|
||||||
if (dataSet[position] is PlaylistEntity) return PLAYLIST
|
if (dataSet[position] is PlaylistEntity) return PLAYLIST
|
||||||
return if (dataSet[position] is Song) SONG else HEADER
|
return if (dataSet[position] is Song) SONG else HEADER
|
||||||
|
@ -66,6 +65,14 @@ class SearchAdapter(
|
||||||
false
|
false
|
||||||
), viewType
|
), viewType
|
||||||
)
|
)
|
||||||
|
else if (viewType == ALBUM || viewType == ARTIST || viewType== ALBUM_ARTIST)
|
||||||
|
ViewHolder(
|
||||||
|
LayoutInflater.from(activity).inflate(
|
||||||
|
R.layout.item_list_big,
|
||||||
|
parent,
|
||||||
|
false
|
||||||
|
), viewType
|
||||||
|
)
|
||||||
else
|
else
|
||||||
ViewHolder(
|
ViewHolder(
|
||||||
LayoutInflater.from(activity).inflate(R.layout.item_list, parent, false),
|
LayoutInflater.from(activity).inflate(R.layout.item_list, parent, false),
|
||||||
|
@ -80,21 +87,23 @@ class SearchAdapter(
|
||||||
val album = dataSet[position] as Album
|
val album = dataSet[position] as Album
|
||||||
holder.title?.text = album.title
|
holder.title?.text = album.title
|
||||||
holder.text?.text = album.artistName
|
holder.text?.text = album.artistName
|
||||||
AlbumGlideRequest.Builder.from(Glide.with(activity), album.safeGetFirstSong())
|
GlideApp.with(activity).asDrawable().albumCoverOptions(album.safeGetFirstSong()).load(RetroGlideExtension.getSongModel(album.safeGetFirstSong()))
|
||||||
.checkIgnoreMediaStore().build().into(holder.image)
|
.into(holder.image!!)
|
||||||
}
|
}
|
||||||
ARTIST -> {
|
ARTIST -> {
|
||||||
holder.imageTextContainer?.isVisible = true
|
holder.imageTextContainer?.isVisible = true
|
||||||
val artist = dataSet[position] as Artist
|
val artist = dataSet[position] as Artist
|
||||||
holder.title?.text = artist.name
|
holder.title?.text = artist.name
|
||||||
holder.text?.text = MusicUtil.getArtistInfoString(activity, artist)
|
holder.text?.text = MusicUtil.getArtistInfoString(activity, artist)
|
||||||
ArtistGlideRequest.Builder.from(Glide.with(activity), artist).build()
|
GlideApp.with(activity).asDrawable().artistImageOptions(artist).load(
|
||||||
.into(holder.image)
|
RetroGlideExtension.getArtistModel(artist)).into(holder.image!!)
|
||||||
}
|
}
|
||||||
SONG -> {
|
SONG -> {
|
||||||
|
holder.imageTextContainer?.isVisible = true
|
||||||
val song = dataSet[position] as Song
|
val song = dataSet[position] as Song
|
||||||
holder.title?.text = song.title
|
holder.title?.text = song.title
|
||||||
holder.text?.text = song.albumName
|
holder.text?.text = song.albumName
|
||||||
|
GlideApp.with(activity).asDrawable().songCoverOptions(song).load(RetroGlideExtension.getSongModel(song)).into(holder.image!!)
|
||||||
}
|
}
|
||||||
GENRE -> {
|
GENRE -> {
|
||||||
val genre = dataSet[position] as Genre
|
val genre = dataSet[position] as Genre
|
||||||
|
@ -113,6 +122,14 @@ class SearchAdapter(
|
||||||
holder.title?.text = playlist.playlistName
|
holder.title?.text = playlist.playlistName
|
||||||
//holder.text?.text = MusicUtil.playlistInfoString(activity, playlist.songs)
|
//holder.text?.text = MusicUtil.playlistInfoString(activity, playlist.songs)
|
||||||
}
|
}
|
||||||
|
ALBUM_ARTIST -> {
|
||||||
|
holder.imageTextContainer?.isVisible = true
|
||||||
|
val artist = dataSet[position] as Artist
|
||||||
|
holder.title?.text = artist.name
|
||||||
|
holder.text?.text = MusicUtil.getArtistInfoString(activity, artist)
|
||||||
|
GlideApp.with(activity).asDrawable().artistImageOptions(artist).load(artist)
|
||||||
|
.into(holder.image!!)
|
||||||
|
}
|
||||||
else -> {
|
else -> {
|
||||||
holder.title?.text = dataSet[position].toString()
|
holder.title?.text = dataSet[position].toString()
|
||||||
holder.title?.setTextColor(ThemeStore.accentColor(activity))
|
holder.title?.setTextColor(ThemeStore.accentColor(activity))
|
||||||
|
@ -174,6 +191,12 @@ class SearchAdapter(
|
||||||
bundleOf(EXTRA_ARTIST_ID to (item as Artist).id)
|
bundleOf(EXTRA_ARTIST_ID to (item as Artist).id)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
ALBUM_ARTIST ->{
|
||||||
|
activity.findNavController(R.id.fragment_container).navigate(
|
||||||
|
R.id.albumArtistDetailsFragment,
|
||||||
|
bundleOf(EXTRA_ARTIST_NAME to (item as Artist).name)
|
||||||
|
)
|
||||||
|
}
|
||||||
GENRE -> {
|
GENRE -> {
|
||||||
activity.findNavController(R.id.fragment_container).navigate(
|
activity.findNavController(R.id.fragment_container).navigate(
|
||||||
R.id.genreDetailsFragment,
|
R.id.genreDetailsFragment,
|
||||||
|
@ -202,5 +225,6 @@ class SearchAdapter(
|
||||||
private const val SONG = 3
|
private const val SONG = 3
|
||||||
private const val GENRE = 4
|
private const val GENRE = 4
|
||||||
private const val PLAYLIST = 5
|
private const val PLAYLIST = 5
|
||||||
|
private const val ALBUM_ARTIST = 6
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,19 +24,20 @@ import code.name.monkey.appthemehelper.util.ATHUtil
|
||||||
import code.name.monkey.retromusic.R
|
import code.name.monkey.retromusic.R
|
||||||
import code.name.monkey.retromusic.adapter.base.AbsMultiSelectAdapter
|
import code.name.monkey.retromusic.adapter.base.AbsMultiSelectAdapter
|
||||||
import code.name.monkey.retromusic.adapter.base.MediaEntryViewHolder
|
import code.name.monkey.retromusic.adapter.base.MediaEntryViewHolder
|
||||||
|
import code.name.monkey.retromusic.glide.GlideApp
|
||||||
|
import code.name.monkey.retromusic.glide.RetroGlideExtension
|
||||||
import code.name.monkey.retromusic.glide.audiocover.AudioFileCover
|
import code.name.monkey.retromusic.glide.audiocover.AudioFileCover
|
||||||
import code.name.monkey.retromusic.interfaces.ICabHolder
|
import code.name.monkey.retromusic.interfaces.ICabHolder
|
||||||
import code.name.monkey.retromusic.interfaces.ICallbacks
|
import code.name.monkey.retromusic.interfaces.ICallbacks
|
||||||
import code.name.monkey.retromusic.util.MusicUtil
|
import code.name.monkey.retromusic.util.MusicUtil
|
||||||
import code.name.monkey.retromusic.util.RetroUtil
|
import code.name.monkey.retromusic.util.RetroUtil
|
||||||
import com.bumptech.glide.Glide
|
|
||||||
import com.bumptech.glide.load.engine.DiskCacheStrategy
|
import com.bumptech.glide.load.engine.DiskCacheStrategy
|
||||||
import com.bumptech.glide.signature.MediaStoreSignature
|
import com.bumptech.glide.signature.MediaStoreSignature
|
||||||
|
import me.zhanghai.android.fastscroll.PopupTextProvider
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.text.DecimalFormat
|
import java.text.DecimalFormat
|
||||||
import kotlin.math.log10
|
import kotlin.math.log10
|
||||||
import kotlin.math.pow
|
import kotlin.math.pow
|
||||||
import me.zhanghai.android.fastscroll.PopupTextProvider
|
|
||||||
|
|
||||||
class SongFileAdapter(
|
class SongFileAdapter(
|
||||||
private val activity: AppCompatActivity,
|
private val activity: AppCompatActivity,
|
||||||
|
@ -111,14 +112,14 @@ class SongFileAdapter(
|
||||||
val error = RetroUtil.getTintedVectorDrawable(
|
val error = RetroUtil.getTintedVectorDrawable(
|
||||||
activity, R.drawable.ic_file_music, iconColor
|
activity, R.drawable.ic_file_music, iconColor
|
||||||
)
|
)
|
||||||
Glide.with(activity)
|
GlideApp.with(activity)
|
||||||
.load(AudioFileCover(file.path))
|
.load(AudioFileCover(file.path))
|
||||||
.diskCacheStrategy(DiskCacheStrategy.NONE)
|
.diskCacheStrategy(DiskCacheStrategy.NONE)
|
||||||
.error(error)
|
.error(error)
|
||||||
.placeholder(error)
|
.placeholder(error)
|
||||||
.animate(android.R.anim.fade_in)
|
.transition(RetroGlideExtension.getDefaultTransition())
|
||||||
.signature(MediaStoreSignature("", file.lastModified(), 0))
|
.signature(MediaStoreSignature("", file.lastModified(), 0))
|
||||||
.into(holder.image)
|
.into(holder.image!!)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -126,7 +127,7 @@ class SongFileAdapter(
|
||||||
return dataSet.size
|
return dataSet.size
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getIdentifier(position: Int): File? {
|
override fun getIdentifier(position: Int): File {
|
||||||
return dataSet[position]
|
return dataSet[position]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,55 @@
|
||||||
|
package code.name.monkey.retromusic.adapter
|
||||||
|
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import android.widget.TextView
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import code.name.monkey.retromusic.R
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
class StorageAdapter(
|
||||||
|
val storageList: List<Storage>,
|
||||||
|
val storageClickListener: StorageClickListener
|
||||||
|
) :
|
||||||
|
RecyclerView.Adapter<StorageAdapter.ViewHolder>() {
|
||||||
|
|
||||||
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
||||||
|
return ViewHolder(
|
||||||
|
LayoutInflater.from(parent.context).inflate(
|
||||||
|
R.layout.item_storage,
|
||||||
|
parent,
|
||||||
|
false
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||||
|
holder.bindData(storageList[position])
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getItemCount(): Int {
|
||||||
|
return storageList.size
|
||||||
|
}
|
||||||
|
|
||||||
|
inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
|
||||||
|
val title: TextView = itemView.findViewById(R.id.title)
|
||||||
|
|
||||||
|
fun bindData(storage: Storage) {
|
||||||
|
title.text = storage.title
|
||||||
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
itemView.setOnClickListener { storageClickListener.onStorageClicked(storageList[bindingAdapterPosition]) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface StorageClickListener {
|
||||||
|
fun onStorageClicked(storage: Storage)
|
||||||
|
}
|
||||||
|
|
||||||
|
class Storage {
|
||||||
|
lateinit var title: String
|
||||||
|
lateinit var file: File
|
||||||
|
}
|
|
@ -24,7 +24,8 @@ import androidx.fragment.app.FragmentActivity
|
||||||
import code.name.monkey.retromusic.R
|
import code.name.monkey.retromusic.R
|
||||||
import code.name.monkey.retromusic.adapter.base.AbsMultiSelectAdapter
|
import code.name.monkey.retromusic.adapter.base.AbsMultiSelectAdapter
|
||||||
import code.name.monkey.retromusic.adapter.base.MediaEntryViewHolder
|
import code.name.monkey.retromusic.adapter.base.MediaEntryViewHolder
|
||||||
import code.name.monkey.retromusic.glide.AlbumGlideRequest
|
import code.name.monkey.retromusic.glide.GlideApp
|
||||||
|
import code.name.monkey.retromusic.glide.RetroGlideExtension
|
||||||
import code.name.monkey.retromusic.glide.RetroMusicColoredTarget
|
import code.name.monkey.retromusic.glide.RetroMusicColoredTarget
|
||||||
import code.name.monkey.retromusic.helper.SortOrder
|
import code.name.monkey.retromusic.helper.SortOrder
|
||||||
import code.name.monkey.retromusic.helper.menu.SongsMenuHelper
|
import code.name.monkey.retromusic.helper.menu.SongsMenuHelper
|
||||||
|
@ -35,7 +36,6 @@ import code.name.monkey.retromusic.model.Song
|
||||||
import code.name.monkey.retromusic.util.MusicUtil
|
import code.name.monkey.retromusic.util.MusicUtil
|
||||||
import code.name.monkey.retromusic.util.PreferenceUtil
|
import code.name.monkey.retromusic.util.PreferenceUtil
|
||||||
import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
|
import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
|
||||||
import com.bumptech.glide.Glide
|
|
||||||
import me.zhanghai.android.fastscroll.PopupTextProvider
|
import me.zhanghai.android.fastscroll.PopupTextProvider
|
||||||
|
|
||||||
open class AlbumAdapter(
|
open class AlbumAdapter(
|
||||||
|
@ -73,7 +73,13 @@ open class AlbumAdapter(
|
||||||
}
|
}
|
||||||
|
|
||||||
protected open fun getAlbumText(album: Album): String? {
|
protected open fun getAlbumText(album: Album): String? {
|
||||||
return album.artistName
|
return album.albumArtist.let {
|
||||||
|
if (it.isNullOrEmpty()) {
|
||||||
|
album.artistName
|
||||||
|
} else {
|
||||||
|
it
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||||
|
@ -82,6 +88,7 @@ open class AlbumAdapter(
|
||||||
holder.itemView.isActivated = isChecked
|
holder.itemView.isActivated = isChecked
|
||||||
holder.title?.text = getAlbumTitle(album)
|
holder.title?.text = getAlbumTitle(album)
|
||||||
holder.text?.text = getAlbumText(album)
|
holder.text?.text = getAlbumText(album)
|
||||||
|
ViewCompat.setTransitionName(holder.image!!, album.id.toString())
|
||||||
loadAlbumCover(album, holder)
|
loadAlbumCover(album, holder)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -92,17 +99,17 @@ open class AlbumAdapter(
|
||||||
holder.paletteColorContainer?.setBackgroundColor(color.backgroundColor)
|
holder.paletteColorContainer?.setBackgroundColor(color.backgroundColor)
|
||||||
}
|
}
|
||||||
holder.mask?.backgroundTintList = ColorStateList.valueOf(color.primaryTextColor)
|
holder.mask?.backgroundTintList = ColorStateList.valueOf(color.primaryTextColor)
|
||||||
holder.imageContainerCard?.setCardBackgroundColor(color.backgroundColor) }
|
holder.imageContainerCard?.setCardBackgroundColor(color.backgroundColor)
|
||||||
|
}
|
||||||
|
|
||||||
protected open fun loadAlbumCover(album: Album, holder: ViewHolder) {
|
protected open fun loadAlbumCover(album: Album, holder: ViewHolder) {
|
||||||
if (holder.image == null) {
|
if (holder.image == null) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
val song = album.safeGetFirstSong()
|
||||||
AlbumGlideRequest.Builder.from(Glide.with(activity), album.safeGetFirstSong())
|
GlideApp.with(activity).asBitmapPalette().albumCoverOptions(song)
|
||||||
.checkIgnoreMediaStore()
|
//.checkIgnoreMediaStore()
|
||||||
.generatePalette(activity)
|
.load(RetroGlideExtension.getSongModel(song))
|
||||||
.build()
|
|
||||||
.into(object : RetroMusicColoredTarget(holder.image!!) {
|
.into(object : RetroMusicColoredTarget(holder.image!!) {
|
||||||
override fun onColorReady(colors: MediaNotificationProcessor) {
|
override fun onColorReady(colors: MediaNotificationProcessor) {
|
||||||
setColors(colors, holder)
|
setColors(colors, holder)
|
||||||
|
@ -161,7 +168,6 @@ open class AlbumAdapter(
|
||||||
inner class ViewHolder(itemView: View) : MediaEntryViewHolder(itemView) {
|
inner class ViewHolder(itemView: View) : MediaEntryViewHolder(itemView) {
|
||||||
|
|
||||||
init {
|
init {
|
||||||
setImageTransitionName("Album")
|
|
||||||
menu?.visibility = View.GONE
|
menu?.visibility = View.GONE
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -171,16 +177,13 @@ open class AlbumAdapter(
|
||||||
toggleChecked(layoutPosition)
|
toggleChecked(layoutPosition)
|
||||||
} else {
|
} else {
|
||||||
image?.let {
|
image?.let {
|
||||||
ViewCompat.setTransitionName(it, "album")
|
|
||||||
listener?.onAlbumClick(dataSet[layoutPosition].id, it)
|
listener?.onAlbumClick(dataSet[layoutPosition].id, it)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onLongClick(v: View?): Boolean {
|
override fun onLongClick(v: View?): Boolean {
|
||||||
toggleChecked(layoutPosition)
|
return toggleChecked(layoutPosition)
|
||||||
return super.onLongClick(v)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,15 +26,15 @@ import androidx.lifecycle.lifecycleScope
|
||||||
import code.name.monkey.retromusic.R
|
import code.name.monkey.retromusic.R
|
||||||
import code.name.monkey.retromusic.fragments.AlbumCoverStyle
|
import code.name.monkey.retromusic.fragments.AlbumCoverStyle
|
||||||
import code.name.monkey.retromusic.fragments.NowPlayingScreen.*
|
import code.name.monkey.retromusic.fragments.NowPlayingScreen.*
|
||||||
|
import code.name.monkey.retromusic.glide.GlideApp
|
||||||
|
import code.name.monkey.retromusic.glide.RetroGlideExtension
|
||||||
import code.name.monkey.retromusic.glide.RetroMusicColoredTarget
|
import code.name.monkey.retromusic.glide.RetroMusicColoredTarget
|
||||||
import code.name.monkey.retromusic.glide.SongGlideRequest
|
|
||||||
import code.name.monkey.retromusic.misc.CustomFragmentStatePagerAdapter
|
import code.name.monkey.retromusic.misc.CustomFragmentStatePagerAdapter
|
||||||
import code.name.monkey.retromusic.model.Song
|
import code.name.monkey.retromusic.model.Song
|
||||||
import code.name.monkey.retromusic.util.MusicUtil
|
import code.name.monkey.retromusic.util.MusicUtil
|
||||||
import code.name.monkey.retromusic.util.NavigationUtil
|
import code.name.monkey.retromusic.util.NavigationUtil
|
||||||
import code.name.monkey.retromusic.util.PreferenceUtil
|
import code.name.monkey.retromusic.util.PreferenceUtil
|
||||||
import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
|
import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
|
||||||
import com.bumptech.glide.Glide
|
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
@ -162,9 +162,10 @@ class AlbumCoverPagerAdapter(
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun loadAlbumCover() {
|
private fun loadAlbumCover() {
|
||||||
SongGlideRequest.Builder.from(Glide.with(requireContext()), song)
|
GlideApp.with(this).asBitmapPalette().songCoverOptions(song)
|
||||||
.checkIgnoreMediaStore(requireContext())
|
//.checkIgnoreMediaStore()
|
||||||
.generatePalette(requireContext()).build()
|
.load(RetroGlideExtension.getSongModel(song))
|
||||||
|
.dontAnimate()
|
||||||
.into(object : RetroMusicColoredTarget(albumCover) {
|
.into(object : RetroMusicColoredTarget(albumCover) {
|
||||||
override fun onColorReady(colors: MediaNotificationProcessor) {
|
override fun onColorReady(colors: MediaNotificationProcessor) {
|
||||||
setColor(colors)
|
setColor(colors)
|
||||||
|
|
|
@ -17,7 +17,8 @@ package code.name.monkey.retromusic.adapter.album
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import androidx.fragment.app.FragmentActivity
|
import androidx.fragment.app.FragmentActivity
|
||||||
import code.name.monkey.retromusic.glide.AlbumGlideRequest
|
import code.name.monkey.retromusic.glide.GlideApp
|
||||||
|
import code.name.monkey.retromusic.glide.RetroGlideExtension
|
||||||
import code.name.monkey.retromusic.glide.RetroMusicColoredTarget
|
import code.name.monkey.retromusic.glide.RetroMusicColoredTarget
|
||||||
import code.name.monkey.retromusic.helper.HorizontalAdapterHelper
|
import code.name.monkey.retromusic.helper.HorizontalAdapterHelper
|
||||||
import code.name.monkey.retromusic.interfaces.IAlbumClickListener
|
import code.name.monkey.retromusic.interfaces.IAlbumClickListener
|
||||||
|
@ -25,7 +26,6 @@ import code.name.monkey.retromusic.interfaces.ICabHolder
|
||||||
import code.name.monkey.retromusic.model.Album
|
import code.name.monkey.retromusic.model.Album
|
||||||
import code.name.monkey.retromusic.util.MusicUtil
|
import code.name.monkey.retromusic.util.MusicUtil
|
||||||
import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
|
import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
|
||||||
import com.bumptech.glide.Glide
|
|
||||||
|
|
||||||
class HorizontalAlbumAdapter(
|
class HorizontalAlbumAdapter(
|
||||||
activity: FragmentActivity,
|
activity: FragmentActivity,
|
||||||
|
@ -49,10 +49,8 @@ class HorizontalAlbumAdapter(
|
||||||
|
|
||||||
override fun loadAlbumCover(album: Album, holder: ViewHolder) {
|
override fun loadAlbumCover(album: Album, holder: ViewHolder) {
|
||||||
if (holder.image == null) return
|
if (holder.image == null) return
|
||||||
AlbumGlideRequest.Builder.from(Glide.with(activity), album.safeGetFirstSong())
|
GlideApp.with(activity).asBitmapPalette().albumCoverOptions(album.safeGetFirstSong())
|
||||||
.checkIgnoreMediaStore()
|
.load(RetroGlideExtension.getSongModel(album.safeGetFirstSong()))
|
||||||
.generatePalette(activity)
|
|
||||||
.build()
|
|
||||||
.into(object : RetroMusicColoredTarget(holder.image!!) {
|
.into(object : RetroMusicColoredTarget(holder.image!!) {
|
||||||
override fun onColorReady(colors: MediaNotificationProcessor) {
|
override fun onColorReady(colors: MediaNotificationProcessor) {
|
||||||
setColors(colors, holder)
|
setColors(colors, holder)
|
||||||
|
@ -60,7 +58,7 @@ class HorizontalAlbumAdapter(
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getAlbumText(album: Album): String? {
|
override fun getAlbumText(album: Album): String {
|
||||||
return MusicUtil.getYearString(album.year)
|
return MusicUtil.getYearString(album.year)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,25 +26,28 @@ import code.name.monkey.retromusic.R
|
||||||
import code.name.monkey.retromusic.adapter.base.AbsMultiSelectAdapter
|
import code.name.monkey.retromusic.adapter.base.AbsMultiSelectAdapter
|
||||||
import code.name.monkey.retromusic.adapter.base.MediaEntryViewHolder
|
import code.name.monkey.retromusic.adapter.base.MediaEntryViewHolder
|
||||||
import code.name.monkey.retromusic.extensions.hide
|
import code.name.monkey.retromusic.extensions.hide
|
||||||
import code.name.monkey.retromusic.glide.ArtistGlideRequest
|
import code.name.monkey.retromusic.glide.GlideApp
|
||||||
|
import code.name.monkey.retromusic.glide.RetroGlideExtension
|
||||||
import code.name.monkey.retromusic.glide.RetroMusicColoredTarget
|
import code.name.monkey.retromusic.glide.RetroMusicColoredTarget
|
||||||
import code.name.monkey.retromusic.helper.menu.SongsMenuHelper
|
import code.name.monkey.retromusic.helper.menu.SongsMenuHelper
|
||||||
|
import code.name.monkey.retromusic.interfaces.IAlbumArtistClickListener
|
||||||
import code.name.monkey.retromusic.interfaces.IArtistClickListener
|
import code.name.monkey.retromusic.interfaces.IArtistClickListener
|
||||||
import code.name.monkey.retromusic.interfaces.ICabHolder
|
import code.name.monkey.retromusic.interfaces.ICabHolder
|
||||||
import code.name.monkey.retromusic.model.Artist
|
import code.name.monkey.retromusic.model.Artist
|
||||||
import code.name.monkey.retromusic.model.Song
|
import code.name.monkey.retromusic.model.Song
|
||||||
import code.name.monkey.retromusic.util.MusicUtil
|
import code.name.monkey.retromusic.util.MusicUtil
|
||||||
|
import code.name.monkey.retromusic.util.PreferenceUtil
|
||||||
import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
|
import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
|
||||||
import com.bumptech.glide.Glide
|
|
||||||
import java.util.*
|
|
||||||
import me.zhanghai.android.fastscroll.PopupTextProvider
|
import me.zhanghai.android.fastscroll.PopupTextProvider
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
class ArtistAdapter(
|
class ArtistAdapter(
|
||||||
val activity: FragmentActivity,
|
val activity: FragmentActivity,
|
||||||
var dataSet: List<Artist>,
|
var dataSet: List<Artist>,
|
||||||
var itemLayoutRes: Int,
|
var itemLayoutRes: Int,
|
||||||
val ICabHolder: ICabHolder?,
|
val ICabHolder: ICabHolder?,
|
||||||
val IArtistClickListener: IArtistClickListener
|
val IArtistClickListener: IArtistClickListener,
|
||||||
|
val IAlbumArtistClickListener: IAlbumArtistClickListener? = null
|
||||||
) : AbsMultiSelectAdapter<ArtistAdapter.ViewHolder, Artist>(
|
) : AbsMultiSelectAdapter<ArtistAdapter.ViewHolder, Artist>(
|
||||||
activity, ICabHolder, R.menu.menu_media_selection
|
activity, ICabHolder, R.menu.menu_media_selection
|
||||||
), PopupTextProvider {
|
), PopupTextProvider {
|
||||||
|
@ -82,6 +85,13 @@ class ArtistAdapter(
|
||||||
holder.itemView.isActivated = isChecked
|
holder.itemView.isActivated = isChecked
|
||||||
holder.title?.text = artist.name
|
holder.title?.text = artist.name
|
||||||
holder.text?.hide()
|
holder.text?.hide()
|
||||||
|
holder.image?.let {
|
||||||
|
if (PreferenceUtil.albumArtistsOnly) {
|
||||||
|
ViewCompat.setTransitionName(it, artist.name)
|
||||||
|
} else {
|
||||||
|
ViewCompat.setTransitionName(it, artist.id.toString())
|
||||||
|
}
|
||||||
|
}
|
||||||
loadArtistImage(artist, holder)
|
loadArtistImage(artist, holder)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -98,9 +108,11 @@ class ArtistAdapter(
|
||||||
if (holder.image == null) {
|
if (holder.image == null) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
ArtistGlideRequest.Builder.from(Glide.with(activity), artist)
|
GlideApp.with(activity)
|
||||||
.generatePalette(activity)
|
.asBitmapPalette()
|
||||||
.build()
|
.load(RetroGlideExtension.getArtistModel(artist))
|
||||||
|
.artistImageOptions(artist)
|
||||||
|
.transition(RetroGlideExtension.getDefaultTransition())
|
||||||
.into(object : RetroMusicColoredTarget(holder.image!!) {
|
.into(object : RetroMusicColoredTarget(holder.image!!) {
|
||||||
override fun onColorReady(colors: MediaNotificationProcessor) {
|
override fun onColorReady(colors: MediaNotificationProcessor) {
|
||||||
setColors(colors, holder)
|
setColors(colors, holder)
|
||||||
|
@ -112,7 +124,7 @@ class ArtistAdapter(
|
||||||
return dataSet.size
|
return dataSet.size
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getIdentifier(position: Int): Artist? {
|
override fun getIdentifier(position: Int): Artist {
|
||||||
return dataSet[position]
|
return dataSet[position]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -154,16 +166,19 @@ class ArtistAdapter(
|
||||||
if (isInQuickSelectMode) {
|
if (isInQuickSelectMode) {
|
||||||
toggleChecked(layoutPosition)
|
toggleChecked(layoutPosition)
|
||||||
} else {
|
} else {
|
||||||
|
val artist = dataSet[layoutPosition]
|
||||||
image?.let {
|
image?.let {
|
||||||
ViewCompat.setTransitionName(it, "artist")
|
if (PreferenceUtil.albumArtistsOnly && IAlbumArtistClickListener != null) {
|
||||||
IArtistClickListener.onArtist(dataSet[layoutPosition].id, it)
|
IAlbumArtistClickListener.onAlbumArtist(artist.name, it)
|
||||||
|
} else {
|
||||||
|
IArtistClickListener.onArtist(artist.id, it)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onLongClick(v: View?): Boolean {
|
override fun onLongClick(v: View?): Boolean {
|
||||||
toggleChecked(layoutPosition)
|
return toggleChecked(layoutPosition)
|
||||||
return super.onLongClick(v)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,29 +1,39 @@
|
||||||
package code.name.monkey.retromusic.adapter.base;
|
package code.name.monkey.retromusic.adapter.base;
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
|
|
||||||
import androidx.annotation.MenuRes;
|
import androidx.annotation.MenuRes;
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.appcompat.widget.AppCompatTextView;
|
||||||
|
import androidx.appcompat.widget.Toolbar;
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
import code.name.monkey.retromusic.R;
|
|
||||||
import code.name.monkey.retromusic.interfaces.ICabHolder;
|
|
||||||
import com.afollestad.materialcab.MaterialCab;
|
import com.afollestad.materialcab.MaterialCab;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public abstract class AbsMultiSelectAdapter<V extends RecyclerView.ViewHolder, I>
|
import code.name.monkey.retromusic.R;
|
||||||
extends RecyclerView.Adapter<V> implements MaterialCab.Callback {
|
import code.name.monkey.retromusic.interfaces.ICabHolder;
|
||||||
|
|
||||||
@Nullable private final ICabHolder ICabHolder;
|
public abstract class AbsMultiSelectAdapter<V extends RecyclerView.ViewHolder, I>
|
||||||
|
extends RecyclerView.Adapter<V> implements MaterialCab.Callback {
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private final ICabHolder ICabHolder;
|
||||||
private final Context context;
|
private final Context context;
|
||||||
private MaterialCab cab;
|
private MaterialCab cab;
|
||||||
private List<I> checked;
|
private final List<I> checked;
|
||||||
private int menuRes;
|
private int menuRes;
|
||||||
|
private AppCompatTextView dummyText;
|
||||||
|
private int oldSize = 0;
|
||||||
|
|
||||||
public AbsMultiSelectAdapter(
|
public AbsMultiSelectAdapter(
|
||||||
@NonNull Context context, @Nullable ICabHolder ICabHolder, @MenuRes int menuRes) {
|
@NonNull Context context, @Nullable ICabHolder ICabHolder, @MenuRes int menuRes) {
|
||||||
this.ICabHolder = ICabHolder;
|
this.ICabHolder = ICabHolder;
|
||||||
checked = new ArrayList<>();
|
checked = new ArrayList<>();
|
||||||
this.menuRes = menuRes;
|
this.menuRes = menuRes;
|
||||||
|
@ -32,12 +42,16 @@ public abstract class AbsMultiSelectAdapter<V extends RecyclerView.ViewHolder, I
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onCabCreated(MaterialCab materialCab, Menu menu) {
|
public boolean onCabCreated(MaterialCab materialCab, Menu menu) {
|
||||||
|
playCreateAnim(materialCab);
|
||||||
|
createDummyTextView();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onCabFinished(MaterialCab materialCab) {
|
public boolean onCabFinished(MaterialCab materialCab) {
|
||||||
clearChecked();
|
clearChecked();
|
||||||
|
cab.getToolbar().removeView(dummyText);
|
||||||
|
oldSize = 0;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -111,6 +125,7 @@ public abstract class AbsMultiSelectAdapter<V extends RecyclerView.ViewHolder, I
|
||||||
notifyDataSetChanged();
|
notifyDataSetChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressLint({"StringFormatInvalid", "StringFormatMatches"})
|
||||||
private void updateCab() {
|
private void updateCab() {
|
||||||
if (ICabHolder != null) {
|
if (ICabHolder != null) {
|
||||||
if (cab == null || !cab.isActive()) {
|
if (cab == null || !cab.isActive()) {
|
||||||
|
@ -120,10 +135,48 @@ public abstract class AbsMultiSelectAdapter<V extends RecyclerView.ViewHolder, I
|
||||||
if (size <= 0) {
|
if (size <= 0) {
|
||||||
cab.finish();
|
cab.finish();
|
||||||
} else if (size == 1) {
|
} else if (size == 1) {
|
||||||
cab.setTitle(getName(checked.get(0)));
|
|
||||||
} else {
|
|
||||||
cab.setTitle(context.getString(R.string.x_selected, size));
|
cab.setTitle(context.getString(R.string.x_selected, size));
|
||||||
|
if (oldSize == 0) {
|
||||||
|
cab.getToolbar().addView(dummyText);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
AppCompatTextView title = (AppCompatTextView) cab.getToolbar().getChildAt(2);
|
||||||
|
dummyText.setText(title.getText());
|
||||||
|
|
||||||
|
title.setAlpha(0);
|
||||||
|
|
||||||
|
cab.setTitle(context.getString(R.string.x_selected, size));
|
||||||
|
dummyText.setTranslationX(title.getLeft() - dummyText.getLeft());
|
||||||
|
|
||||||
|
dummyText.setAlpha(1);
|
||||||
|
|
||||||
|
dummyText.setTranslationY(0);
|
||||||
|
if (oldSize > size) {
|
||||||
|
title.setTranslationY(40);
|
||||||
|
dummyText.animate().translationY(-40).alpha(0.0F).setDuration(300).start();
|
||||||
|
} else {
|
||||||
|
title.setTranslationY(-40);
|
||||||
|
dummyText.animate().translationY(40).alpha(0.0F).setDuration(300).start();
|
||||||
|
}
|
||||||
|
title.animate().translationY(0).alpha(1.0F).setDuration(300).start();
|
||||||
}
|
}
|
||||||
|
oldSize = size;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void playCreateAnim(MaterialCab materialCab) {
|
||||||
|
Toolbar cabToolbar = materialCab.getToolbar();
|
||||||
|
int height = context.getResources().getDimensionPixelSize(R.dimen.toolbar_height);
|
||||||
|
cabToolbar.setTranslationY(-height);
|
||||||
|
cabToolbar.animate().translationYBy(height).setDuration(300).start();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createDummyTextView() {
|
||||||
|
if (dummyText != null) return;
|
||||||
|
dummyText = new AppCompatTextView(context);
|
||||||
|
dummyText.setSingleLine();
|
||||||
|
dummyText.setTextAppearance(context, R.style.ToolbarTextAppearanceNormal);
|
||||||
|
Toolbar.LayoutParams l1 = new Toolbar.LayoutParams(Toolbar.LayoutParams.WRAP_CONTENT, Toolbar.LayoutParams.WRAP_CONTENT);
|
||||||
|
dummyText.setLayoutParams(l1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,11 +24,11 @@ import androidx.annotation.Nullable;
|
||||||
import androidx.appcompat.widget.AppCompatImageView;
|
import androidx.appcompat.widget.AppCompatImageView;
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
import code.name.monkey.retromusic.R;
|
|
||||||
|
|
||||||
import com.google.android.material.card.MaterialCardView;
|
import com.google.android.material.card.MaterialCardView;
|
||||||
import com.h6ah4i.android.widget.advrecyclerview.utils.AbstractDraggableSwipeableItemViewHolder;
|
import com.h6ah4i.android.widget.advrecyclerview.utils.AbstractDraggableSwipeableItemViewHolder;
|
||||||
|
|
||||||
|
import code.name.monkey.retromusic.R;
|
||||||
|
|
||||||
public class MediaEntryViewHolder extends AbstractDraggableSwipeableItemViewHolder
|
public class MediaEntryViewHolder extends AbstractDraggableSwipeableItemViewHolder
|
||||||
implements View.OnLongClickListener, View.OnClickListener {
|
implements View.OnLongClickListener, View.OnClickListener {
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,6 @@
|
||||||
package code.name.monkey.retromusic.adapter.playlist
|
package code.name.monkey.retromusic.adapter.playlist
|
||||||
|
|
||||||
import android.graphics.Color
|
import android.graphics.Color
|
||||||
import android.graphics.drawable.Drawable
|
|
||||||
import android.text.TextUtils
|
import android.text.TextUtils
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.MenuItem
|
import android.view.MenuItem
|
||||||
|
@ -24,29 +23,22 @@ import android.view.ViewGroup
|
||||||
import androidx.appcompat.widget.PopupMenu
|
import androidx.appcompat.widget.PopupMenu
|
||||||
import androidx.core.view.ViewCompat
|
import androidx.core.view.ViewCompat
|
||||||
import androidx.fragment.app.FragmentActivity
|
import androidx.fragment.app.FragmentActivity
|
||||||
import androidx.lifecycle.lifecycleScope
|
|
||||||
import code.name.monkey.appthemehelper.util.ATHUtil
|
|
||||||
import code.name.monkey.appthemehelper.util.TintHelper
|
|
||||||
import code.name.monkey.retromusic.R
|
import code.name.monkey.retromusic.R
|
||||||
import code.name.monkey.retromusic.adapter.base.AbsMultiSelectAdapter
|
import code.name.monkey.retromusic.adapter.base.AbsMultiSelectAdapter
|
||||||
import code.name.monkey.retromusic.adapter.base.MediaEntryViewHolder
|
import code.name.monkey.retromusic.adapter.base.MediaEntryViewHolder
|
||||||
import code.name.monkey.retromusic.db.PlaylistEntity
|
import code.name.monkey.retromusic.db.PlaylistEntity
|
||||||
import code.name.monkey.retromusic.db.PlaylistWithSongs
|
import code.name.monkey.retromusic.db.PlaylistWithSongs
|
||||||
import code.name.monkey.retromusic.db.SongEntity
|
|
||||||
import code.name.monkey.retromusic.db.toSongs
|
import code.name.monkey.retromusic.db.toSongs
|
||||||
import code.name.monkey.retromusic.extensions.hide
|
import code.name.monkey.retromusic.extensions.hide
|
||||||
import code.name.monkey.retromusic.extensions.show
|
import code.name.monkey.retromusic.extensions.show
|
||||||
|
import code.name.monkey.retromusic.glide.GlideApp
|
||||||
|
import code.name.monkey.retromusic.glide.playlistPreview.PlaylistPreview
|
||||||
import code.name.monkey.retromusic.helper.menu.PlaylistMenuHelper
|
import code.name.monkey.retromusic.helper.menu.PlaylistMenuHelper
|
||||||
import code.name.monkey.retromusic.helper.menu.SongsMenuHelper
|
import code.name.monkey.retromusic.helper.menu.SongsMenuHelper
|
||||||
import code.name.monkey.retromusic.interfaces.ICabHolder
|
import code.name.monkey.retromusic.interfaces.ICabHolder
|
||||||
import code.name.monkey.retromusic.interfaces.IPlaylistClickListener
|
import code.name.monkey.retromusic.interfaces.IPlaylistClickListener
|
||||||
import code.name.monkey.retromusic.model.Song
|
import code.name.monkey.retromusic.model.Song
|
||||||
import code.name.monkey.retromusic.util.AutoGeneratedPlaylistBitmap
|
|
||||||
import code.name.monkey.retromusic.util.MusicUtil
|
import code.name.monkey.retromusic.util.MusicUtil
|
||||||
import kotlinx.coroutines.Dispatchers.IO
|
|
||||||
import kotlinx.coroutines.Dispatchers.Main
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import kotlinx.coroutines.withContext
|
|
||||||
|
|
||||||
class PlaylistAdapter(
|
class PlaylistAdapter(
|
||||||
private val activity: FragmentActivity,
|
private val activity: FragmentActivity,
|
||||||
|
@ -95,27 +87,23 @@ class PlaylistAdapter(
|
||||||
holder.itemView.isActivated = isChecked(playlist)
|
holder.itemView.isActivated = isChecked(playlist)
|
||||||
holder.title?.text = getPlaylistTitle(playlist.playlistEntity)
|
holder.title?.text = getPlaylistTitle(playlist.playlistEntity)
|
||||||
holder.text?.text = getPlaylistText(playlist)
|
holder.text?.text = getPlaylistText(playlist)
|
||||||
holder.image?.setImageDrawable(getIconRes())
|
|
||||||
val isChecked = isChecked(playlist)
|
val isChecked = isChecked(playlist)
|
||||||
if (isChecked) {
|
if (isChecked) {
|
||||||
holder.menu?.hide()
|
holder.menu?.hide()
|
||||||
} else {
|
} else {
|
||||||
holder.menu?.show()
|
holder.menu?.show()
|
||||||
}
|
}
|
||||||
//playlistBitmapLoader(activity, holder, playlist)
|
GlideApp.with(activity)
|
||||||
|
.load(PlaylistPreview(playlist))
|
||||||
|
.playlistOptions()
|
||||||
|
.into(holder.image!!)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getIconRes(): Drawable = TintHelper.createTintedDrawable(
|
|
||||||
activity,
|
|
||||||
R.drawable.ic_playlist_play,
|
|
||||||
ATHUtil.resolveColor(activity, R.attr.colorControlNormal)
|
|
||||||
)
|
|
||||||
|
|
||||||
override fun getItemCount(): Int {
|
override fun getItemCount(): Int {
|
||||||
return dataSet.size
|
return dataSet.size
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getIdentifier(position: Int): PlaylistWithSongs? {
|
override fun getIdentifier(position: Int): PlaylistWithSongs {
|
||||||
return dataSet[position]
|
return dataSet[position]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -141,18 +129,8 @@ class PlaylistAdapter(
|
||||||
return songs
|
return songs
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getSongs(playlist: PlaylistWithSongs): List<SongEntity> =
|
|
||||||
mutableListOf<SongEntity>().apply {
|
|
||||||
addAll(playlist.songs)
|
|
||||||
}
|
|
||||||
|
|
||||||
inner class ViewHolder(itemView: View) : MediaEntryViewHolder(itemView) {
|
inner class ViewHolder(itemView: View) : MediaEntryViewHolder(itemView) {
|
||||||
init {
|
init {
|
||||||
image?.apply {
|
|
||||||
val iconPadding =
|
|
||||||
activity.resources.getDimensionPixelSize(R.dimen.list_item_image_icon_padding)
|
|
||||||
setPadding(iconPadding, iconPadding, iconPadding, iconPadding)
|
|
||||||
}
|
|
||||||
menu?.setOnClickListener { view ->
|
menu?.setOnClickListener { view ->
|
||||||
val popupMenu = PopupMenu(activity, view)
|
val popupMenu = PopupMenu(activity, view)
|
||||||
popupMenu.inflate(R.menu.menu_item_playlist)
|
popupMenu.inflate(R.menu.menu_item_playlist)
|
||||||
|
@ -183,37 +161,6 @@ class PlaylistAdapter(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun playlistBitmapLoader(
|
|
||||||
activity: FragmentActivity,
|
|
||||||
viewHolder: ViewHolder,
|
|
||||||
playlist: PlaylistWithSongs
|
|
||||||
) {
|
|
||||||
|
|
||||||
activity.lifecycleScope.launch(IO) {
|
|
||||||
val songs = playlist.songs.toSongs()
|
|
||||||
val bitmap = AutoGeneratedPlaylistBitmap.getBitmap(activity, songs, false, false)
|
|
||||||
withContext(Main) { viewHolder.image?.setImageBitmap(bitmap) }
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
override fun doInBackground(vararg params: Void?): Bitmap {
|
|
||||||
val songs = playlist.songs.toSongs()
|
|
||||||
return AutoGeneratedPlaylistBitmap.getBitmap(activity, songs, false, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onPostExecute(result: Bitmap?) {
|
|
||||||
super.onPostExecute(result)
|
|
||||||
viewHolder.image?.setImageBitmap(result)
|
|
||||||
val color = RetroColorUtil.getColor(
|
|
||||||
RetroColorUtil.generatePalette(
|
|
||||||
result
|
|
||||||
),
|
|
||||||
ATHUtil.resolveColor(activity, R.attr.colorSurface)
|
|
||||||
)
|
|
||||||
viewHolder.paletteColorContainer?.setBackgroundColor(color)
|
|
||||||
}*/
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
val TAG: String = PlaylistAdapter::class.java.simpleName
|
val TAG: String = PlaylistAdapter::class.java.simpleName
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,105 +17,99 @@ package code.name.monkey.retromusic.adapter.song
|
||||||
import android.view.MenuItem
|
import android.view.MenuItem
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import androidx.fragment.app.FragmentActivity
|
import androidx.fragment.app.FragmentActivity
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
import code.name.monkey.appthemehelper.ThemeStore
|
||||||
import code.name.monkey.retromusic.R
|
import code.name.monkey.retromusic.R
|
||||||
import code.name.monkey.retromusic.R.menu
|
|
||||||
import code.name.monkey.retromusic.db.PlaylistEntity
|
import code.name.monkey.retromusic.db.PlaylistEntity
|
||||||
import code.name.monkey.retromusic.db.toSongEntity
|
import code.name.monkey.retromusic.db.toSongEntity
|
||||||
import code.name.monkey.retromusic.db.toSongs
|
import code.name.monkey.retromusic.db.toSongsEntity
|
||||||
import code.name.monkey.retromusic.dialogs.RemoveSongFromPlaylistDialog
|
import code.name.monkey.retromusic.dialogs.RemoveSongFromPlaylistDialog
|
||||||
|
import code.name.monkey.retromusic.extensions.applyColor
|
||||||
|
import code.name.monkey.retromusic.extensions.applyOutlineColor
|
||||||
|
import code.name.monkey.retromusic.fragments.LibraryViewModel
|
||||||
|
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
||||||
import code.name.monkey.retromusic.interfaces.ICabHolder
|
import code.name.monkey.retromusic.interfaces.ICabHolder
|
||||||
import code.name.monkey.retromusic.model.PlaylistSong
|
|
||||||
import code.name.monkey.retromusic.model.Song
|
import code.name.monkey.retromusic.model.Song
|
||||||
import code.name.monkey.retromusic.util.ViewUtil
|
import com.google.android.material.button.MaterialButton
|
||||||
import com.h6ah4i.android.widget.advrecyclerview.draggable.DraggableItemAdapter
|
import com.h6ah4i.android.widget.advrecyclerview.draggable.DraggableItemAdapter
|
||||||
import com.h6ah4i.android.widget.advrecyclerview.draggable.DraggableItemViewHolder
|
|
||||||
import com.h6ah4i.android.widget.advrecyclerview.draggable.ItemDraggableRange
|
import com.h6ah4i.android.widget.advrecyclerview.draggable.ItemDraggableRange
|
||||||
import com.h6ah4i.android.widget.advrecyclerview.draggable.annotation.DraggableItemStateFlags
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import org.koin.androidx.viewmodel.ext.android.viewModel
|
||||||
|
|
||||||
class OrderablePlaylistSongAdapter(
|
class OrderablePlaylistSongAdapter(
|
||||||
private val playlist: PlaylistEntity,
|
private val playlist: PlaylistEntity,
|
||||||
activity: FragmentActivity,
|
activity: FragmentActivity,
|
||||||
dataSet: ArrayList<Song>,
|
dataSet: MutableList<Song>,
|
||||||
itemLayoutRes: Int,
|
itemLayoutRes: Int,
|
||||||
ICabHolder: ICabHolder?,
|
ICabHolder: ICabHolder?,
|
||||||
private val onMoveItemListener: OnMoveItemListener?
|
) : AbsOffsetSongAdapter(activity, dataSet, itemLayoutRes, ICabHolder),
|
||||||
) : SongAdapter(
|
DraggableItemAdapter<OrderablePlaylistSongAdapter.ViewHolder> {
|
||||||
activity,
|
|
||||||
dataSet,
|
val libraryViewModel: LibraryViewModel by activity.viewModel()
|
||||||
itemLayoutRes,
|
val tempDataSet = dataSet
|
||||||
ICabHolder
|
|
||||||
), DraggableItemAdapter<OrderablePlaylistSongAdapter.ViewHolder> {
|
|
||||||
|
|
||||||
init {
|
init {
|
||||||
setMultiSelectMenuRes(menu.menu_playlists_songs_selection)
|
this.setHasStableIds(true)
|
||||||
|
this.setMultiSelectMenuRes(R.menu.menu_playlists_songs_selection)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getItemId(position: Int): Long {
|
||||||
|
// requires static value, it means need to keep the same value
|
||||||
|
// even if the item position has been changed.
|
||||||
|
return if (position != 0) {
|
||||||
|
dataSet[position - 1].id
|
||||||
|
} else {
|
||||||
|
-1
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun createViewHolder(view: View): SongAdapter.ViewHolder {
|
override fun createViewHolder(view: View): SongAdapter.ViewHolder {
|
||||||
return ViewHolder(view)
|
return ViewHolder(view)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getItemId(position: Int): Long {
|
override fun getItemViewType(position: Int): Int {
|
||||||
var positionFinal = position
|
return if (position == 0) OFFSET_ITEM else SONG
|
||||||
positionFinal--
|
}
|
||||||
|
|
||||||
var long: Long = 0
|
override fun onBindViewHolder(holder: SongAdapter.ViewHolder, position: Int) {
|
||||||
if (positionFinal < 0) {
|
if (holder.itemViewType == OFFSET_ITEM) {
|
||||||
long = -2
|
val color = ThemeStore.accentColor(activity)
|
||||||
} else {
|
val viewHolder = holder as ViewHolder
|
||||||
if (dataSet[positionFinal] is PlaylistSong) {
|
viewHolder.playAction?.let {
|
||||||
long = (dataSet[positionFinal] as PlaylistSong).idInPlayList.toLong()
|
it.setOnClickListener {
|
||||||
|
MusicPlayerRemote.openQueue(dataSet, 0, true)
|
||||||
|
}
|
||||||
|
it.applyOutlineColor(color)
|
||||||
}
|
}
|
||||||
|
viewHolder.shuffleAction?.let {
|
||||||
|
it.setOnClickListener {
|
||||||
|
MusicPlayerRemote.openAndShuffleQueue(dataSet, true)
|
||||||
|
}
|
||||||
|
it.applyColor(color)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
super.onBindViewHolder(holder, position - 1)
|
||||||
}
|
}
|
||||||
return long
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onMultipleItemAction(menuItem: MenuItem, selection: List<Song>) {
|
override fun onMultipleItemAction(menuItem: MenuItem, selection: List<Song>) {
|
||||||
when (menuItem.itemId) {
|
when (menuItem.itemId) {
|
||||||
R.id.action_remove_from_playlist -> {
|
R.id.action_remove_from_playlist -> RemoveSongFromPlaylistDialog.create(
|
||||||
RemoveSongFromPlaylistDialog.create(selection.toSongs(playlist.playListId))
|
selection.toSongsEntity(
|
||||||
.show(activity.supportFragmentManager, "REMOVE_FROM_PLAYLIST")
|
playlist
|
||||||
return
|
)
|
||||||
}
|
)
|
||||||
}
|
.show(activity.supportFragmentManager, "REMOVE_FROM_PLAYLIST")
|
||||||
super.onMultipleItemAction(menuItem, selection)
|
else -> super.onMultipleItemAction(menuItem, selection)
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCheckCanStartDrag(holder: ViewHolder, position: Int, x: Int, y: Int): Boolean {
|
|
||||||
return onMoveItemListener != null && position > 0 && (ViewUtil.hitTest(
|
|
||||||
holder.dragView!!, x, y
|
|
||||||
) || ViewUtil.hitTest(holder.image!!, x, y))
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onGetItemDraggableRange(holder: ViewHolder, position: Int): ItemDraggableRange {
|
|
||||||
return ItemDraggableRange(1, dataSet.size)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onMoveItem(fromPosition: Int, toPosition: Int) {
|
|
||||||
if (onMoveItemListener != null && fromPosition != toPosition) {
|
|
||||||
onMoveItemListener.onMoveItem(fromPosition - 1, toPosition - 1)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCheckCanDrop(draggingPosition: Int, dropPosition: Int): Boolean {
|
inner class ViewHolder(itemView: View) : AbsOffsetSongAdapter.ViewHolder(itemView) {
|
||||||
return dropPosition > 0
|
val playAction: MaterialButton? = itemView.findViewById(R.id.playAction)
|
||||||
}
|
val shuffleAction: MaterialButton? = itemView.findViewById(R.id.shuffleAction)
|
||||||
|
|
||||||
override fun onItemDragStarted(position: Int) {
|
|
||||||
notifyDataSetChanged()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onItemDragFinished(fromPosition: Int, toPosition: Int, result: Boolean) {
|
|
||||||
notifyDataSetChanged()
|
|
||||||
}
|
|
||||||
|
|
||||||
interface OnMoveItemListener {
|
|
||||||
fun onMoveItem(fromPosition: Int, toPosition: Int)
|
|
||||||
}
|
|
||||||
|
|
||||||
inner class ViewHolder(itemView: View) : SongAdapter.ViewHolder(itemView),
|
|
||||||
DraggableItemViewHolder {
|
|
||||||
@DraggableItemStateFlags
|
|
||||||
private var mDragStateFlags: Int = 0
|
|
||||||
|
|
||||||
override var songMenuRes: Int
|
override var songMenuRes: Int
|
||||||
get() = R.menu.menu_item_playlist_song
|
get() = R.menu.menu_item_playlist_song
|
||||||
|
@ -123,16 +117,6 @@ class OrderablePlaylistSongAdapter(
|
||||||
super.songMenuRes = value
|
super.songMenuRes = value
|
||||||
}
|
}
|
||||||
|
|
||||||
init {
|
|
||||||
if (dragView != null) {
|
|
||||||
if (onMoveItemListener != null) {
|
|
||||||
dragView?.visibility = View.VISIBLE
|
|
||||||
} else {
|
|
||||||
dragView?.visibility = View.GONE
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onSongMenuItemClick(item: MenuItem): Boolean {
|
override fun onSongMenuItemClick(item: MenuItem): Boolean {
|
||||||
when (item.itemId) {
|
when (item.itemId) {
|
||||||
R.id.action_remove_from_playlist -> {
|
R.id.action_remove_from_playlist -> {
|
||||||
|
@ -144,13 +128,58 @@ class OrderablePlaylistSongAdapter(
|
||||||
return super.onSongMenuItemClick(item)
|
return super.onSongMenuItemClick(item)
|
||||||
}
|
}
|
||||||
|
|
||||||
@DraggableItemStateFlags
|
init {
|
||||||
override fun getDragStateFlags(): Int {
|
dragView?.visibility = View.VISIBLE
|
||||||
return mDragStateFlags
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun setDragStateFlags(@DraggableItemStateFlags flags: Int) {
|
override fun onClick(v: View?) {
|
||||||
mDragStateFlags = flags
|
if (itemViewType == OFFSET_ITEM) {
|
||||||
|
MusicPlayerRemote.openAndShuffleQueue(dataSet, true)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
super.onClick(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCheckCanStartDrag(holder: ViewHolder, position: Int, x: Int, y: Int): Boolean {
|
||||||
|
if (dataSet.size == 0 or 1) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
val dragHandle = holder.dragView ?: return false
|
||||||
|
|
||||||
|
val handleWidth = dragHandle.width
|
||||||
|
val handleHeight = dragHandle.height
|
||||||
|
val handleLeft = dragHandle.left
|
||||||
|
val handleTop = dragHandle.top
|
||||||
|
|
||||||
|
return (x >= handleLeft && x < handleLeft + handleWidth &&
|
||||||
|
y >= handleTop && y < handleTop + handleHeight) && position != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onMoveItem(fromPosition: Int, toPosition: Int) {
|
||||||
|
dataSet.add(toPosition - 1, dataSet.removeAt(fromPosition - 1))
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
override fun onGetItemDraggableRange(holder: ViewHolder, position: Int): ItemDraggableRange {
|
||||||
|
return ItemDraggableRange(1, itemCount - 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCheckCanDrop(draggingPosition: Int, dropPosition: Int): Boolean {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onItemDragStarted(position: Int) {
|
||||||
|
notifyDataSetChanged()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onItemDragFinished(fromPosition: Int, toPosition: Int, result: Boolean) {
|
||||||
|
notifyDataSetChanged()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun saveSongs(playlistEntity: PlaylistEntity) {
|
||||||
|
activity.lifecycleScope.launch(Dispatchers.IO) {
|
||||||
|
libraryViewModel.insertSongs(dataSet.toSongsEntity(playlistEntity))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,8 +19,9 @@ import android.view.View
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.fragment.app.FragmentActivity
|
import androidx.fragment.app.FragmentActivity
|
||||||
import code.name.monkey.retromusic.R
|
import code.name.monkey.retromusic.R
|
||||||
|
import code.name.monkey.retromusic.glide.GlideApp
|
||||||
|
import code.name.monkey.retromusic.glide.RetroGlideExtension
|
||||||
import code.name.monkey.retromusic.glide.RetroMusicColoredTarget
|
import code.name.monkey.retromusic.glide.RetroMusicColoredTarget
|
||||||
import code.name.monkey.retromusic.glide.SongGlideRequest
|
|
||||||
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
||||||
import code.name.monkey.retromusic.helper.MusicPlayerRemote.isPlaying
|
import code.name.monkey.retromusic.helper.MusicPlayerRemote.isPlaying
|
||||||
import code.name.monkey.retromusic.helper.MusicPlayerRemote.playNextSong
|
import code.name.monkey.retromusic.helper.MusicPlayerRemote.playNextSong
|
||||||
|
@ -29,7 +30,6 @@ import code.name.monkey.retromusic.model.Song
|
||||||
import code.name.monkey.retromusic.util.MusicUtil
|
import code.name.monkey.retromusic.util.MusicUtil
|
||||||
import code.name.monkey.retromusic.util.ViewUtil
|
import code.name.monkey.retromusic.util.ViewUtil
|
||||||
import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
|
import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
|
||||||
import com.bumptech.glide.Glide
|
|
||||||
import com.h6ah4i.android.widget.advrecyclerview.draggable.DraggableItemAdapter
|
import com.h6ah4i.android.widget.advrecyclerview.draggable.DraggableItemAdapter
|
||||||
import com.h6ah4i.android.widget.advrecyclerview.draggable.ItemDraggableRange
|
import com.h6ah4i.android.widget.advrecyclerview.draggable.ItemDraggableRange
|
||||||
import com.h6ah4i.android.widget.advrecyclerview.draggable.annotation.DraggableItemStateFlags
|
import com.h6ah4i.android.widget.advrecyclerview.draggable.annotation.DraggableItemStateFlags
|
||||||
|
@ -79,9 +79,8 @@ class PlayingQueueAdapter(
|
||||||
if (holder.image == null) {
|
if (holder.image == null) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
SongGlideRequest.Builder.from(Glide.with(activity), song)
|
GlideApp.with(activity).asBitmapPalette().songCoverOptions(song)
|
||||||
.checkIgnoreMediaStore(activity)
|
.load(RetroGlideExtension.getSongModel(song))
|
||||||
.generatePalette(activity).build()
|
|
||||||
.into(object : RetroMusicColoredTarget(holder.image!!) {
|
.into(object : RetroMusicColoredTarget(holder.image!!) {
|
||||||
override fun onColorReady(colors: MediaNotificationProcessor) {
|
override fun onColorReady(colors: MediaNotificationProcessor) {
|
||||||
//setColors(colors, holder)
|
//setColors(colors, holder)
|
||||||
|
|
|
@ -23,6 +23,8 @@ import code.name.monkey.retromusic.extensions.applyOutlineColor
|
||||||
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
||||||
import code.name.monkey.retromusic.interfaces.ICabHolder
|
import code.name.monkey.retromusic.interfaces.ICabHolder
|
||||||
import code.name.monkey.retromusic.model.Song
|
import code.name.monkey.retromusic.model.Song
|
||||||
|
import code.name.monkey.retromusic.util.PreferenceUtil
|
||||||
|
import code.name.monkey.retromusic.util.RetroUtil
|
||||||
import com.google.android.material.button.MaterialButton
|
import com.google.android.material.button.MaterialButton
|
||||||
|
|
||||||
class ShuffleButtonSongAdapter(
|
class ShuffleButtonSongAdapter(
|
||||||
|
@ -32,10 +34,15 @@ class ShuffleButtonSongAdapter(
|
||||||
ICabHolder: ICabHolder?
|
ICabHolder: ICabHolder?
|
||||||
) : AbsOffsetSongAdapter(activity, dataSet, itemLayoutRes, ICabHolder) {
|
) : AbsOffsetSongAdapter(activity, dataSet, itemLayoutRes, ICabHolder) {
|
||||||
|
|
||||||
|
|
||||||
override fun createViewHolder(view: View): SongAdapter.ViewHolder {
|
override fun createViewHolder(view: View): SongAdapter.ViewHolder {
|
||||||
return ViewHolder(view)
|
return ViewHolder(view)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun getItemViewType(position: Int): Int {
|
||||||
|
return if (position == 0) OFFSET_ITEM else SONG
|
||||||
|
}
|
||||||
|
|
||||||
override fun onBindViewHolder(holder: SongAdapter.ViewHolder, position: Int) {
|
override fun onBindViewHolder(holder: SongAdapter.ViewHolder, position: Int) {
|
||||||
if (holder.itemViewType == OFFSET_ITEM) {
|
if (holder.itemViewType == OFFSET_ITEM) {
|
||||||
val color = ThemeStore.accentColor(activity)
|
val color = ThemeStore.accentColor(activity)
|
||||||
|
@ -54,6 +61,10 @@ class ShuffleButtonSongAdapter(
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
super.onBindViewHolder(holder, position - 1)
|
super.onBindViewHolder(holder, position - 1)
|
||||||
|
val landscape = RetroUtil.isLandscape()
|
||||||
|
if ((PreferenceUtil.songGridSize > 2 && !landscape) || (PreferenceUtil.songGridSizeLand > 5 && landscape)) {
|
||||||
|
holder.menu?.visibility = View.GONE
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -69,4 +80,5 @@ class ShuffleButtonSongAdapter(
|
||||||
super.onClick(v)
|
super.onClick(v)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,8 +30,9 @@ import code.name.monkey.retromusic.adapter.base.AbsMultiSelectAdapter
|
||||||
import code.name.monkey.retromusic.adapter.base.MediaEntryViewHolder
|
import code.name.monkey.retromusic.adapter.base.MediaEntryViewHolder
|
||||||
import code.name.monkey.retromusic.extensions.hide
|
import code.name.monkey.retromusic.extensions.hide
|
||||||
import code.name.monkey.retromusic.extensions.show
|
import code.name.monkey.retromusic.extensions.show
|
||||||
|
import code.name.monkey.retromusic.glide.GlideApp
|
||||||
|
import code.name.monkey.retromusic.glide.RetroGlideExtension
|
||||||
import code.name.monkey.retromusic.glide.RetroMusicColoredTarget
|
import code.name.monkey.retromusic.glide.RetroMusicColoredTarget
|
||||||
import code.name.monkey.retromusic.glide.SongGlideRequest
|
|
||||||
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
||||||
import code.name.monkey.retromusic.helper.SortOrder
|
import code.name.monkey.retromusic.helper.SortOrder
|
||||||
import code.name.monkey.retromusic.helper.menu.SongMenuHelper
|
import code.name.monkey.retromusic.helper.menu.SongMenuHelper
|
||||||
|
@ -42,7 +43,6 @@ import code.name.monkey.retromusic.util.MusicUtil
|
||||||
import code.name.monkey.retromusic.util.PreferenceUtil
|
import code.name.monkey.retromusic.util.PreferenceUtil
|
||||||
import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
|
import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
|
||||||
import com.afollestad.materialcab.MaterialCab
|
import com.afollestad.materialcab.MaterialCab
|
||||||
import com.bumptech.glide.Glide
|
|
||||||
import me.zhanghai.android.fastscroll.PopupTextProvider
|
import me.zhanghai.android.fastscroll.PopupTextProvider
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -120,9 +120,8 @@ open class SongAdapter(
|
||||||
if (holder.image == null) {
|
if (holder.image == null) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
SongGlideRequest.Builder.from(Glide.with(activity), song)
|
GlideApp.with(activity).asBitmapPalette().songCoverOptions(song)
|
||||||
.checkIgnoreMediaStore(activity)
|
.load(RetroGlideExtension.getSongModel(song))
|
||||||
.generatePalette(activity).build()
|
|
||||||
.into(object : RetroMusicColoredTarget(holder.image!!) {
|
.into(object : RetroMusicColoredTarget(holder.image!!) {
|
||||||
override fun onColorReady(colors: MediaNotificationProcessor) {
|
override fun onColorReady(colors: MediaNotificationProcessor) {
|
||||||
setColors(colors, holder)
|
setColors(colors, holder)
|
||||||
|
@ -130,15 +129,15 @@ open class SongAdapter(
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getSongTitle(song: Song): String? {
|
private fun getSongTitle(song: Song): String {
|
||||||
return song.title
|
return song.title
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getSongText(song: Song): String? {
|
private fun getSongText(song: Song): String {
|
||||||
return song.artistName
|
return song.artistName
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getSongText2(song: Song): String? {
|
private fun getSongText2(song: Song): String {
|
||||||
return song.albumName
|
return song.albumName
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -165,6 +164,7 @@ open class SongAdapter(
|
||||||
SortOrder.SongSortOrder.SONG_ARTIST -> dataSet[position].artistName
|
SortOrder.SongSortOrder.SONG_ARTIST -> dataSet[position].artistName
|
||||||
SortOrder.SongSortOrder.SONG_YEAR -> return MusicUtil.getYearString(dataSet[position].year)
|
SortOrder.SongSortOrder.SONG_YEAR -> return MusicUtil.getYearString(dataSet[position].year)
|
||||||
SortOrder.SongSortOrder.COMPOSER -> dataSet[position].composer
|
SortOrder.SongSortOrder.COMPOSER -> dataSet[position].composer
|
||||||
|
SortOrder.SongSortOrder.SONG_ALBUM_ARTIST -> dataSet[position].albumArtist
|
||||||
else -> {
|
else -> {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,7 +24,6 @@ import android.os.Build
|
||||||
import code.name.monkey.retromusic.appshortcuts.shortcuttype.LastAddedShortcutType
|
import code.name.monkey.retromusic.appshortcuts.shortcuttype.LastAddedShortcutType
|
||||||
import code.name.monkey.retromusic.appshortcuts.shortcuttype.ShuffleAllShortcutType
|
import code.name.monkey.retromusic.appshortcuts.shortcuttype.ShuffleAllShortcutType
|
||||||
import code.name.monkey.retromusic.appshortcuts.shortcuttype.TopTracksShortcutType
|
import code.name.monkey.retromusic.appshortcuts.shortcuttype.TopTracksShortcutType
|
||||||
import java.util.*
|
|
||||||
|
|
||||||
@TargetApi(Build.VERSION_CODES.N_MR1)
|
@TargetApi(Build.VERSION_CODES.N_MR1)
|
||||||
class DynamicShortcutManager(private val context: Context) {
|
class DynamicShortcutManager(private val context: Context) {
|
||||||
|
|
|
@ -27,15 +27,16 @@ import code.name.monkey.appthemehelper.util.MaterialValueHelper
|
||||||
import code.name.monkey.retromusic.R
|
import code.name.monkey.retromusic.R
|
||||||
import code.name.monkey.retromusic.activities.MainActivity
|
import code.name.monkey.retromusic.activities.MainActivity
|
||||||
import code.name.monkey.retromusic.appwidgets.base.BaseAppWidget
|
import code.name.monkey.retromusic.appwidgets.base.BaseAppWidget
|
||||||
import code.name.monkey.retromusic.glide.SongGlideRequest
|
import code.name.monkey.retromusic.glide.GlideApp
|
||||||
|
import code.name.monkey.retromusic.glide.RetroGlideExtension
|
||||||
import code.name.monkey.retromusic.service.MusicService
|
import code.name.monkey.retromusic.service.MusicService
|
||||||
import code.name.monkey.retromusic.service.MusicService.*
|
import code.name.monkey.retromusic.service.MusicService.*
|
||||||
import code.name.monkey.retromusic.util.PreferenceUtil
|
import code.name.monkey.retromusic.util.PreferenceUtil
|
||||||
import code.name.monkey.retromusic.util.RetroUtil
|
import code.name.monkey.retromusic.util.RetroUtil
|
||||||
import com.bumptech.glide.Glide
|
import com.bumptech.glide.Glide
|
||||||
import com.bumptech.glide.request.animation.GlideAnimation
|
|
||||||
import com.bumptech.glide.request.target.SimpleTarget
|
import com.bumptech.glide.request.target.SimpleTarget
|
||||||
import com.bumptech.glide.request.target.Target
|
import com.bumptech.glide.request.target.Target
|
||||||
|
import com.bumptech.glide.request.transition.Transition
|
||||||
|
|
||||||
class AppWidgetBig : BaseAppWidget() {
|
class AppWidgetBig : BaseAppWidget() {
|
||||||
private var target: Target<Bitmap>? = null // for cancellation
|
private var target: Target<Bitmap>? = null // for cancellation
|
||||||
|
@ -158,20 +159,22 @@ class AppWidgetBig : BaseAppWidget() {
|
||||||
val appContext = service.applicationContext
|
val appContext = service.applicationContext
|
||||||
service.runOnUiThread {
|
service.runOnUiThread {
|
||||||
if (target != null) {
|
if (target != null) {
|
||||||
Glide.clear(target)
|
Glide.with(service).clear(target)
|
||||||
}
|
}
|
||||||
target = SongGlideRequest.Builder.from(Glide.with(appContext), song)
|
target = GlideApp.with(appContext)
|
||||||
.checkIgnoreMediaStore(appContext).asBitmap().build()
|
.asBitmap()
|
||||||
|
//.checkIgnoreMediaStore()
|
||||||
|
.load(RetroGlideExtension.getSongModel(song))
|
||||||
.into(object : SimpleTarget<Bitmap>(widgetImageSize, widgetImageSize) {
|
.into(object : SimpleTarget<Bitmap>(widgetImageSize, widgetImageSize) {
|
||||||
override fun onResourceReady(
|
override fun onResourceReady(
|
||||||
resource: Bitmap,
|
resource: Bitmap,
|
||||||
glideAnimation: GlideAnimation<in Bitmap>
|
transition: Transition<in Bitmap>?
|
||||||
) {
|
) {
|
||||||
update(resource)
|
update(resource)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onLoadFailed(e: Exception?, errorDrawable: Drawable?) {
|
override fun onLoadFailed(errorDrawable: Drawable?) {
|
||||||
super.onLoadFailed(e, errorDrawable)
|
super.onLoadFailed(errorDrawable)
|
||||||
update(null)
|
update(null)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,8 @@ import code.name.monkey.appthemehelper.util.MaterialValueHelper
|
||||||
import code.name.monkey.retromusic.R
|
import code.name.monkey.retromusic.R
|
||||||
import code.name.monkey.retromusic.activities.MainActivity
|
import code.name.monkey.retromusic.activities.MainActivity
|
||||||
import code.name.monkey.retromusic.appwidgets.base.BaseAppWidget
|
import code.name.monkey.retromusic.appwidgets.base.BaseAppWidget
|
||||||
import code.name.monkey.retromusic.glide.SongGlideRequest
|
import code.name.monkey.retromusic.glide.GlideApp
|
||||||
|
import code.name.monkey.retromusic.glide.RetroGlideExtension
|
||||||
import code.name.monkey.retromusic.glide.palette.BitmapPaletteWrapper
|
import code.name.monkey.retromusic.glide.palette.BitmapPaletteWrapper
|
||||||
import code.name.monkey.retromusic.service.MusicService
|
import code.name.monkey.retromusic.service.MusicService
|
||||||
import code.name.monkey.retromusic.service.MusicService.*
|
import code.name.monkey.retromusic.service.MusicService.*
|
||||||
|
@ -35,9 +36,9 @@ import code.name.monkey.retromusic.util.ImageUtil
|
||||||
import code.name.monkey.retromusic.util.PreferenceUtil
|
import code.name.monkey.retromusic.util.PreferenceUtil
|
||||||
import code.name.monkey.retromusic.util.RetroUtil
|
import code.name.monkey.retromusic.util.RetroUtil
|
||||||
import com.bumptech.glide.Glide
|
import com.bumptech.glide.Glide
|
||||||
import com.bumptech.glide.request.animation.GlideAnimation
|
|
||||||
import com.bumptech.glide.request.target.SimpleTarget
|
import com.bumptech.glide.request.target.SimpleTarget
|
||||||
import com.bumptech.glide.request.target.Target
|
import com.bumptech.glide.request.target.Target
|
||||||
|
import com.bumptech.glide.request.transition.Transition
|
||||||
|
|
||||||
class AppWidgetCard : BaseAppWidget() {
|
class AppWidgetCard : BaseAppWidget() {
|
||||||
private var target: Target<BitmapPaletteWrapper>? = null // for cancellation
|
private var target: Target<BitmapPaletteWrapper>? = null // for cancellation
|
||||||
|
@ -150,14 +151,15 @@ class AppWidgetCard : BaseAppWidget() {
|
||||||
// Load the album cover async and push the update on completion
|
// Load the album cover async and push the update on completion
|
||||||
service.runOnUiThread {
|
service.runOnUiThread {
|
||||||
if (target != null) {
|
if (target != null) {
|
||||||
Glide.clear(target)
|
Glide.with(service).clear(target)
|
||||||
}
|
}
|
||||||
target = SongGlideRequest.Builder.from(Glide.with(service), song)
|
target = GlideApp.with(service).asBitmapPalette().songCoverOptions(song)
|
||||||
.checkIgnoreMediaStore(service).generatePalette(service).build().centerCrop()
|
.load(RetroGlideExtension.getSongModel(song))
|
||||||
|
.centerCrop()
|
||||||
.into(object : SimpleTarget<BitmapPaletteWrapper>(imageSize, imageSize) {
|
.into(object : SimpleTarget<BitmapPaletteWrapper>(imageSize, imageSize) {
|
||||||
override fun onResourceReady(
|
override fun onResourceReady(
|
||||||
resource: BitmapPaletteWrapper,
|
resource: BitmapPaletteWrapper,
|
||||||
glideAnimation: GlideAnimation<in BitmapPaletteWrapper>
|
transition: Transition<in BitmapPaletteWrapper>?
|
||||||
) {
|
) {
|
||||||
val palette = resource.palette
|
val palette = resource.palette
|
||||||
update(
|
update(
|
||||||
|
@ -171,8 +173,8 @@ class AppWidgetCard : BaseAppWidget() {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onLoadFailed(e: Exception?, errorDrawable: Drawable?) {
|
override fun onLoadFailed(errorDrawable: Drawable?) {
|
||||||
super.onLoadFailed(e, errorDrawable)
|
super.onLoadFailed(errorDrawable)
|
||||||
update(null, MaterialValueHelper.getSecondaryTextColor(service, true))
|
update(null, MaterialValueHelper.getSecondaryTextColor(service, true))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -28,7 +28,8 @@ import code.name.monkey.appthemehelper.util.MaterialValueHelper
|
||||||
import code.name.monkey.retromusic.R
|
import code.name.monkey.retromusic.R
|
||||||
import code.name.monkey.retromusic.activities.MainActivity
|
import code.name.monkey.retromusic.activities.MainActivity
|
||||||
import code.name.monkey.retromusic.appwidgets.base.BaseAppWidget
|
import code.name.monkey.retromusic.appwidgets.base.BaseAppWidget
|
||||||
import code.name.monkey.retromusic.glide.SongGlideRequest
|
import code.name.monkey.retromusic.glide.GlideApp
|
||||||
|
import code.name.monkey.retromusic.glide.RetroGlideExtension
|
||||||
import code.name.monkey.retromusic.glide.palette.BitmapPaletteWrapper
|
import code.name.monkey.retromusic.glide.palette.BitmapPaletteWrapper
|
||||||
import code.name.monkey.retromusic.service.MusicService
|
import code.name.monkey.retromusic.service.MusicService
|
||||||
import code.name.monkey.retromusic.service.MusicService.*
|
import code.name.monkey.retromusic.service.MusicService.*
|
||||||
|
@ -36,9 +37,9 @@ import code.name.monkey.retromusic.util.ImageUtil
|
||||||
import code.name.monkey.retromusic.util.PreferenceUtil
|
import code.name.monkey.retromusic.util.PreferenceUtil
|
||||||
import code.name.monkey.retromusic.util.RetroUtil
|
import code.name.monkey.retromusic.util.RetroUtil
|
||||||
import com.bumptech.glide.Glide
|
import com.bumptech.glide.Glide
|
||||||
import com.bumptech.glide.request.animation.GlideAnimation
|
|
||||||
import com.bumptech.glide.request.target.SimpleTarget
|
import com.bumptech.glide.request.target.SimpleTarget
|
||||||
import com.bumptech.glide.request.target.Target
|
import com.bumptech.glide.request.target.Target
|
||||||
|
import com.bumptech.glide.request.transition.Transition
|
||||||
|
|
||||||
class AppWidgetClassic : BaseAppWidget() {
|
class AppWidgetClassic : BaseAppWidget() {
|
||||||
private var target: Target<BitmapPaletteWrapper>? = null // for cancellation
|
private var target: Target<BitmapPaletteWrapper>? = null // for cancellation
|
||||||
|
@ -120,14 +121,16 @@ class AppWidgetClassic : BaseAppWidget() {
|
||||||
val appContext = service.applicationContext
|
val appContext = service.applicationContext
|
||||||
service.runOnUiThread {
|
service.runOnUiThread {
|
||||||
if (target != null) {
|
if (target != null) {
|
||||||
Glide.clear(target)
|
Glide.with(service).clear(target)
|
||||||
}
|
}
|
||||||
target = SongGlideRequest.Builder.from(Glide.with(service), song)
|
target = GlideApp.with(service).asBitmapPalette().songCoverOptions(song)
|
||||||
.checkIgnoreMediaStore(service).generatePalette(service).build().centerCrop()
|
.load(RetroGlideExtension.getSongModel(song))
|
||||||
|
//.checkIgnoreMediaStore()
|
||||||
|
.centerCrop()
|
||||||
.into(object : SimpleTarget<BitmapPaletteWrapper>(imageSize, imageSize) {
|
.into(object : SimpleTarget<BitmapPaletteWrapper>(imageSize, imageSize) {
|
||||||
override fun onResourceReady(
|
override fun onResourceReady(
|
||||||
resource: BitmapPaletteWrapper,
|
resource: BitmapPaletteWrapper,
|
||||||
glideAnimation: GlideAnimation<in BitmapPaletteWrapper>
|
transition: Transition<in BitmapPaletteWrapper>?
|
||||||
) {
|
) {
|
||||||
val palette = resource.palette
|
val palette = resource.palette
|
||||||
update(
|
update(
|
||||||
|
@ -143,8 +146,8 @@ class AppWidgetClassic : BaseAppWidget() {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onLoadFailed(e: Exception?, errorDrawable: Drawable?) {
|
override fun onLoadFailed(errorDrawable: Drawable?) {
|
||||||
super.onLoadFailed(e, errorDrawable)
|
super.onLoadFailed(errorDrawable)
|
||||||
update(null, Color.WHITE)
|
update(null, Color.WHITE)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -27,16 +27,17 @@ import code.name.monkey.appthemehelper.util.MaterialValueHelper
|
||||||
import code.name.monkey.retromusic.R
|
import code.name.monkey.retromusic.R
|
||||||
import code.name.monkey.retromusic.activities.MainActivity
|
import code.name.monkey.retromusic.activities.MainActivity
|
||||||
import code.name.monkey.retromusic.appwidgets.base.BaseAppWidget
|
import code.name.monkey.retromusic.appwidgets.base.BaseAppWidget
|
||||||
import code.name.monkey.retromusic.glide.SongGlideRequest
|
import code.name.monkey.retromusic.glide.GlideApp
|
||||||
|
import code.name.monkey.retromusic.glide.RetroGlideExtension
|
||||||
import code.name.monkey.retromusic.glide.palette.BitmapPaletteWrapper
|
import code.name.monkey.retromusic.glide.palette.BitmapPaletteWrapper
|
||||||
import code.name.monkey.retromusic.service.MusicService
|
import code.name.monkey.retromusic.service.MusicService
|
||||||
import code.name.monkey.retromusic.service.MusicService.*
|
import code.name.monkey.retromusic.service.MusicService.*
|
||||||
import code.name.monkey.retromusic.util.PreferenceUtil
|
import code.name.monkey.retromusic.util.PreferenceUtil
|
||||||
import code.name.monkey.retromusic.util.RetroUtil
|
import code.name.monkey.retromusic.util.RetroUtil
|
||||||
import com.bumptech.glide.Glide
|
import com.bumptech.glide.Glide
|
||||||
import com.bumptech.glide.request.animation.GlideAnimation
|
|
||||||
import com.bumptech.glide.request.target.SimpleTarget
|
import com.bumptech.glide.request.target.SimpleTarget
|
||||||
import com.bumptech.glide.request.target.Target
|
import com.bumptech.glide.request.target.Target
|
||||||
|
import com.bumptech.glide.request.transition.Transition
|
||||||
|
|
||||||
class AppWidgetSmall : BaseAppWidget() {
|
class AppWidgetSmall : BaseAppWidget() {
|
||||||
private var target: Target<BitmapPaletteWrapper>? = null // for cancellation
|
private var target: Target<BitmapPaletteWrapper>? = null // for cancellation
|
||||||
|
@ -123,14 +124,16 @@ class AppWidgetSmall : BaseAppWidget() {
|
||||||
val appContext = service.applicationContext
|
val appContext = service.applicationContext
|
||||||
service.runOnUiThread {
|
service.runOnUiThread {
|
||||||
if (target != null) {
|
if (target != null) {
|
||||||
Glide.clear(target)
|
Glide.with(service).clear(target)
|
||||||
}
|
}
|
||||||
target = SongGlideRequest.Builder.from(Glide.with(service), song)
|
target = GlideApp.with(service).asBitmapPalette().songCoverOptions(song)
|
||||||
.checkIgnoreMediaStore(service).generatePalette(service).build().centerCrop()
|
//.checkIgnoreMediaStore()
|
||||||
|
.load(RetroGlideExtension.getSongModel(song))
|
||||||
|
.centerCrop()
|
||||||
.into(object : SimpleTarget<BitmapPaletteWrapper>(imageSize, imageSize) {
|
.into(object : SimpleTarget<BitmapPaletteWrapper>(imageSize, imageSize) {
|
||||||
override fun onResourceReady(
|
override fun onResourceReady(
|
||||||
resource: BitmapPaletteWrapper,
|
resource: BitmapPaletteWrapper,
|
||||||
glideAnimation: GlideAnimation<in BitmapPaletteWrapper>
|
transition: Transition<in BitmapPaletteWrapper>?
|
||||||
) {
|
) {
|
||||||
val palette = resource.palette
|
val palette = resource.palette
|
||||||
update(
|
update(
|
||||||
|
@ -144,8 +147,8 @@ class AppWidgetSmall : BaseAppWidget() {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onLoadFailed(e: Exception?, errorDrawable: Drawable?) {
|
override fun onLoadFailed(errorDrawable: Drawable?) {
|
||||||
super.onLoadFailed(e, errorDrawable)
|
super.onLoadFailed(errorDrawable)
|
||||||
update(null, MaterialValueHelper.getSecondaryTextColor(service, true))
|
update(null, MaterialValueHelper.getSecondaryTextColor(service, true))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,101 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2019 Hemanth Savarala.
|
||||||
|
*
|
||||||
|
* Licensed under the GNU General Public License v3
|
||||||
|
*
|
||||||
|
* This is free software: you can redistribute it and/or modify it under
|
||||||
|
* the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation either version 3 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||||
|
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
|
* See the GNU General Public License for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package code.name.monkey.retromusic.auto;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by Beesham Sarendranauth (Beesham)
|
||||||
|
*/
|
||||||
|
public class AutoMediaIDHelper {
|
||||||
|
|
||||||
|
// Media IDs used on browseable items of MediaBrowser
|
||||||
|
public static final String MEDIA_ID_EMPTY_ROOT = "__EMPTY_ROOT__";
|
||||||
|
public static final String MEDIA_ID_ROOT = "__ROOT__";
|
||||||
|
public static final String MEDIA_ID_MUSICS_BY_SEARCH = "__BY_SEARCH__"; // TODO
|
||||||
|
public static final String MEDIA_ID_MUSICS_BY_HISTORY = "__BY_HISTORY__";
|
||||||
|
public static final String MEDIA_ID_MUSICS_BY_TOP_TRACKS = "__BY_TOP_TRACKS__";
|
||||||
|
public static final String MEDIA_ID_MUSICS_BY_SUGGESTIONS = "__BY_SUGGESTIONS__";
|
||||||
|
public static final String MEDIA_ID_MUSICS_BY_PLAYLIST = "__BY_PLAYLIST__";
|
||||||
|
public static final String MEDIA_ID_MUSICS_BY_ALBUM = "__BY_ALBUM__";
|
||||||
|
public static final String MEDIA_ID_MUSICS_BY_ARTIST = "__BY_ARTIST__";
|
||||||
|
public static final String MEDIA_ID_MUSICS_BY_ALBUM_ARTIST = "__BY_ALBUM_ARTIST__";
|
||||||
|
public static final String MEDIA_ID_MUSICS_BY_GENRE = "__BY_GENRE__";
|
||||||
|
public static final String MEDIA_ID_MUSICS_BY_SHUFFLE = "__BY_SHUFFLE__";
|
||||||
|
public static final String MEDIA_ID_MUSICS_BY_QUEUE = "__BY_QUEUE__";
|
||||||
|
|
||||||
|
private static final String CATEGORY_SEPARATOR = "__/__";
|
||||||
|
private static final String LEAF_SEPARATOR = "__|__";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a String value that represents a playable or a browsable media.
|
||||||
|
* <p/>
|
||||||
|
* Encode the media browseable categories, if any, and the unique music ID, if any,
|
||||||
|
* into a single String mediaID.
|
||||||
|
* <p/>
|
||||||
|
* MediaIDs are of the form <categoryType>__/__<categoryValue>__|__<musicUniqueId>, to make it
|
||||||
|
* easy to find the category (like genre) that a music was selected from, so we
|
||||||
|
* can correctly build the playing queue. This is specially useful when
|
||||||
|
* one music can appear in more than one list, like "by genre -> genre_1"
|
||||||
|
* and "by artist -> artist_1".
|
||||||
|
*
|
||||||
|
* @param mediaID Unique ID for playable items, or null for browseable items.
|
||||||
|
* @param categories Hierarchy of categories representing this item's browsing parents.
|
||||||
|
* @return A hierarchy-aware media ID.
|
||||||
|
*/
|
||||||
|
public static String createMediaID(String mediaID, String... categories) {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
if (categories != null) {
|
||||||
|
for (int i = 0; i < categories.length; i++) {
|
||||||
|
if (!isValidCategory(categories[i])) {
|
||||||
|
throw new IllegalArgumentException("Invalid category: " + categories[i]);
|
||||||
|
}
|
||||||
|
sb.append(categories[i]);
|
||||||
|
if (i < categories.length - 1) {
|
||||||
|
sb.append(CATEGORY_SEPARATOR);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (mediaID != null) {
|
||||||
|
sb.append(LEAF_SEPARATOR).append(mediaID);
|
||||||
|
}
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String extractCategory(@NonNull String mediaID) {
|
||||||
|
int pos = mediaID.indexOf(LEAF_SEPARATOR);
|
||||||
|
if (pos >= 0) {
|
||||||
|
return mediaID.substring(0, pos);
|
||||||
|
}
|
||||||
|
return mediaID;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String extractMusicID(@NonNull String mediaID) {
|
||||||
|
int pos = mediaID.indexOf(LEAF_SEPARATOR);
|
||||||
|
if (pos >= 0) {
|
||||||
|
return mediaID.substring(pos + LEAF_SEPARATOR.length());
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isBrowseable(@NonNull String mediaID) {
|
||||||
|
return !mediaID.contains(LEAF_SEPARATOR);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isValidCategory(String category) {
|
||||||
|
return category == null ||
|
||||||
|
(!category.contains(CATEGORY_SEPARATOR) && !category.contains(LEAF_SEPARATOR));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,285 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2019 Hemanth Savarala.
|
||||||
|
*
|
||||||
|
* Licensed under the GNU General Public License v3
|
||||||
|
*
|
||||||
|
* This is free software: you can redistribute it and/or modify it under
|
||||||
|
* the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation either version 3 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||||
|
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
|
* See the GNU General Public License for more details.
|
||||||
|
*/
|
||||||
|
package code.name.monkey.retromusic.auto
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.res.Resources
|
||||||
|
import android.net.Uri
|
||||||
|
import android.support.v4.media.MediaBrowserCompat
|
||||||
|
import code.name.monkey.retromusic.R
|
||||||
|
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
||||||
|
import code.name.monkey.retromusic.model.CategoryInfo
|
||||||
|
import code.name.monkey.retromusic.model.Song
|
||||||
|
import code.name.monkey.retromusic.repository.*
|
||||||
|
import code.name.monkey.retromusic.service.MusicService
|
||||||
|
import code.name.monkey.retromusic.util.MusicUtil
|
||||||
|
import code.name.monkey.retromusic.util.PreferenceUtil
|
||||||
|
import java.lang.ref.WeakReference
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by Beesham Sarendranauth (Beesham)
|
||||||
|
*/
|
||||||
|
class AutoMusicProvider(
|
||||||
|
val mContext: Context,
|
||||||
|
private val songsRepository: SongRepository,
|
||||||
|
private val albumsRepository: AlbumRepository,
|
||||||
|
private val artistsRepository: ArtistRepository,
|
||||||
|
private val genresRepository: GenreRepository,
|
||||||
|
private val playlistsRepository: PlaylistRepository,
|
||||||
|
private val topPlayedRepository: TopPlayedRepository
|
||||||
|
) {
|
||||||
|
private var mMusicService: WeakReference<MusicService>? = null
|
||||||
|
|
||||||
|
fun setMusicService(service: MusicService) {
|
||||||
|
mMusicService = WeakReference(service)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getChildren(mediaId: String?, resources: Resources): List<MediaBrowserCompat.MediaItem> {
|
||||||
|
val mediaItems: MutableList<MediaBrowserCompat.MediaItem> = ArrayList()
|
||||||
|
when (mediaId) {
|
||||||
|
AutoMediaIDHelper.MEDIA_ID_ROOT -> {
|
||||||
|
mediaItems.addAll(getRootChildren(resources))
|
||||||
|
}
|
||||||
|
AutoMediaIDHelper.MEDIA_ID_MUSICS_BY_PLAYLIST -> for (playlist in playlistsRepository.playlists()) {
|
||||||
|
mediaItems.add(
|
||||||
|
AutoMediaItem.with(mContext)
|
||||||
|
.path(AutoMediaIDHelper.MEDIA_ID_MUSICS_BY_PLAYLIST, playlist.id)
|
||||||
|
.icon(R.drawable.ic_playlist_play)
|
||||||
|
.title(playlist.name)
|
||||||
|
.subTitle(playlist.getInfoString(mContext))
|
||||||
|
.asPlayable()
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
AutoMediaIDHelper.MEDIA_ID_MUSICS_BY_ALBUM -> for (album in albumsRepository.albums()) {
|
||||||
|
mediaItems.add(
|
||||||
|
AutoMediaItem.with(mContext)
|
||||||
|
.path(mediaId, album.id)
|
||||||
|
.title(album.title)
|
||||||
|
.subTitle(album.albumArtist ?: album.artistName)
|
||||||
|
.icon(MusicUtil.getMediaStoreAlbumCoverUri(album.id))
|
||||||
|
.asPlayable()
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
AutoMediaIDHelper.MEDIA_ID_MUSICS_BY_ARTIST -> for (artist in artistsRepository.artists()) {
|
||||||
|
mediaItems.add(
|
||||||
|
AutoMediaItem.with(mContext)
|
||||||
|
.asPlayable()
|
||||||
|
.path(mediaId, artist.id)
|
||||||
|
.title(artist.name)
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
AutoMediaIDHelper.MEDIA_ID_MUSICS_BY_ALBUM_ARTIST -> for (artist in artistsRepository.albumArtists()) {
|
||||||
|
mediaItems.add(
|
||||||
|
AutoMediaItem.with(mContext)
|
||||||
|
.asPlayable()
|
||||||
|
// we just pass album id here as we don't have album artist id's
|
||||||
|
.path(mediaId, artist.safeGetFirstAlbum().id)
|
||||||
|
.title(artist.name)
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
AutoMediaIDHelper.MEDIA_ID_MUSICS_BY_GENRE -> for (genre in genresRepository.genres()) {
|
||||||
|
mediaItems.add(
|
||||||
|
AutoMediaItem.with(mContext)
|
||||||
|
.asPlayable()
|
||||||
|
.path(mediaId, genre.id)
|
||||||
|
.title(genre.name)
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
AutoMediaIDHelper.MEDIA_ID_MUSICS_BY_QUEUE ->
|
||||||
|
mMusicService?.get()?.playingQueue
|
||||||
|
?.let {
|
||||||
|
for (song in it) {
|
||||||
|
mediaItems.add(
|
||||||
|
AutoMediaItem.with(mContext)
|
||||||
|
.asPlayable()
|
||||||
|
.path(mediaId, song.id)
|
||||||
|
.title(song.title)
|
||||||
|
.subTitle(song.artistName)
|
||||||
|
.icon(MusicUtil.getMediaStoreAlbumCoverUri(song.albumId))
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
getPlaylistChildren(mediaId, mediaItems)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return mediaItems
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getPlaylistChildren(
|
||||||
|
mediaId: String?,
|
||||||
|
mediaItems: MutableList<MediaBrowserCompat.MediaItem>
|
||||||
|
) {
|
||||||
|
val songs = when (mediaId) {
|
||||||
|
AutoMediaIDHelper.MEDIA_ID_MUSICS_BY_TOP_TRACKS -> {
|
||||||
|
topPlayedRepository.topTracks()
|
||||||
|
}
|
||||||
|
AutoMediaIDHelper.MEDIA_ID_MUSICS_BY_HISTORY -> {
|
||||||
|
topPlayedRepository.recentlyPlayedTracks()
|
||||||
|
}
|
||||||
|
AutoMediaIDHelper.MEDIA_ID_MUSICS_BY_SUGGESTIONS -> {
|
||||||
|
topPlayedRepository.notRecentlyPlayedTracks().take(8)
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
emptyList()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
songs.forEach { song ->
|
||||||
|
mediaItems.add(
|
||||||
|
getPlayableSong(mediaId, song)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getRootChildren(resources: Resources): List<MediaBrowserCompat.MediaItem> {
|
||||||
|
val mediaItems: MutableList<MediaBrowserCompat.MediaItem> = ArrayList()
|
||||||
|
val libraryCategories = PreferenceUtil.libraryCategory
|
||||||
|
libraryCategories.forEach {
|
||||||
|
if (it.visible) {
|
||||||
|
when (it.category) {
|
||||||
|
CategoryInfo.Category.Albums -> {
|
||||||
|
mediaItems.add(
|
||||||
|
AutoMediaItem.with(mContext)
|
||||||
|
.asBrowsable()
|
||||||
|
.path(AutoMediaIDHelper.MEDIA_ID_MUSICS_BY_ALBUM)
|
||||||
|
.gridLayout(true)
|
||||||
|
.icon(R.drawable.ic_album)
|
||||||
|
.title(resources.getString(R.string.albums)).build()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
CategoryInfo.Category.Artists -> {
|
||||||
|
if (PreferenceUtil.albumArtistsOnly) {
|
||||||
|
mediaItems.add(
|
||||||
|
AutoMediaItem.with(mContext)
|
||||||
|
.asBrowsable()
|
||||||
|
.path(AutoMediaIDHelper.MEDIA_ID_MUSICS_BY_ALBUM_ARTIST)
|
||||||
|
.icon(R.drawable.ic_album_artist)
|
||||||
|
.title(resources.getString(R.string.album_artist)).build()
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
mediaItems.add(
|
||||||
|
AutoMediaItem.with(mContext)
|
||||||
|
.asBrowsable()
|
||||||
|
.path(AutoMediaIDHelper.MEDIA_ID_MUSICS_BY_ARTIST)
|
||||||
|
.icon(R.drawable.ic_artist)
|
||||||
|
.title(resources.getString(R.string.artists)).build()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
CategoryInfo.Category.Genres -> {
|
||||||
|
mediaItems.add(
|
||||||
|
AutoMediaItem.with(mContext)
|
||||||
|
.asBrowsable()
|
||||||
|
.path(AutoMediaIDHelper.MEDIA_ID_MUSICS_BY_GENRE)
|
||||||
|
.icon(R.drawable.ic_guitar)
|
||||||
|
.title(resources.getString(R.string.genres)).build()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
CategoryInfo.Category.Playlists -> {
|
||||||
|
mediaItems.add(
|
||||||
|
AutoMediaItem.with(mContext)
|
||||||
|
.asBrowsable()
|
||||||
|
.path(AutoMediaIDHelper.MEDIA_ID_MUSICS_BY_PLAYLIST)
|
||||||
|
.icon(R.drawable.ic_playlist_play)
|
||||||
|
.title(resources.getString(R.string.playlists)).build()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mediaItems.add(
|
||||||
|
AutoMediaItem.with(mContext)
|
||||||
|
.asPlayable()
|
||||||
|
.path(AutoMediaIDHelper.MEDIA_ID_MUSICS_BY_SHUFFLE)
|
||||||
|
.icon(R.drawable.ic_shuffle)
|
||||||
|
.title(resources.getString(R.string.action_shuffle_all))
|
||||||
|
.subTitle(MusicUtil.getPlaylistInfoString(mContext, songsRepository.songs()))
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
mediaItems.add(
|
||||||
|
AutoMediaItem.with(mContext)
|
||||||
|
.asBrowsable()
|
||||||
|
.path(AutoMediaIDHelper.MEDIA_ID_MUSICS_BY_QUEUE)
|
||||||
|
.icon(R.drawable.ic_queue_music)
|
||||||
|
.title(resources.getString(R.string.queue))
|
||||||
|
.subTitle(MusicUtil.getPlaylistInfoString(mContext, MusicPlayerRemote.playingQueue))
|
||||||
|
.asBrowsable().build()
|
||||||
|
)
|
||||||
|
mediaItems.add(
|
||||||
|
AutoMediaItem.with(mContext)
|
||||||
|
.asBrowsable()
|
||||||
|
.path(AutoMediaIDHelper.MEDIA_ID_MUSICS_BY_TOP_TRACKS)
|
||||||
|
.icon(R.drawable.ic_trending_up)
|
||||||
|
.title(resources.getString(R.string.my_top_tracks))
|
||||||
|
.subTitle(
|
||||||
|
MusicUtil.getPlaylistInfoString(
|
||||||
|
mContext,
|
||||||
|
topPlayedRepository.topTracks()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.asBrowsable().build()
|
||||||
|
)
|
||||||
|
mediaItems.add(
|
||||||
|
AutoMediaItem.with(mContext)
|
||||||
|
.asBrowsable()
|
||||||
|
.path(AutoMediaIDHelper.MEDIA_ID_MUSICS_BY_SUGGESTIONS)
|
||||||
|
.icon(R.drawable.ic_face)
|
||||||
|
.title(resources.getString(R.string.suggestion_songs))
|
||||||
|
.subTitle(
|
||||||
|
MusicUtil.getPlaylistInfoString(
|
||||||
|
mContext,
|
||||||
|
topPlayedRepository.notRecentlyPlayedTracks().takeIf {
|
||||||
|
it.size > 9
|
||||||
|
} ?: emptyList()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.asBrowsable().build()
|
||||||
|
)
|
||||||
|
mediaItems.add(
|
||||||
|
AutoMediaItem.with(mContext)
|
||||||
|
.asBrowsable()
|
||||||
|
.path(AutoMediaIDHelper.MEDIA_ID_MUSICS_BY_HISTORY)
|
||||||
|
.icon(R.drawable.ic_history)
|
||||||
|
.title(resources.getString(R.string.history))
|
||||||
|
.subTitle(
|
||||||
|
MusicUtil.getPlaylistInfoString(
|
||||||
|
mContext,
|
||||||
|
topPlayedRepository.recentlyPlayedTracks()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.asBrowsable().build()
|
||||||
|
)
|
||||||
|
return mediaItems
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getPlayableSong(mediaId: String?, song: Song): MediaBrowserCompat.MediaItem {
|
||||||
|
return AutoMediaItem.with(mContext)
|
||||||
|
.asPlayable()
|
||||||
|
.path(mediaId, song.id)
|
||||||
|
.title(song.title)
|
||||||
|
.subTitle(song.artistName)
|
||||||
|
.icon(MusicUtil.getMediaStoreAlbumCoverUri(song.albumId))
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,102 @@
|
||||||
|
package code.name.monkey.retromusic.auto
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.net.Uri
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.support.v4.media.MediaBrowserCompat
|
||||||
|
import android.support.v4.media.MediaDescriptionCompat
|
||||||
|
import code.name.monkey.retromusic.util.ImageUtil
|
||||||
|
|
||||||
|
|
||||||
|
internal object AutoMediaItem {
|
||||||
|
fun with(context: Context): Builder {
|
||||||
|
return Builder(context)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class Builder(val mContext: Context) {
|
||||||
|
private var mBuilder: MediaDescriptionCompat.Builder?
|
||||||
|
private var mFlags = 0
|
||||||
|
fun path(fullPath: String): Builder {
|
||||||
|
mBuilder?.setMediaId(fullPath)
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun path(path: String?, id: Long): Builder {
|
||||||
|
return path(AutoMediaIDHelper.createMediaID(id.toString(), path))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun title(title: String): Builder {
|
||||||
|
mBuilder?.setTitle(title)
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun subTitle(subTitle: String): Builder {
|
||||||
|
mBuilder?.setSubtitle(subTitle)
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun icon(uri: Uri?): Builder {
|
||||||
|
mBuilder?.setIconUri(uri)
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun icon(iconDrawableId: Int): Builder {
|
||||||
|
mBuilder?.setIconBitmap(
|
||||||
|
ImageUtil.createBitmap(
|
||||||
|
ImageUtil.getVectorDrawable(
|
||||||
|
mContext.resources,
|
||||||
|
iconDrawableId,
|
||||||
|
mContext.theme
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun gridLayout(isGrid: Boolean): Builder {
|
||||||
|
|
||||||
|
val hints = Bundle()
|
||||||
|
hints.putBoolean(CONTENT_STYLE_SUPPORTED, true)
|
||||||
|
hints.putInt(
|
||||||
|
CONTENT_STYLE_BROWSABLE_HINT,
|
||||||
|
if (isGrid) CONTENT_STYLE_GRID_ITEM_HINT_VALUE else CONTENT_STYLE_LIST_ITEM_HINT_VALUE
|
||||||
|
)
|
||||||
|
hints.putInt(
|
||||||
|
CONTENT_STYLE_PLAYABLE_HINT,
|
||||||
|
if (isGrid) CONTENT_STYLE_GRID_ITEM_HINT_VALUE else CONTENT_STYLE_LIST_ITEM_HINT_VALUE
|
||||||
|
)
|
||||||
|
mBuilder?.setExtras(hints)
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun asBrowsable(): Builder {
|
||||||
|
mFlags = mFlags or MediaBrowserCompat.MediaItem.FLAG_BROWSABLE
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun asPlayable(): Builder {
|
||||||
|
mFlags = mFlags or MediaBrowserCompat.MediaItem.FLAG_PLAYABLE
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun build(): MediaBrowserCompat.MediaItem {
|
||||||
|
val result = MediaBrowserCompat.MediaItem(mBuilder!!.build(), mFlags)
|
||||||
|
mBuilder = null
|
||||||
|
mFlags = 0
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
mBuilder = MediaDescriptionCompat.Builder()
|
||||||
|
}
|
||||||
|
companion object{
|
||||||
|
// Hints - see https://developer.android.com/training/cars/media#default-content-style
|
||||||
|
const val CONTENT_STYLE_SUPPORTED = "android.media.browse.CONTENT_STYLE_SUPPORTED"
|
||||||
|
const val CONTENT_STYLE_BROWSABLE_HINT = "android.media.browse.CONTENT_STYLE_BROWSABLE_HINT"
|
||||||
|
const val CONTENT_STYLE_PLAYABLE_HINT = "android.media.browse.CONTENT_STYLE_PLAYABLE_HINT"
|
||||||
|
const val CONTENT_STYLE_LIST_ITEM_HINT_VALUE = 1
|
||||||
|
const val CONTENT_STYLE_GRID_ITEM_HINT_VALUE = 2
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,83 @@
|
||||||
|
package code.name.monkey.retromusic.cast
|
||||||
|
|
||||||
|
import androidx.core.net.toUri
|
||||||
|
import code.name.monkey.retromusic.cast.RetroWebServer.Companion.MIME_TYPE_AUDIO
|
||||||
|
import code.name.monkey.retromusic.cast.RetroWebServer.Companion.PART_COVER_ART
|
||||||
|
import code.name.monkey.retromusic.cast.RetroWebServer.Companion.PART_SONG
|
||||||
|
import code.name.monkey.retromusic.model.Song
|
||||||
|
import code.name.monkey.retromusic.util.RetroUtil
|
||||||
|
import com.google.android.gms.cast.*
|
||||||
|
import com.google.android.gms.cast.MediaInfo.STREAM_TYPE_BUFFERED
|
||||||
|
import com.google.android.gms.cast.MediaMetadata.*
|
||||||
|
import com.google.android.gms.cast.framework.CastSession
|
||||||
|
import com.google.android.gms.common.images.WebImage
|
||||||
|
import java.net.MalformedURLException
|
||||||
|
import java.net.URL
|
||||||
|
|
||||||
|
object CastHelper {
|
||||||
|
|
||||||
|
private const val CAST_MUSIC_METADATA_ID = "metadata_id"
|
||||||
|
private const val CAST_MUSIC_METADATA_ALBUM_ID = "metadata_album_id"
|
||||||
|
private const val CAST_URL_PROTOCOL = "http"
|
||||||
|
|
||||||
|
fun castSong(castSession: CastSession, song: Song) {
|
||||||
|
try {
|
||||||
|
val remoteMediaClient = castSession.remoteMediaClient
|
||||||
|
val mediaLoadOptions = MediaLoadOptions.Builder().apply {
|
||||||
|
setPlayPosition(0)
|
||||||
|
setAutoplay(true)
|
||||||
|
}.build()
|
||||||
|
remoteMediaClient?.load(song.toMediaInfo()!!, mediaLoadOptions)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
e.printStackTrace()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun castQueue(castSession: CastSession, songs: List<Song>, position: Int, progress: Long) {
|
||||||
|
try {
|
||||||
|
val remoteMediaClient = castSession.remoteMediaClient
|
||||||
|
remoteMediaClient?.queueLoad(
|
||||||
|
songs.toMediaInfoList(),
|
||||||
|
position,
|
||||||
|
MediaStatus.REPEAT_MODE_REPEAT_OFF,
|
||||||
|
progress,
|
||||||
|
null
|
||||||
|
)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
e.printStackTrace()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun List<Song>.toMediaInfoList(): Array<MediaQueueItem> {
|
||||||
|
return map { MediaQueueItem.Builder(it.toMediaInfo()!!).build() }.toTypedArray()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun Song.toMediaInfo(): MediaInfo? {
|
||||||
|
val song = this
|
||||||
|
val baseUrl: URL
|
||||||
|
try {
|
||||||
|
baseUrl = URL(CAST_URL_PROTOCOL, RetroUtil.getIpAddress(true), SERVER_PORT, "")
|
||||||
|
} catch (e: MalformedURLException) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
val songUrl = "$baseUrl/$PART_SONG?id=${song.id}"
|
||||||
|
val albumArtUrl = "$baseUrl/$PART_COVER_ART?id=${song.albumId}"
|
||||||
|
val musicMetadata = MediaMetadata(MEDIA_TYPE_MUSIC_TRACK).apply {
|
||||||
|
putInt(CAST_MUSIC_METADATA_ID, song.id.toInt())
|
||||||
|
putInt(CAST_MUSIC_METADATA_ALBUM_ID, song.albumId.toInt())
|
||||||
|
putString(KEY_TITLE, song.title)
|
||||||
|
putString(KEY_ARTIST, song.artistName)
|
||||||
|
putString(KEY_ALBUM_TITLE, song.albumName)
|
||||||
|
putInt(KEY_TRACK_NUMBER, song.trackNumber)
|
||||||
|
addImage(WebImage(albumArtUrl.toUri()))
|
||||||
|
}
|
||||||
|
return MediaInfo.Builder(songUrl).apply {
|
||||||
|
setStreamType(STREAM_TYPE_BUFFERED)
|
||||||
|
setContentType(MIME_TYPE_AUDIO)
|
||||||
|
setMetadata(musicMetadata)
|
||||||
|
setStreamDuration(song.duration)
|
||||||
|
}.build()
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,41 @@
|
||||||
|
package code.name.monkey.retromusic.cast
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import com.google.android.gms.cast.CastMediaControlIntent
|
||||||
|
import com.google.android.gms.cast.framework.CastOptions
|
||||||
|
import com.google.android.gms.cast.framework.OptionsProvider
|
||||||
|
import com.google.android.gms.cast.framework.SessionProvider
|
||||||
|
import com.google.android.gms.cast.framework.media.CastMediaOptions
|
||||||
|
import com.google.android.gms.cast.framework.media.MediaIntentReceiver
|
||||||
|
import com.google.android.gms.cast.framework.media.NotificationOptions
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
|
||||||
|
class CastOptionsProvider : OptionsProvider {
|
||||||
|
override fun getCastOptions(context: Context): CastOptions {
|
||||||
|
val buttonActions: MutableList<String> = ArrayList()
|
||||||
|
buttonActions.add(MediaIntentReceiver.ACTION_SKIP_PREV)
|
||||||
|
buttonActions.add(MediaIntentReceiver.ACTION_TOGGLE_PLAYBACK)
|
||||||
|
buttonActions.add(MediaIntentReceiver.ACTION_SKIP_NEXT)
|
||||||
|
buttonActions.add(MediaIntentReceiver.ACTION_STOP_CASTING)
|
||||||
|
val compatButtonActionsIndices = intArrayOf(1, 3)
|
||||||
|
val notificationOptions = NotificationOptions.Builder()
|
||||||
|
.setActions(buttonActions, compatButtonActionsIndices)
|
||||||
|
.setTargetActivityClassName(ExpandedControlsActivity::class.java.name)
|
||||||
|
.build()
|
||||||
|
|
||||||
|
val mediaOptions = CastMediaOptions.Builder()
|
||||||
|
.setNotificationOptions(notificationOptions)
|
||||||
|
.setExpandedControllerActivityClassName(ExpandedControlsActivity::class.java.name)
|
||||||
|
.build()
|
||||||
|
|
||||||
|
return CastOptions.Builder()
|
||||||
|
.setReceiverApplicationId(CastMediaControlIntent.DEFAULT_MEDIA_RECEIVER_APPLICATION_ID)
|
||||||
|
.setCastMediaOptions(mediaOptions)
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getAdditionalSessionProviders(context: Context?): List<SessionProvider>? {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
package code.name.monkey.retromusic.cast
|
||||||
|
|
||||||
|
|
||||||
|
import android.view.Menu
|
||||||
|
import code.name.monkey.retromusic.R
|
||||||
|
|
||||||
|
import com.google.android.gms.cast.framework.CastButtonFactory
|
||||||
|
|
||||||
|
import com.google.android.gms.cast.framework.media.widget.ExpandedControllerActivity
|
||||||
|
|
||||||
|
|
||||||
|
class ExpandedControlsActivity : ExpandedControllerActivity() {
|
||||||
|
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
|
||||||
|
super.onCreateOptionsMenu(menu)
|
||||||
|
menuInflater.inflate(R.menu.menu_cast, menu)
|
||||||
|
CastButtonFactory.setUpMediaRouteButton(this, menu, R.id.action_cast)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
package code.name.monkey.retromusic.cast
|
||||||
|
|
||||||
|
import com.google.android.gms.cast.framework.CastSession
|
||||||
|
import com.google.android.gms.cast.framework.SessionManagerListener
|
||||||
|
|
||||||
|
interface RetroSessionManager : SessionManagerListener<CastSession> {
|
||||||
|
override fun onSessionResuming(p0: CastSession, p1: String) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onSessionStartFailed(p0: CastSession, p1: Int) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onSessionResumeFailed(p0: CastSession, p1: Int) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onSessionEnding(p0: CastSession) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,140 @@
|
||||||
|
package code.name.monkey.retromusic.cast
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import code.name.monkey.retromusic.util.MusicUtil
|
||||||
|
import fi.iki.elonen.NanoHTTPD
|
||||||
|
import fi.iki.elonen.NanoHTTPD.Response.Status
|
||||||
|
import java.io.*
|
||||||
|
|
||||||
|
|
||||||
|
const val SERVER_PORT = 9090
|
||||||
|
|
||||||
|
class RetroWebServer(val context: Context) : NanoHTTPD(SERVER_PORT) {
|
||||||
|
companion object {
|
||||||
|
private const val MIME_TYPE_IMAGE = "image/jpg"
|
||||||
|
const val MIME_TYPE_AUDIO = "audio/mp3"
|
||||||
|
|
||||||
|
const val PART_COVER_ART = "coverart"
|
||||||
|
const val PART_SONG = "song"
|
||||||
|
const val PARAM_ID = "id"
|
||||||
|
var mRetroWebServer: RetroWebServer? = null
|
||||||
|
fun getInstance(context: Context): RetroWebServer {
|
||||||
|
if (mRetroWebServer == null) {
|
||||||
|
mRetroWebServer = RetroWebServer(context)
|
||||||
|
}
|
||||||
|
return mRetroWebServer!!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun serve(
|
||||||
|
uri: String?,
|
||||||
|
method: Method?,
|
||||||
|
headers: MutableMap<String, String>?,
|
||||||
|
parms: MutableMap<String, String>?,
|
||||||
|
files: MutableMap<String, String>?
|
||||||
|
): Response {
|
||||||
|
if (uri?.contains(PART_COVER_ART) == true) {
|
||||||
|
val albumId = parms?.get(PARAM_ID) ?: return errorResponse()
|
||||||
|
val albumArtUri = MusicUtil.getMediaStoreAlbumCoverUri(albumId.toLong())
|
||||||
|
val fis: InputStream?
|
||||||
|
try {
|
||||||
|
fis = context.contentResolver.openInputStream(albumArtUri)
|
||||||
|
} catch (e: FileNotFoundException) {
|
||||||
|
return errorResponse()
|
||||||
|
}
|
||||||
|
return newChunkedResponse(Status.OK, MIME_TYPE_IMAGE, fis)
|
||||||
|
} else if (uri?.contains(PART_SONG) == true) {
|
||||||
|
val songId = parms?.get(PARAM_ID) ?: return errorResponse()
|
||||||
|
val songUri = MusicUtil.getSongFileUri(songId.toLong())
|
||||||
|
val songPath = MusicUtil.getSongFilePath(context, songUri)
|
||||||
|
val song = File(songPath)
|
||||||
|
return serveFile(headers!!, song, MIME_TYPE_AUDIO)
|
||||||
|
}
|
||||||
|
return newFixedLengthResponse(Status.NOT_FOUND, MIME_PLAINTEXT, "Not Found")
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun serveFile(
|
||||||
|
header: MutableMap<String, String>, file: File,
|
||||||
|
mime: String
|
||||||
|
): Response {
|
||||||
|
var res: Response
|
||||||
|
try {
|
||||||
|
// Support (simple) skipping:
|
||||||
|
var startFrom: Long = 0
|
||||||
|
var endAt: Long = -1
|
||||||
|
// The value of header range will be bytes=0-1024 something like this
|
||||||
|
// We get the value of from Bytes i.e. startFrom and toBytes i.e. endAt below
|
||||||
|
var range = header["range"]
|
||||||
|
if (range != null) {
|
||||||
|
if (range.startsWith("bytes=")) {
|
||||||
|
range = range.substring("bytes=".length)
|
||||||
|
val minus = range.indexOf('-')
|
||||||
|
try {
|
||||||
|
if (minus > 0) {
|
||||||
|
startFrom = range
|
||||||
|
.substring(0, minus).toLong()
|
||||||
|
endAt = range.substring(minus + 1).toLong()
|
||||||
|
}
|
||||||
|
} catch (ignored: NumberFormatException) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Chunked Response is used when serving audio file
|
||||||
|
// Change return code and add Content-Range header when skipping is
|
||||||
|
// requested
|
||||||
|
val fileLen = file.length()
|
||||||
|
if (range != null && startFrom >= 0) {
|
||||||
|
if (startFrom >= fileLen) {
|
||||||
|
res = newFixedLengthResponse(
|
||||||
|
Status.RANGE_NOT_SATISFIABLE,
|
||||||
|
MIME_PLAINTEXT, ""
|
||||||
|
)
|
||||||
|
res.addHeader("Content-Range", "bytes 0-0/$fileLen")
|
||||||
|
} else {
|
||||||
|
if (endAt < 0) {
|
||||||
|
endAt = fileLen - 1
|
||||||
|
}
|
||||||
|
var newLen = endAt - startFrom + 1
|
||||||
|
if (newLen < 0) {
|
||||||
|
newLen = 0
|
||||||
|
}
|
||||||
|
val dataLen = newLen
|
||||||
|
val fis: FileInputStream = object : FileInputStream(file) {
|
||||||
|
@Throws(IOException::class)
|
||||||
|
override fun available(): Int {
|
||||||
|
return dataLen.toInt()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fis.skip(startFrom)
|
||||||
|
res = newChunkedResponse(
|
||||||
|
Status.PARTIAL_CONTENT, mime,
|
||||||
|
fis
|
||||||
|
)
|
||||||
|
res.addHeader("Content-Length", "" + dataLen)
|
||||||
|
res.addHeader(
|
||||||
|
"Content-Range", "bytes " + startFrom + "-"
|
||||||
|
+ endAt + "/" + fileLen
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
res = newFixedLengthResponse(
|
||||||
|
Status.OK, mime,
|
||||||
|
FileInputStream(file), file.length()
|
||||||
|
)
|
||||||
|
res.addHeader("Accept-Ranges", "bytes")
|
||||||
|
res.addHeader("Content-Length", "" + fileLen)
|
||||||
|
}
|
||||||
|
} catch (ioe: IOException) {
|
||||||
|
res = newFixedLengthResponse(
|
||||||
|
Status.FORBIDDEN,
|
||||||
|
MIME_PLAINTEXT, "FORBIDDEN: Reading file failed."
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun errorResponse(message: String = "Error Occurred"): Response {
|
||||||
|
return newFixedLengthResponse(Status.INTERNAL_ERROR, MIME_PLAINTEXT, message)
|
||||||
|
}
|
||||||
|
}
|
|
@ -47,7 +47,7 @@ interface PlaylistDao {
|
||||||
@Query("SELECT * FROM SongEntity WHERE playlist_creator_id = :playlistId AND id = :songId")
|
@Query("SELECT * FROM SongEntity WHERE playlist_creator_id = :playlistId AND id = :songId")
|
||||||
suspend fun isSongExistsInPlaylist(playlistId: Long, songId: Long): List<SongEntity>
|
suspend fun isSongExistsInPlaylist(playlistId: Long, songId: Long): List<SongEntity>
|
||||||
|
|
||||||
@Query("SELECT * FROM SongEntity WHERE playlist_creator_id = :playlistId")
|
@Query("SELECT * FROM SongEntity WHERE playlist_creator_id = :playlistId ORDER BY song_key asc")
|
||||||
fun songsFromPlaylist(playlistId: Long): LiveData<List<SongEntity>>
|
fun songsFromPlaylist(playlistId: Long): LiveData<List<SongEntity>>
|
||||||
|
|
||||||
@Delete
|
@Delete
|
||||||
|
|
|
@ -18,7 +18,7 @@ import android.os.Parcelable
|
||||||
import androidx.room.ColumnInfo
|
import androidx.room.ColumnInfo
|
||||||
import androidx.room.Entity
|
import androidx.room.Entity
|
||||||
import androidx.room.PrimaryKey
|
import androidx.room.PrimaryKey
|
||||||
import kotlinx.android.parcel.Parcelize
|
import kotlinx.parcelize.Parcelize
|
||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
@Parcelize
|
@Parcelize
|
||||||
|
|
|
@ -17,7 +17,7 @@ package code.name.monkey.retromusic.db
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
import androidx.room.Embedded
|
import androidx.room.Embedded
|
||||||
import androidx.room.Relation
|
import androidx.room.Relation
|
||||||
import kotlinx.android.parcel.Parcelize
|
import kotlinx.parcelize.Parcelize
|
||||||
|
|
||||||
@Parcelize
|
@Parcelize
|
||||||
data class PlaylistWithSongs(
|
data class PlaylistWithSongs(
|
||||||
|
|
|
@ -19,7 +19,7 @@ import androidx.room.ColumnInfo
|
||||||
import androidx.room.Entity
|
import androidx.room.Entity
|
||||||
import androidx.room.Index
|
import androidx.room.Index
|
||||||
import androidx.room.PrimaryKey
|
import androidx.room.PrimaryKey
|
||||||
import kotlinx.android.parcel.Parcelize
|
import kotlinx.parcelize.Parcelize
|
||||||
|
|
||||||
@Parcelize
|
@Parcelize
|
||||||
@Entity(indices = [Index(value = ["playlist_creator_id", "id"], unique = true)])
|
@Entity(indices = [Index(value = ["playlist_creator_id", "id"], unique = true)])
|
||||||
|
|
|
@ -1,155 +0,0 @@
|
||||||
package code.name.monkey.retromusic.dialogs;
|
|
||||||
|
|
||||||
import android.Manifest;
|
|
||||||
import android.app.Dialog;
|
|
||||||
import android.content.pm.PackageManager;
|
|
||||||
import android.os.Build;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.os.Environment;
|
|
||||||
import android.view.View;
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import androidx.core.app.ActivityCompat;
|
|
||||||
import androidx.fragment.app.DialogFragment;
|
|
||||||
import code.name.monkey.retromusic.R;
|
|
||||||
import com.afollestad.materialdialogs.MaterialDialog;
|
|
||||||
import java.io.File;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.Comparator;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class BlacklistFolderChooserDialog extends DialogFragment
|
|
||||||
implements MaterialDialog.ListCallback {
|
|
||||||
|
|
||||||
String initialPath = Environment.getExternalStorageDirectory().getAbsolutePath();
|
|
||||||
private File parentFolder;
|
|
||||||
private File[] parentContents;
|
|
||||||
private boolean canGoUp = false;
|
|
||||||
private FolderCallback callback;
|
|
||||||
|
|
||||||
public static BlacklistFolderChooserDialog create() {
|
|
||||||
return new BlacklistFolderChooserDialog();
|
|
||||||
}
|
|
||||||
|
|
||||||
private String[] getContentsArray() {
|
|
||||||
if (parentContents == null) {
|
|
||||||
if (canGoUp) {
|
|
||||||
return new String[] {".."};
|
|
||||||
}
|
|
||||||
return new String[] {};
|
|
||||||
}
|
|
||||||
String[] results = new String[parentContents.length + (canGoUp ? 1 : 0)];
|
|
||||||
if (canGoUp) {
|
|
||||||
results[0] = "..";
|
|
||||||
}
|
|
||||||
for (int i = 0; i < parentContents.length; i++) {
|
|
||||||
results[canGoUp ? i + 1 : i] = parentContents[i].getName();
|
|
||||||
}
|
|
||||||
return results;
|
|
||||||
}
|
|
||||||
|
|
||||||
private File[] listFiles() {
|
|
||||||
File[] contents = parentFolder.listFiles();
|
|
||||||
List<File> results = new ArrayList<>();
|
|
||||||
if (contents != null) {
|
|
||||||
for (File fi : contents) {
|
|
||||||
if (fi.isDirectory()) {
|
|
||||||
results.add(fi);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Collections.sort(results, new FolderSorter());
|
|
||||||
return results.toArray(new File[results.size()]);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
@Override
|
|
||||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
|
|
||||||
&& ActivityCompat.checkSelfPermission(
|
|
||||||
requireActivity(), Manifest.permission.READ_EXTERNAL_STORAGE)
|
|
||||||
!= PackageManager.PERMISSION_GRANTED) {
|
|
||||||
return new MaterialDialog.Builder(requireActivity())
|
|
||||||
.title(R.string.md_error_label)
|
|
||||||
.content(R.string.md_storage_perm_error)
|
|
||||||
.positiveText(android.R.string.ok)
|
|
||||||
.build();
|
|
||||||
}
|
|
||||||
if (savedInstanceState == null) {
|
|
||||||
savedInstanceState = new Bundle();
|
|
||||||
}
|
|
||||||
if (!savedInstanceState.containsKey("current_path")) {
|
|
||||||
savedInstanceState.putString("current_path", initialPath);
|
|
||||||
}
|
|
||||||
parentFolder = new File(savedInstanceState.getString("current_path", File.pathSeparator));
|
|
||||||
checkIfCanGoUp();
|
|
||||||
parentContents = listFiles();
|
|
||||||
MaterialDialog.Builder builder =
|
|
||||||
new MaterialDialog.Builder(requireContext())
|
|
||||||
.title(parentFolder.getAbsolutePath())
|
|
||||||
.items((CharSequence[]) getContentsArray())
|
|
||||||
.itemsCallback(this)
|
|
||||||
.autoDismiss(false)
|
|
||||||
.onPositive(
|
|
||||||
(dialog, which) -> {
|
|
||||||
callback.onFolderSelection(BlacklistFolderChooserDialog.this, parentFolder);
|
|
||||||
dismiss();
|
|
||||||
})
|
|
||||||
.onNegative((materialDialog, dialogAction) -> dismiss())
|
|
||||||
.positiveText(R.string.add_action)
|
|
||||||
.negativeText(android.R.string.cancel);
|
|
||||||
return builder.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onSelection(MaterialDialog materialDialog, View view, int i, CharSequence s) {
|
|
||||||
if (canGoUp && i == 0) {
|
|
||||||
parentFolder = parentFolder.getParentFile();
|
|
||||||
if (parentFolder.getAbsolutePath().equals("/storage/emulated")) {
|
|
||||||
parentFolder = parentFolder.getParentFile();
|
|
||||||
}
|
|
||||||
checkIfCanGoUp();
|
|
||||||
} else {
|
|
||||||
parentFolder = parentContents[canGoUp ? i - 1 : i];
|
|
||||||
canGoUp = true;
|
|
||||||
if (parentFolder.getAbsolutePath().equals("/storage/emulated")) {
|
|
||||||
parentFolder = Environment.getExternalStorageDirectory();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
reload();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void checkIfCanGoUp() {
|
|
||||||
canGoUp = parentFolder.getParent() != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void reload() {
|
|
||||||
parentContents = listFiles();
|
|
||||||
MaterialDialog dialog = (MaterialDialog) getDialog();
|
|
||||||
dialog.setTitle(parentFolder.getAbsolutePath());
|
|
||||||
dialog.setItems((CharSequence[]) getContentsArray());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onSaveInstanceState(Bundle outState) {
|
|
||||||
super.onSaveInstanceState(outState);
|
|
||||||
outState.putString("current_path", parentFolder.getAbsolutePath());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setCallback(FolderCallback callback) {
|
|
||||||
this.callback = callback;
|
|
||||||
}
|
|
||||||
|
|
||||||
public interface FolderCallback {
|
|
||||||
void onFolderSelection(@NonNull BlacklistFolderChooserDialog dialog, @NonNull File folder);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class FolderSorter implements Comparator<File> {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int compare(File lhs, File rhs) {
|
|
||||||
return lhs.getName().compareTo(rhs.getName());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,152 @@
|
||||||
|
package code.name.monkey.retromusic.dialogs
|
||||||
|
|
||||||
|
import android.Manifest
|
||||||
|
import android.app.Dialog
|
||||||
|
import android.content.pm.PackageManager
|
||||||
|
import android.os.Build
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.os.Environment
|
||||||
|
import androidx.core.app.ActivityCompat
|
||||||
|
import androidx.fragment.app.DialogFragment
|
||||||
|
import code.name.monkey.retromusic.R
|
||||||
|
import com.afollestad.materialdialogs.MaterialDialog
|
||||||
|
import com.afollestad.materialdialogs.list.listItems
|
||||||
|
import com.afollestad.materialdialogs.list.updateListItems
|
||||||
|
import java.io.File
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
class BlacklistFolderChooserDialog : DialogFragment() {
|
||||||
|
private var initialPath: String = Environment.getExternalStorageDirectory().absolutePath
|
||||||
|
private var parentFolder: File? = null
|
||||||
|
private var parentContents: Array<File>? = null
|
||||||
|
private var canGoUp = false
|
||||||
|
private var callback: FolderCallback? = null
|
||||||
|
private val contentsArray: Array<String?>
|
||||||
|
get() {
|
||||||
|
if (parentContents == null) {
|
||||||
|
return if (canGoUp) {
|
||||||
|
arrayOf("..")
|
||||||
|
} else arrayOf()
|
||||||
|
}
|
||||||
|
val results = arrayOfNulls<String>(parentContents!!.size + if (canGoUp) 1 else 0)
|
||||||
|
if (canGoUp) {
|
||||||
|
results[0] = ".."
|
||||||
|
}
|
||||||
|
for (i in parentContents!!.indices) {
|
||||||
|
results[if (canGoUp) i + 1 else i] = parentContents!![i].name
|
||||||
|
}
|
||||||
|
return results
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun listFiles(): Array<File>? {
|
||||||
|
val contents = parentFolder!!.listFiles()
|
||||||
|
val results: MutableList<File> = ArrayList()
|
||||||
|
if (contents != null) {
|
||||||
|
for (fi in contents) {
|
||||||
|
if (fi.isDirectory) {
|
||||||
|
results.add(fi)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Collections.sort(results, FolderSorter())
|
||||||
|
return results.toTypedArray()
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||||
|
var mSavedInstanceState = savedInstanceState
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
|
||||||
|
&& ActivityCompat.checkSelfPermission(
|
||||||
|
requireActivity(), Manifest.permission.READ_EXTERNAL_STORAGE
|
||||||
|
)
|
||||||
|
!= PackageManager.PERMISSION_GRANTED
|
||||||
|
) {
|
||||||
|
return MaterialDialog(requireActivity()).show {
|
||||||
|
title(res = R.string.md_error_label)
|
||||||
|
message(res = R.string.md_storage_perm_error)
|
||||||
|
positiveButton(res = android.R.string.ok)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (mSavedInstanceState == null) {
|
||||||
|
mSavedInstanceState = Bundle()
|
||||||
|
}
|
||||||
|
if (!mSavedInstanceState.containsKey("current_path")) {
|
||||||
|
mSavedInstanceState.putString("current_path", initialPath)
|
||||||
|
}
|
||||||
|
parentFolder = File(mSavedInstanceState.getString("current_path", File.pathSeparator))
|
||||||
|
checkIfCanGoUp()
|
||||||
|
parentContents = listFiles()
|
||||||
|
return MaterialDialog(requireContext())
|
||||||
|
.title(text = parentFolder!!.absolutePath)
|
||||||
|
.listItems(
|
||||||
|
items = contentsArray.toCharSequence(),
|
||||||
|
waitForPositiveButton = false
|
||||||
|
) { _: MaterialDialog, i: Int, _: CharSequence ->
|
||||||
|
onSelection(i)
|
||||||
|
}
|
||||||
|
.noAutoDismiss()
|
||||||
|
.cornerRadius(literalDp = 20F)
|
||||||
|
.positiveButton(res = R.string.add_action) {
|
||||||
|
callback!!.onFolderSelection(this@BlacklistFolderChooserDialog, parentFolder!!)
|
||||||
|
dismiss()
|
||||||
|
}
|
||||||
|
.negativeButton(res = android.R.string.cancel) { dismiss() }
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun onSelection(i: Int) {
|
||||||
|
if (canGoUp && i == 0) {
|
||||||
|
parentFolder = parentFolder!!.parentFile
|
||||||
|
if (parentFolder!!.absolutePath == "/storage/emulated") {
|
||||||
|
parentFolder = parentFolder!!.parentFile
|
||||||
|
}
|
||||||
|
checkIfCanGoUp()
|
||||||
|
} else {
|
||||||
|
parentFolder = parentContents!![if (canGoUp) i - 1 else i]
|
||||||
|
canGoUp = true
|
||||||
|
if (parentFolder!!.absolutePath == "/storage/emulated") {
|
||||||
|
parentFolder = Environment.getExternalStorageDirectory()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
reload()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun checkIfCanGoUp() {
|
||||||
|
canGoUp = parentFolder!!.parent != null
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun reload() {
|
||||||
|
parentContents = listFiles()
|
||||||
|
val dialog = dialog as MaterialDialog?
|
||||||
|
dialog!!.setTitle(parentFolder!!.absolutePath)
|
||||||
|
dialog.updateListItems(items = contentsArray.toCharSequence())
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun Array<String?>.toCharSequence(): List<CharSequence> {
|
||||||
|
return map { it as CharSequence }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onSaveInstanceState(outState: Bundle) {
|
||||||
|
super.onSaveInstanceState(outState)
|
||||||
|
outState.putString("current_path", parentFolder!!.absolutePath)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setCallback(callback: FolderCallback?) {
|
||||||
|
this.callback = callback
|
||||||
|
}
|
||||||
|
|
||||||
|
interface FolderCallback {
|
||||||
|
fun onFolderSelection(dialog: BlacklistFolderChooserDialog, folder: File)
|
||||||
|
}
|
||||||
|
|
||||||
|
private class FolderSorter : Comparator<File> {
|
||||||
|
override fun compare(lhs: File, rhs: File): Int {
|
||||||
|
return lhs.name.compareTo(rhs.name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun create(): BlacklistFolderChooserDialog {
|
||||||
|
return BlacklistFolderChooserDialog()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -17,29 +17,23 @@ package code.name.monkey.retromusic.dialogs
|
||||||
import android.app.Dialog
|
import android.app.Dialog
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.text.TextUtils
|
import android.text.TextUtils
|
||||||
import android.view.LayoutInflater
|
|
||||||
import android.widget.Toast
|
|
||||||
import androidx.core.os.bundleOf
|
import androidx.core.os.bundleOf
|
||||||
import androidx.fragment.app.DialogFragment
|
import androidx.fragment.app.DialogFragment
|
||||||
import androidx.lifecycle.lifecycleScope
|
|
||||||
import code.name.monkey.retromusic.EXTRA_SONG
|
import code.name.monkey.retromusic.EXTRA_SONG
|
||||||
import code.name.monkey.retromusic.R
|
import code.name.monkey.retromusic.R
|
||||||
import code.name.monkey.retromusic.db.PlaylistEntity
|
import code.name.monkey.retromusic.databinding.DialogPlaylistBinding
|
||||||
import code.name.monkey.retromusic.db.toSongEntity
|
|
||||||
import code.name.monkey.retromusic.extensions.colorButtons
|
import code.name.monkey.retromusic.extensions.colorButtons
|
||||||
import code.name.monkey.retromusic.extensions.extra
|
import code.name.monkey.retromusic.extensions.extra
|
||||||
import code.name.monkey.retromusic.extensions.materialDialog
|
import code.name.monkey.retromusic.extensions.materialDialog
|
||||||
import code.name.monkey.retromusic.fragments.LibraryViewModel
|
import code.name.monkey.retromusic.fragments.LibraryViewModel
|
||||||
import code.name.monkey.retromusic.fragments.ReloadType.Playlists
|
|
||||||
import code.name.monkey.retromusic.model.Song
|
import code.name.monkey.retromusic.model.Song
|
||||||
import com.google.android.material.textfield.TextInputEditText
|
import com.google.android.material.textfield.TextInputEditText
|
||||||
import com.google.android.material.textfield.TextInputLayout
|
import com.google.android.material.textfield.TextInputLayout
|
||||||
import kotlinx.android.synthetic.main.dialog_playlist.view.*
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import org.koin.androidx.viewmodel.ext.android.sharedViewModel
|
import org.koin.androidx.viewmodel.ext.android.sharedViewModel
|
||||||
|
|
||||||
class CreatePlaylistDialog : DialogFragment() {
|
class CreatePlaylistDialog : DialogFragment() {
|
||||||
|
private var _binding: DialogPlaylistBinding? = null
|
||||||
|
private val binding get() = _binding!!
|
||||||
private val libraryViewModel by sharedViewModel<LibraryViewModel>()
|
private val libraryViewModel by sharedViewModel<LibraryViewModel>()
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
@ -56,13 +50,15 @@ class CreatePlaylistDialog : DialogFragment() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||||
val view = LayoutInflater.from(requireActivity()).inflate(R.layout.dialog_playlist, null)
|
_binding = DialogPlaylistBinding.inflate(layoutInflater)
|
||||||
|
|
||||||
val songs: List<Song> = extra<List<Song>>(EXTRA_SONG).value ?: emptyList()
|
val songs: List<Song> = extra<List<Song>>(EXTRA_SONG).value ?: emptyList()
|
||||||
val playlistView: TextInputEditText = view.actionNewPlaylist
|
val playlistView: TextInputEditText = binding.actionNewPlaylist
|
||||||
val playlistContainer: TextInputLayout = view.actionNewPlaylistContainer
|
val playlistContainer: TextInputLayout = binding.actionNewPlaylistContainer
|
||||||
return materialDialog(R.string.new_playlist_title)
|
return materialDialog(R.string.new_playlist_title)
|
||||||
.setView(view)
|
.setView(binding.root)
|
||||||
.setPositiveButton(
|
.setPositiveButton(
|
||||||
R.string.create_action
|
R.string.create_action
|
||||||
) { _, _ ->
|
) { _, _ ->
|
||||||
|
@ -77,4 +73,9 @@ class CreatePlaylistDialog : DialogFragment() {
|
||||||
.create()
|
.create()
|
||||||
.colorButtons()
|
.colorButtons()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onDestroyView() {
|
||||||
|
super.onDestroyView()
|
||||||
|
_binding = null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,24 +14,31 @@
|
||||||
*/
|
*/
|
||||||
package code.name.monkey.retromusic.dialogs
|
package code.name.monkey.retromusic.dialogs
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
import android.app.Dialog
|
import android.app.Dialog
|
||||||
|
import android.content.Intent
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import androidx.core.os.bundleOf
|
import androidx.core.os.bundleOf
|
||||||
import androidx.core.text.HtmlCompat
|
import androidx.core.text.HtmlCompat
|
||||||
import androidx.fragment.app.DialogFragment
|
import androidx.fragment.app.DialogFragment
|
||||||
import code.name.monkey.retromusic.EXTRA_SONG
|
import code.name.monkey.retromusic.EXTRA_SONG
|
||||||
import code.name.monkey.retromusic.R
|
import code.name.monkey.retromusic.R
|
||||||
import code.name.monkey.retromusic.extensions.colorButtons
|
import code.name.monkey.retromusic.activities.saf.SAFGuideActivity
|
||||||
import code.name.monkey.retromusic.extensions.extraNotNull
|
import code.name.monkey.retromusic.extensions.extraNotNull
|
||||||
import code.name.monkey.retromusic.extensions.materialDialog
|
|
||||||
import code.name.monkey.retromusic.fragments.LibraryViewModel
|
import code.name.monkey.retromusic.fragments.LibraryViewModel
|
||||||
|
import code.name.monkey.retromusic.fragments.ReloadType
|
||||||
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
||||||
import code.name.monkey.retromusic.model.Song
|
import code.name.monkey.retromusic.model.Song
|
||||||
import code.name.monkey.retromusic.util.MusicUtil
|
import code.name.monkey.retromusic.util.MusicUtil
|
||||||
import org.koin.androidx.viewmodel.ext.android.sharedViewModel
|
import code.name.monkey.retromusic.util.SAFUtil
|
||||||
|
import com.afollestad.materialdialogs.MaterialDialog
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import org.koin.androidx.viewmodel.ext.android.getViewModel
|
||||||
|
|
||||||
class DeleteSongsDialog : DialogFragment() {
|
class DeleteSongsDialog : DialogFragment() {
|
||||||
private val libraryViewModel by sharedViewModel<LibraryViewModel>()
|
lateinit var libraryViewModel: LibraryViewModel
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun create(song: Song): DeleteSongsDialog {
|
fun create(song: Song): DeleteSongsDialog {
|
||||||
|
@ -50,6 +57,7 @@ class DeleteSongsDialog : DialogFragment() {
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||||
|
libraryViewModel = activity?.getViewModel() as LibraryViewModel
|
||||||
val songs = extraNotNull<List<Song>>(EXTRA_SONG).value
|
val songs = extraNotNull<List<Song>>(EXTRA_SONG).value
|
||||||
val pair = if (songs.size > 1) {
|
val pair = if (songs.size > 1) {
|
||||||
Pair(
|
Pair(
|
||||||
|
@ -69,17 +77,66 @@ class DeleteSongsDialog : DialogFragment() {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return materialDialog(pair.first)
|
return MaterialDialog(requireContext())
|
||||||
.setMessage(pair.second)
|
.title(pair.first)
|
||||||
.setCancelable(false)
|
.message(text = pair.second)
|
||||||
.setPositiveButton(R.string.action_delete) { _, _ ->
|
.noAutoDismiss()
|
||||||
if (songs.isNotEmpty() and (songs.size == 1) and MusicPlayerRemote.isPlaying(songs.first())) {
|
.cornerRadius(16F)
|
||||||
|
.negativeButton(android.R.string.cancel) {
|
||||||
|
dismiss()
|
||||||
|
}
|
||||||
|
.positiveButton(R.string.action_delete) {
|
||||||
|
if ((songs.size == 1) && MusicPlayerRemote.isPlaying(songs[0])) {
|
||||||
MusicPlayerRemote.playNextSong()
|
MusicPlayerRemote.playNextSong()
|
||||||
}
|
}
|
||||||
MusicUtil.deleteTracks(requireActivity(), songs)
|
if (!SAFUtil.isSAFRequiredForSongs(songs)) {
|
||||||
libraryViewModel.deleteTracks(songs)
|
CoroutineScope(Dispatchers.IO).launch {
|
||||||
|
dismiss()
|
||||||
|
MusicUtil.deleteTracks(requireContext(), songs)
|
||||||
|
reloadTabs()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (SAFUtil.isSDCardAccessGranted(requireActivity())) {
|
||||||
|
deleteSongs(songs)
|
||||||
|
} else {
|
||||||
|
startActivityForResult(
|
||||||
|
Intent(requireActivity(), SAFGuideActivity::class.java),
|
||||||
|
SAFGuideActivity.REQUEST_CODE_SAF_GUIDE
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.create()
|
}
|
||||||
.colorButtons()
|
|
||||||
|
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||||
|
super.onActivityResult(requestCode, resultCode, data)
|
||||||
|
when (requestCode) {
|
||||||
|
SAFGuideActivity.REQUEST_CODE_SAF_GUIDE -> {
|
||||||
|
SAFUtil.openTreePicker(this)
|
||||||
|
}
|
||||||
|
SAFUtil.REQUEST_SAF_PICK_TREE,
|
||||||
|
SAFUtil.REQUEST_SAF_PICK_FILE -> {
|
||||||
|
if (resultCode == Activity.RESULT_OK) {
|
||||||
|
SAFUtil.saveTreeUri(requireActivity(), data)
|
||||||
|
val songs = extraNotNull<List<Song>>(EXTRA_SONG).value
|
||||||
|
deleteSongs(songs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun deleteSongs(songs: List<Song>) {
|
||||||
|
CoroutineScope(Dispatchers.IO).launch {
|
||||||
|
dismiss()
|
||||||
|
MusicUtil.deleteTracks(requireActivity(), songs, null, null)
|
||||||
|
reloadTabs()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun reloadTabs() {
|
||||||
|
libraryViewModel.forceReload(ReloadType.Songs)
|
||||||
|
libraryViewModel.forceReload(ReloadType.HomeSections)
|
||||||
|
libraryViewModel.forceReload(ReloadType.Artists)
|
||||||
|
libraryViewModel.forceReload(ReloadType.Albums)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,8 +38,9 @@ import code.name.monkey.retromusic.service.MusicService
|
||||||
import code.name.monkey.retromusic.service.MusicService.ACTION_PENDING_QUIT
|
import code.name.monkey.retromusic.service.MusicService.ACTION_PENDING_QUIT
|
||||||
import code.name.monkey.retromusic.service.MusicService.ACTION_QUIT
|
import code.name.monkey.retromusic.service.MusicService.ACTION_QUIT
|
||||||
import code.name.monkey.retromusic.util.PreferenceUtil
|
import code.name.monkey.retromusic.util.PreferenceUtil
|
||||||
import com.afollestad.materialdialogs.DialogAction
|
|
||||||
import com.afollestad.materialdialogs.MaterialDialog
|
import com.afollestad.materialdialogs.MaterialDialog
|
||||||
|
import com.afollestad.materialdialogs.WhichButton
|
||||||
|
import com.afollestad.materialdialogs.actions.getActionButton
|
||||||
|
|
||||||
class SleepTimerDialog : DialogFragment() {
|
class SleepTimerDialog : DialogFragment() {
|
||||||
|
|
||||||
|
@ -150,14 +151,14 @@ class SleepTimerDialog : DialogFragment() {
|
||||||
private fun updateCancelButton() {
|
private fun updateCancelButton() {
|
||||||
val musicService = MusicPlayerRemote.musicService
|
val musicService = MusicPlayerRemote.musicService
|
||||||
if (musicService != null && musicService.pendingQuit) {
|
if (musicService != null && musicService.pendingQuit) {
|
||||||
dialog.getActionButton(DialogAction.NEUTRAL).text =
|
dialog.getActionButton(WhichButton.NEUTRAL).text =
|
||||||
dialog.context.getString(R.string.cancel_current_timer)
|
dialog.context.getString(R.string.cancel_current_timer)
|
||||||
} else {
|
} else {
|
||||||
dialog.getActionButton(DialogAction.NEUTRAL).text = null
|
dialog.getActionButton(WhichButton.NEUTRAL).text = null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private inner class TimerUpdater() :
|
private inner class TimerUpdater :
|
||||||
CountDownTimer(
|
CountDownTimer(
|
||||||
PreferenceUtil.nextSleepTimerElapsedRealTime - SystemClock.elapsedRealtime(),
|
PreferenceUtil.nextSleepTimerElapsedRealTime - SystemClock.elapsedRealtime(),
|
||||||
1000
|
1000
|
||||||
|
|
|
@ -32,13 +32,13 @@ import code.name.monkey.retromusic.extensions.colorButtons
|
||||||
import code.name.monkey.retromusic.extensions.materialDialog
|
import code.name.monkey.retromusic.extensions.materialDialog
|
||||||
import code.name.monkey.retromusic.model.Song
|
import code.name.monkey.retromusic.model.Song
|
||||||
import code.name.monkey.retromusic.util.MusicUtil
|
import code.name.monkey.retromusic.util.MusicUtil
|
||||||
import java.io.File
|
|
||||||
import java.io.IOException
|
|
||||||
import org.jaudiotagger.audio.AudioFileIO
|
import org.jaudiotagger.audio.AudioFileIO
|
||||||
import org.jaudiotagger.audio.exceptions.CannotReadException
|
import org.jaudiotagger.audio.exceptions.CannotReadException
|
||||||
import org.jaudiotagger.audio.exceptions.InvalidAudioFrameException
|
import org.jaudiotagger.audio.exceptions.InvalidAudioFrameException
|
||||||
import org.jaudiotagger.audio.exceptions.ReadOnlyFileException
|
import org.jaudiotagger.audio.exceptions.ReadOnlyFileException
|
||||||
import org.jaudiotagger.tag.TagException
|
import org.jaudiotagger.tag.TagException
|
||||||
|
import java.io.File
|
||||||
|
import java.io.IOException
|
||||||
|
|
||||||
class SongDetailDialog : DialogFragment() {
|
class SongDetailDialog : DialogFragment() {
|
||||||
|
|
||||||
|
|
|
@ -41,7 +41,6 @@ import com.google.android.material.button.MaterialButton
|
||||||
import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
|
import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
|
||||||
import com.google.android.material.floatingactionbutton.FloatingActionButton
|
import com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||||
import com.google.android.material.progressindicator.CircularProgressIndicator
|
import com.google.android.material.progressindicator.CircularProgressIndicator
|
||||||
import com.google.android.material.textfield.TextInputEditText
|
|
||||||
import com.google.android.material.textfield.TextInputLayout
|
import com.google.android.material.textfield.TextInputLayout
|
||||||
|
|
||||||
fun Int.ripAlpha(): Int {
|
fun Int.ripAlpha(): Int {
|
||||||
|
|
|
@ -18,14 +18,10 @@ import android.content.Context
|
||||||
import android.graphics.Bitmap
|
import android.graphics.Bitmap
|
||||||
import android.graphics.BitmapFactory
|
import android.graphics.BitmapFactory
|
||||||
import android.graphics.Canvas
|
import android.graphics.Canvas
|
||||||
import android.graphics.drawable.AdaptiveIconDrawable
|
|
||||||
import android.graphics.drawable.BitmapDrawable
|
import android.graphics.drawable.BitmapDrawable
|
||||||
import android.graphics.drawable.Drawable
|
import android.graphics.drawable.Drawable
|
||||||
import android.os.Build
|
|
||||||
import androidx.annotation.DimenRes
|
import androidx.annotation.DimenRes
|
||||||
import androidx.annotation.DrawableRes
|
import androidx.annotation.DrawableRes
|
||||||
import androidx.core.content.ContextCompat
|
|
||||||
import code.name.monkey.retromusic.R
|
|
||||||
|
|
||||||
fun Context.scaledDrawableResources(
|
fun Context.scaledDrawableResources(
|
||||||
@DrawableRes id: Int,
|
@DrawableRes id: Int,
|
||||||
|
|
|
@ -17,6 +17,7 @@ package code.name.monkey.retromusic.fragments
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import androidx.core.os.bundleOf
|
import androidx.core.os.bundleOf
|
||||||
|
import androidx.core.view.doOnPreDraw
|
||||||
import androidx.navigation.fragment.FragmentNavigatorExtras
|
import androidx.navigation.fragment.FragmentNavigatorExtras
|
||||||
import androidx.navigation.fragment.findNavController
|
import androidx.navigation.fragment.findNavController
|
||||||
import androidx.navigation.fragment.navArgs
|
import androidx.navigation.fragment.navArgs
|
||||||
|
@ -28,26 +29,48 @@ import code.name.monkey.retromusic.adapter.album.AlbumAdapter
|
||||||
import code.name.monkey.retromusic.adapter.artist.ArtistAdapter
|
import code.name.monkey.retromusic.adapter.artist.ArtistAdapter
|
||||||
import code.name.monkey.retromusic.adapter.song.ShuffleButtonSongAdapter
|
import code.name.monkey.retromusic.adapter.song.ShuffleButtonSongAdapter
|
||||||
import code.name.monkey.retromusic.adapter.song.SongAdapter
|
import code.name.monkey.retromusic.adapter.song.SongAdapter
|
||||||
|
import code.name.monkey.retromusic.databinding.FragmentPlaylistDetailBinding
|
||||||
import code.name.monkey.retromusic.db.toSong
|
import code.name.monkey.retromusic.db.toSong
|
||||||
import code.name.monkey.retromusic.extensions.dipToPix
|
import code.name.monkey.retromusic.extensions.dipToPix
|
||||||
import code.name.monkey.retromusic.extensions.hide
|
|
||||||
import code.name.monkey.retromusic.fragments.base.AbsMainActivityFragment
|
import code.name.monkey.retromusic.fragments.base.AbsMainActivityFragment
|
||||||
import code.name.monkey.retromusic.interfaces.IAlbumClickListener
|
import code.name.monkey.retromusic.interfaces.IAlbumClickListener
|
||||||
import code.name.monkey.retromusic.interfaces.IArtistClickListener
|
import code.name.monkey.retromusic.interfaces.IArtistClickListener
|
||||||
import code.name.monkey.retromusic.model.Album
|
import code.name.monkey.retromusic.model.Album
|
||||||
import code.name.monkey.retromusic.model.Artist
|
import code.name.monkey.retromusic.model.Artist
|
||||||
import code.name.monkey.retromusic.util.RetroUtil
|
import code.name.monkey.retromusic.util.RetroUtil
|
||||||
import kotlinx.android.synthetic.main.fragment_playlist_detail.*
|
import com.google.android.material.transition.MaterialSharedAxis
|
||||||
|
|
||||||
class DetailListFragment : AbsMainActivityFragment(R.layout.fragment_playlist_detail),
|
class DetailListFragment : AbsMainActivityFragment(R.layout.fragment_playlist_detail),
|
||||||
IArtistClickListener, IAlbumClickListener {
|
IArtistClickListener, IAlbumClickListener {
|
||||||
private val args by navArgs<DetailListFragmentArgs>()
|
private val args by navArgs<DetailListFragmentArgs>()
|
||||||
|
private var _binding: FragmentPlaylistDetailBinding? = null
|
||||||
|
private val binding get() = _binding!!
|
||||||
|
|
||||||
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
_binding = FragmentPlaylistDetailBinding.bind(view)
|
||||||
|
when (args.type) {
|
||||||
|
TOP_ARTISTS,
|
||||||
|
RECENT_ARTISTS,
|
||||||
|
TOP_ALBUMS,
|
||||||
|
RECENT_ALBUMS,
|
||||||
|
FAVOURITES -> {
|
||||||
|
enterTransition = MaterialSharedAxis(MaterialSharedAxis.X, true)
|
||||||
|
returnTransition = MaterialSharedAxis(MaterialSharedAxis.X, false)
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
enterTransition = MaterialSharedAxis(MaterialSharedAxis.Y, true)
|
||||||
|
returnTransition = MaterialSharedAxis(MaterialSharedAxis.Y, false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
postponeEnterTransition()
|
||||||
|
view.doOnPreDraw { startPostponedEnterTransition() }
|
||||||
|
}
|
||||||
|
|
||||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
||||||
super.onActivityCreated(savedInstanceState)
|
super.onActivityCreated(savedInstanceState)
|
||||||
mainActivity.setBottomBarVisibility(false)
|
mainActivity.setSupportActionBar(binding.toolbar)
|
||||||
mainActivity.setSupportActionBar(toolbar)
|
binding.progressIndicator.hide()
|
||||||
progressIndicator.hide()
|
|
||||||
when (args.type) {
|
when (args.type) {
|
||||||
TOP_ARTISTS -> loadArtists(R.string.top_artists, TOP_ARTISTS)
|
TOP_ARTISTS -> loadArtists(R.string.top_artists, TOP_ARTISTS)
|
||||||
RECENT_ARTISTS -> loadArtists(R.string.recent_artists, RECENT_ARTISTS)
|
RECENT_ARTISTS -> loadArtists(R.string.recent_artists, RECENT_ARTISTS)
|
||||||
|
@ -59,23 +82,23 @@ class DetailListFragment : AbsMainActivityFragment(R.layout.fragment_playlist_de
|
||||||
TOP_PLAYED_PLAYLIST -> topPlayed()
|
TOP_PLAYED_PLAYLIST -> topPlayed()
|
||||||
}
|
}
|
||||||
|
|
||||||
recyclerView.adapter?.registerAdapterDataObserver(object : AdapterDataObserver() {
|
binding.recyclerView.adapter?.registerAdapterDataObserver(object : AdapterDataObserver() {
|
||||||
override fun onChanged() {
|
override fun onChanged() {
|
||||||
super.onChanged()
|
super.onChanged()
|
||||||
val height = dipToPix(52f)
|
val height = dipToPix(52f)
|
||||||
recyclerView.setPadding(0, 0, 0, height.toInt())
|
binding.recyclerView.setPadding(0, 0, 0, height.toInt())
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun lastAddedSongs() {
|
private fun lastAddedSongs() {
|
||||||
toolbar.setTitle(R.string.last_added)
|
binding.toolbar.setTitle(R.string.last_added)
|
||||||
val songAdapter = ShuffleButtonSongAdapter(
|
val songAdapter = ShuffleButtonSongAdapter(
|
||||||
requireActivity(),
|
requireActivity(),
|
||||||
mutableListOf(),
|
mutableListOf(),
|
||||||
R.layout.item_list, null
|
R.layout.item_list, null
|
||||||
)
|
)
|
||||||
recyclerView.apply {
|
binding.recyclerView.apply {
|
||||||
adapter = songAdapter
|
adapter = songAdapter
|
||||||
layoutManager = linearLayoutManager()
|
layoutManager = linearLayoutManager()
|
||||||
}
|
}
|
||||||
|
@ -85,13 +108,13 @@ class DetailListFragment : AbsMainActivityFragment(R.layout.fragment_playlist_de
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun topPlayed() {
|
private fun topPlayed() {
|
||||||
toolbar.setTitle(R.string.my_top_tracks)
|
binding.toolbar.setTitle(R.string.my_top_tracks)
|
||||||
val songAdapter = ShuffleButtonSongAdapter(
|
val songAdapter = ShuffleButtonSongAdapter(
|
||||||
requireActivity(),
|
requireActivity(),
|
||||||
mutableListOf(),
|
mutableListOf(),
|
||||||
R.layout.item_list, null
|
R.layout.item_list, null
|
||||||
)
|
)
|
||||||
recyclerView.apply {
|
binding.recyclerView.apply {
|
||||||
adapter = songAdapter
|
adapter = songAdapter
|
||||||
layoutManager = linearLayoutManager()
|
layoutManager = linearLayoutManager()
|
||||||
}
|
}
|
||||||
|
@ -101,14 +124,14 @@ class DetailListFragment : AbsMainActivityFragment(R.layout.fragment_playlist_de
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun loadHistory() {
|
private fun loadHistory() {
|
||||||
toolbar.setTitle(R.string.history)
|
binding.toolbar.setTitle(R.string.history)
|
||||||
|
|
||||||
val songAdapter = ShuffleButtonSongAdapter(
|
val songAdapter = ShuffleButtonSongAdapter(
|
||||||
requireActivity(),
|
requireActivity(),
|
||||||
mutableListOf(),
|
mutableListOf(),
|
||||||
R.layout.item_list, null
|
R.layout.item_list, null
|
||||||
)
|
)
|
||||||
recyclerView.apply {
|
binding.recyclerView.apply {
|
||||||
adapter = songAdapter
|
adapter = songAdapter
|
||||||
layoutManager = linearLayoutManager()
|
layoutManager = linearLayoutManager()
|
||||||
}
|
}
|
||||||
|
@ -118,13 +141,13 @@ class DetailListFragment : AbsMainActivityFragment(R.layout.fragment_playlist_de
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun loadFavorite() {
|
private fun loadFavorite() {
|
||||||
toolbar.setTitle(R.string.favorites)
|
binding.toolbar.setTitle(R.string.favorites)
|
||||||
val songAdapter = SongAdapter(
|
val songAdapter = SongAdapter(
|
||||||
requireActivity(),
|
requireActivity(),
|
||||||
mutableListOf(),
|
mutableListOf(),
|
||||||
R.layout.item_list, null
|
R.layout.item_list, null
|
||||||
)
|
)
|
||||||
recyclerView.apply {
|
binding.recyclerView.apply {
|
||||||
adapter = songAdapter
|
adapter = songAdapter
|
||||||
layoutManager = linearLayoutManager()
|
layoutManager = linearLayoutManager()
|
||||||
}
|
}
|
||||||
|
@ -135,9 +158,9 @@ class DetailListFragment : AbsMainActivityFragment(R.layout.fragment_playlist_de
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun loadArtists(title: Int, type: Int) {
|
private fun loadArtists(title: Int, type: Int) {
|
||||||
toolbar.setTitle(title)
|
binding.toolbar.setTitle(title)
|
||||||
libraryViewModel.artists(type).observe(viewLifecycleOwner, { artists ->
|
libraryViewModel.artists(type).observe(viewLifecycleOwner, { artists ->
|
||||||
recyclerView.apply {
|
binding.recyclerView.apply {
|
||||||
adapter = artistAdapter(artists)
|
adapter = artistAdapter(artists)
|
||||||
layoutManager = gridLayoutManager()
|
layoutManager = gridLayoutManager()
|
||||||
}
|
}
|
||||||
|
@ -145,9 +168,9 @@ class DetailListFragment : AbsMainActivityFragment(R.layout.fragment_playlist_de
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun loadAlbums(title: Int, type: Int) {
|
private fun loadAlbums(title: Int, type: Int) {
|
||||||
toolbar.setTitle(title)
|
binding.toolbar.setTitle(title)
|
||||||
libraryViewModel.albums(type).observe(viewLifecycleOwner, { albums ->
|
libraryViewModel.albums(type).observe(viewLifecycleOwner, { albums ->
|
||||||
recyclerView.apply {
|
binding.recyclerView.apply {
|
||||||
adapter = albumAdapter(albums)
|
adapter = albumAdapter(albums)
|
||||||
layoutManager = gridLayoutManager()
|
layoutManager = gridLayoutManager()
|
||||||
}
|
}
|
||||||
|
@ -186,7 +209,7 @@ class DetailListFragment : AbsMainActivityFragment(R.layout.fragment_playlist_de
|
||||||
R.id.artistDetailsFragment,
|
R.id.artistDetailsFragment,
|
||||||
bundleOf(EXTRA_ARTIST_ID to artistId),
|
bundleOf(EXTRA_ARTIST_ID to artistId),
|
||||||
null,
|
null,
|
||||||
FragmentNavigatorExtras(view to "artist")
|
FragmentNavigatorExtras(view to artistId.toString())
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -196,8 +219,13 @@ class DetailListFragment : AbsMainActivityFragment(R.layout.fragment_playlist_de
|
||||||
bundleOf(EXTRA_ALBUM_ID to albumId),
|
bundleOf(EXTRA_ALBUM_ID to albumId),
|
||||||
null,
|
null,
|
||||||
FragmentNavigatorExtras(
|
FragmentNavigatorExtras(
|
||||||
view to "album"
|
view to albumId.toString()
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onDestroyView() {
|
||||||
|
super.onDestroyView()
|
||||||
|
_binding = null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,9 +25,7 @@ import code.name.monkey.retromusic.model.*
|
||||||
import code.name.monkey.retromusic.repository.RealRepository
|
import code.name.monkey.retromusic.repository.RealRepository
|
||||||
import code.name.monkey.retromusic.util.PreferenceUtil
|
import code.name.monkey.retromusic.util.PreferenceUtil
|
||||||
import kotlinx.coroutines.Dispatchers.IO
|
import kotlinx.coroutines.Dispatchers.IO
|
||||||
import kotlinx.coroutines.Dispatchers.Main
|
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
|
||||||
|
|
||||||
class LibraryViewModel(
|
class LibraryViewModel(
|
||||||
private val repository: RealRepository
|
private val repository: RealRepository
|
||||||
|
@ -129,16 +127,16 @@ class LibraryViewModel(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun fetchHomeSections() {
|
fun fetchHomeSections() {
|
||||||
viewModelScope.launch(IO) {
|
viewModelScope.launch(IO) {
|
||||||
home.postValue(repository.homeSections())
|
home.postValue(repository.homeSections())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun search(query: String?) {
|
fun search(query: String?, filters: List<Boolean>) {
|
||||||
viewModelScope.launch(IO) {
|
viewModelScope.launch(IO) {
|
||||||
val result = repository.search(query)
|
val result = repository.search(query, filters)
|
||||||
withContext(Main) { searchResults.postValue(result) }
|
searchResults.postValue(result)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -190,6 +188,10 @@ class LibraryViewModel(
|
||||||
println("onShuffleModeChanged")
|
println("onShuffleModeChanged")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onFavoriteStateChanged() {
|
||||||
|
println("onFavoriteStateChanged")
|
||||||
|
}
|
||||||
|
|
||||||
fun shuffleSongs() = viewModelScope.launch(IO) {
|
fun shuffleSongs() = viewModelScope.launch(IO) {
|
||||||
val songs = repository.allSongs()
|
val songs = repository.allSongs()
|
||||||
MusicPlayerRemote.openAndShuffleQueue(
|
MusicPlayerRemote.openAndShuffleQueue(
|
||||||
|
|
|
@ -26,23 +26,26 @@ import android.view.MotionEvent
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.animation.DecelerateInterpolator
|
import android.view.animation.DecelerateInterpolator
|
||||||
import code.name.monkey.retromusic.R
|
import code.name.monkey.retromusic.R
|
||||||
|
import code.name.monkey.retromusic.databinding.FragmentMiniPlayerBinding
|
||||||
import code.name.monkey.retromusic.extensions.accentColor
|
import code.name.monkey.retromusic.extensions.accentColor
|
||||||
import code.name.monkey.retromusic.extensions.applyColor
|
|
||||||
import code.name.monkey.retromusic.extensions.show
|
import code.name.monkey.retromusic.extensions.show
|
||||||
import code.name.monkey.retromusic.extensions.textColorPrimary
|
import code.name.monkey.retromusic.extensions.textColorPrimary
|
||||||
import code.name.monkey.retromusic.extensions.textColorSecondary
|
import code.name.monkey.retromusic.extensions.textColorSecondary
|
||||||
import code.name.monkey.retromusic.fragments.base.AbsMusicServiceFragment
|
import code.name.monkey.retromusic.fragments.base.AbsMusicServiceFragment
|
||||||
|
import code.name.monkey.retromusic.glide.GlideApp
|
||||||
|
import code.name.monkey.retromusic.glide.RetroGlideExtension
|
||||||
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
||||||
import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper
|
import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper
|
||||||
import code.name.monkey.retromusic.helper.PlayPauseButtonOnClickHandler
|
import code.name.monkey.retromusic.helper.PlayPauseButtonOnClickHandler
|
||||||
import code.name.monkey.retromusic.util.PreferenceUtil
|
import code.name.monkey.retromusic.util.PreferenceUtil
|
||||||
import code.name.monkey.retromusic.util.RetroUtil
|
import code.name.monkey.retromusic.util.RetroUtil
|
||||||
import kotlin.math.abs
|
import kotlin.math.abs
|
||||||
import kotlinx.android.synthetic.main.fragment_mini_player.*
|
|
||||||
|
|
||||||
open class MiniPlayerFragment : AbsMusicServiceFragment(R.layout.fragment_mini_player),
|
open class MiniPlayerFragment : AbsMusicServiceFragment(R.layout.fragment_mini_player),
|
||||||
MusicProgressViewUpdateHelper.Callback, View.OnClickListener {
|
MusicProgressViewUpdateHelper.Callback, View.OnClickListener {
|
||||||
|
|
||||||
|
private var _binding: FragmentMiniPlayerBinding? = null
|
||||||
|
private val binding get() = _binding!!
|
||||||
private lateinit var progressViewUpdateHelper: MusicProgressViewUpdateHelper
|
private lateinit var progressViewUpdateHelper: MusicProgressViewUpdateHelper
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
@ -59,38 +62,38 @@ open class MiniPlayerFragment : AbsMusicServiceFragment(R.layout.fragment_mini_p
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
_binding = FragmentMiniPlayerBinding.bind(view)
|
||||||
view.setOnTouchListener(FlingPlayBackController(requireContext()))
|
view.setOnTouchListener(FlingPlayBackController(requireContext()))
|
||||||
setUpMiniPlayer()
|
setUpMiniPlayer()
|
||||||
|
|
||||||
if (RetroUtil.isTablet()) {
|
if (RetroUtil.isTablet()) {
|
||||||
actionNext.show()
|
binding.actionNext.show()
|
||||||
actionPrevious.show()
|
binding.actionPrevious.show()
|
||||||
actionNext?.show()
|
|
||||||
actionPrevious?.show()
|
|
||||||
} else {
|
} else {
|
||||||
actionNext.visibility = if (PreferenceUtil.isExtraControls) View.VISIBLE else View.GONE
|
binding.actionNext.visibility =
|
||||||
actionPrevious.visibility =
|
if (PreferenceUtil.isExtraControls) View.VISIBLE else View.GONE
|
||||||
|
binding.actionPrevious.visibility =
|
||||||
if (PreferenceUtil.isExtraControls) View.VISIBLE else View.GONE
|
if (PreferenceUtil.isExtraControls) View.VISIBLE else View.GONE
|
||||||
}
|
}
|
||||||
actionNext.setOnClickListener(this)
|
binding.actionNext.setOnClickListener(this)
|
||||||
actionPrevious.setOnClickListener(this)
|
binding.actionPrevious.setOnClickListener(this)
|
||||||
actionNext?.setOnClickListener(this)
|
|
||||||
actionPrevious?.setOnClickListener(this)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setUpMiniPlayer() {
|
private fun setUpMiniPlayer() {
|
||||||
setUpPlayPauseButton()
|
setUpPlayPauseButton()
|
||||||
progressBar.accentColor()
|
binding.progressBar.accentColor()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setUpPlayPauseButton() {
|
private fun setUpPlayPauseButton() {
|
||||||
miniPlayerPlayPauseButton.setOnClickListener(PlayPauseButtonOnClickHandler())
|
binding.miniPlayerPlayPauseButton.setOnClickListener(PlayPauseButtonOnClickHandler())
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateSongTitle() {
|
private fun updateSongTitle() {
|
||||||
val builder = SpannableStringBuilder()
|
|
||||||
|
|
||||||
val song = MusicPlayerRemote.currentSong
|
val song = MusicPlayerRemote.currentSong
|
||||||
|
|
||||||
|
val builder = SpannableStringBuilder()
|
||||||
|
|
||||||
val title = SpannableString(song.title)
|
val title = SpannableString(song.title)
|
||||||
title.setSpan(ForegroundColorSpan(textColorPrimary()), 0, title.length, 0)
|
title.setSpan(ForegroundColorSpan(textColorPrimary()), 0, title.length, 0)
|
||||||
|
|
||||||
|
@ -99,17 +102,34 @@ open class MiniPlayerFragment : AbsMusicServiceFragment(R.layout.fragment_mini_p
|
||||||
|
|
||||||
builder.append(title).append(" • ").append(text)
|
builder.append(title).append(" • ").append(text)
|
||||||
|
|
||||||
miniPlayerTitle.isSelected = true
|
binding.miniPlayerTitle.isSelected = true
|
||||||
miniPlayerTitle.text = builder
|
binding.miniPlayerTitle.text = builder
|
||||||
|
|
||||||
|
// binding.title.isSelected = true
|
||||||
|
// binding.title.text = song.title
|
||||||
|
// binding.text.isSelected = true
|
||||||
|
// binding.text.text = song.artistName
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun updateSongCover() {
|
||||||
|
// val song = MusicPlayerRemote.currentSong
|
||||||
|
// GlideApp.with(requireContext())
|
||||||
|
// .asBitmap()
|
||||||
|
// .songCoverOptions(song)
|
||||||
|
// .transition(RetroGlideExtension.getDefaultTransition())
|
||||||
|
// .load(RetroGlideExtension.getSongModel(song))
|
||||||
|
// .into(binding.image)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onServiceConnected() {
|
override fun onServiceConnected() {
|
||||||
updateSongTitle()
|
updateSongTitle()
|
||||||
|
updateSongCover()
|
||||||
updatePlayPauseDrawableState()
|
updatePlayPauseDrawableState()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onPlayingMetaChanged() {
|
override fun onPlayingMetaChanged() {
|
||||||
updateSongTitle()
|
updateSongTitle()
|
||||||
|
updateSongCover()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onPlayStateChanged() {
|
override fun onPlayStateChanged() {
|
||||||
|
@ -117,8 +137,8 @@ open class MiniPlayerFragment : AbsMusicServiceFragment(R.layout.fragment_mini_p
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onUpdateProgressViews(progress: Int, total: Int) {
|
override fun onUpdateProgressViews(progress: Int, total: Int) {
|
||||||
progressBar.max = total
|
binding.progressBar.max = total
|
||||||
val animator = ObjectAnimator.ofInt(progressBar, "progress", progress)
|
val animator = ObjectAnimator.ofInt(binding.progressBar, "progress", progress)
|
||||||
animator.duration = 1000
|
animator.duration = 1000
|
||||||
animator.interpolator = DecelerateInterpolator()
|
animator.interpolator = DecelerateInterpolator()
|
||||||
animator.start()
|
animator.start()
|
||||||
|
@ -136,16 +156,12 @@ open class MiniPlayerFragment : AbsMusicServiceFragment(R.layout.fragment_mini_p
|
||||||
|
|
||||||
protected fun updatePlayPauseDrawableState() {
|
protected fun updatePlayPauseDrawableState() {
|
||||||
if (MusicPlayerRemote.isPlaying) {
|
if (MusicPlayerRemote.isPlaying) {
|
||||||
miniPlayerPlayPauseButton.setImageResource(R.drawable.ic_pause)
|
binding.miniPlayerPlayPauseButton.setImageResource(R.drawable.ic_pause)
|
||||||
} else {
|
} else {
|
||||||
miniPlayerPlayPauseButton.setImageResource(R.drawable.ic_play_arrow)
|
binding.miniPlayerPlayPauseButton.setImageResource(R.drawable.ic_play_arrow)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun updateProgressBar(paletteColor: Int) {
|
|
||||||
progressBar.applyColor(paletteColor)
|
|
||||||
}
|
|
||||||
|
|
||||||
class FlingPlayBackController(context: Context) : View.OnTouchListener {
|
class FlingPlayBackController(context: Context) : View.OnTouchListener {
|
||||||
|
|
||||||
private var flingPlayBackController: GestureDetector
|
private var flingPlayBackController: GestureDetector
|
||||||
|
@ -178,4 +194,9 @@ open class MiniPlayerFragment : AbsMusicServiceFragment(R.layout.fragment_mini_p
|
||||||
return flingPlayBackController.onTouchEvent(event)
|
return flingPlayBackController.onTouchEvent(event)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onDestroyView() {
|
||||||
|
super.onDestroyView()
|
||||||
|
_binding = null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,34 +26,38 @@ import android.widget.SeekBar
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import code.name.monkey.appthemehelper.ThemeStore
|
import code.name.monkey.appthemehelper.ThemeStore
|
||||||
import code.name.monkey.retromusic.R
|
import code.name.monkey.retromusic.R
|
||||||
|
import code.name.monkey.retromusic.databinding.FragmentVolumeBinding
|
||||||
import code.name.monkey.retromusic.extensions.applyColor
|
import code.name.monkey.retromusic.extensions.applyColor
|
||||||
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
||||||
import code.name.monkey.retromusic.util.PreferenceUtil
|
import code.name.monkey.retromusic.util.PreferenceUtil
|
||||||
import code.name.monkey.retromusic.volume.AudioVolumeObserver
|
import code.name.monkey.retromusic.volume.AudioVolumeObserver
|
||||||
import code.name.monkey.retromusic.volume.OnAudioVolumeChangedListener
|
import code.name.monkey.retromusic.volume.OnAudioVolumeChangedListener
|
||||||
import kotlinx.android.synthetic.main.fragment_volume.*
|
|
||||||
|
|
||||||
class VolumeFragment : Fragment(), SeekBar.OnSeekBarChangeListener, OnAudioVolumeChangedListener,
|
class VolumeFragment : Fragment(), SeekBar.OnSeekBarChangeListener, OnAudioVolumeChangedListener,
|
||||||
View.OnClickListener {
|
View.OnClickListener {
|
||||||
|
|
||||||
|
private var _binding: FragmentVolumeBinding? = null
|
||||||
|
private val binding get() = _binding!!
|
||||||
|
|
||||||
private var audioVolumeObserver: AudioVolumeObserver? = null
|
private var audioVolumeObserver: AudioVolumeObserver? = null
|
||||||
|
|
||||||
private val audioManager: AudioManager?
|
private val audioManager: AudioManager
|
||||||
get() = requireContext().getSystemService(Context.AUDIO_SERVICE) as AudioManager
|
get() = requireContext().getSystemService(Context.AUDIO_SERVICE) as AudioManager
|
||||||
|
|
||||||
override fun onCreateView(
|
override fun onCreateView(
|
||||||
inflater: LayoutInflater,
|
inflater: LayoutInflater,
|
||||||
container: ViewGroup?,
|
container: ViewGroup?,
|
||||||
savedInstanceState: Bundle?
|
savedInstanceState: Bundle?
|
||||||
): View? {
|
): View {
|
||||||
return inflater.inflate(R.layout.fragment_volume, container, false)
|
_binding = FragmentVolumeBinding.inflate(inflater, container, false)
|
||||||
|
return binding.root
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
setTintable(ThemeStore.accentColor(requireContext()))
|
setTintable(ThemeStore.accentColor(requireContext()))
|
||||||
volumeDown.setOnClickListener(this)
|
binding.volumeDown.setOnClickListener(this)
|
||||||
volumeUp.setOnClickListener(this)
|
binding.volumeUp.setOnClickListener(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onResume() {
|
override fun onResume() {
|
||||||
|
@ -64,33 +68,30 @@ class VolumeFragment : Fragment(), SeekBar.OnSeekBarChangeListener, OnAudioVolum
|
||||||
audioVolumeObserver?.register(AudioManager.STREAM_MUSIC, this)
|
audioVolumeObserver?.register(AudioManager.STREAM_MUSIC, this)
|
||||||
|
|
||||||
val audioManager = audioManager
|
val audioManager = audioManager
|
||||||
if (audioManager != null) {
|
binding.volumeSeekBar.max = audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC)
|
||||||
volumeSeekBar.max = audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC)
|
binding.volumeSeekBar.progress = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC)
|
||||||
volumeSeekBar.progress = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC)
|
binding.volumeSeekBar.setOnSeekBarChangeListener(this)
|
||||||
}
|
|
||||||
volumeSeekBar.setOnSeekBarChangeListener(this)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onAudioVolumeChanged(currentVolume: Int, maxVolume: Int) {
|
override fun onAudioVolumeChanged(currentVolume: Int, maxVolume: Int) {
|
||||||
if (volumeSeekBar == null) {
|
if (_binding != null) {
|
||||||
return
|
binding.volumeSeekBar.max = maxVolume
|
||||||
|
binding.volumeSeekBar.progress = currentVolume
|
||||||
|
binding.volumeDown.setImageResource(if (currentVolume == 0) R.drawable.ic_volume_off else R.drawable.ic_volume_down)
|
||||||
}
|
}
|
||||||
|
|
||||||
volumeSeekBar.max = maxVolume
|
|
||||||
volumeSeekBar.progress = currentVolume
|
|
||||||
volumeDown.setImageResource(if (currentVolume == 0) R.drawable.ic_volume_off else R.drawable.ic_volume_down)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDestroyView() {
|
override fun onDestroyView() {
|
||||||
super.onDestroyView()
|
super.onDestroyView()
|
||||||
audioVolumeObserver?.unregister()
|
audioVolumeObserver?.unregister()
|
||||||
|
_binding = null
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onProgressChanged(seekBar: SeekBar, i: Int, b: Boolean) {
|
override fun onProgressChanged(seekBar: SeekBar, i: Int, b: Boolean) {
|
||||||
val audioManager = audioManager
|
val audioManager = audioManager
|
||||||
audioManager?.setStreamVolume(AudioManager.STREAM_MUSIC, i, 0)
|
audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, i, 0)
|
||||||
setPauseWhenZeroVolume(i < 1)
|
setPauseWhenZeroVolume(i < 1)
|
||||||
volumeDown?.setImageResource(if (i == 0) R.drawable.ic_volume_off else R.drawable.ic_volume_down)
|
binding.volumeDown.setImageResource(if (i == 0) R.drawable.ic_volume_off else R.drawable.ic_volume_down)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onStartTrackingTouch(seekBar: SeekBar) {
|
override fun onStartTrackingTouch(seekBar: SeekBar) {
|
||||||
|
@ -102,10 +103,10 @@ class VolumeFragment : Fragment(), SeekBar.OnSeekBarChangeListener, OnAudioVolum
|
||||||
override fun onClick(view: View) {
|
override fun onClick(view: View) {
|
||||||
val audioManager = audioManager
|
val audioManager = audioManager
|
||||||
when (view.id) {
|
when (view.id) {
|
||||||
R.id.volumeDown -> audioManager?.adjustStreamVolume(
|
R.id.volumeDown -> audioManager.adjustStreamVolume(
|
||||||
AudioManager.STREAM_MUSIC, AudioManager.ADJUST_LOWER, 0
|
AudioManager.STREAM_MUSIC, AudioManager.ADJUST_LOWER, 0
|
||||||
)
|
)
|
||||||
R.id.volumeUp -> audioManager?.adjustStreamVolume(
|
R.id.volumeUp -> audioManager.adjustStreamVolume(
|
||||||
AudioManager.STREAM_MUSIC, AudioManager.ADJUST_RAISE, 0
|
AudioManager.STREAM_MUSIC, AudioManager.ADJUST_RAISE, 0
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -113,13 +114,13 @@ class VolumeFragment : Fragment(), SeekBar.OnSeekBarChangeListener, OnAudioVolum
|
||||||
|
|
||||||
fun tintWhiteColor() {
|
fun tintWhiteColor() {
|
||||||
val color = Color.WHITE
|
val color = Color.WHITE
|
||||||
volumeDown.setColorFilter(color, PorterDuff.Mode.SRC_IN)
|
binding.volumeDown.setColorFilter(color, PorterDuff.Mode.SRC_IN)
|
||||||
volumeUp.setColorFilter(color, PorterDuff.Mode.SRC_IN)
|
binding.volumeUp.setColorFilter(color, PorterDuff.Mode.SRC_IN)
|
||||||
volumeSeekBar.applyColor(color)
|
binding.volumeSeekBar.applyColor(color)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setTintable(color: Int) {
|
fun setTintable(color: Int) {
|
||||||
volumeSeekBar.applyColor(color)
|
binding.volumeSeekBar.applyColor(color)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setPauseWhenZeroVolume(pauseWhenZeroVolume: Boolean) {
|
private fun setPauseWhenZeroVolume(pauseWhenZeroVolume: Boolean) {
|
||||||
|
@ -129,10 +130,10 @@ class VolumeFragment : Fragment(), SeekBar.OnSeekBarChangeListener, OnAudioVolum
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setTintableColor(color: Int) {
|
fun setTintableColor(color: Int) {
|
||||||
volumeDown.setColorFilter(color, PorterDuff.Mode.SRC_IN)
|
binding.volumeDown.setColorFilter(color, PorterDuff.Mode.SRC_IN)
|
||||||
volumeUp.setColorFilter(color, PorterDuff.Mode.SRC_IN)
|
binding.volumeUp.setColorFilter(color, PorterDuff.Mode.SRC_IN)
|
||||||
// TintHelper.setTint(volumeSeekBar, color, false)
|
// TintHelper.setTint(volumeSeekBar, color, false)
|
||||||
volumeSeekBar.applyColor(color)
|
binding.volumeSeekBar.applyColor(color)
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
|
@ -27,20 +27,20 @@ import code.name.monkey.retromusic.App
|
||||||
import code.name.monkey.retromusic.Constants
|
import code.name.monkey.retromusic.Constants
|
||||||
import code.name.monkey.retromusic.R
|
import code.name.monkey.retromusic.R
|
||||||
import code.name.monkey.retromusic.adapter.ContributorAdapter
|
import code.name.monkey.retromusic.adapter.ContributorAdapter
|
||||||
|
import code.name.monkey.retromusic.databinding.FragmentAboutBinding
|
||||||
import code.name.monkey.retromusic.fragments.LibraryViewModel
|
import code.name.monkey.retromusic.fragments.LibraryViewModel
|
||||||
import code.name.monkey.retromusic.util.NavigationUtil
|
import code.name.monkey.retromusic.util.NavigationUtil
|
||||||
import kotlinx.android.synthetic.main.card_credit.*
|
|
||||||
import kotlinx.android.synthetic.main.card_other.*
|
|
||||||
import kotlinx.android.synthetic.main.card_retro_info.*
|
|
||||||
import kotlinx.android.synthetic.main.card_social.*
|
|
||||||
import org.koin.androidx.viewmodel.ext.android.sharedViewModel
|
import org.koin.androidx.viewmodel.ext.android.sharedViewModel
|
||||||
|
|
||||||
class AboutFragment : Fragment(R.layout.fragment_about), View.OnClickListener {
|
class AboutFragment : Fragment(R.layout.fragment_about), View.OnClickListener {
|
||||||
|
private var _binding: FragmentAboutBinding? = null
|
||||||
|
private val binding get() = _binding!!
|
||||||
private val libraryViewModel by sharedViewModel<LibraryViewModel>()
|
private val libraryViewModel by sharedViewModel<LibraryViewModel>()
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
version.setSummary(getAppVersion())
|
_binding = FragmentAboutBinding.bind(view)
|
||||||
|
binding.aboutContent.cardOther.version.setSummary(getAppVersion())
|
||||||
setUpView()
|
setUpView()
|
||||||
loadContributors()
|
loadContributors()
|
||||||
}
|
}
|
||||||
|
@ -53,19 +53,20 @@ class AboutFragment : Fragment(R.layout.fragment_about), View.OnClickListener {
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setUpView() {
|
private fun setUpView() {
|
||||||
appGithub.setOnClickListener(this)
|
binding.aboutContent.cardRetroInfo.appGithub.setOnClickListener(this)
|
||||||
faqLink.setOnClickListener(this)
|
binding.aboutContent.cardRetroInfo.faqLink.setOnClickListener(this)
|
||||||
telegramLink.setOnClickListener(this)
|
binding.aboutContent.cardSocial.telegramLink.setOnClickListener(this)
|
||||||
appRate.setOnClickListener(this)
|
binding.aboutContent.cardRetroInfo.appRate.setOnClickListener(this)
|
||||||
appTranslation.setOnClickListener(this)
|
binding.aboutContent.cardRetroInfo.appTranslation.setOnClickListener(this)
|
||||||
appShare.setOnClickListener(this)
|
binding.aboutContent.cardRetroInfo.appShare.setOnClickListener(this)
|
||||||
donateLink.setOnClickListener(this)
|
binding.aboutContent.cardRetroInfo.donateLink.setOnClickListener(this)
|
||||||
instagramLink.setOnClickListener(this)
|
binding.aboutContent.cardSocial.instagramLink.setOnClickListener(this)
|
||||||
twitterLink.setOnClickListener(this)
|
binding.aboutContent.cardSocial.twitterLink.setOnClickListener(this)
|
||||||
changelog.setOnClickListener(this)
|
binding.aboutContent.cardOther.changelog.setOnClickListener(this)
|
||||||
openSource.setOnClickListener(this)
|
binding.aboutContent.cardOther.openSource.setOnClickListener(this)
|
||||||
pinterestLink.setOnClickListener(this)
|
binding.aboutContent.cardSocial.pinterestLink.setOnClickListener(this)
|
||||||
bugReportLink.setOnClickListener(this)
|
binding.aboutContent.cardRetroInfo.bugReportLink.setOnClickListener(this)
|
||||||
|
binding.aboutContent.cardSocial.websiteLink.setOnClickListener(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onClick(view: View) {
|
override fun onClick(view: View) {
|
||||||
|
@ -80,9 +81,10 @@ class AboutFragment : Fragment(R.layout.fragment_about), View.OnClickListener {
|
||||||
R.id.donateLink -> NavigationUtil.goToSupportDevelopment(requireActivity())
|
R.id.donateLink -> NavigationUtil.goToSupportDevelopment(requireActivity())
|
||||||
R.id.instagramLink -> openUrl(Constants.APP_INSTAGRAM_LINK)
|
R.id.instagramLink -> openUrl(Constants.APP_INSTAGRAM_LINK)
|
||||||
R.id.twitterLink -> openUrl(Constants.APP_TWITTER_LINK)
|
R.id.twitterLink -> openUrl(Constants.APP_TWITTER_LINK)
|
||||||
R.id.changelog -> openUrl(Constants.TELEGRAM_CHANGE_LOG)
|
R.id.changelog -> NavigationUtil.gotoWhatNews(requireActivity())
|
||||||
R.id.openSource -> NavigationUtil.goToOpenSource(requireActivity())
|
R.id.openSource -> NavigationUtil.goToOpenSource(requireActivity())
|
||||||
R.id.bugReportLink -> NavigationUtil.bugReport(requireActivity())
|
R.id.bugReportLink -> NavigationUtil.bugReport(requireActivity())
|
||||||
|
R.id.websiteLink -> openUrl(Constants.WEBSITE)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -99,7 +101,7 @@ class AboutFragment : Fragment(R.layout.fragment_about), View.OnClickListener {
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun shareApp() {
|
private fun shareApp() {
|
||||||
ShareCompat.IntentBuilder.from(requireActivity()).setType("text/plain")
|
ShareCompat.IntentBuilder(requireActivity()).setType("text/plain")
|
||||||
.setChooserTitle(R.string.share_app)
|
.setChooserTitle(R.string.share_app)
|
||||||
.setText(String.format(getString(R.string.app_share), requireActivity().packageName))
|
.setText(String.format(getString(R.string.app_share), requireActivity().packageName))
|
||||||
.startChooser()
|
.startChooser()
|
||||||
|
@ -107,7 +109,7 @@ class AboutFragment : Fragment(R.layout.fragment_about), View.OnClickListener {
|
||||||
|
|
||||||
private fun loadContributors() {
|
private fun loadContributors() {
|
||||||
val contributorAdapter = ContributorAdapter(emptyList())
|
val contributorAdapter = ContributorAdapter(emptyList())
|
||||||
recyclerView.apply {
|
binding.aboutContent.cardCredit.recyclerView.apply {
|
||||||
layoutManager = LinearLayoutManager(requireContext())
|
layoutManager = LinearLayoutManager(requireContext())
|
||||||
itemAnimator = DefaultItemAnimator()
|
itemAnimator = DefaultItemAnimator()
|
||||||
adapter = contributorAdapter
|
adapter = contributorAdapter
|
||||||
|
@ -116,4 +118,9 @@ class AboutFragment : Fragment(R.layout.fragment_about), View.OnClickListener {
|
||||||
contributorAdapter.swapData(contributors)
|
contributorAdapter.swapData(contributors)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onDestroyView() {
|
||||||
|
super.onDestroyView()
|
||||||
|
_binding = null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,11 +19,12 @@ import android.content.Intent
|
||||||
import android.graphics.Color
|
import android.graphics.Color
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.*
|
import android.view.*
|
||||||
|
import androidx.activity.addCallback
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.core.os.bundleOf
|
import androidx.core.os.bundleOf
|
||||||
import androidx.core.text.HtmlCompat
|
import androidx.core.text.HtmlCompat
|
||||||
import androidx.core.view.ViewCompat
|
import androidx.core.view.ViewCompat
|
||||||
import androidx.lifecycle.Observer
|
import androidx.core.view.doOnPreDraw
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.navigation.fragment.FragmentNavigatorExtras
|
import androidx.navigation.fragment.FragmentNavigatorExtras
|
||||||
import androidx.navigation.fragment.findNavController
|
import androidx.navigation.fragment.findNavController
|
||||||
|
@ -35,17 +36,19 @@ import code.name.monkey.appthemehelper.common.ATHToolbarActivity.getToolbarBackg
|
||||||
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
|
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
|
||||||
import code.name.monkey.retromusic.EXTRA_ALBUM_ID
|
import code.name.monkey.retromusic.EXTRA_ALBUM_ID
|
||||||
import code.name.monkey.retromusic.EXTRA_ARTIST_ID
|
import code.name.monkey.retromusic.EXTRA_ARTIST_ID
|
||||||
|
import code.name.monkey.retromusic.EXTRA_ARTIST_NAME
|
||||||
import code.name.monkey.retromusic.R
|
import code.name.monkey.retromusic.R
|
||||||
import code.name.monkey.retromusic.activities.tageditor.AbsTagEditorActivity
|
import code.name.monkey.retromusic.activities.tageditor.AbsTagEditorActivity
|
||||||
import code.name.monkey.retromusic.activities.tageditor.AlbumTagEditorActivity
|
import code.name.monkey.retromusic.activities.tageditor.AlbumTagEditorActivity
|
||||||
import code.name.monkey.retromusic.adapter.album.HorizontalAlbumAdapter
|
import code.name.monkey.retromusic.adapter.album.HorizontalAlbumAdapter
|
||||||
import code.name.monkey.retromusic.adapter.song.SimpleSongAdapter
|
import code.name.monkey.retromusic.adapter.song.SimpleSongAdapter
|
||||||
|
import code.name.monkey.retromusic.databinding.FragmentAlbumDetailsBinding
|
||||||
import code.name.monkey.retromusic.dialogs.AddToPlaylistDialog
|
import code.name.monkey.retromusic.dialogs.AddToPlaylistDialog
|
||||||
import code.name.monkey.retromusic.dialogs.DeleteSongsDialog
|
import code.name.monkey.retromusic.dialogs.DeleteSongsDialog
|
||||||
import code.name.monkey.retromusic.extensions.*
|
import code.name.monkey.retromusic.extensions.*
|
||||||
import code.name.monkey.retromusic.fragments.base.AbsMainActivityFragment
|
import code.name.monkey.retromusic.fragments.base.AbsMainActivityFragment
|
||||||
import code.name.monkey.retromusic.glide.AlbumGlideRequest
|
import code.name.monkey.retromusic.glide.GlideApp
|
||||||
import code.name.monkey.retromusic.glide.ArtistGlideRequest
|
import code.name.monkey.retromusic.glide.RetroGlideExtension
|
||||||
import code.name.monkey.retromusic.glide.RetroMusicColoredTarget
|
import code.name.monkey.retromusic.glide.RetroMusicColoredTarget
|
||||||
import code.name.monkey.retromusic.glide.SingleColorTarget
|
import code.name.monkey.retromusic.glide.SingleColorTarget
|
||||||
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
||||||
|
@ -54,6 +57,7 @@ import code.name.monkey.retromusic.helper.SortOrder.AlbumSongSortOrder.Companion
|
||||||
import code.name.monkey.retromusic.helper.SortOrder.AlbumSongSortOrder.Companion.SONG_TRACK_LIST
|
import code.name.monkey.retromusic.helper.SortOrder.AlbumSongSortOrder.Companion.SONG_TRACK_LIST
|
||||||
import code.name.monkey.retromusic.helper.SortOrder.AlbumSongSortOrder.Companion.SONG_Z_A
|
import code.name.monkey.retromusic.helper.SortOrder.AlbumSongSortOrder.Companion.SONG_Z_A
|
||||||
import code.name.monkey.retromusic.interfaces.IAlbumClickListener
|
import code.name.monkey.retromusic.interfaces.IAlbumClickListener
|
||||||
|
import code.name.monkey.retromusic.interfaces.ICabHolder
|
||||||
import code.name.monkey.retromusic.model.Album
|
import code.name.monkey.retromusic.model.Album
|
||||||
import code.name.monkey.retromusic.model.Artist
|
import code.name.monkey.retromusic.model.Artist
|
||||||
import code.name.monkey.retromusic.network.Result
|
import code.name.monkey.retromusic.network.Result
|
||||||
|
@ -61,14 +65,12 @@ import code.name.monkey.retromusic.network.model.LastFmAlbum
|
||||||
import code.name.monkey.retromusic.repository.RealRepository
|
import code.name.monkey.retromusic.repository.RealRepository
|
||||||
import code.name.monkey.retromusic.util.MusicUtil
|
import code.name.monkey.retromusic.util.MusicUtil
|
||||||
import code.name.monkey.retromusic.util.PreferenceUtil
|
import code.name.monkey.retromusic.util.PreferenceUtil
|
||||||
|
import code.name.monkey.retromusic.util.RetroColorUtil
|
||||||
import code.name.monkey.retromusic.util.RetroUtil
|
import code.name.monkey.retromusic.util.RetroUtil
|
||||||
import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
|
import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
|
||||||
import com.bumptech.glide.Glide
|
import com.afollestad.materialcab.MaterialCab
|
||||||
import com.google.android.material.transition.MaterialArcMotion
|
import com.google.android.material.transition.MaterialArcMotion
|
||||||
import com.google.android.material.transition.MaterialContainerTransform
|
import com.google.android.material.transition.MaterialContainerTransform
|
||||||
import com.google.android.material.transition.MaterialElevationScale
|
|
||||||
import kotlinx.android.synthetic.main.fragment_album_content.*
|
|
||||||
import kotlinx.android.synthetic.main.fragment_album_details.*
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
@ -78,7 +80,10 @@ import org.koin.core.parameter.parametersOf
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
class AlbumDetailsFragment : AbsMainActivityFragment(R.layout.fragment_album_details),
|
class AlbumDetailsFragment : AbsMainActivityFragment(R.layout.fragment_album_details),
|
||||||
IAlbumClickListener {
|
IAlbumClickListener, ICabHolder {
|
||||||
|
|
||||||
|
private var _binding: FragmentAlbumDetailsBinding? = null
|
||||||
|
private val binding get() = _binding!!
|
||||||
|
|
||||||
private val arguments by navArgs<AlbumDetailsFragmentArgs>()
|
private val arguments by navArgs<AlbumDetailsFragmentArgs>()
|
||||||
private val detailsViewModel by viewModel<AlbumDetailsViewModel> {
|
private val detailsViewModel by viewModel<AlbumDetailsViewModel> {
|
||||||
|
@ -87,6 +92,7 @@ class AlbumDetailsFragment : AbsMainActivityFragment(R.layout.fragment_album_det
|
||||||
|
|
||||||
private lateinit var simpleSongAdapter: SimpleSongAdapter
|
private lateinit var simpleSongAdapter: SimpleSongAdapter
|
||||||
private lateinit var album: Album
|
private lateinit var album: Album
|
||||||
|
private var albumArtistExists = false
|
||||||
|
|
||||||
private val savedSortOrder: String
|
private val savedSortOrder: String
|
||||||
get() = PreferenceUtil.albumDetailSongSortOrder
|
get() = PreferenceUtil.albumDetailSongSortOrder
|
||||||
|
@ -104,51 +110,70 @@ class AlbumDetailsFragment : AbsMainActivityFragment(R.layout.fragment_album_det
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
_binding = FragmentAlbumDetailsBinding.bind(view)
|
||||||
setHasOptionsMenu(true)
|
setHasOptionsMenu(true)
|
||||||
mainActivity.setBottomBarVisibility(false)
|
|
||||||
mainActivity.addMusicServiceEventListener(detailsViewModel)
|
mainActivity.addMusicServiceEventListener(detailsViewModel)
|
||||||
mainActivity.setSupportActionBar(toolbar)
|
mainActivity.setSupportActionBar(binding.toolbar)
|
||||||
|
|
||||||
toolbar.title = " "
|
binding.toolbar.title = " "
|
||||||
ViewCompat.setTransitionName(albumCoverContainer, "album")
|
ViewCompat.setTransitionName(binding.albumCoverContainer, arguments.extraAlbumId.toString())
|
||||||
postponeEnterTransition()
|
postponeEnterTransition()
|
||||||
detailsViewModel.getAlbum().observe(viewLifecycleOwner, Observer {
|
detailsViewModel.getAlbum().observe(viewLifecycleOwner, {
|
||||||
startPostponedEnterTransition()
|
requireView().doOnPreDraw {
|
||||||
|
startPostponedEnterTransition()
|
||||||
|
}
|
||||||
|
albumArtistExists = !it.albumArtist.isNullOrEmpty()
|
||||||
showAlbum(it)
|
showAlbum(it)
|
||||||
|
if (albumArtistExists) {
|
||||||
|
ViewCompat.setTransitionName(binding.artistImage, album.albumArtist)
|
||||||
|
} else {
|
||||||
|
ViewCompat.setTransitionName(binding.artistImage, album.artistId.toString())
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
setupRecyclerView()
|
setupRecyclerView()
|
||||||
artistImage.setOnClickListener { artistView ->
|
binding.artistImage.setOnClickListener { artistView ->
|
||||||
ViewCompat.setTransitionName(artistView, "artist")
|
if (albumArtistExists) {
|
||||||
exitTransition = MaterialElevationScale(false).apply {
|
findActivityNavController(R.id.fragment_container)
|
||||||
duration = 300L
|
.navigate(
|
||||||
|
R.id.albumArtistDetailsFragment,
|
||||||
|
bundleOf(EXTRA_ARTIST_NAME to album.albumArtist),
|
||||||
|
null,
|
||||||
|
FragmentNavigatorExtras(artistView to album.albumArtist.toString())
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
findActivityNavController(R.id.fragment_container)
|
||||||
|
.navigate(
|
||||||
|
R.id.artistDetailsFragment,
|
||||||
|
bundleOf(EXTRA_ARTIST_ID to album.artistId),
|
||||||
|
null,
|
||||||
|
FragmentNavigatorExtras(artistView to album.artistId.toString())
|
||||||
|
)
|
||||||
}
|
}
|
||||||
reenterTransition = MaterialElevationScale(true).apply {
|
|
||||||
duration = 300L
|
|
||||||
}
|
|
||||||
findActivityNavController(R.id.fragment_container)
|
|
||||||
.navigate(
|
|
||||||
R.id.artistDetailsFragment,
|
|
||||||
bundleOf(EXTRA_ARTIST_ID to album.artistId),
|
|
||||||
null,
|
|
||||||
FragmentNavigatorExtras(artistView to "artist")
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
playAction.setOnClickListener {
|
binding.fragmentAlbumContent.playAction.setOnClickListener {
|
||||||
MusicPlayerRemote.openQueue(album.songs, 0, true)
|
MusicPlayerRemote.openQueue(album.songs, 0, true)
|
||||||
}
|
}
|
||||||
shuffleAction.setOnClickListener {
|
binding.fragmentAlbumContent.shuffleAction.setOnClickListener {
|
||||||
MusicPlayerRemote.openAndShuffleQueue(
|
MusicPlayerRemote.openAndShuffleQueue(
|
||||||
album.songs,
|
album.songs,
|
||||||
true
|
true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
aboutAlbumText.setOnClickListener {
|
binding.fragmentAlbumContent.aboutAlbumText.setOnClickListener {
|
||||||
if (aboutAlbumText.maxLines == 4) {
|
if (binding.fragmentAlbumContent.aboutAlbumText.maxLines == 4) {
|
||||||
aboutAlbumText.maxLines = Integer.MAX_VALUE
|
binding.fragmentAlbumContent.aboutAlbumText.maxLines = Integer.MAX_VALUE
|
||||||
} else {
|
} else {
|
||||||
aboutAlbumText.maxLines = 4
|
binding.fragmentAlbumContent.aboutAlbumText.maxLines = 4
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner) {
|
||||||
|
if (!handleBackPress()) {
|
||||||
|
remove()
|
||||||
|
requireActivity().onBackPressed()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -163,9 +188,9 @@ class AlbumDetailsFragment : AbsMainActivityFragment(R.layout.fragment_album_det
|
||||||
requireActivity() as AppCompatActivity,
|
requireActivity() as AppCompatActivity,
|
||||||
ArrayList(),
|
ArrayList(),
|
||||||
R.layout.item_song,
|
R.layout.item_song,
|
||||||
null
|
this
|
||||||
)
|
)
|
||||||
recyclerView.apply {
|
binding.fragmentAlbumContent.recyclerView.apply {
|
||||||
layoutManager = LinearLayoutManager(requireContext())
|
layoutManager = LinearLayoutManager(requireContext())
|
||||||
itemAnimator = DefaultItemAnimator()
|
itemAnimator = DefaultItemAnimator()
|
||||||
isNestedScrollingEnabled = false
|
isNestedScrollingEnabled = false
|
||||||
|
@ -179,21 +204,21 @@ class AlbumDetailsFragment : AbsMainActivityFragment(R.layout.fragment_album_det
|
||||||
}
|
}
|
||||||
this.album = album
|
this.album = album
|
||||||
|
|
||||||
albumTitle.text = album.title
|
binding.albumTitle.text = album.title
|
||||||
val songText = resources.getQuantityString(
|
val songText = resources.getQuantityString(
|
||||||
R.plurals.albumSongs,
|
R.plurals.albumSongs,
|
||||||
album.songCount,
|
album.songCount,
|
||||||
album.songCount
|
album.songCount
|
||||||
)
|
)
|
||||||
songTitle.text = songText
|
binding.fragmentAlbumContent.songTitle.text = songText
|
||||||
if (MusicUtil.getYearString(album.year) == "-") {
|
if (MusicUtil.getYearString(album.year) == "-") {
|
||||||
albumText.text = String.format(
|
binding.albumText.text = String.format(
|
||||||
"%s • %s",
|
"%s • %s",
|
||||||
album.artistName,
|
if (albumArtistExists) album.albumArtist else album.artistName,
|
||||||
MusicUtil.getReadableDurationString(MusicUtil.getTotalDuration(album.songs))
|
MusicUtil.getReadableDurationString(MusicUtil.getTotalDuration(album.songs))
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
albumText.text = String.format(
|
binding.albumText.text = String.format(
|
||||||
"%s • %s • %s",
|
"%s • %s • %s",
|
||||||
album.artistName,
|
album.artistName,
|
||||||
MusicUtil.getYearString(album.year),
|
MusicUtil.getYearString(album.year),
|
||||||
|
@ -202,11 +227,18 @@ class AlbumDetailsFragment : AbsMainActivityFragment(R.layout.fragment_album_det
|
||||||
}
|
}
|
||||||
loadAlbumCover(album)
|
loadAlbumCover(album)
|
||||||
simpleSongAdapter.swapDataSet(album.songs)
|
simpleSongAdapter.swapDataSet(album.songs)
|
||||||
detailsViewModel.getArtist(album.artistId).observe(viewLifecycleOwner, Observer {
|
if (albumArtistExists) {
|
||||||
loadArtistImage(it)
|
detailsViewModel.getAlbumArtist(album.albumArtist.toString()).observe(viewLifecycleOwner, {
|
||||||
})
|
loadArtistImage(it)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
detailsViewModel.getArtist(album.artistId).observe(viewLifecycleOwner, {
|
||||||
|
loadArtistImage(it)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
detailsViewModel.getAlbumInfo(album).observe(viewLifecycleOwner, Observer { result ->
|
|
||||||
|
detailsViewModel.getAlbumInfo(album).observe(viewLifecycleOwner, { result ->
|
||||||
when (result) {
|
when (result) {
|
||||||
is Result.Loading -> {
|
is Result.Loading -> {
|
||||||
println("Loading")
|
println("Loading")
|
||||||
|
@ -222,41 +254,44 @@ class AlbumDetailsFragment : AbsMainActivityFragment(R.layout.fragment_album_det
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun moreAlbums(albums: List<Album>) {
|
private fun moreAlbums(albums: List<Album>) {
|
||||||
moreTitle.show()
|
binding.fragmentAlbumContent.moreTitle.show()
|
||||||
moreRecyclerView.show()
|
binding.fragmentAlbumContent.moreRecyclerView.show()
|
||||||
moreTitle.text = String.format(getString(R.string.label_more_from), album.artistName)
|
binding.fragmentAlbumContent.moreTitle.text =
|
||||||
|
String.format(getString(R.string.label_more_from), album.artistName)
|
||||||
|
|
||||||
val albumAdapter =
|
val albumAdapter =
|
||||||
HorizontalAlbumAdapter(requireActivity() as AppCompatActivity, albums, null, this)
|
HorizontalAlbumAdapter(requireActivity() as AppCompatActivity, albums, this, this)
|
||||||
moreRecyclerView.layoutManager = GridLayoutManager(
|
binding.fragmentAlbumContent.moreRecyclerView.layoutManager = GridLayoutManager(
|
||||||
requireContext(),
|
requireContext(),
|
||||||
1,
|
1,
|
||||||
GridLayoutManager.HORIZONTAL,
|
GridLayoutManager.HORIZONTAL,
|
||||||
false
|
false
|
||||||
)
|
)
|
||||||
moreRecyclerView.adapter = albumAdapter
|
binding.fragmentAlbumContent.moreRecyclerView.adapter = albumAdapter
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun aboutAlbum(lastFmAlbum: LastFmAlbum) {
|
private fun aboutAlbum(lastFmAlbum: LastFmAlbum) {
|
||||||
if (lastFmAlbum.album != null) {
|
if (lastFmAlbum.album != null) {
|
||||||
if (lastFmAlbum.album.wiki != null) {
|
if (lastFmAlbum.album.wiki != null) {
|
||||||
aboutAlbumText.show()
|
binding.fragmentAlbumContent.aboutAlbumText.show()
|
||||||
aboutAlbumTitle.show()
|
binding.fragmentAlbumContent.aboutAlbumTitle.show()
|
||||||
aboutAlbumTitle.text =
|
binding.fragmentAlbumContent.aboutAlbumTitle.text =
|
||||||
String.format(getString(R.string.about_album_label), lastFmAlbum.album.name)
|
String.format(getString(R.string.about_album_label), lastFmAlbum.album.name)
|
||||||
aboutAlbumText.text = HtmlCompat.fromHtml(
|
binding.fragmentAlbumContent.aboutAlbumText.text = HtmlCompat.fromHtml(
|
||||||
lastFmAlbum.album.wiki.content,
|
lastFmAlbum.album.wiki.content,
|
||||||
HtmlCompat.FROM_HTML_MODE_LEGACY
|
HtmlCompat.FROM_HTML_MODE_LEGACY
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
if (lastFmAlbum.album.listeners.isNotEmpty()) {
|
if (lastFmAlbum.album.listeners.isNotEmpty()) {
|
||||||
listeners.show()
|
binding.fragmentAlbumContent.listeners.show()
|
||||||
listenersLabel.show()
|
binding.fragmentAlbumContent.listenersLabel.show()
|
||||||
scrobbles.show()
|
binding.fragmentAlbumContent.scrobbles.show()
|
||||||
scrobblesLabel.show()
|
binding.fragmentAlbumContent.scrobblesLabel.show()
|
||||||
|
|
||||||
listeners.text = RetroUtil.formatValue(lastFmAlbum.album.listeners.toFloat())
|
binding.fragmentAlbumContent.listeners.text =
|
||||||
scrobbles.text = RetroUtil.formatValue(lastFmAlbum.album.playcount.toFloat())
|
RetroUtil.formatValue(lastFmAlbum.album.listeners.toFloat())
|
||||||
|
binding.fragmentAlbumContent.scrobbles.text =
|
||||||
|
RetroUtil.formatValue(lastFmAlbum.album.playcount.toFloat())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -265,24 +300,22 @@ class AlbumDetailsFragment : AbsMainActivityFragment(R.layout.fragment_album_det
|
||||||
detailsViewModel.getMoreAlbums(artist).observe(viewLifecycleOwner, {
|
detailsViewModel.getMoreAlbums(artist).observe(viewLifecycleOwner, {
|
||||||
moreAlbums(it)
|
moreAlbums(it)
|
||||||
})
|
})
|
||||||
ArtistGlideRequest.Builder.from(Glide.with(requireContext()), artist)
|
GlideApp.with(requireContext()).asBitmapPalette().artistImageOptions(artist)
|
||||||
.forceDownload(PreferenceUtil.isAllowedToDownloadMetadata())
|
//.forceDownload(PreferenceUtil.isAllowedToDownloadMetadata())
|
||||||
.generatePalette(requireContext())
|
.load(RetroGlideExtension.getArtistModel(artist, PreferenceUtil.isAllowedToDownloadMetadata()))
|
||||||
.build()
|
|
||||||
.dontAnimate()
|
.dontAnimate()
|
||||||
.dontTransform()
|
.dontTransform()
|
||||||
.into(object : RetroMusicColoredTarget(artistImage) {
|
.into(object : RetroMusicColoredTarget(binding.artistImage) {
|
||||||
override fun onColorReady(colors: MediaNotificationProcessor) {
|
override fun onColorReady(colors: MediaNotificationProcessor) {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun loadAlbumCover(album: Album) {
|
private fun loadAlbumCover(album: Album) {
|
||||||
AlbumGlideRequest.Builder.from(Glide.with(requireContext()), album.safeGetFirstSong())
|
GlideApp.with(requireContext()).asBitmapPalette().albumCoverOptions(album.safeGetFirstSong())
|
||||||
.checkIgnoreMediaStore()
|
//.checkIgnoreMediaStore()
|
||||||
.generatePalette(requireContext())
|
.load(RetroGlideExtension.getSongModel(album.safeGetFirstSong()))
|
||||||
.build()
|
.into(object : SingleColorTarget(binding.image) {
|
||||||
.into(object : SingleColorTarget(image) {
|
|
||||||
override fun onColorReady(color: Int) {
|
override fun onColorReady(color: Int) {
|
||||||
setColors(color)
|
setColors(color)
|
||||||
}
|
}
|
||||||
|
@ -290,23 +323,17 @@ class AlbumDetailsFragment : AbsMainActivityFragment(R.layout.fragment_album_det
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setColors(color: Int) {
|
private fun setColors(color: Int) {
|
||||||
shuffleAction?.applyColor(color)
|
binding.fragmentAlbumContent.shuffleAction.applyColor(color)
|
||||||
playAction?.applyOutlineColor(color)
|
binding.fragmentAlbumContent.playAction.applyOutlineColor(color)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onAlbumClick(albumId: Long, view: View) {
|
override fun onAlbumClick(albumId: Long, view: View) {
|
||||||
exitTransition = MaterialElevationScale(false).apply {
|
|
||||||
duration = 300L
|
|
||||||
}
|
|
||||||
reenterTransition = MaterialElevationScale(false).apply {
|
|
||||||
duration = 300L
|
|
||||||
}
|
|
||||||
findNavController().navigate(
|
findNavController().navigate(
|
||||||
R.id.albumDetailsFragment,
|
R.id.albumDetailsFragment,
|
||||||
bundleOf(EXTRA_ALBUM_ID to albumId),
|
bundleOf(EXTRA_ALBUM_ID to albumId),
|
||||||
null,
|
null,
|
||||||
FragmentNavigatorExtras(
|
FragmentNavigatorExtras(
|
||||||
view to "album"
|
view to albumId.toString()
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -318,9 +345,9 @@ class AlbumDetailsFragment : AbsMainActivityFragment(R.layout.fragment_album_det
|
||||||
setUpSortOrderMenu(sortOrder.subMenu)
|
setUpSortOrderMenu(sortOrder.subMenu)
|
||||||
ToolbarContentTintHelper.handleOnCreateOptionsMenu(
|
ToolbarContentTintHelper.handleOnCreateOptionsMenu(
|
||||||
requireContext(),
|
requireContext(),
|
||||||
toolbar,
|
binding.toolbar,
|
||||||
menu,
|
menu,
|
||||||
getToolbarBackgroundColor(toolbar)
|
getToolbarBackgroundColor(binding.toolbar)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -360,7 +387,7 @@ class AlbumDetailsFragment : AbsMainActivityFragment(R.layout.fragment_album_det
|
||||||
intent.putExtra(AbsTagEditorActivity.EXTRA_ID, album.id)
|
intent.putExtra(AbsTagEditorActivity.EXTRA_ID, album.id)
|
||||||
val options = ActivityOptions.makeSceneTransitionAnimation(
|
val options = ActivityOptions.makeSceneTransitionAnimation(
|
||||||
requireActivity(),
|
requireActivity(),
|
||||||
albumCoverContainer,
|
binding.albumCoverContainer,
|
||||||
"${getString(R.string.transition_album_art)}_${album.id}"
|
"${getString(R.string.transition_album_art)}_${album.id}"
|
||||||
)
|
)
|
||||||
startActivityForResult(
|
startActivityForResult(
|
||||||
|
@ -421,6 +448,37 @@ class AlbumDetailsFragment : AbsMainActivityFragment(R.layout.fragment_album_det
|
||||||
simpleSongAdapter.swapDataSet(album.songs)
|
simpleSongAdapter.swapDataSet(album.songs)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun handleBackPress(): Boolean {
|
||||||
|
cab?.let {
|
||||||
|
if (it.isActive) {
|
||||||
|
it.finish()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
private var cab: MaterialCab? = null
|
||||||
|
|
||||||
|
override fun openCab(menuRes: Int, callback: MaterialCab.Callback): MaterialCab {
|
||||||
|
cab?.let {
|
||||||
|
if (it.isActive) {
|
||||||
|
it.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cab = MaterialCab(mainActivity, R.id.cab_stub)
|
||||||
|
.setMenu(menuRes)
|
||||||
|
.setCloseDrawableRes(R.drawable.ic_close)
|
||||||
|
.setBackgroundColor(RetroColorUtil.shiftBackgroundColorForLightText(surfaceColor()))
|
||||||
|
.start(callback)
|
||||||
|
return cab as MaterialCab
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroyView() {
|
||||||
|
super.onDestroyView()
|
||||||
|
_binding = null
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val TAG_EDITOR_REQUEST = 9002
|
const val TAG_EDITOR_REQUEST = 9002
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,11 +14,7 @@
|
||||||
*/
|
*/
|
||||||
package code.name.monkey.retromusic.fragments.albums
|
package code.name.monkey.retromusic.fragments.albums
|
||||||
|
|
||||||
import androidx.lifecycle.LiveData
|
import androidx.lifecycle.*
|
||||||
import androidx.lifecycle.MutableLiveData
|
|
||||||
import androidx.lifecycle.ViewModel
|
|
||||||
import androidx.lifecycle.liveData
|
|
||||||
import androidx.lifecycle.viewModelScope
|
|
||||||
import code.name.monkey.retromusic.interfaces.IMusicServiceEventListener
|
import code.name.monkey.retromusic.interfaces.IMusicServiceEventListener
|
||||||
import code.name.monkey.retromusic.model.Album
|
import code.name.monkey.retromusic.model.Album
|
||||||
import code.name.monkey.retromusic.model.Artist
|
import code.name.monkey.retromusic.model.Artist
|
||||||
|
@ -51,6 +47,11 @@ class AlbumDetailsViewModel(
|
||||||
emit(artist)
|
emit(artist)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getAlbumArtist(artistName: String): LiveData<Artist> = liveData(IO) {
|
||||||
|
val artist = repository.albumArtistByName(artistName)
|
||||||
|
emit(artist)
|
||||||
|
}
|
||||||
|
|
||||||
fun getAlbumInfo(album: Album): LiveData<Result<LastFmAlbum>> = liveData {
|
fun getAlbumInfo(album: Album): LiveData<Result<LastFmAlbum>> = liveData {
|
||||||
emit(Result.Loading)
|
emit(Result.Loading)
|
||||||
emit(repository.albumInfo(album.artistName ?: "-", album.title ?: "-"))
|
emit(repository.albumInfo(album.artistName ?: "-", album.title ?: "-"))
|
||||||
|
@ -73,4 +74,5 @@ class AlbumDetailsViewModel(
|
||||||
override fun onPlayStateChanged() {}
|
override fun onPlayStateChanged() {}
|
||||||
override fun onRepeatModeChanged() {}
|
override fun onRepeatModeChanged() {}
|
||||||
override fun onShuffleModeChanged() {}
|
override fun onShuffleModeChanged() {}
|
||||||
|
override fun onFavoriteStateChanged() {}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,7 @@ package code.name.monkey.retromusic.fragments.albums
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.*
|
import android.view.*
|
||||||
|
import androidx.activity.addCallback
|
||||||
import androidx.core.os.bundleOf
|
import androidx.core.os.bundleOf
|
||||||
import androidx.navigation.fragment.FragmentNavigatorExtras
|
import androidx.navigation.fragment.FragmentNavigatorExtras
|
||||||
import androidx.navigation.fragment.findNavController
|
import androidx.navigation.fragment.findNavController
|
||||||
|
@ -33,7 +34,7 @@ import code.name.monkey.retromusic.util.PreferenceUtil
|
||||||
import code.name.monkey.retromusic.util.RetroColorUtil
|
import code.name.monkey.retromusic.util.RetroColorUtil
|
||||||
import code.name.monkey.retromusic.util.RetroUtil
|
import code.name.monkey.retromusic.util.RetroUtil
|
||||||
import com.afollestad.materialcab.MaterialCab
|
import com.afollestad.materialcab.MaterialCab
|
||||||
import com.google.android.material.transition.MaterialElevationScale
|
import com.google.android.gms.cast.framework.CastButtonFactory
|
||||||
|
|
||||||
class AlbumsFragment : AbsRecyclerViewCustomGridSizeFragment<AlbumAdapter, GridLayoutManager>(),
|
class AlbumsFragment : AbsRecyclerViewCustomGridSizeFragment<AlbumAdapter, GridLayoutManager>(),
|
||||||
IAlbumClickListener, ICabHolder {
|
IAlbumClickListener, ICabHolder {
|
||||||
|
@ -46,8 +47,17 @@ class AlbumsFragment : AbsRecyclerViewCustomGridSizeFragment<AlbumAdapter, GridL
|
||||||
else
|
else
|
||||||
adapter?.swapDataSet(listOf())
|
adapter?.swapDataSet(listOf())
|
||||||
})
|
})
|
||||||
|
requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner) {
|
||||||
|
if (!handleBackPress()) {
|
||||||
|
remove()
|
||||||
|
requireActivity().onBackPressed()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override val titleRes: Int
|
||||||
|
get() = R.string.albums
|
||||||
|
|
||||||
override val emptyMessage: Int
|
override val emptyMessage: Int
|
||||||
get() = R.string.no_albums
|
get() = R.string.no_albums
|
||||||
|
|
||||||
|
@ -114,20 +124,15 @@ class AlbumsFragment : AbsRecyclerViewCustomGridSizeFragment<AlbumAdapter, GridL
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onAlbumClick(albumId: Long, view: View) {
|
override fun onAlbumClick(albumId: Long, view: View) {
|
||||||
exitTransition = MaterialElevationScale(false).apply {
|
|
||||||
duration = 300L
|
|
||||||
}
|
|
||||||
reenterTransition = MaterialElevationScale(true).apply {
|
|
||||||
duration = 300L
|
|
||||||
}
|
|
||||||
findNavController().navigate(
|
findNavController().navigate(
|
||||||
R.id.albumDetailsFragment,
|
R.id.albumDetailsFragment,
|
||||||
bundleOf(EXTRA_ALBUM_ID to albumId),
|
bundleOf(EXTRA_ALBUM_ID to albumId),
|
||||||
null,
|
null,
|
||||||
FragmentNavigatorExtras(
|
FragmentNavigatorExtras(
|
||||||
view to "album"
|
view to albumId.toString()
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
reenterTransition = null
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
||||||
|
@ -140,6 +145,8 @@ class AlbumsFragment : AbsRecyclerViewCustomGridSizeFragment<AlbumAdapter, GridL
|
||||||
val layoutItem = menu.findItem(R.id.action_layout_type)
|
val layoutItem = menu.findItem(R.id.action_layout_type)
|
||||||
setupLayoutMenu(layoutItem.subMenu)
|
setupLayoutMenu(layoutItem.subMenu)
|
||||||
setUpSortOrderMenu(menu.findItem(R.id.action_sort_order).subMenu)
|
setUpSortOrderMenu(menu.findItem(R.id.action_sort_order).subMenu)
|
||||||
|
//Setting up cast button
|
||||||
|
CastButtonFactory.setUpMediaRouteButton(requireContext(), menu, R.id.action_cast)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setUpSortOrderMenu(
|
private fun setUpSortOrderMenu(
|
||||||
|
@ -175,6 +182,13 @@ class AlbumsFragment : AbsRecyclerViewCustomGridSizeFragment<AlbumAdapter, GridL
|
||||||
R.string.sort_order_year
|
R.string.sort_order_year
|
||||||
).isChecked =
|
).isChecked =
|
||||||
currentSortOrder.equals(AlbumSortOrder.ALBUM_YEAR)
|
currentSortOrder.equals(AlbumSortOrder.ALBUM_YEAR)
|
||||||
|
sortOrderMenu.add(
|
||||||
|
0,
|
||||||
|
R.id.action_album_sort_order_num_songs,
|
||||||
|
4,
|
||||||
|
R.string.sort_order_num_songs
|
||||||
|
).isChecked =
|
||||||
|
currentSortOrder.equals(AlbumSortOrder.ALBUM_NUMBER_OF_SONGS)
|
||||||
|
|
||||||
sortOrderMenu.setGroupCheckable(0, true, true)
|
sortOrderMenu.setGroupCheckable(0, true, true)
|
||||||
}
|
}
|
||||||
|
@ -251,6 +265,7 @@ class AlbumsFragment : AbsRecyclerViewCustomGridSizeFragment<AlbumAdapter, GridL
|
||||||
R.id.action_album_sort_order_desc -> AlbumSortOrder.ALBUM_Z_A
|
R.id.action_album_sort_order_desc -> AlbumSortOrder.ALBUM_Z_A
|
||||||
R.id.action_album_sort_order_artist -> AlbumSortOrder.ALBUM_ARTIST
|
R.id.action_album_sort_order_artist -> AlbumSortOrder.ALBUM_ARTIST
|
||||||
R.id.action_album_sort_order_year -> AlbumSortOrder.ALBUM_YEAR
|
R.id.action_album_sort_order_year -> AlbumSortOrder.ALBUM_YEAR
|
||||||
|
R.id.action_album_sort_order_num_songs -> AlbumSortOrder.ALBUM_NUMBER_OF_SONGS
|
||||||
else -> PreferenceUtil.albumSortOrder
|
else -> PreferenceUtil.albumSortOrder
|
||||||
}
|
}
|
||||||
if (sortOrder != PreferenceUtil.albumSortOrder) {
|
if (sortOrder != PreferenceUtil.albumSortOrder) {
|
||||||
|
@ -303,6 +318,21 @@ class AlbumsFragment : AbsRecyclerViewCustomGridSizeFragment<AlbumAdapter, GridL
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onResume() {
|
||||||
|
super.onResume()
|
||||||
|
libraryViewModel.forceReload(ReloadType.Albums)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handleBackPress(): Boolean {
|
||||||
|
cab?.let {
|
||||||
|
if (it.isActive) {
|
||||||
|
it.finish()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
private var cab: MaterialCab? = null
|
private var cab: MaterialCab? = null
|
||||||
|
|
||||||
override fun openCab(menuRes: Int, callback: MaterialCab.Callback): MaterialCab {
|
override fun openCab(menuRes: Int, callback: MaterialCab.Callback): MaterialCab {
|
||||||
|
|
|
@ -0,0 +1,342 @@
|
||||||
|
package code.name.monkey.retromusic.fragments.artists
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.content.Intent
|
||||||
|
import android.graphics.Color
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.text.Spanned
|
||||||
|
import android.view.Menu
|
||||||
|
import android.view.MenuInflater
|
||||||
|
import android.view.MenuItem
|
||||||
|
import android.view.View
|
||||||
|
import androidx.activity.addCallback
|
||||||
|
import androidx.core.os.bundleOf
|
||||||
|
import androidx.core.text.HtmlCompat
|
||||||
|
import androidx.core.view.ViewCompat
|
||||||
|
import androidx.core.view.doOnPreDraw
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
import androidx.navigation.fragment.FragmentNavigatorExtras
|
||||||
|
import androidx.navigation.fragment.findNavController
|
||||||
|
import androidx.recyclerview.widget.DefaultItemAnimator
|
||||||
|
import androidx.recyclerview.widget.GridLayoutManager
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
|
import code.name.monkey.retromusic.EXTRA_ALBUM_ID
|
||||||
|
import code.name.monkey.retromusic.R
|
||||||
|
import code.name.monkey.retromusic.adapter.album.HorizontalAlbumAdapter
|
||||||
|
import code.name.monkey.retromusic.adapter.song.SimpleSongAdapter
|
||||||
|
import code.name.monkey.retromusic.databinding.FragmentArtistDetailsBinding
|
||||||
|
import code.name.monkey.retromusic.dialogs.AddToPlaylistDialog
|
||||||
|
import code.name.monkey.retromusic.extensions.*
|
||||||
|
import code.name.monkey.retromusic.fragments.base.AbsMainActivityFragment
|
||||||
|
import code.name.monkey.retromusic.glide.GlideApp
|
||||||
|
import code.name.monkey.retromusic.glide.RetroGlideExtension
|
||||||
|
import code.name.monkey.retromusic.glide.SingleColorTarget
|
||||||
|
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
||||||
|
import code.name.monkey.retromusic.interfaces.IAlbumClickListener
|
||||||
|
import code.name.monkey.retromusic.interfaces.ICabHolder
|
||||||
|
import code.name.monkey.retromusic.model.Artist
|
||||||
|
import code.name.monkey.retromusic.network.Result
|
||||||
|
import code.name.monkey.retromusic.network.model.LastFmArtist
|
||||||
|
import code.name.monkey.retromusic.repository.RealRepository
|
||||||
|
import code.name.monkey.retromusic.util.CustomArtistImageUtil
|
||||||
|
import code.name.monkey.retromusic.util.MusicUtil
|
||||||
|
import code.name.monkey.retromusic.util.RetroColorUtil
|
||||||
|
import code.name.monkey.retromusic.util.RetroUtil
|
||||||
|
import com.afollestad.materialcab.MaterialCab
|
||||||
|
import com.google.android.material.transition.MaterialContainerTransform
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
import org.koin.android.ext.android.get
|
||||||
|
import java.util.*
|
||||||
|
import kotlin.collections.ArrayList
|
||||||
|
|
||||||
|
abstract class AbsArtistDetailsFragment : AbsMainActivityFragment(R.layout.fragment_artist_details),
|
||||||
|
IAlbumClickListener, ICabHolder {
|
||||||
|
private var _binding: FragmentArtistDetailsBinding? = null
|
||||||
|
private val binding get() = _binding!!
|
||||||
|
|
||||||
|
abstract val detailsViewModel: ArtistDetailsViewModel
|
||||||
|
abstract val artistId: Long?
|
||||||
|
abstract val artistName: String?
|
||||||
|
private lateinit var artist: Artist
|
||||||
|
private lateinit var songAdapter: SimpleSongAdapter
|
||||||
|
private lateinit var albumAdapter: HorizontalAlbumAdapter
|
||||||
|
private var forceDownload: Boolean = false
|
||||||
|
private var lang: String? = null
|
||||||
|
private var biography: Spanned? = null
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
sharedElementEnterTransition = MaterialContainerTransform().apply {
|
||||||
|
drawingViewId = R.id.fragment_container
|
||||||
|
duration = 300L
|
||||||
|
scrimColor = Color.TRANSPARENT
|
||||||
|
setAllContainerColors(requireContext().resolveColor(R.attr.colorSurface))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
_binding = FragmentArtistDetailsBinding.bind(view)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
||||||
|
super.onActivityCreated(savedInstanceState)
|
||||||
|
setHasOptionsMenu(true)
|
||||||
|
mainActivity.addMusicServiceEventListener(detailsViewModel)
|
||||||
|
mainActivity.setSupportActionBar(binding.toolbar)
|
||||||
|
binding.toolbar.title = null
|
||||||
|
ViewCompat.setTransitionName(
|
||||||
|
binding.artistCoverContainer,
|
||||||
|
(artistId ?: artistName).toString()
|
||||||
|
)
|
||||||
|
postponeEnterTransition()
|
||||||
|
detailsViewModel.getArtist().observe(viewLifecycleOwner, {
|
||||||
|
requireView().doOnPreDraw {
|
||||||
|
startPostponedEnterTransition()
|
||||||
|
}
|
||||||
|
showArtist(it)
|
||||||
|
})
|
||||||
|
setupRecyclerView()
|
||||||
|
|
||||||
|
binding.fragmentArtistContent.playAction.apply {
|
||||||
|
setOnClickListener { MusicPlayerRemote.openQueue(artist.songs, 0, true) }
|
||||||
|
}
|
||||||
|
binding.fragmentArtistContent.shuffleAction.apply {
|
||||||
|
setOnClickListener { MusicPlayerRemote.openAndShuffleQueue(artist.songs, true) }
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.fragmentArtistContent.biographyText.setOnClickListener {
|
||||||
|
if (binding.fragmentArtistContent.biographyText.maxLines == 4) {
|
||||||
|
binding.fragmentArtistContent.biographyText.maxLines = Integer.MAX_VALUE
|
||||||
|
} else {
|
||||||
|
binding.fragmentArtistContent.biographyText.maxLines = 4
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner) {
|
||||||
|
if (!handleBackPress()) {
|
||||||
|
remove()
|
||||||
|
requireActivity().onBackPressed()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setupRecyclerView() {
|
||||||
|
albumAdapter = HorizontalAlbumAdapter(requireActivity(), ArrayList(), this, this)
|
||||||
|
binding.fragmentArtistContent.albumRecyclerView.apply {
|
||||||
|
itemAnimator = DefaultItemAnimator()
|
||||||
|
layoutManager = GridLayoutManager(this.context, 1, GridLayoutManager.HORIZONTAL, false)
|
||||||
|
adapter = albumAdapter
|
||||||
|
}
|
||||||
|
songAdapter = SimpleSongAdapter(requireActivity(), ArrayList(), R.layout.item_song, this)
|
||||||
|
binding.fragmentArtistContent.recyclerView.apply {
|
||||||
|
itemAnimator = DefaultItemAnimator()
|
||||||
|
layoutManager = LinearLayoutManager(this.context)
|
||||||
|
adapter = songAdapter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun showArtist(artist: Artist) {
|
||||||
|
this.artist = artist
|
||||||
|
loadArtistImage(artist)
|
||||||
|
if (RetroUtil.isAllowedToDownloadMetadata(requireContext())) {
|
||||||
|
loadBiography(artist.name)
|
||||||
|
}
|
||||||
|
binding.artistTitle.text = artist.name
|
||||||
|
binding.text.text = String.format(
|
||||||
|
"%s • %s",
|
||||||
|
MusicUtil.getArtistInfoString(requireContext(), artist),
|
||||||
|
MusicUtil.getReadableDurationString(MusicUtil.getTotalDuration(artist.songs))
|
||||||
|
)
|
||||||
|
val songText = resources.getQuantityString(
|
||||||
|
R.plurals.albumSongs,
|
||||||
|
artist.songCount,
|
||||||
|
artist.songCount
|
||||||
|
)
|
||||||
|
val albumText = resources.getQuantityString(
|
||||||
|
R.plurals.albums,
|
||||||
|
artist.songCount,
|
||||||
|
artist.songCount
|
||||||
|
)
|
||||||
|
binding.fragmentArtistContent.songTitle.text = songText
|
||||||
|
binding.fragmentArtistContent.albumTitle.text = albumText
|
||||||
|
songAdapter.swapDataSet(artist.songs.sortedBy { it.trackNumber })
|
||||||
|
albumAdapter.swapDataSet(artist.albums)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun loadBiography(
|
||||||
|
name: String,
|
||||||
|
lang: String? = Locale.getDefault().language
|
||||||
|
) {
|
||||||
|
biography = null
|
||||||
|
this.lang = lang
|
||||||
|
detailsViewModel.getArtistInfo(name, lang, null)
|
||||||
|
.observe(viewLifecycleOwner, { result ->
|
||||||
|
when (result) {
|
||||||
|
is Result.Loading -> println("Loading")
|
||||||
|
is Result.Error -> println("Error")
|
||||||
|
is Result.Success -> artistInfo(result.data)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun artistInfo(lastFmArtist: LastFmArtist?) {
|
||||||
|
if (lastFmArtist != null && lastFmArtist.artist != null && lastFmArtist.artist.bio != null) {
|
||||||
|
val bioContent = lastFmArtist.artist.bio.content
|
||||||
|
if (bioContent != null && bioContent.trim { it <= ' ' }.isNotEmpty()) {
|
||||||
|
binding.fragmentArtistContent.biographyText.visibility = View.VISIBLE
|
||||||
|
binding.fragmentArtistContent.biographyTitle.visibility = View.VISIBLE
|
||||||
|
biography = HtmlCompat.fromHtml(bioContent, HtmlCompat.FROM_HTML_MODE_LEGACY)
|
||||||
|
binding.fragmentArtistContent.biographyText.text = biography
|
||||||
|
if (lastFmArtist.artist.stats.listeners.isNotEmpty()) {
|
||||||
|
binding.fragmentArtistContent.listeners.show()
|
||||||
|
binding.fragmentArtistContent.listenersLabel.show()
|
||||||
|
binding.fragmentArtistContent.scrobbles.show()
|
||||||
|
binding.fragmentArtistContent.scrobblesLabel.show()
|
||||||
|
binding.fragmentArtistContent.listeners.text =
|
||||||
|
RetroUtil.formatValue(lastFmArtist.artist.stats.listeners.toFloat())
|
||||||
|
binding.fragmentArtistContent.scrobbles.text =
|
||||||
|
RetroUtil.formatValue(lastFmArtist.artist.stats.playcount.toFloat())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the "lang" parameter is set and no biography is given, retry with default language
|
||||||
|
if (biography == null && lang != null) {
|
||||||
|
loadBiography(artist.name, null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun loadArtistImage(artist: Artist) {
|
||||||
|
GlideApp.with(requireContext()).asBitmapPalette().artistImageOptions(artist)
|
||||||
|
.load(RetroGlideExtension.getArtistModel(artist))
|
||||||
|
.dontAnimate()
|
||||||
|
.into(object : SingleColorTarget(binding.image) {
|
||||||
|
override fun onColorReady(color: Int) {
|
||||||
|
setColors(color)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setColors(color: Int) {
|
||||||
|
if (_binding != null) {
|
||||||
|
binding.fragmentArtistContent.shuffleAction.applyColor(color)
|
||||||
|
binding.fragmentArtistContent.playAction.applyOutlineColor(color)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onAlbumClick(albumId: Long, view: View) {
|
||||||
|
findNavController().navigate(
|
||||||
|
R.id.albumDetailsFragment,
|
||||||
|
bundleOf(EXTRA_ALBUM_ID to albumId),
|
||||||
|
null,
|
||||||
|
FragmentNavigatorExtras(
|
||||||
|
view to albumId.toString()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||||
|
return handleSortOrderMenuItem(item)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handleSortOrderMenuItem(item: MenuItem): Boolean {
|
||||||
|
val songs = artist.songs
|
||||||
|
when (item.itemId) {
|
||||||
|
android.R.id.home -> findNavController().navigateUp()
|
||||||
|
R.id.action_play_next -> {
|
||||||
|
MusicPlayerRemote.playNext(songs)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
R.id.action_add_to_current_playing -> {
|
||||||
|
MusicPlayerRemote.enqueue(songs)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
R.id.action_add_to_playlist -> {
|
||||||
|
lifecycleScope.launch(Dispatchers.IO) {
|
||||||
|
val playlists = get<RealRepository>().fetchPlaylists()
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
|
AddToPlaylistDialog.create(playlists, songs)
|
||||||
|
.show(childFragmentManager, "ADD_PLAYLIST")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
R.id.action_set_artist_image -> {
|
||||||
|
val intent = Intent(Intent.ACTION_GET_CONTENT)
|
||||||
|
intent.type = "image/*"
|
||||||
|
startActivityForResult(
|
||||||
|
Intent.createChooser(intent, getString(R.string.pick_from_local_storage)),
|
||||||
|
REQUEST_CODE_SELECT_IMAGE
|
||||||
|
)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
R.id.action_reset_artist_image -> {
|
||||||
|
showToast(resources.getString(R.string.updating))
|
||||||
|
CustomArtistImageUtil.getInstance(requireContext()).resetCustomArtistImage(artist)
|
||||||
|
forceDownload = true
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||||
|
super.onActivityResult(requestCode, resultCode, data)
|
||||||
|
when (requestCode) {
|
||||||
|
REQUEST_CODE_SELECT_IMAGE -> if (resultCode == Activity.RESULT_OK) {
|
||||||
|
data?.data?.let {
|
||||||
|
CustomArtistImageUtil.getInstance(requireContext())
|
||||||
|
.setCustomArtistImage(artist, it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> if (resultCode == Activity.RESULT_OK) {
|
||||||
|
println("OK")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
||||||
|
super.onCreateOptionsMenu(menu, inflater)
|
||||||
|
inflater.inflate(R.menu.menu_artist_detail, menu)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun handleBackPress(): Boolean {
|
||||||
|
cab?.let {
|
||||||
|
if (it.isActive) {
|
||||||
|
it.finish()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
private var cab: MaterialCab? = null
|
||||||
|
|
||||||
|
override fun openCab(menuRes: Int, callback: MaterialCab.Callback): MaterialCab {
|
||||||
|
cab?.let {
|
||||||
|
if (it.isActive) {
|
||||||
|
it.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cab = MaterialCab(mainActivity, R.id.cab_stub)
|
||||||
|
.setMenu(menuRes)
|
||||||
|
.setCloseDrawableRes(R.drawable.ic_close)
|
||||||
|
.setBackgroundColor(RetroColorUtil.shiftBackgroundColorForLightText(surfaceColor()))
|
||||||
|
.start(callback)
|
||||||
|
return cab as MaterialCab
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
override fun onDestroyView() {
|
||||||
|
super.onDestroyView()
|
||||||
|
_binding = null
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val REQUEST_CODE_SELECT_IMAGE = 9002
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020 Hemanth Savarla.
|
||||||
|
*
|
||||||
|
* Licensed under the GNU General Public License v3
|
||||||
|
*
|
||||||
|
* This is free software: you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||||
|
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
|
* See the GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package code.name.monkey.retromusic.fragments.artists
|
||||||
|
|
||||||
|
import androidx.navigation.fragment.navArgs
|
||||||
|
import org.koin.androidx.viewmodel.ext.android.viewModel
|
||||||
|
import org.koin.core.parameter.parametersOf
|
||||||
|
|
||||||
|
class AlbumArtistDetailsFragment : AbsArtistDetailsFragment() {
|
||||||
|
|
||||||
|
private val arguments by navArgs<AlbumArtistDetailsFragmentArgs>()
|
||||||
|
|
||||||
|
override val detailsViewModel: ArtistDetailsViewModel by viewModel {
|
||||||
|
parametersOf(null, arguments.extraArtistName)
|
||||||
|
}
|
||||||
|
override val artistId: Long?
|
||||||
|
get() = null
|
||||||
|
override val artistName: String
|
||||||
|
get() = arguments.extraArtistName
|
||||||
|
}
|
|
@ -0,0 +1,66 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020 Hemanth Savarla.
|
||||||
|
*
|
||||||
|
* Licensed under the GNU General Public License v3
|
||||||
|
*
|
||||||
|
* This is free software: you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||||
|
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
|
* See the GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package code.name.monkey.retromusic.fragments.artists
|
||||||
|
|
||||||
|
import androidx.lifecycle.*
|
||||||
|
import code.name.monkey.retromusic.interfaces.IMusicServiceEventListener
|
||||||
|
import code.name.monkey.retromusic.model.Artist
|
||||||
|
import code.name.monkey.retromusic.network.Result
|
||||||
|
import code.name.monkey.retromusic.network.model.LastFmArtist
|
||||||
|
import code.name.monkey.retromusic.repository.RealRepository
|
||||||
|
import kotlinx.coroutines.Dispatchers.IO
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
class AlbumArtistDetailsViewModel(
|
||||||
|
private val realRepository: RealRepository,
|
||||||
|
private val artistName: String
|
||||||
|
) : ViewModel(), IMusicServiceEventListener {
|
||||||
|
private val artistDetails = MutableLiveData<Artist>()
|
||||||
|
|
||||||
|
init {
|
||||||
|
fetchAlbumArtist()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun fetchAlbumArtist() {
|
||||||
|
viewModelScope.launch(IO) {
|
||||||
|
artistDetails.postValue(realRepository.albumArtistByName(artistName))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getArtist(): LiveData<Artist> = artistDetails
|
||||||
|
|
||||||
|
fun getArtistInfo(
|
||||||
|
name: String,
|
||||||
|
lang: String?,
|
||||||
|
cache: String?
|
||||||
|
): LiveData<Result<LastFmArtist>> = liveData(IO) {
|
||||||
|
emit(Result.Loading)
|
||||||
|
val info = realRepository.artistInfo(name, lang, cache)
|
||||||
|
emit(info)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onMediaStoreChanged() {
|
||||||
|
fetchAlbumArtist()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onServiceConnected() {}
|
||||||
|
override fun onServiceDisconnected() {}
|
||||||
|
override fun onQueueChanged() {}
|
||||||
|
override fun onFavoriteStateChanged() {}
|
||||||
|
override fun onPlayingMetaChanged() {}
|
||||||
|
override fun onPlayStateChanged() {}
|
||||||
|
override fun onRepeatModeChanged() {}
|
||||||
|
override fun onShuffleModeChanged() {}
|
||||||
|
}
|
|
@ -14,298 +14,18 @@
|
||||||
*/
|
*/
|
||||||
package code.name.monkey.retromusic.fragments.artists
|
package code.name.monkey.retromusic.fragments.artists
|
||||||
|
|
||||||
import android.app.Activity
|
|
||||||
import android.content.Intent
|
|
||||||
import android.graphics.Color
|
|
||||||
import android.os.Bundle
|
|
||||||
import android.text.Spanned
|
|
||||||
import android.view.Menu
|
|
||||||
import android.view.MenuInflater
|
|
||||||
import android.view.MenuItem
|
|
||||||
import android.view.View
|
|
||||||
import androidx.core.os.bundleOf
|
|
||||||
import androidx.core.text.HtmlCompat
|
|
||||||
import androidx.core.view.ViewCompat
|
|
||||||
import androidx.lifecycle.Observer
|
|
||||||
import androidx.lifecycle.lifecycleScope
|
|
||||||
import androidx.navigation.fragment.FragmentNavigatorExtras
|
|
||||||
import androidx.navigation.fragment.findNavController
|
|
||||||
import androidx.navigation.fragment.navArgs
|
import androidx.navigation.fragment.navArgs
|
||||||
import androidx.recyclerview.widget.DefaultItemAnimator
|
|
||||||
import androidx.recyclerview.widget.GridLayoutManager
|
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
|
||||||
import code.name.monkey.appthemehelper.util.ATHUtil
|
|
||||||
import code.name.monkey.retromusic.EXTRA_ALBUM_ID
|
|
||||||
import code.name.monkey.retromusic.R
|
|
||||||
import code.name.monkey.retromusic.adapter.album.HorizontalAlbumAdapter
|
|
||||||
import code.name.monkey.retromusic.adapter.song.SimpleSongAdapter
|
|
||||||
import code.name.monkey.retromusic.dialogs.AddToPlaylistDialog
|
|
||||||
import code.name.monkey.retromusic.extensions.*
|
|
||||||
import code.name.monkey.retromusic.fragments.base.AbsMainActivityFragment
|
|
||||||
import code.name.monkey.retromusic.glide.ArtistGlideRequest
|
|
||||||
import code.name.monkey.retromusic.glide.SingleColorTarget
|
|
||||||
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
|
||||||
import code.name.monkey.retromusic.interfaces.IAlbumClickListener
|
|
||||||
import code.name.monkey.retromusic.model.Artist
|
|
||||||
import code.name.monkey.retromusic.network.Result
|
|
||||||
import code.name.monkey.retromusic.network.model.LastFmArtist
|
|
||||||
import code.name.monkey.retromusic.repository.RealRepository
|
|
||||||
import code.name.monkey.retromusic.util.CustomArtistImageUtil
|
|
||||||
import code.name.monkey.retromusic.util.MusicUtil
|
|
||||||
import code.name.monkey.retromusic.util.RetroUtil
|
|
||||||
import com.bumptech.glide.Glide
|
|
||||||
import com.google.android.material.transition.MaterialContainerTransform
|
|
||||||
import com.google.android.material.transition.MaterialElevationScale
|
|
||||||
import kotlinx.android.synthetic.main.fragment_artist_content.*
|
|
||||||
import kotlinx.android.synthetic.main.fragment_artist_details.*
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import kotlinx.coroutines.withContext
|
|
||||||
import org.koin.android.ext.android.get
|
|
||||||
import org.koin.androidx.viewmodel.ext.android.viewModel
|
import org.koin.androidx.viewmodel.ext.android.viewModel
|
||||||
import org.koin.core.parameter.parametersOf
|
import org.koin.core.parameter.parametersOf
|
||||||
import java.util.*
|
|
||||||
import kotlin.collections.ArrayList
|
|
||||||
|
|
||||||
class ArtistDetailsFragment : AbsMainActivityFragment(R.layout.fragment_artist_details),
|
class ArtistDetailsFragment : AbsArtistDetailsFragment() {
|
||||||
IAlbumClickListener {
|
|
||||||
private val arguments by navArgs<ArtistDetailsFragmentArgs>()
|
private val arguments by navArgs<ArtistDetailsFragmentArgs>()
|
||||||
private val detailsViewModel: ArtistDetailsViewModel by viewModel {
|
override val detailsViewModel: ArtistDetailsViewModel by viewModel {
|
||||||
parametersOf(arguments.extraArtistId)
|
parametersOf(arguments.extraArtistId, null)
|
||||||
}
|
}
|
||||||
private lateinit var artist: Artist
|
override val artistId: Long
|
||||||
private lateinit var songAdapter: SimpleSongAdapter
|
get() = arguments.extraArtistId
|
||||||
private lateinit var albumAdapter: HorizontalAlbumAdapter
|
override val artistName: String?
|
||||||
private var forceDownload: Boolean = false
|
get() = null
|
||||||
private var lang: String? = null
|
|
||||||
private var biography: Spanned? = null
|
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
|
||||||
super.onCreate(savedInstanceState)
|
|
||||||
sharedElementEnterTransition = MaterialContainerTransform().apply {
|
|
||||||
drawingViewId = R.id.fragment_container
|
|
||||||
duration = 300L
|
|
||||||
scrimColor = Color.TRANSPARENT
|
|
||||||
setAllContainerColors(requireContext().resolveColor(R.attr.colorSurface))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
|
||||||
super.onActivityCreated(savedInstanceState)
|
|
||||||
setHasOptionsMenu(true)
|
|
||||||
mainActivity.setBottomBarVisibility(false)
|
|
||||||
mainActivity.addMusicServiceEventListener(detailsViewModel)
|
|
||||||
mainActivity.setSupportActionBar(toolbar)
|
|
||||||
toolbar.title = null
|
|
||||||
ViewCompat.setTransitionName(artistCoverContainer, "artist")
|
|
||||||
postponeEnterTransition()
|
|
||||||
detailsViewModel.getArtist().observe(viewLifecycleOwner, {
|
|
||||||
startPostponedEnterTransition()
|
|
||||||
showArtist(it)
|
|
||||||
})
|
|
||||||
setupRecyclerView()
|
|
||||||
|
|
||||||
playAction.apply {
|
|
||||||
setOnClickListener { MusicPlayerRemote.openQueue(artist.songs, 0, true) }
|
|
||||||
}
|
|
||||||
shuffleAction.apply {
|
|
||||||
setOnClickListener { MusicPlayerRemote.openAndShuffleQueue(artist.songs, true) }
|
|
||||||
}
|
|
||||||
|
|
||||||
biographyText.setOnClickListener {
|
|
||||||
if (biographyText.maxLines == 4) {
|
|
||||||
biographyText.maxLines = Integer.MAX_VALUE
|
|
||||||
} else {
|
|
||||||
biographyText.maxLines = 4
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun setupRecyclerView() {
|
|
||||||
albumAdapter = HorizontalAlbumAdapter(requireActivity(), ArrayList(), null, this)
|
|
||||||
albumRecyclerView.apply {
|
|
||||||
itemAnimator = DefaultItemAnimator()
|
|
||||||
layoutManager = GridLayoutManager(this.context, 1, GridLayoutManager.HORIZONTAL, false)
|
|
||||||
adapter = albumAdapter
|
|
||||||
}
|
|
||||||
songAdapter = SimpleSongAdapter(requireActivity(), ArrayList(), R.layout.item_song, null)
|
|
||||||
recyclerView.apply {
|
|
||||||
itemAnimator = DefaultItemAnimator()
|
|
||||||
layoutManager = LinearLayoutManager(this.context)
|
|
||||||
adapter = songAdapter
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun showArtist(artist: Artist) {
|
|
||||||
this.artist = artist
|
|
||||||
loadArtistImage(artist)
|
|
||||||
if (RetroUtil.isAllowedToDownloadMetadata(requireContext())) {
|
|
||||||
loadBiography(artist.name)
|
|
||||||
}
|
|
||||||
artistTitle.text = artist.name
|
|
||||||
text.text = String.format(
|
|
||||||
"%s • %s",
|
|
||||||
MusicUtil.getArtistInfoString(requireContext(), artist),
|
|
||||||
MusicUtil.getReadableDurationString(MusicUtil.getTotalDuration(artist.songs))
|
|
||||||
)
|
|
||||||
val songText = resources.getQuantityString(
|
|
||||||
R.plurals.albumSongs,
|
|
||||||
artist.songCount,
|
|
||||||
artist.songCount
|
|
||||||
)
|
|
||||||
val albumText = resources.getQuantityString(
|
|
||||||
R.plurals.albums,
|
|
||||||
artist.songCount,
|
|
||||||
artist.songCount
|
|
||||||
)
|
|
||||||
songTitle.text = songText
|
|
||||||
albumTitle.text = albumText
|
|
||||||
songAdapter.swapDataSet(artist.songs.sortedBy { it.trackNumber })
|
|
||||||
albumAdapter.swapDataSet(artist.albums)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun loadBiography(
|
|
||||||
name: String,
|
|
||||||
lang: String? = Locale.getDefault().language
|
|
||||||
) {
|
|
||||||
biography = null
|
|
||||||
this.lang = lang
|
|
||||||
detailsViewModel.getArtistInfo(name, lang, null)
|
|
||||||
.observe(viewLifecycleOwner, { result ->
|
|
||||||
when (result) {
|
|
||||||
is Result.Loading -> println("Loading")
|
|
||||||
is Result.Error -> println("Error")
|
|
||||||
is Result.Success -> artistInfo(result.data)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun artistInfo(lastFmArtist: LastFmArtist?) {
|
|
||||||
if (lastFmArtist != null && lastFmArtist.artist != null) {
|
|
||||||
val bioContent = lastFmArtist.artist.bio.content
|
|
||||||
if (bioContent != null && bioContent.trim { it <= ' ' }.isNotEmpty()) {
|
|
||||||
biographyText.visibility = View.VISIBLE
|
|
||||||
biographyTitle.visibility = View.VISIBLE
|
|
||||||
biography = HtmlCompat.fromHtml(bioContent, HtmlCompat.FROM_HTML_MODE_LEGACY)
|
|
||||||
biographyText.text = biography
|
|
||||||
if (lastFmArtist.artist.stats.listeners.isNotEmpty()) {
|
|
||||||
listeners.show()
|
|
||||||
listenersLabel.show()
|
|
||||||
scrobbles.show()
|
|
||||||
scrobblesLabel.show()
|
|
||||||
listeners.text =
|
|
||||||
RetroUtil.formatValue(lastFmArtist.artist.stats.listeners.toFloat())
|
|
||||||
scrobbles.text =
|
|
||||||
RetroUtil.formatValue(lastFmArtist.artist.stats.playcount.toFloat())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the "lang" parameter is set and no biography is given, retry with default language
|
|
||||||
if (biography == null && lang != null) {
|
|
||||||
loadBiography(artist.name, null)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun loadArtistImage(artist: Artist) {
|
|
||||||
ArtistGlideRequest.Builder.from(Glide.with(requireContext()), artist)
|
|
||||||
.generatePalette(requireContext()).build()
|
|
||||||
.dontAnimate()
|
|
||||||
.into(object : SingleColorTarget(image) {
|
|
||||||
override fun onColorReady(color: Int) {
|
|
||||||
setColors(color)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun setColors(color: Int) {
|
|
||||||
shuffleAction?.applyColor(color)
|
|
||||||
playAction?.applyOutlineColor(color)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onAlbumClick(albumId: Long, view: View) {
|
|
||||||
exitTransition = MaterialElevationScale(false).apply {
|
|
||||||
duration = 300L
|
|
||||||
}
|
|
||||||
reenterTransition = MaterialElevationScale(false).apply {
|
|
||||||
duration = 300L
|
|
||||||
}
|
|
||||||
findNavController().navigate(
|
|
||||||
R.id.albumDetailsFragment,
|
|
||||||
bundleOf(EXTRA_ALBUM_ID to albumId),
|
|
||||||
null,
|
|
||||||
FragmentNavigatorExtras(
|
|
||||||
view to "album"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
|
||||||
return handleSortOrderMenuItem(item)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun handleSortOrderMenuItem(item: MenuItem): Boolean {
|
|
||||||
val songs = artist.songs
|
|
||||||
when (item.itemId) {
|
|
||||||
android.R.id.home -> findNavController().navigateUp()
|
|
||||||
R.id.action_play_next -> {
|
|
||||||
MusicPlayerRemote.playNext(songs)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
R.id.action_add_to_current_playing -> {
|
|
||||||
MusicPlayerRemote.enqueue(songs)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
R.id.action_add_to_playlist -> {
|
|
||||||
lifecycleScope.launch(Dispatchers.IO) {
|
|
||||||
val playlists = get<RealRepository>().fetchPlaylists()
|
|
||||||
withContext(Dispatchers.Main) {
|
|
||||||
AddToPlaylistDialog.create(playlists, songs)
|
|
||||||
.show(childFragmentManager, "ADD_PLAYLIST")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
R.id.action_set_artist_image -> {
|
|
||||||
val intent = Intent(Intent.ACTION_GET_CONTENT)
|
|
||||||
intent.type = "image/*"
|
|
||||||
startActivityForResult(
|
|
||||||
Intent.createChooser(intent, getString(R.string.pick_from_local_storage)),
|
|
||||||
REQUEST_CODE_SELECT_IMAGE
|
|
||||||
)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
R.id.action_reset_artist_image -> {
|
|
||||||
showToast(resources.getString(R.string.updating))
|
|
||||||
CustomArtistImageUtil.getInstance(requireContext()).resetCustomArtistImage(artist)
|
|
||||||
forceDownload = true
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
|
||||||
super.onActivityResult(requestCode, resultCode, data)
|
|
||||||
when (requestCode) {
|
|
||||||
REQUEST_CODE_SELECT_IMAGE -> if (resultCode == Activity.RESULT_OK) {
|
|
||||||
data?.data?.let {
|
|
||||||
CustomArtistImageUtil.getInstance(requireContext())
|
|
||||||
.setCustomArtistImage(artist, it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else -> if (resultCode == Activity.RESULT_OK) {
|
|
||||||
println("OK")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
|
||||||
super.onCreateOptionsMenu(menu, inflater)
|
|
||||||
inflater.inflate(R.menu.menu_artist_detail, menu)
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
const val REQUEST_CODE_SELECT_IMAGE = 9002
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,11 +14,7 @@
|
||||||
*/
|
*/
|
||||||
package code.name.monkey.retromusic.fragments.artists
|
package code.name.monkey.retromusic.fragments.artists
|
||||||
|
|
||||||
import androidx.lifecycle.LiveData
|
import androidx.lifecycle.*
|
||||||
import androidx.lifecycle.MutableLiveData
|
|
||||||
import androidx.lifecycle.ViewModel
|
|
||||||
import androidx.lifecycle.liveData
|
|
||||||
import androidx.lifecycle.viewModelScope
|
|
||||||
import code.name.monkey.retromusic.interfaces.IMusicServiceEventListener
|
import code.name.monkey.retromusic.interfaces.IMusicServiceEventListener
|
||||||
import code.name.monkey.retromusic.model.Artist
|
import code.name.monkey.retromusic.model.Artist
|
||||||
import code.name.monkey.retromusic.network.Result
|
import code.name.monkey.retromusic.network.Result
|
||||||
|
@ -29,7 +25,8 @@ import kotlinx.coroutines.launch
|
||||||
|
|
||||||
class ArtistDetailsViewModel(
|
class ArtistDetailsViewModel(
|
||||||
private val realRepository: RealRepository,
|
private val realRepository: RealRepository,
|
||||||
private val artistId: Long
|
private val artistId: Long?,
|
||||||
|
private val artistName: String?
|
||||||
) : ViewModel(), IMusicServiceEventListener {
|
) : ViewModel(), IMusicServiceEventListener {
|
||||||
private val artistDetails = MutableLiveData<Artist>()
|
private val artistDetails = MutableLiveData<Artist>()
|
||||||
|
|
||||||
|
@ -39,7 +36,9 @@ class ArtistDetailsViewModel(
|
||||||
|
|
||||||
private fun fetchArtist() {
|
private fun fetchArtist() {
|
||||||
viewModelScope.launch(IO) {
|
viewModelScope.launch(IO) {
|
||||||
artistDetails.postValue(realRepository.artistById(artistId))
|
artistId?.let { artistDetails.postValue(realRepository.artistById(it)) }
|
||||||
|
|
||||||
|
artistName?.let { artistDetails.postValue(realRepository.albumArtistByName(it)) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -56,7 +55,7 @@ class ArtistDetailsViewModel(
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onMediaStoreChanged() {
|
override fun onMediaStoreChanged() {
|
||||||
fetchArtist()
|
fetchArtist()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onServiceConnected() {}
|
override fun onServiceConnected() {}
|
||||||
|
@ -66,4 +65,5 @@ class ArtistDetailsViewModel(
|
||||||
override fun onPlayStateChanged() {}
|
override fun onPlayStateChanged() {}
|
||||||
override fun onRepeatModeChanged() {}
|
override fun onRepeatModeChanged() {}
|
||||||
override fun onShuffleModeChanged() {}
|
override fun onShuffleModeChanged() {}
|
||||||
|
override fun onFavoriteStateChanged() {}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,38 +16,48 @@ package code.name.monkey.retromusic.fragments.artists
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.*
|
import android.view.*
|
||||||
|
import androidx.activity.addCallback
|
||||||
import androidx.core.os.bundleOf
|
import androidx.core.os.bundleOf
|
||||||
import androidx.lifecycle.Observer
|
|
||||||
import androidx.navigation.fragment.FragmentNavigatorExtras
|
import androidx.navigation.fragment.FragmentNavigatorExtras
|
||||||
import androidx.navigation.fragment.findNavController
|
import androidx.navigation.fragment.findNavController
|
||||||
import androidx.recyclerview.widget.GridLayoutManager
|
import androidx.recyclerview.widget.GridLayoutManager
|
||||||
import code.name.monkey.retromusic.EXTRA_ARTIST_ID
|
import code.name.monkey.retromusic.EXTRA_ARTIST_ID
|
||||||
|
import code.name.monkey.retromusic.EXTRA_ARTIST_NAME
|
||||||
import code.name.monkey.retromusic.R
|
import code.name.monkey.retromusic.R
|
||||||
import code.name.monkey.retromusic.adapter.artist.ArtistAdapter
|
import code.name.monkey.retromusic.adapter.artist.ArtistAdapter
|
||||||
import code.name.monkey.retromusic.extensions.surfaceColor
|
import code.name.monkey.retromusic.extensions.surfaceColor
|
||||||
import code.name.monkey.retromusic.fragments.ReloadType
|
import code.name.monkey.retromusic.fragments.ReloadType
|
||||||
import code.name.monkey.retromusic.fragments.base.AbsRecyclerViewCustomGridSizeFragment
|
import code.name.monkey.retromusic.fragments.base.AbsRecyclerViewCustomGridSizeFragment
|
||||||
import code.name.monkey.retromusic.helper.SortOrder.ArtistSortOrder
|
import code.name.monkey.retromusic.helper.SortOrder.ArtistSortOrder
|
||||||
|
import code.name.monkey.retromusic.interfaces.IAlbumArtistClickListener
|
||||||
import code.name.monkey.retromusic.interfaces.IArtistClickListener
|
import code.name.monkey.retromusic.interfaces.IArtistClickListener
|
||||||
import code.name.monkey.retromusic.interfaces.ICabHolder
|
import code.name.monkey.retromusic.interfaces.ICabHolder
|
||||||
import code.name.monkey.retromusic.util.PreferenceUtil
|
import code.name.monkey.retromusic.util.PreferenceUtil
|
||||||
import code.name.monkey.retromusic.util.RetroColorUtil
|
import code.name.monkey.retromusic.util.RetroColorUtil
|
||||||
import code.name.monkey.retromusic.util.RetroUtil
|
import code.name.monkey.retromusic.util.RetroUtil
|
||||||
import com.afollestad.materialcab.MaterialCab
|
import com.afollestad.materialcab.MaterialCab
|
||||||
import com.google.android.material.transition.MaterialElevationScale
|
import com.google.android.gms.cast.framework.CastButtonFactory
|
||||||
|
|
||||||
class ArtistsFragment : AbsRecyclerViewCustomGridSizeFragment<ArtistAdapter, GridLayoutManager>(),
|
class ArtistsFragment : AbsRecyclerViewCustomGridSizeFragment<ArtistAdapter, GridLayoutManager>(),
|
||||||
IArtistClickListener, ICabHolder {
|
IArtistClickListener, IAlbumArtistClickListener, ICabHolder {
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
libraryViewModel.getArtists().observe(viewLifecycleOwner, Observer {
|
libraryViewModel.getArtists().observe(viewLifecycleOwner, {
|
||||||
if (it.isNotEmpty())
|
if (it.isNotEmpty())
|
||||||
adapter?.swapDataSet(it)
|
adapter?.swapDataSet(it)
|
||||||
else
|
else
|
||||||
adapter?.swapDataSet(listOf())
|
adapter?.swapDataSet(listOf())
|
||||||
})
|
})
|
||||||
|
requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner) {
|
||||||
|
if (!handleBackPress()) {
|
||||||
|
remove()
|
||||||
|
requireActivity().onBackPressed()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override val titleRes: Int
|
||||||
|
get() = R.string.artists
|
||||||
override val emptyMessage: Int
|
override val emptyMessage: Int
|
||||||
get() = R.string.no_artists
|
get() = R.string.no_artists
|
||||||
|
|
||||||
|
@ -66,7 +76,7 @@ class ArtistsFragment : AbsRecyclerViewCustomGridSizeFragment<ArtistAdapter, Gri
|
||||||
dataSet,
|
dataSet,
|
||||||
itemLayoutRes(),
|
itemLayoutRes(),
|
||||||
this,
|
this,
|
||||||
this
|
this, this
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -115,18 +125,23 @@ class ArtistsFragment : AbsRecyclerViewCustomGridSizeFragment<ArtistAdapter, Gri
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onArtist(artistId: Long, view: View) {
|
override fun onArtist(artistId: Long, view: View) {
|
||||||
exitTransition = MaterialElevationScale(true).apply {
|
|
||||||
duration = 300L
|
|
||||||
}
|
|
||||||
reenterTransition = MaterialElevationScale(false).apply {
|
|
||||||
duration = 300L
|
|
||||||
}
|
|
||||||
findNavController().navigate(
|
findNavController().navigate(
|
||||||
R.id.artistDetailsFragment,
|
R.id.artistDetailsFragment,
|
||||||
bundleOf(EXTRA_ARTIST_ID to artistId),
|
bundleOf(EXTRA_ARTIST_ID to artistId),
|
||||||
null,
|
null,
|
||||||
FragmentNavigatorExtras(view to "artist")
|
FragmentNavigatorExtras(view to artistId.toString())
|
||||||
)
|
)
|
||||||
|
reenterTransition = null
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onAlbumArtist(artistName: String, view: View) {
|
||||||
|
findNavController().navigate(
|
||||||
|
R.id.albumArtistDetailsFragment,
|
||||||
|
bundleOf(EXTRA_ARTIST_NAME to artistName),
|
||||||
|
null,
|
||||||
|
FragmentNavigatorExtras(view to artistName)
|
||||||
|
)
|
||||||
|
reenterTransition = null
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
||||||
|
@ -139,6 +154,16 @@ class ArtistsFragment : AbsRecyclerViewCustomGridSizeFragment<ArtistAdapter, Gri
|
||||||
val layoutItem = menu.findItem(R.id.action_layout_type)
|
val layoutItem = menu.findItem(R.id.action_layout_type)
|
||||||
setupLayoutMenu(layoutItem.subMenu)
|
setupLayoutMenu(layoutItem.subMenu)
|
||||||
setUpSortOrderMenu(menu.findItem(R.id.action_sort_order).subMenu)
|
setUpSortOrderMenu(menu.findItem(R.id.action_sort_order).subMenu)
|
||||||
|
setupAlbumArtistMenu(menu)
|
||||||
|
//Setting up cast button
|
||||||
|
CastButtonFactory.setUpMediaRouteButton(requireContext(), menu, R.id.action_cast)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setupAlbumArtistMenu(menu: Menu) {
|
||||||
|
menu.add(0, R.id.action_album_artist, 0, R.string.show_album_artists).apply {
|
||||||
|
isCheckable = true
|
||||||
|
isChecked = PreferenceUtil.albumArtistsOnly
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setUpSortOrderMenu(
|
private fun setUpSortOrderMenu(
|
||||||
|
@ -222,9 +247,23 @@ class ArtistsFragment : AbsRecyclerViewCustomGridSizeFragment<ArtistAdapter, Gri
|
||||||
if (handleSortOrderMenuItem(item)) {
|
if (handleSortOrderMenuItem(item)) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
if (handleAlbumArtistMenu(item)) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
return super.onOptionsItemSelected(item)
|
return super.onOptionsItemSelected(item)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun handleAlbumArtistMenu(item: MenuItem): Boolean {
|
||||||
|
return if (item.itemId == R.id.action_album_artist) {
|
||||||
|
PreferenceUtil.albumArtistsOnly = !item.isChecked
|
||||||
|
item.isChecked = !item.isChecked
|
||||||
|
libraryViewModel.forceReload(ReloadType.Artists)
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun handleSortOrderMenuItem(
|
private fun handleSortOrderMenuItem(
|
||||||
item: MenuItem
|
item: MenuItem
|
||||||
): Boolean {
|
): Boolean {
|
||||||
|
@ -283,6 +322,16 @@ class ArtistsFragment : AbsRecyclerViewCustomGridSizeFragment<ArtistAdapter, Gri
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun handleBackPress(): Boolean {
|
||||||
|
cab?.let {
|
||||||
|
if (it.isActive) {
|
||||||
|
it.finish()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
private var cab: MaterialCab? = null
|
private var cab: MaterialCab? = null
|
||||||
|
|
||||||
override fun openCab(menuRes: Int, callback: MaterialCab.Callback): MaterialCab {
|
override fun openCab(menuRes: Int, callback: MaterialCab.Callback): MaterialCab {
|
||||||
|
@ -298,4 +347,9 @@ class ArtistsFragment : AbsRecyclerViewCustomGridSizeFragment<ArtistAdapter, Gri
|
||||||
.start(callback)
|
.start(callback)
|
||||||
return cab as MaterialCab
|
return cab as MaterialCab
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onResume() {
|
||||||
|
super.onResume()
|
||||||
|
libraryViewModel.forceReload(ReloadType.Artists)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,10 +27,9 @@ import code.name.monkey.retromusic.activities.base.AbsMusicServiceActivity
|
||||||
import code.name.monkey.retromusic.interfaces.IMusicServiceEventListener
|
import code.name.monkey.retromusic.interfaces.IMusicServiceEventListener
|
||||||
import code.name.monkey.retromusic.model.Song
|
import code.name.monkey.retromusic.model.Song
|
||||||
import code.name.monkey.retromusic.util.RetroUtil
|
import code.name.monkey.retromusic.util.RetroUtil
|
||||||
|
import org.jaudiotagger.audio.AudioFileIO
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.net.URLEncoder
|
import java.net.URLEncoder
|
||||||
import java.util.*
|
|
||||||
import org.jaudiotagger.audio.AudioFileIO
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by hemanths on 18/08/17.
|
* Created by hemanths on 18/08/17.
|
||||||
|
@ -78,6 +77,9 @@ open class AbsMusicServiceFragment(@LayoutRes layout: Int) : Fragment(layout),
|
||||||
serviceActivity?.removeMusicServiceEventListener(this)
|
serviceActivity?.removeMusicServiceEventListener(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onFavoriteStateChanged() {
|
||||||
|
}
|
||||||
|
|
||||||
override fun onPlayingMetaChanged() {
|
override fun onPlayingMetaChanged() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -121,10 +123,10 @@ open class AbsMusicServiceFragment(@LayoutRes layout: Int) : Fragment(layout),
|
||||||
return "-"
|
return "-"
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getMimeType(url: String): String? {
|
private fun getMimeType(url: String): String {
|
||||||
var type: String? = MimeTypeMap.getFileExtensionFromUrl(
|
var type: String? = MimeTypeMap.getFileExtensionFromUrl(
|
||||||
URLEncoder.encode(url, "utf-8")
|
URLEncoder.encode(url, "utf-8")
|
||||||
).toUpperCase(Locale.getDefault())
|
).uppercase()
|
||||||
if (type == null) {
|
if (type == null) {
|
||||||
type = url.substring(url.lastIndexOf(".") + 1)
|
type = url.substring(url.lastIndexOf(".") + 1)
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,7 +44,7 @@ abstract class AbsPlayerControlsFragment(@LayoutRes layout: Int) : AbsMusicServi
|
||||||
|
|
||||||
abstract fun setColor(color: MediaNotificationProcessor)
|
abstract fun setColor(color: MediaNotificationProcessor)
|
||||||
|
|
||||||
fun showBonceAnimation(view: View) {
|
fun showBounceAnimation(view: View) {
|
||||||
view.apply {
|
view.apply {
|
||||||
clearAnimation()
|
clearAnimation()
|
||||||
scaleX = 0.9f
|
scaleX = 0.9f
|
||||||
|
|
|
@ -14,31 +14,41 @@
|
||||||
*/
|
*/
|
||||||
package code.name.monkey.retromusic.fragments.base
|
package code.name.monkey.retromusic.fragments.base
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
|
import android.app.Activity
|
||||||
import android.content.ContentUris
|
import android.content.ContentUris
|
||||||
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
|
import android.graphics.drawable.AnimatedVectorDrawable
|
||||||
import android.graphics.drawable.Drawable
|
import android.graphics.drawable.Drawable
|
||||||
import android.media.MediaMetadataRetriever
|
import android.media.MediaMetadataRetriever
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.provider.MediaStore
|
import android.provider.MediaStore
|
||||||
import android.text.TextUtils
|
import android.text.TextUtils
|
||||||
|
import android.view.GestureDetector
|
||||||
import android.view.MenuItem
|
import android.view.MenuItem
|
||||||
|
import android.view.MotionEvent
|
||||||
import android.view.View
|
import android.view.View
|
||||||
|
import android.widget.RelativeLayout
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.annotation.LayoutRes
|
import androidx.annotation.LayoutRes
|
||||||
import androidx.appcompat.widget.Toolbar
|
import androidx.appcompat.widget.Toolbar
|
||||||
import androidx.core.os.bundleOf
|
import androidx.core.os.bundleOf
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.navigation.findNavController
|
import androidx.navigation.findNavController
|
||||||
|
import androidx.viewpager.widget.ViewPager
|
||||||
import code.name.monkey.retromusic.EXTRA_ALBUM_ID
|
import code.name.monkey.retromusic.EXTRA_ALBUM_ID
|
||||||
import code.name.monkey.retromusic.EXTRA_ARTIST_ID
|
import code.name.monkey.retromusic.EXTRA_ARTIST_ID
|
||||||
import code.name.monkey.retromusic.R
|
import code.name.monkey.retromusic.R
|
||||||
|
import code.name.monkey.retromusic.activities.MainActivity
|
||||||
import code.name.monkey.retromusic.activities.tageditor.AbsTagEditorActivity
|
import code.name.monkey.retromusic.activities.tageditor.AbsTagEditorActivity
|
||||||
import code.name.monkey.retromusic.activities.tageditor.SongTagEditorActivity
|
import code.name.monkey.retromusic.activities.tageditor.SongTagEditorActivity
|
||||||
import code.name.monkey.retromusic.db.PlaylistEntity
|
import code.name.monkey.retromusic.db.PlaylistEntity
|
||||||
import code.name.monkey.retromusic.db.SongEntity
|
import code.name.monkey.retromusic.db.SongEntity
|
||||||
import code.name.monkey.retromusic.db.toSongEntity
|
import code.name.monkey.retromusic.db.toSongEntity
|
||||||
import code.name.monkey.retromusic.dialogs.*
|
import code.name.monkey.retromusic.dialogs.*
|
||||||
|
import code.name.monkey.retromusic.extensions.currentFragment
|
||||||
import code.name.monkey.retromusic.extensions.hide
|
import code.name.monkey.retromusic.extensions.hide
|
||||||
import code.name.monkey.retromusic.extensions.whichFragment
|
import code.name.monkey.retromusic.extensions.whichFragment
|
||||||
import code.name.monkey.retromusic.fragments.ReloadType
|
import code.name.monkey.retromusic.fragments.ReloadType
|
||||||
|
@ -50,13 +60,14 @@ import code.name.monkey.retromusic.model.lyrics.Lyrics
|
||||||
import code.name.monkey.retromusic.repository.RealRepository
|
import code.name.monkey.retromusic.repository.RealRepository
|
||||||
import code.name.monkey.retromusic.service.MusicService
|
import code.name.monkey.retromusic.service.MusicService
|
||||||
import code.name.monkey.retromusic.util.*
|
import code.name.monkey.retromusic.util.*
|
||||||
import kotlinx.android.synthetic.main.shadow_statusbar_toolbar.*
|
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
||||||
import kotlinx.coroutines.Dispatchers.IO
|
import kotlinx.coroutines.Dispatchers.IO
|
||||||
import kotlinx.coroutines.Dispatchers.Main
|
import kotlinx.coroutines.Dispatchers.Main
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import org.koin.android.ext.android.get
|
import org.koin.android.ext.android.get
|
||||||
import java.io.FileNotFoundException
|
import java.io.FileNotFoundException
|
||||||
|
import kotlin.math.abs
|
||||||
|
|
||||||
abstract class AbsPlayerFragment(@LayoutRes layout: Int) : AbsMainActivityFragment(layout),
|
abstract class AbsPlayerFragment(@LayoutRes layout: Int) : AbsMainActivityFragment(layout),
|
||||||
Toolbar.OnMenuItemClickListener, IPaletteColorHolder, PlayerAlbumCoverFragment.Callbacks {
|
Toolbar.OnMenuItemClickListener, IPaletteColorHolder, PlayerAlbumCoverFragment.Callbacks {
|
||||||
|
@ -68,6 +79,11 @@ abstract class AbsPlayerFragment(@LayoutRes layout: Int) : AbsMainActivityFragme
|
||||||
): Boolean {
|
): Boolean {
|
||||||
val song = MusicPlayerRemote.currentSong
|
val song = MusicPlayerRemote.currentSong
|
||||||
when (item.itemId) {
|
when (item.itemId) {
|
||||||
|
R.id.action_toggle_lyrics -> {
|
||||||
|
PreferenceUtil.showLyrics = !PreferenceUtil.showLyrics
|
||||||
|
showLyricsIcon(item)
|
||||||
|
return true
|
||||||
|
}
|
||||||
R.id.action_toggle_favorite -> {
|
R.id.action_toggle_favorite -> {
|
||||||
toggleFavorite(song)
|
toggleFavorite(song)
|
||||||
return true
|
return true
|
||||||
|
@ -114,6 +130,8 @@ abstract class AbsPlayerFragment(@LayoutRes layout: Int) : AbsMainActivityFragme
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
R.id.action_go_to_album -> {
|
R.id.action_go_to_album -> {
|
||||||
|
//Hide Bottom Bar First, else Bottom Sheet doesn't collapse fully
|
||||||
|
mainActivity.setBottomBarVisibility(false)
|
||||||
mainActivity.collapsePanel()
|
mainActivity.collapsePanel()
|
||||||
requireActivity().findNavController(R.id.fragment_container).navigate(
|
requireActivity().findNavController(R.id.fragment_container).navigate(
|
||||||
R.id.albumDetailsFragment,
|
R.id.albumDetailsFragment,
|
||||||
|
@ -122,11 +140,7 @@ abstract class AbsPlayerFragment(@LayoutRes layout: Int) : AbsMainActivityFragme
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
R.id.action_go_to_artist -> {
|
R.id.action_go_to_artist -> {
|
||||||
mainActivity.collapsePanel()
|
goToArtist(requireActivity())
|
||||||
requireActivity().findNavController(R.id.fragment_container).navigate(
|
|
||||||
R.id.artistDetailsFragment,
|
|
||||||
bundleOf(EXTRA_ARTIST_ID to song.artistId)
|
|
||||||
)
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
R.id.now_playing -> {
|
R.id.now_playing -> {
|
||||||
|
@ -158,7 +172,7 @@ abstract class AbsPlayerFragment(@LayoutRes layout: Int) : AbsMainActivityFragme
|
||||||
val trackUri =
|
val trackUri =
|
||||||
ContentUris.withAppendedId(
|
ContentUris.withAppendedId(
|
||||||
MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
|
MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
|
||||||
song.id.toLong()
|
song.id
|
||||||
)
|
)
|
||||||
retriever.setDataSource(activity, trackUri)
|
retriever.setDataSource(activity, trackUri)
|
||||||
var genre: String? =
|
var genre: String? =
|
||||||
|
@ -173,6 +187,18 @@ abstract class AbsPlayerFragment(@LayoutRes layout: Int) : AbsMainActivityFragme
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun showLyricsIcon(item: MenuItem) {
|
||||||
|
val icon =
|
||||||
|
if (PreferenceUtil.showLyrics) R.drawable.ic_lyrics else R.drawable.ic_lyrics_outline
|
||||||
|
val drawable: Drawable? = RetroUtil.getTintedVectorDrawable(
|
||||||
|
requireContext(),
|
||||||
|
icon,
|
||||||
|
toolbarIconColor()
|
||||||
|
)
|
||||||
|
item.isChecked = PreferenceUtil.showLyrics
|
||||||
|
item.icon = drawable
|
||||||
|
}
|
||||||
|
|
||||||
abstract fun playerToolbar(): Toolbar?
|
abstract fun playerToolbar(): Toolbar?
|
||||||
|
|
||||||
abstract fun onShow()
|
abstract fun onShow()
|
||||||
|
@ -185,7 +211,6 @@ abstract class AbsPlayerFragment(@LayoutRes layout: Int) : AbsMainActivityFragme
|
||||||
|
|
||||||
override fun onServiceConnected() {
|
override fun onServiceConnected() {
|
||||||
updateIsFavorite()
|
updateIsFavorite()
|
||||||
updateLyrics()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onPlayingMetaChanged() {
|
override fun onPlayingMetaChanged() {
|
||||||
|
@ -193,9 +218,13 @@ abstract class AbsPlayerFragment(@LayoutRes layout: Int) : AbsMainActivityFragme
|
||||||
updateLyrics()
|
updateLyrics()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onFavoriteStateChanged() {
|
||||||
|
updateIsFavorite(animate = true)
|
||||||
|
}
|
||||||
|
|
||||||
protected open fun toggleFavorite(song: Song) {
|
protected open fun toggleFavorite(song: Song) {
|
||||||
lifecycleScope.launch(IO) {
|
lifecycleScope.launch(IO) {
|
||||||
val playlist: PlaylistEntity? = libraryViewModel.favoritePlaylist()
|
val playlist: PlaylistEntity = libraryViewModel.favoritePlaylist()
|
||||||
if (playlist != null) {
|
if (playlist != null) {
|
||||||
val songEntity = song.toSongEntity(playlist.playListId)
|
val songEntity = song.toSongEntity(playlist.playListId)
|
||||||
val isFavorite = libraryViewModel.isFavoriteSong(songEntity).isNotEmpty()
|
val isFavorite = libraryViewModel.isFavoriteSong(songEntity).isNotEmpty()
|
||||||
|
@ -210,26 +239,36 @@ abstract class AbsPlayerFragment(@LayoutRes layout: Int) : AbsMainActivityFragme
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun updateIsFavorite() {
|
fun updateIsFavorite(animate: Boolean = false) {
|
||||||
lifecycleScope.launch(IO) {
|
lifecycleScope.launch(IO) {
|
||||||
val playlist: PlaylistEntity? = libraryViewModel.favoritePlaylist()
|
val playlist: PlaylistEntity = libraryViewModel.favoritePlaylist()
|
||||||
if (playlist != null) {
|
if (playlist != null) {
|
||||||
val song: SongEntity =
|
val song: SongEntity =
|
||||||
MusicPlayerRemote.currentSong.toSongEntity(playlist.playListId)
|
MusicPlayerRemote.currentSong.toSongEntity(playlist.playListId)
|
||||||
val isFavorite: Boolean = libraryViewModel.isFavoriteSong(song).isNotEmpty()
|
val isFavorite: Boolean = libraryViewModel.isFavoriteSong(song).isNotEmpty()
|
||||||
withContext(Main) {
|
withContext(Main) {
|
||||||
val icon =
|
val icon = if (animate) {
|
||||||
|
if (isFavorite) R.drawable.avd_favorite else R.drawable.avd_unfavorite
|
||||||
|
} else {
|
||||||
if (isFavorite) R.drawable.ic_favorite else R.drawable.ic_favorite_border
|
if (isFavorite) R.drawable.ic_favorite else R.drawable.ic_favorite_border
|
||||||
|
}
|
||||||
val drawable: Drawable? = RetroUtil.getTintedVectorDrawable(
|
val drawable: Drawable? = RetroUtil.getTintedVectorDrawable(
|
||||||
requireContext(),
|
requireContext(),
|
||||||
icon,
|
icon,
|
||||||
toolbarIconColor()
|
toolbarIconColor()
|
||||||
)
|
)
|
||||||
if (playerToolbar() != null) {
|
if (playerToolbar() != null) {
|
||||||
playerToolbar()?.menu?.findItem(R.id.action_toggle_favorite)
|
playerToolbar()?.menu?.findItem(R.id.action_toggle_favorite)?.apply {
|
||||||
?.setIcon(drawable)?.title =
|
setIcon(drawable)
|
||||||
if (isFavorite) getString(R.string.action_remove_from_favorites)
|
title =
|
||||||
else getString(R.string.action_add_to_favorites)
|
if (isFavorite) getString(R.string.action_remove_from_favorites)
|
||||||
|
else getString(R.string.action_add_to_favorites)
|
||||||
|
getIcon().also {
|
||||||
|
if (it is AnimatedVectorDrawable) {
|
||||||
|
it.start()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -273,7 +312,50 @@ abstract class AbsPlayerFragment(@LayoutRes layout: Int) : AbsMainActivityFragme
|
||||||
playerAlbumCoverFragment?.setCallbacks(this)
|
playerAlbumCoverFragment?.setCallbacks(this)
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
|
||||||
statusBarShadow?.hide()
|
view.findViewById<RelativeLayout>(R.id.statusBarShadow)?.hide()
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("ClickableViewAccessibility")
|
||||||
|
override fun onStart() {
|
||||||
|
super.onStart()
|
||||||
|
requireView().setOnTouchListener(
|
||||||
|
SwipeDetector(
|
||||||
|
requireContext(),
|
||||||
|
playerAlbumCoverFragment?.viewPager,
|
||||||
|
requireView()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
class SwipeDetector(val context: Context, val viewPager: ViewPager?, val view: View) :
|
||||||
|
View.OnTouchListener {
|
||||||
|
private var flingPlayBackController: GestureDetector = GestureDetector(
|
||||||
|
context,
|
||||||
|
object : GestureDetector.SimpleOnGestureListener() {
|
||||||
|
override fun onScroll(
|
||||||
|
e1: MotionEvent?,
|
||||||
|
e2: MotionEvent?,
|
||||||
|
distanceX: Float,
|
||||||
|
distanceY: Float
|
||||||
|
): Boolean {
|
||||||
|
return when {
|
||||||
|
abs(distanceX) > abs(distanceY) -> {
|
||||||
|
// Disallow Intercept Touch Event so that parent(BottomSheet) doesn't consume the events
|
||||||
|
view.parent.requestDisallowInterceptTouchEvent(true)
|
||||||
|
true
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
@SuppressLint("ClickableViewAccessibility")
|
||||||
|
override fun onTouch(v: View, event: MotionEvent?): Boolean {
|
||||||
|
viewPager?.dispatchTouchEvent(event)
|
||||||
|
return flingPlayBackController.onTouchEvent(event)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
@ -290,3 +372,44 @@ abstract class AbsPlayerFragment(@LayoutRes layout: Int) : AbsMainActivityFragme
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun goToArtist(activity: Activity) {
|
||||||
|
if (activity !is MainActivity) return
|
||||||
|
val song = MusicPlayerRemote.currentSong
|
||||||
|
activity.apply {
|
||||||
|
|
||||||
|
// Remove exit transition of current fragment so
|
||||||
|
// it doesn't exit with a weird transition
|
||||||
|
currentFragment(R.id.fragment_container)?.exitTransition = null
|
||||||
|
|
||||||
|
//Hide Bottom Bar First, else Bottom Sheet doesn't collapse fully
|
||||||
|
setBottomBarVisibility(false)
|
||||||
|
if (getBottomSheetBehavior().state == BottomSheetBehavior.STATE_EXPANDED) {
|
||||||
|
collapsePanel()
|
||||||
|
}
|
||||||
|
|
||||||
|
findNavController(R.id.fragment_container).navigate(
|
||||||
|
R.id.artistDetailsFragment,
|
||||||
|
bundleOf(EXTRA_ARTIST_ID to song.artistId)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun goToAlbum(activity: Activity) {
|
||||||
|
if (activity !is MainActivity) return
|
||||||
|
val song = MusicPlayerRemote.currentSong
|
||||||
|
activity.apply {
|
||||||
|
currentFragment(R.id.fragment_container)?.exitTransition = null
|
||||||
|
|
||||||
|
//Hide Bottom Bar First, else Bottom Sheet doesn't collapse fully
|
||||||
|
setBottomBarVisibility(false)
|
||||||
|
if (getBottomSheetBehavior().state == BottomSheetBehavior.STATE_EXPANDED) {
|
||||||
|
collapsePanel()
|
||||||
|
}
|
||||||
|
|
||||||
|
findNavController(R.id.fragment_container).navigate(
|
||||||
|
R.id.albumDetailsFragment,
|
||||||
|
bundleOf(EXTRA_ALBUM_ID to song.albumId)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
|
@ -14,12 +14,12 @@
|
||||||
*/
|
*/
|
||||||
package code.name.monkey.retromusic.fragments.base
|
package code.name.monkey.retromusic.fragments.base
|
||||||
|
|
||||||
import android.os.Bundle
|
import androidx.core.view.isVisible
|
||||||
import android.view.View
|
|
||||||
import androidx.annotation.LayoutRes
|
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import androidx.transition.TransitionManager
|
||||||
import code.name.monkey.retromusic.R
|
import code.name.monkey.retromusic.R
|
||||||
import code.name.monkey.retromusic.util.RetroUtil
|
import code.name.monkey.retromusic.util.RetroUtil
|
||||||
|
import com.google.android.material.transition.MaterialFade
|
||||||
|
|
||||||
abstract class AbsRecyclerViewCustomGridSizeFragment<A : RecyclerView.Adapter<*>, LM : RecyclerView.LayoutManager> :
|
abstract class AbsRecyclerViewCustomGridSizeFragment<A : RecyclerView.Adapter<*>, LM : RecyclerView.LayoutManager> :
|
||||||
AbsRecyclerViewFragment<A, LM>() {
|
AbsRecyclerViewFragment<A, LM>() {
|
||||||
|
@ -86,6 +86,7 @@ abstract class AbsRecyclerViewCustomGridSizeFragment<A : RecyclerView.Adapter<*>
|
||||||
} else {
|
} else {
|
||||||
saveGridSize(gridSize)
|
saveGridSize(gridSize)
|
||||||
}
|
}
|
||||||
|
recyclerView().isVisible = false
|
||||||
invalidateLayoutManager()
|
invalidateLayoutManager()
|
||||||
// only recreate the adapter and layout manager if the layout currentLayoutRes has changed
|
// only recreate the adapter and layout manager if the layout currentLayoutRes has changed
|
||||||
if (oldLayoutRes != itemLayoutRes()) {
|
if (oldLayoutRes != itemLayoutRes()) {
|
||||||
|
@ -93,26 +94,11 @@ abstract class AbsRecyclerViewCustomGridSizeFragment<A : RecyclerView.Adapter<*>
|
||||||
} else {
|
} else {
|
||||||
setGridSize(gridSize)
|
setGridSize(gridSize)
|
||||||
}
|
}
|
||||||
}
|
val transition = MaterialFade().apply {
|
||||||
|
addTarget(recyclerView())
|
||||||
protected fun notifyLayoutResChanged(@LayoutRes res: Int) {
|
|
||||||
this.currentLayoutRes = res
|
|
||||||
val recyclerView = recyclerView()
|
|
||||||
applyRecyclerViewPaddingForLayoutRes(recyclerView, currentLayoutRes)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
|
||||||
super.onViewCreated(view, savedInstanceState)
|
|
||||||
applyRecyclerViewPaddingForLayoutRes(recyclerView(), currentLayoutRes)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun applyRecyclerViewPaddingForLayoutRes(recyclerView: RecyclerView, res: Int) {
|
|
||||||
val padding: Int = if (res == R.layout.item_grid) {
|
|
||||||
(resources.displayMetrics.density * 2).toInt()
|
|
||||||
} else {
|
|
||||||
0
|
|
||||||
}
|
}
|
||||||
//recyclerView.setPadding(padding, padding, padding, padding)
|
TransitionManager.beginDelayedTransition(getContainer(), transition)
|
||||||
|
recyclerView().isVisible = true
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract fun setGridSize(gridSize: Int)
|
protected abstract fun setGridSize(gridSize: Int)
|
||||||
|
|
|
@ -15,80 +15,73 @@
|
||||||
package code.name.monkey.retromusic.fragments.base
|
package code.name.monkey.retromusic.fragments.base
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.Menu
|
import android.view.*
|
||||||
import android.view.MenuInflater
|
|
||||||
import android.view.MenuItem
|
|
||||||
import android.view.View
|
|
||||||
import androidx.annotation.NonNull
|
import androidx.annotation.NonNull
|
||||||
import androidx.annotation.StringRes
|
import androidx.annotation.StringRes
|
||||||
import androidx.core.text.HtmlCompat
|
import androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||||
import androidx.core.view.doOnPreDraw
|
import androidx.core.view.doOnPreDraw
|
||||||
import androidx.core.view.updatePadding
|
import androidx.core.view.updatePadding
|
||||||
import androidx.navigation.fragment.findNavController
|
import androidx.navigation.fragment.findNavController
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import code.name.monkey.appthemehelper.ThemeStore
|
|
||||||
import code.name.monkey.appthemehelper.common.ATHToolbarActivity
|
import code.name.monkey.appthemehelper.common.ATHToolbarActivity
|
||||||
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
|
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
|
||||||
import code.name.monkey.retromusic.R
|
import code.name.monkey.retromusic.R
|
||||||
|
import code.name.monkey.retromusic.databinding.FragmentMainRecyclerBinding
|
||||||
import code.name.monkey.retromusic.dialogs.CreatePlaylistDialog
|
import code.name.monkey.retromusic.dialogs.CreatePlaylistDialog
|
||||||
import code.name.monkey.retromusic.dialogs.ImportPlaylistDialog
|
import code.name.monkey.retromusic.dialogs.ImportPlaylistDialog
|
||||||
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
||||||
import code.name.monkey.retromusic.util.DensityUtil
|
import code.name.monkey.retromusic.util.DensityUtil
|
||||||
import code.name.monkey.retromusic.util.ThemedFastScroller.create
|
import code.name.monkey.retromusic.util.ThemedFastScroller.create
|
||||||
import com.google.android.material.appbar.AppBarLayout
|
import com.google.android.material.transition.MaterialFadeThrough
|
||||||
import kotlinx.android.synthetic.main.fragment_main_recycler.*
|
import com.google.android.material.transition.MaterialSharedAxis
|
||||||
import me.zhanghai.android.fastscroll.FastScroller
|
import me.zhanghai.android.fastscroll.FastScroller
|
||||||
import me.zhanghai.android.fastscroll.FastScrollerBuilder
|
import me.zhanghai.android.fastscroll.FastScrollerBuilder
|
||||||
|
|
||||||
abstract class AbsRecyclerViewFragment<A : RecyclerView.Adapter<*>, LM : RecyclerView.LayoutManager> :
|
abstract class AbsRecyclerViewFragment<A : RecyclerView.Adapter<*>, LM : RecyclerView.LayoutManager> :
|
||||||
AbsMainActivityFragment(R.layout.fragment_main_recycler),
|
AbsMainActivityFragment(R.layout.fragment_main_recycler) {
|
||||||
AppBarLayout.OnOffsetChangedListener {
|
|
||||||
|
|
||||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
|
||||||
super.onActivityCreated(savedInstanceState)
|
|
||||||
setHasOptionsMenu(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
private var _binding: FragmentMainRecyclerBinding? = null
|
||||||
|
private val binding get() = _binding!!
|
||||||
protected var adapter: A? = null
|
protected var adapter: A? = null
|
||||||
protected var layoutManager: LM? = null
|
protected var layoutManager: LM? = null
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
_binding = FragmentMainRecyclerBinding.bind(view)
|
||||||
|
enterTransition = MaterialFadeThrough()
|
||||||
|
exitTransition = MaterialFadeThrough()
|
||||||
postponeEnterTransition()
|
postponeEnterTransition()
|
||||||
view.doOnPreDraw { startPostponedEnterTransition() }
|
view.doOnPreDraw { startPostponedEnterTransition() }
|
||||||
|
|
||||||
mainActivity.setBottomBarVisibility(true)
|
mainActivity.setSupportActionBar(binding.toolbar)
|
||||||
mainActivity.setSupportActionBar(toolbar)
|
|
||||||
mainActivity.supportActionBar?.title = null
|
mainActivity.supportActionBar?.title = null
|
||||||
initLayoutManager()
|
initLayoutManager()
|
||||||
initAdapter()
|
initAdapter()
|
||||||
setUpRecyclerView()
|
setUpRecyclerView()
|
||||||
setupTitle()
|
setupToolbar()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setupTitle() {
|
private fun setupToolbar() {
|
||||||
toolbar.setNavigationOnClickListener {
|
binding.toolbar.setNavigationOnClickListener {
|
||||||
|
exitTransition = MaterialSharedAxis(MaterialSharedAxis.Z, true).addTarget(requireView())
|
||||||
|
reenterTransition = MaterialSharedAxis(MaterialSharedAxis.Z, false)
|
||||||
findNavController().navigate(
|
findNavController().navigate(
|
||||||
R.id.searchFragment,
|
R.id.searchFragment,
|
||||||
null,
|
null,
|
||||||
navOptions
|
navOptions
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
val color = ThemeStore.accentColor(requireContext())
|
val appName = resources.getString(titleRes)
|
||||||
val hexColor = String.format("#%06X", 0xFFFFFF and color)
|
binding.appNameText.text = appName
|
||||||
val appName = HtmlCompat.fromHtml(
|
|
||||||
"Retro <span style='color:$hexColor';>Music</span>",
|
|
||||||
HtmlCompat.FROM_HTML_MODE_COMPACT
|
|
||||||
)
|
|
||||||
appNameText.text = appName
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
abstract val titleRes: Int
|
||||||
|
|
||||||
private fun setUpRecyclerView() {
|
private fun setUpRecyclerView() {
|
||||||
recyclerView.apply {
|
binding.recyclerView.apply {
|
||||||
layoutManager = this@AbsRecyclerViewFragment.layoutManager
|
layoutManager = this@AbsRecyclerViewFragment.layoutManager
|
||||||
adapter = this@AbsRecyclerViewFragment.adapter
|
adapter = this@AbsRecyclerViewFragment.adapter
|
||||||
val fastScroller = create(this)
|
create(this)
|
||||||
}
|
}
|
||||||
checkForPadding()
|
checkForPadding()
|
||||||
}
|
}
|
||||||
|
@ -116,8 +109,8 @@ abstract class AbsRecyclerViewFragment<A : RecyclerView.Adapter<*>, LM : Recycle
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun checkIsEmpty() {
|
private fun checkIsEmpty() {
|
||||||
emptyText.setText(emptyMessage)
|
binding.emptyText.setText(emptyMessage)
|
||||||
empty.visibility = if (adapter!!.itemCount == 0) View.VISIBLE else View.GONE
|
binding.empty.visibility = if (adapter!!.itemCount == 0) View.VISIBLE else View.GONE
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun checkForPadding() {
|
private fun checkForPadding() {
|
||||||
|
@ -125,10 +118,10 @@ abstract class AbsRecyclerViewFragment<A : RecyclerView.Adapter<*>, LM : Recycle
|
||||||
|
|
||||||
if (itemCount > 0 && MusicPlayerRemote.playingQueue.isNotEmpty()) {
|
if (itemCount > 0 && MusicPlayerRemote.playingQueue.isNotEmpty()) {
|
||||||
val height = DensityUtil.dip2px(requireContext(), 112f)
|
val height = DensityUtil.dip2px(requireContext(), 112f)
|
||||||
recyclerView.updatePadding(0, 0, 0, height)
|
binding.recyclerView.updatePadding(0, 0, 0, height)
|
||||||
} else {
|
} else {
|
||||||
val height = DensityUtil.dip2px(requireContext(), 56f)
|
val height = DensityUtil.dip2px(requireContext(), 56f)
|
||||||
recyclerView.updatePadding(0, 0, 0, height)
|
binding.recyclerView.updatePadding(0, 0, 0, height)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -141,15 +134,6 @@ abstract class AbsRecyclerViewFragment<A : RecyclerView.Adapter<*>, LM : Recycle
|
||||||
@NonNull
|
@NonNull
|
||||||
protected abstract fun createAdapter(): A
|
protected abstract fun createAdapter(): A
|
||||||
|
|
||||||
override fun onOffsetChanged(p0: AppBarLayout?, i: Int) {
|
|
||||||
/*recyclerView.setPadding(
|
|
||||||
recyclerView.paddingLeft,
|
|
||||||
recyclerView.paddingTop,
|
|
||||||
recyclerView.paddingRight,
|
|
||||||
i
|
|
||||||
)*/
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onQueueChanged() {
|
override fun onQueueChanged() {
|
||||||
super.onQueueChanged()
|
super.onQueueChanged()
|
||||||
checkForPadding()
|
checkForPadding()
|
||||||
|
@ -162,22 +146,31 @@ abstract class AbsRecyclerViewFragment<A : RecyclerView.Adapter<*>, LM : Recycle
|
||||||
|
|
||||||
protected fun invalidateLayoutManager() {
|
protected fun invalidateLayoutManager() {
|
||||||
initLayoutManager()
|
initLayoutManager()
|
||||||
recyclerView.layoutManager = layoutManager
|
binding.recyclerView.layoutManager = layoutManager
|
||||||
}
|
}
|
||||||
|
|
||||||
protected fun invalidateAdapter() {
|
protected fun invalidateAdapter() {
|
||||||
initAdapter()
|
initAdapter()
|
||||||
checkIsEmpty()
|
checkIsEmpty()
|
||||||
recyclerView.adapter = adapter
|
binding.recyclerView.adapter = adapter
|
||||||
}
|
}
|
||||||
|
|
||||||
fun recyclerView(): RecyclerView {
|
fun recyclerView(): RecyclerView {
|
||||||
return recyclerView
|
return binding.recyclerView
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getContainer(): CoordinatorLayout {
|
||||||
|
return binding.root
|
||||||
|
}
|
||||||
|
|
||||||
|
fun scrollToTop() {
|
||||||
|
recyclerView().scrollToPosition(0)
|
||||||
|
binding.appBarLayout.setExpanded(true, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onPrepareOptionsMenu(menu: Menu) {
|
override fun onPrepareOptionsMenu(menu: Menu) {
|
||||||
super.onPrepareOptionsMenu(menu)
|
super.onPrepareOptionsMenu(menu)
|
||||||
ToolbarContentTintHelper.handleOnPrepareOptionsMenu(requireActivity(), toolbar)
|
ToolbarContentTintHelper.handleOnPrepareOptionsMenu(requireActivity(), binding.toolbar)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
||||||
|
@ -185,9 +178,9 @@ abstract class AbsRecyclerViewFragment<A : RecyclerView.Adapter<*>, LM : Recycle
|
||||||
inflater.inflate(R.menu.menu_main, menu)
|
inflater.inflate(R.menu.menu_main, menu)
|
||||||
ToolbarContentTintHelper.handleOnCreateOptionsMenu(
|
ToolbarContentTintHelper.handleOnCreateOptionsMenu(
|
||||||
requireContext(),
|
requireContext(),
|
||||||
toolbar,
|
binding.toolbar,
|
||||||
menu,
|
menu,
|
||||||
ATHToolbarActivity.getToolbarBackgroundColor(toolbar)
|
ATHToolbarActivity.getToolbarBackgroundColor(binding.toolbar)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -209,4 +202,9 @@ abstract class AbsRecyclerViewFragment<A : RecyclerView.Adapter<*>, LM : Recycle
|
||||||
}
|
}
|
||||||
return super.onOptionsItemSelected(item)
|
return super.onOptionsItemSelected(item)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onDestroyView() {
|
||||||
|
super.onDestroyView()
|
||||||
|
_binding = null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,13 +14,14 @@
|
||||||
|
|
||||||
package code.name.monkey.retromusic.fragments.folder;
|
package code.name.monkey.retromusic.fragments.folder;
|
||||||
|
|
||||||
|
import static code.name.monkey.appthemehelper.common.ATHToolbarActivity.getToolbarBackgroundColor;
|
||||||
|
|
||||||
import android.app.Dialog;
|
import android.app.Dialog;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.media.MediaScannerConnection;
|
import android.media.MediaScannerConnection;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Environment;
|
import android.os.Environment;
|
||||||
import android.text.Html;
|
import android.text.Html;
|
||||||
import android.text.Spanned;
|
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import android.view.MenuInflater;
|
import android.view.MenuInflater;
|
||||||
|
@ -30,13 +31,11 @@ import android.view.ViewGroup;
|
||||||
import android.view.ViewGroup.MarginLayoutParams;
|
import android.view.ViewGroup.MarginLayoutParams;
|
||||||
import android.webkit.MimeTypeMap;
|
import android.webkit.MimeTypeMap;
|
||||||
import android.widget.PopupMenu;
|
import android.widget.PopupMenu;
|
||||||
import android.widget.TextView;
|
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import androidx.activity.OnBackPressedCallback;
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.appcompat.widget.Toolbar;
|
|
||||||
import androidx.core.text.HtmlCompat;
|
|
||||||
import androidx.loader.app.LoaderManager;
|
import androidx.loader.app.LoaderManager;
|
||||||
import androidx.loader.content.Loader;
|
import androidx.loader.content.Loader;
|
||||||
import androidx.navigation.Navigation;
|
import androidx.navigation.Navigation;
|
||||||
|
@ -46,25 +45,36 @@ import androidx.recyclerview.widget.RecyclerView;
|
||||||
import com.afollestad.materialcab.MaterialCab;
|
import com.afollestad.materialcab.MaterialCab;
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||||
import com.google.android.material.snackbar.Snackbar;
|
import com.google.android.material.snackbar.Snackbar;
|
||||||
|
import com.google.android.material.transition.MaterialFadeThrough;
|
||||||
|
import com.google.android.material.transition.MaterialSharedAxis;
|
||||||
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileFilter;
|
import java.io.FileFilter;
|
||||||
|
import java.io.FileReader;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.lang.ref.WeakReference;
|
import java.lang.ref.WeakReference;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.StringTokenizer;
|
||||||
|
|
||||||
import code.name.monkey.appthemehelper.ThemeStore;
|
import code.name.monkey.appthemehelper.ThemeStore;
|
||||||
import code.name.monkey.appthemehelper.util.ATHUtil;
|
import code.name.monkey.appthemehelper.util.ATHUtil;
|
||||||
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper;
|
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper;
|
||||||
|
import code.name.monkey.retromusic.App;
|
||||||
import code.name.monkey.retromusic.R;
|
import code.name.monkey.retromusic.R;
|
||||||
import code.name.monkey.retromusic.adapter.SongFileAdapter;
|
import code.name.monkey.retromusic.adapter.SongFileAdapter;
|
||||||
|
import code.name.monkey.retromusic.adapter.Storage;
|
||||||
|
import code.name.monkey.retromusic.adapter.StorageAdapter;
|
||||||
|
import code.name.monkey.retromusic.adapter.StorageClickListener;
|
||||||
|
import code.name.monkey.retromusic.databinding.FragmentFolderBinding;
|
||||||
import code.name.monkey.retromusic.fragments.base.AbsMainActivityFragment;
|
import code.name.monkey.retromusic.fragments.base.AbsMainActivityFragment;
|
||||||
import code.name.monkey.retromusic.helper.MusicPlayerRemote;
|
import code.name.monkey.retromusic.helper.MusicPlayerRemote;
|
||||||
import code.name.monkey.retromusic.helper.menu.SongMenuHelper;
|
import code.name.monkey.retromusic.helper.menu.SongMenuHelper;
|
||||||
|
@ -76,6 +86,7 @@ import code.name.monkey.retromusic.misc.DialogAsyncTask;
|
||||||
import code.name.monkey.retromusic.misc.UpdateToastMediaScannerCompletionListener;
|
import code.name.monkey.retromusic.misc.UpdateToastMediaScannerCompletionListener;
|
||||||
import code.name.monkey.retromusic.misc.WrappedAsyncTaskLoader;
|
import code.name.monkey.retromusic.misc.WrappedAsyncTaskLoader;
|
||||||
import code.name.monkey.retromusic.model.Song;
|
import code.name.monkey.retromusic.model.Song;
|
||||||
|
import code.name.monkey.retromusic.providers.BlacklistStore;
|
||||||
import code.name.monkey.retromusic.util.DensityUtil;
|
import code.name.monkey.retromusic.util.DensityUtil;
|
||||||
import code.name.monkey.retromusic.util.FileUtil;
|
import code.name.monkey.retromusic.util.FileUtil;
|
||||||
import code.name.monkey.retromusic.util.PreferenceUtil;
|
import code.name.monkey.retromusic.util.PreferenceUtil;
|
||||||
|
@ -85,15 +96,14 @@ import code.name.monkey.retromusic.views.BreadCrumbLayout;
|
||||||
import code.name.monkey.retromusic.views.ScrollingViewOnApplyWindowInsetsListener;
|
import code.name.monkey.retromusic.views.ScrollingViewOnApplyWindowInsetsListener;
|
||||||
import me.zhanghai.android.fastscroll.FastScroller;
|
import me.zhanghai.android.fastscroll.FastScroller;
|
||||||
|
|
||||||
import static code.name.monkey.appthemehelper.common.ATHToolbarActivity.getToolbarBackgroundColor;
|
|
||||||
|
|
||||||
public class FoldersFragment extends AbsMainActivityFragment
|
public class FoldersFragment extends AbsMainActivityFragment
|
||||||
implements IMainActivityFragmentCallbacks,
|
implements IMainActivityFragmentCallbacks,
|
||||||
ICabHolder,
|
ICabHolder,
|
||||||
BreadCrumbLayout.SelectionCallback,
|
BreadCrumbLayout.SelectionCallback,
|
||||||
ICallbacks,
|
ICallbacks,
|
||||||
LoaderManager.LoaderCallbacks<List<File>> {
|
LoaderManager.LoaderCallbacks<List<File>>, StorageClickListener {
|
||||||
|
|
||||||
|
private FragmentFolderBinding binding;
|
||||||
public static final String TAG = FoldersFragment.class.getSimpleName();
|
public static final String TAG = FoldersFragment.class.getSimpleName();
|
||||||
public static final FileFilter AUDIO_FILE_FILTER =
|
public static final FileFilter AUDIO_FILE_FILTER =
|
||||||
file ->
|
file ->
|
||||||
|
@ -106,14 +116,9 @@ public class FoldersFragment extends AbsMainActivityFragment
|
||||||
private static final String CRUMBS = "crumbs";
|
private static final String CRUMBS = "crumbs";
|
||||||
private static final int LOADER_ID = 5;
|
private static final int LOADER_ID = 5;
|
||||||
private SongFileAdapter adapter;
|
private SongFileAdapter adapter;
|
||||||
private Toolbar toolbar;
|
private StorageAdapter storageAdapter;
|
||||||
private TextView appNameText;
|
|
||||||
private BreadCrumbLayout breadCrumbs;
|
|
||||||
private MaterialCab cab;
|
private MaterialCab cab;
|
||||||
private View coordinatorLayout;
|
private final Comparator<File> fileComparator =
|
||||||
private View empty;
|
|
||||||
private TextView emojiText;
|
|
||||||
private Comparator<File> fileComparator =
|
|
||||||
(lhs, rhs) -> {
|
(lhs, rhs) -> {
|
||||||
if (lhs.isDirectory() && !rhs.isDirectory()) {
|
if (lhs.isDirectory() && !rhs.isDirectory()) {
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -123,7 +128,7 @@ public class FoldersFragment extends AbsMainActivityFragment
|
||||||
return lhs.getName().compareToIgnoreCase(rhs.getName());
|
return lhs.getName().compareToIgnoreCase(rhs.getName());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
private RecyclerView recyclerView;
|
private final ArrayList<Storage> storageItems = new ArrayList<>();
|
||||||
|
|
||||||
public FoldersFragment() {
|
public FoldersFragment() {
|
||||||
super(R.layout.fragment_folder);
|
super(R.layout.fragment_folder);
|
||||||
|
@ -158,35 +163,43 @@ public class FoldersFragment extends AbsMainActivityFragment
|
||||||
@Override
|
@Override
|
||||||
public View onCreateView(
|
public View onCreateView(
|
||||||
@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||||
View view = inflater.inflate(R.layout.fragment_folder, container, false);
|
binding = FragmentFolderBinding.inflate(inflater, container, false);
|
||||||
initViews(view);
|
return binding.getRoot();
|
||||||
return view;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||||
|
setEnterTransition(new MaterialFadeThrough());
|
||||||
|
setExitTransition(new MaterialFadeThrough());
|
||||||
getMainActivity().addMusicServiceEventListener(getLibraryViewModel());
|
getMainActivity().addMusicServiceEventListener(getLibraryViewModel());
|
||||||
getMainActivity().setBottomBarVisibility(true);
|
getMainActivity().setSupportActionBar(binding.toolbar);
|
||||||
getMainActivity().setSupportActionBar(toolbar);
|
|
||||||
getMainActivity().getSupportActionBar().setTitle(null);
|
getMainActivity().getSupportActionBar().setTitle(null);
|
||||||
setStatusBarColorAuto(view);
|
setStatusBarColorAuto(view);
|
||||||
setUpAppbarColor();
|
setUpAppbarColor();
|
||||||
setUpBreadCrumbs();
|
setUpBreadCrumbs();
|
||||||
setUpRecyclerView();
|
setUpRecyclerView();
|
||||||
|
listRoots();
|
||||||
setUpAdapter();
|
setUpAdapter();
|
||||||
setUpTitle();
|
setUpTitle();
|
||||||
|
requireActivity().getOnBackPressedDispatcher().addCallback(getViewLifecycleOwner(), new OnBackPressedCallback(true) {
|
||||||
|
@Override
|
||||||
|
public void handleOnBackPressed() {
|
||||||
|
if (!handleBackPress()) {
|
||||||
|
remove();
|
||||||
|
requireActivity().onBackPressed();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setUpTitle() {
|
private void setUpTitle() {
|
||||||
toolbar.setNavigationOnClickListener(
|
binding.toolbar.setNavigationOnClickListener(
|
||||||
v -> Navigation.findNavController(v).navigate(R.id.searchFragment, null, getNavOptions()));
|
v -> {
|
||||||
int color = ThemeStore.Companion.accentColor(requireContext());
|
setExitTransition(new MaterialSharedAxis(MaterialSharedAxis.Z, true).setDuration(300));
|
||||||
String hexColor = String.format("#%06X", 0xFFFFFF & color);
|
setReenterTransition(new MaterialSharedAxis(MaterialSharedAxis.Z, false).setDuration(300));
|
||||||
Spanned appName =
|
Navigation.findNavController(v).navigate(R.id.searchFragment, null, getNavOptions());
|
||||||
HtmlCompat.fromHtml(
|
});
|
||||||
"Retro <span style='color:" + hexColor + ";'>Music</span>",
|
binding.appNameText.setText(getResources().getString(R.string.folders));
|
||||||
HtmlCompat.FROM_HTML_MODE_COMPACT);
|
|
||||||
appNameText.setText(appName);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -194,12 +207,14 @@ public class FoldersFragment extends AbsMainActivityFragment
|
||||||
super.onActivityCreated(savedInstanceState);
|
super.onActivityCreated(savedInstanceState);
|
||||||
setHasOptionsMenu(true);
|
setHasOptionsMenu(true);
|
||||||
if (savedInstanceState == null) {
|
if (savedInstanceState == null) {
|
||||||
|
switchToFileAdapter();
|
||||||
setCrumb(
|
setCrumb(
|
||||||
new BreadCrumbLayout.Crumb(
|
new BreadCrumbLayout.Crumb(
|
||||||
FileUtil.safeGetCanonicalFile(PreferenceUtil.INSTANCE.getStartDirectory())),
|
FileUtil.safeGetCanonicalFile(PreferenceUtil.INSTANCE.getStartDirectory())),
|
||||||
true);
|
true);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
breadCrumbs.restoreFromStateWrapper(savedInstanceState.getParcelable(CRUMBS));
|
binding.breadCrumbs.restoreFromStateWrapper(savedInstanceState.getParcelable(CRUMBS));
|
||||||
LoaderManager.getInstance(this).initLoader(LOADER_ID, null, this);
|
LoaderManager.getInstance(this).initLoader(LOADER_ID, null, this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -213,8 +228,8 @@ public class FoldersFragment extends AbsMainActivityFragment
|
||||||
@Override
|
@Override
|
||||||
public void onSaveInstanceState(@NonNull Bundle outState) {
|
public void onSaveInstanceState(@NonNull Bundle outState) {
|
||||||
super.onSaveInstanceState(outState);
|
super.onSaveInstanceState(outState);
|
||||||
if (breadCrumbs != null) {
|
if (binding != null) {
|
||||||
outState.putParcelable(CRUMBS, breadCrumbs.getStateWrapper());
|
outState.putParcelable(CRUMBS, binding.breadCrumbs.getStateWrapper());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -224,8 +239,8 @@ public class FoldersFragment extends AbsMainActivityFragment
|
||||||
cab.finish();
|
cab.finish();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (breadCrumbs != null && breadCrumbs.popHistory()) {
|
if (binding.breadCrumbs.popHistory()) {
|
||||||
setCrumb(breadCrumbs.lastHistory(), false);
|
setCrumb(binding.breadCrumbs.lastHistory(), false);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
@ -268,6 +283,9 @@ public class FoldersFragment extends AbsMainActivityFragment
|
||||||
new ListSongsAsyncTask.LoadingInfo(
|
new ListSongsAsyncTask.LoadingInfo(
|
||||||
toList(file), AUDIO_FILE_FILTER, getFileComparator()));
|
toList(file), AUDIO_FILE_FILTER, getFileComparator()));
|
||||||
return true;
|
return true;
|
||||||
|
case R.id.action_add_to_blacklist:
|
||||||
|
BlacklistStore.getInstance(App.Companion.getContext()).addPath(file);
|
||||||
|
return true;
|
||||||
case R.id.action_set_as_start_directory:
|
case R.id.action_set_as_start_directory:
|
||||||
PreferenceUtil.INSTANCE.setStartDirectory(file);
|
PreferenceUtil.INSTANCE.setStartDirectory(file);
|
||||||
Toast.makeText(
|
Toast.makeText(
|
||||||
|
@ -347,7 +365,7 @@ public class FoldersFragment extends AbsMainActivityFragment
|
||||||
} else {
|
} else {
|
||||||
final File finalFile = file1;
|
final File finalFile = file1;
|
||||||
Snackbar.make(
|
Snackbar.make(
|
||||||
coordinatorLayout,
|
binding.coordinatorLayout,
|
||||||
Html.fromHtml(
|
Html.fromHtml(
|
||||||
String.format(
|
String.format(
|
||||||
getString(R.string.not_listed_in_media_store), file1.getName())),
|
getString(R.string.not_listed_in_media_store), file1.getName())),
|
||||||
|
@ -376,7 +394,7 @@ public class FoldersFragment extends AbsMainActivityFragment
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onLoaderReset(@NonNull Loader<List<File>> loader) {
|
public void onLoaderReset(@NonNull Loader<List<File>> loader) {
|
||||||
updateAdapter(new LinkedList<File>());
|
updateAdapter(new LinkedList<>());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -393,7 +411,7 @@ public class FoldersFragment extends AbsMainActivityFragment
|
||||||
@Override
|
@Override
|
||||||
public void onPrepareOptionsMenu(@NonNull Menu menu) {
|
public void onPrepareOptionsMenu(@NonNull Menu menu) {
|
||||||
super.onPrepareOptionsMenu(menu);
|
super.onPrepareOptionsMenu(menu);
|
||||||
ToolbarContentTintHelper.handleOnPrepareOptionsMenu(requireActivity(), toolbar);
|
ToolbarContentTintHelper.handleOnPrepareOptionsMenu(requireActivity(), binding.toolbar);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -407,7 +425,7 @@ public class FoldersFragment extends AbsMainActivityFragment
|
||||||
menu.removeItem(R.id.action_layout_type);
|
menu.removeItem(R.id.action_layout_type);
|
||||||
menu.removeItem(R.id.action_sort_order);
|
menu.removeItem(R.id.action_sort_order);
|
||||||
ToolbarContentTintHelper.handleOnCreateOptionsMenu(
|
ToolbarContentTintHelper.handleOnCreateOptionsMenu(
|
||||||
requireContext(), toolbar, menu, getToolbarBackgroundColor(toolbar));
|
requireContext(), binding.toolbar, menu, getToolbarBackgroundColor(binding.toolbar));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -462,26 +480,32 @@ public class FoldersFragment extends AbsMainActivityFragment
|
||||||
|
|
||||||
private void checkForPadding() {
|
private void checkForPadding() {
|
||||||
final int count = adapter.getItemCount();
|
final int count = adapter.getItemCount();
|
||||||
final MarginLayoutParams params = (MarginLayoutParams) coordinatorLayout.getLayoutParams();
|
if (binding != null) {
|
||||||
params.bottomMargin =
|
final MarginLayoutParams params = (MarginLayoutParams) binding.coordinatorLayout.getLayoutParams();
|
||||||
count > 0 && !MusicPlayerRemote.getPlayingQueue().isEmpty()
|
params.bottomMargin =
|
||||||
? DensityUtil.dip2px(requireContext(), 104f)
|
count > 0 && !MusicPlayerRemote.getPlayingQueue().isEmpty()
|
||||||
: DensityUtil.dip2px(requireContext(), 54f);
|
? DensityUtil.dip2px(requireContext(), 104f)
|
||||||
|
: DensityUtil.dip2px(requireContext(), 54f);
|
||||||
|
binding.coordinatorLayout.setLayoutParams(params);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkIsEmpty() {
|
private void checkIsEmpty() {
|
||||||
emojiText.setText(getEmojiByUnicode(0x1F631));
|
if (binding != null) {
|
||||||
if (empty != null) {
|
binding.emptyEmoji.setText(getEmojiByUnicode(0x1F631));
|
||||||
empty.setVisibility(
|
binding.empty.setVisibility(
|
||||||
adapter == null || adapter.getItemCount() == 0 ? View.VISIBLE : View.GONE);
|
adapter == null || adapter.getItemCount() == 0 ? View.VISIBLE : View.GONE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private BreadCrumbLayout.Crumb getActiveCrumb() {
|
private BreadCrumbLayout.Crumb getActiveCrumb() {
|
||||||
return breadCrumbs != null && breadCrumbs.size() > 0
|
if (binding != null) {
|
||||||
? breadCrumbs.getCrumb(breadCrumbs.getActiveIndex())
|
return binding.breadCrumbs.size() > 0
|
||||||
: null;
|
? binding.breadCrumbs.getCrumb(binding.breadCrumbs.getActiveIndex())
|
||||||
|
: null;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getEmojiByUnicode(int unicode) {
|
private String getEmojiByUnicode(int unicode) {
|
||||||
|
@ -492,21 +516,11 @@ public class FoldersFragment extends AbsMainActivityFragment
|
||||||
return fileComparator;
|
return fileComparator;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initViews(View view) {
|
|
||||||
coordinatorLayout = view.findViewById(R.id.coordinatorLayout);
|
|
||||||
recyclerView = view.findViewById(R.id.recyclerView);
|
|
||||||
breadCrumbs = view.findViewById(R.id.breadCrumbs);
|
|
||||||
empty = view.findViewById(android.R.id.empty);
|
|
||||||
emojiText = view.findViewById(R.id.emptyEmoji);
|
|
||||||
toolbar = view.findViewById(R.id.toolbar);
|
|
||||||
appNameText = view.findViewById(R.id.appNameText);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void saveScrollPosition() {
|
private void saveScrollPosition() {
|
||||||
BreadCrumbLayout.Crumb crumb = getActiveCrumb();
|
BreadCrumbLayout.Crumb crumb = getActiveCrumb();
|
||||||
if (crumb != null) {
|
if (crumb != null) {
|
||||||
crumb.setScrollPosition(
|
crumb.setScrollPosition(
|
||||||
((LinearLayoutManager) recyclerView.getLayoutManager()).findFirstVisibleItemPosition());
|
((LinearLayoutManager) binding.recyclerView.getLayoutManager()).findFirstVisibleItemPosition());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -529,46 +543,39 @@ public class FoldersFragment extends AbsMainActivityFragment
|
||||||
if (crumb == null) {
|
if (crumb == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
saveScrollPosition();
|
String path = crumb.getFile().getPath();
|
||||||
breadCrumbs.setActiveOrAdd(crumb, false);
|
if (path.equals("/") || path.equals("/storage") || path.equals("/storage/emulated")) {
|
||||||
if (addToHistory) {
|
switchToStorageAdapter();
|
||||||
breadCrumbs.addHistory(crumb);
|
} else {
|
||||||
|
saveScrollPosition();
|
||||||
|
binding.breadCrumbs.setActiveOrAdd(crumb, false);
|
||||||
|
if (addToHistory) {
|
||||||
|
binding.breadCrumbs.addHistory(crumb);
|
||||||
|
}
|
||||||
|
LoaderManager.getInstance(this).restartLoader(LOADER_ID, null, this);
|
||||||
}
|
}
|
||||||
LoaderManager.getInstance(this).restartLoader(LOADER_ID, null, this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setUpAdapter() {
|
private void setUpAdapter() {
|
||||||
adapter =
|
switchToFileAdapter();
|
||||||
new SongFileAdapter(getMainActivity(), new LinkedList<>(), R.layout.item_list, this, this);
|
|
||||||
adapter.registerAdapterDataObserver(
|
|
||||||
new RecyclerView.AdapterDataObserver() {
|
|
||||||
@Override
|
|
||||||
public void onChanged() {
|
|
||||||
super.onChanged();
|
|
||||||
checkIsEmpty();
|
|
||||||
checkForPadding();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
recyclerView.setAdapter(adapter);
|
|
||||||
checkIsEmpty();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setUpAppbarColor() {
|
private void setUpAppbarColor() {
|
||||||
breadCrumbs.setActivatedContentColor(
|
binding.breadCrumbs.setActivatedContentColor(
|
||||||
ATHUtil.INSTANCE.resolveColor(requireContext(), android.R.attr.textColorPrimary));
|
ATHUtil.INSTANCE.resolveColor(requireContext(), android.R.attr.textColorPrimary));
|
||||||
breadCrumbs.setDeactivatedContentColor(
|
binding.breadCrumbs.setDeactivatedContentColor(
|
||||||
ATHUtil.INSTANCE.resolveColor(requireContext(), android.R.attr.textColorSecondary));
|
ATHUtil.INSTANCE.resolveColor(requireContext(), android.R.attr.textColorSecondary));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setUpBreadCrumbs() {
|
private void setUpBreadCrumbs() {
|
||||||
breadCrumbs.setCallback(this);
|
binding.breadCrumbs.setCallback(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setUpRecyclerView() {
|
private void setUpRecyclerView() {
|
||||||
recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
|
binding.recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
|
||||||
FastScroller fastScroller = ThemedFastScroller.INSTANCE.create(recyclerView);
|
FastScroller fastScroller = ThemedFastScroller.INSTANCE.create(binding.recyclerView);
|
||||||
recyclerView.setOnApplyWindowInsetsListener(
|
binding.recyclerView.setOnApplyWindowInsetsListener(
|
||||||
new ScrollingViewOnApplyWindowInsetsListener(recyclerView, fastScroller));
|
new ScrollingViewOnApplyWindowInsetsListener(binding.recyclerView, fastScroller));
|
||||||
}
|
}
|
||||||
|
|
||||||
private ArrayList<File> toList(File file) {
|
private ArrayList<File> toList(File file) {
|
||||||
|
@ -580,16 +587,22 @@ public class FoldersFragment extends AbsMainActivityFragment
|
||||||
private void updateAdapter(@NonNull List<File> files) {
|
private void updateAdapter(@NonNull List<File> files) {
|
||||||
adapter.swapDataSet(files);
|
adapter.swapDataSet(files);
|
||||||
BreadCrumbLayout.Crumb crumb = getActiveCrumb();
|
BreadCrumbLayout.Crumb crumb = getActiveCrumb();
|
||||||
if (crumb != null && recyclerView != null) {
|
if (crumb != null) {
|
||||||
((LinearLayoutManager) recyclerView.getLayoutManager())
|
((LinearLayoutManager) binding.recyclerView.getLayoutManager())
|
||||||
.scrollToPositionWithOffset(crumb.getScrollPosition(), 0);
|
.scrollToPositionWithOffset(crumb.getScrollPosition(), 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDestroyView() {
|
||||||
|
super.onDestroyView();
|
||||||
|
binding = null;
|
||||||
|
}
|
||||||
|
|
||||||
public static class ListPathsAsyncTask
|
public static class ListPathsAsyncTask
|
||||||
extends ListingFilesDialogAsyncTask<ListPathsAsyncTask.LoadingInfo, String, String[]> {
|
extends ListingFilesDialogAsyncTask<ListPathsAsyncTask.LoadingInfo, String, String[]> {
|
||||||
|
|
||||||
private WeakReference<OnPathsListedCallback> onPathsListedCallbackWeakReference;
|
private final WeakReference<OnPathsListedCallback> onPathsListedCallbackWeakReference;
|
||||||
|
|
||||||
public ListPathsAsyncTask(Context context, OnPathsListedCallback callback) {
|
public ListPathsAsyncTask(Context context, OnPathsListedCallback callback) {
|
||||||
super(context);
|
super(context);
|
||||||
|
@ -679,7 +692,7 @@ public class FoldersFragment extends AbsMainActivityFragment
|
||||||
|
|
||||||
private static class AsyncFileLoader extends WrappedAsyncTaskLoader<List<File>> {
|
private static class AsyncFileLoader extends WrappedAsyncTaskLoader<List<File>> {
|
||||||
|
|
||||||
private WeakReference<FoldersFragment> fragmentWeakReference;
|
private final WeakReference<FoldersFragment> fragmentWeakReference;
|
||||||
|
|
||||||
AsyncFileLoader(FoldersFragment foldersFragment) {
|
AsyncFileLoader(FoldersFragment foldersFragment) {
|
||||||
super(foldersFragment.requireActivity());
|
super(foldersFragment.requireActivity());
|
||||||
|
@ -710,8 +723,8 @@ public class FoldersFragment extends AbsMainActivityFragment
|
||||||
extends ListingFilesDialogAsyncTask<ListSongsAsyncTask.LoadingInfo, Void, List<Song>> {
|
extends ListingFilesDialogAsyncTask<ListSongsAsyncTask.LoadingInfo, Void, List<Song>> {
|
||||||
|
|
||||||
private final Object extra;
|
private final Object extra;
|
||||||
private WeakReference<OnSongsListedCallback> callbackWeakReference;
|
private final WeakReference<OnSongsListedCallback> callbackWeakReference;
|
||||||
private WeakReference<Context> contextWeakReference;
|
private final WeakReference<Context> contextWeakReference;
|
||||||
|
|
||||||
ListSongsAsyncTask(Context context, Object extra, OnSongsListedCallback callback) {
|
ListSongsAsyncTask(Context context, Object extra, OnSongsListedCallback callback) {
|
||||||
super(context);
|
super(context);
|
||||||
|
@ -823,4 +836,107 @@ public class FoldersFragment extends AbsMainActivityFragment
|
||||||
.create();
|
.create();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// https://github.com/DrKLO/Telegram/blob/ab221dafadbc17459d78d9ea3e643ae18e934b16/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatAttachAlertDocumentLayout.java#L939
|
||||||
|
private void listRoots() {
|
||||||
|
storageItems.clear();
|
||||||
|
HashSet<String> paths = new HashSet<>();
|
||||||
|
String defaultPath = Environment.getExternalStorageDirectory().getPath();
|
||||||
|
String defaultPathState = Environment.getExternalStorageState();
|
||||||
|
if (defaultPathState.equals(Environment.MEDIA_MOUNTED) || defaultPathState.equals(Environment.MEDIA_MOUNTED_READ_ONLY)) {
|
||||||
|
Storage ext = new Storage();
|
||||||
|
if (Environment.isExternalStorageRemovable()) {
|
||||||
|
ext.title = "SD Card";
|
||||||
|
} else {
|
||||||
|
ext.title = "Internal Storage";
|
||||||
|
}
|
||||||
|
ext.file = Environment.getExternalStorageDirectory();
|
||||||
|
storageItems.add(ext);
|
||||||
|
paths.add(defaultPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
BufferedReader bufferedReader = null;
|
||||||
|
try {
|
||||||
|
bufferedReader = new BufferedReader(new FileReader("/proc/mounts"));
|
||||||
|
String line;
|
||||||
|
while ((line = bufferedReader.readLine()) != null) {
|
||||||
|
if (line.contains("vfat") || line.contains("/mnt")) {
|
||||||
|
StringTokenizer tokens = new StringTokenizer(line, " ");
|
||||||
|
tokens.nextToken();
|
||||||
|
String path = tokens.nextToken();
|
||||||
|
if (paths.contains(path)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (line.contains("/dev/block/vold")) {
|
||||||
|
if (!line.contains("/mnt/secure") && !line.contains("/mnt/asec") && !line.contains("/mnt/obb") && !line.contains("/dev/mapper") && !line.contains("tmpfs")) {
|
||||||
|
if (!new File(path).isDirectory()) {
|
||||||
|
int index = path.lastIndexOf('/');
|
||||||
|
if (index != -1) {
|
||||||
|
String newPath = "/storage/" + path.substring(index + 1);
|
||||||
|
if (new File(newPath).isDirectory()) {
|
||||||
|
path = newPath;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
paths.add(path);
|
||||||
|
try {
|
||||||
|
Storage item = new Storage();
|
||||||
|
if (path.toLowerCase().contains("sd")) {
|
||||||
|
item.title = "SD Card";
|
||||||
|
} else {
|
||||||
|
item.title = "External Storage";
|
||||||
|
}
|
||||||
|
item.file = new File(path);
|
||||||
|
storageItems.add(item);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
} finally {
|
||||||
|
if (bufferedReader != null) {
|
||||||
|
try {
|
||||||
|
bufferedReader.close();
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStorageClicked(@NonNull Storage storage) {
|
||||||
|
switchToFileAdapter();
|
||||||
|
setCrumb(
|
||||||
|
new BreadCrumbLayout.Crumb(
|
||||||
|
FileUtil.safeGetCanonicalFile(storage.file)),
|
||||||
|
true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void switchToFileAdapter() {
|
||||||
|
adapter =
|
||||||
|
new SongFileAdapter(getMainActivity(), new LinkedList<>(), R.layout.item_list, this, this);
|
||||||
|
adapter.registerAdapterDataObserver(
|
||||||
|
new RecyclerView.AdapterDataObserver() {
|
||||||
|
@Override
|
||||||
|
public void onChanged() {
|
||||||
|
super.onChanged();
|
||||||
|
checkIsEmpty();
|
||||||
|
checkForPadding();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
binding.recyclerView.setAdapter(adapter);
|
||||||
|
checkIsEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void switchToStorageAdapter() {
|
||||||
|
listRoots();
|
||||||
|
storageAdapter = new StorageAdapter(storageItems, this);
|
||||||
|
binding.recyclerView.setAdapter(storageAdapter);
|
||||||
|
binding.breadCrumbs.clearCrumbs();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,28 +14,26 @@
|
||||||
*/
|
*/
|
||||||
package code.name.monkey.retromusic.fragments.genres
|
package code.name.monkey.retromusic.fragments.genres
|
||||||
|
|
||||||
import android.graphics.Color
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.Menu
|
import android.view.Menu
|
||||||
import android.view.MenuInflater
|
import android.view.MenuInflater
|
||||||
import android.view.MenuItem
|
import android.view.MenuItem
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import androidx.core.view.ViewCompat
|
import androidx.core.view.ViewCompat
|
||||||
|
import androidx.core.view.doOnPreDraw
|
||||||
import androidx.navigation.fragment.navArgs
|
import androidx.navigation.fragment.navArgs
|
||||||
import androidx.recyclerview.widget.DefaultItemAnimator
|
import androidx.recyclerview.widget.DefaultItemAnimator
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import code.name.monkey.retromusic.R
|
import code.name.monkey.retromusic.R
|
||||||
import code.name.monkey.retromusic.adapter.song.SongAdapter
|
import code.name.monkey.retromusic.adapter.song.SongAdapter
|
||||||
|
import code.name.monkey.retromusic.databinding.FragmentPlaylistDetailBinding
|
||||||
import code.name.monkey.retromusic.extensions.dipToPix
|
import code.name.monkey.retromusic.extensions.dipToPix
|
||||||
import code.name.monkey.retromusic.extensions.resolveColor
|
|
||||||
import code.name.monkey.retromusic.fragments.base.AbsMainActivityFragment
|
import code.name.monkey.retromusic.fragments.base.AbsMainActivityFragment
|
||||||
import code.name.monkey.retromusic.helper.menu.GenreMenuHelper
|
import code.name.monkey.retromusic.helper.menu.GenreMenuHelper
|
||||||
import code.name.monkey.retromusic.model.Genre
|
import code.name.monkey.retromusic.model.Genre
|
||||||
import code.name.monkey.retromusic.model.Song
|
import code.name.monkey.retromusic.model.Song
|
||||||
import com.google.android.material.transition.MaterialArcMotion
|
import com.google.android.material.transition.MaterialSharedAxis
|
||||||
import com.google.android.material.transition.MaterialContainerTransform
|
|
||||||
import kotlinx.android.synthetic.main.fragment_playlist_detail.*
|
|
||||||
import org.koin.androidx.viewmodel.ext.android.viewModel
|
import org.koin.androidx.viewmodel.ext.android.viewModel
|
||||||
import org.koin.core.parameter.parametersOf
|
import org.koin.core.parameter.parametersOf
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
@ -47,37 +45,33 @@ class GenreDetailsFragment : AbsMainActivityFragment(R.layout.fragment_playlist_
|
||||||
}
|
}
|
||||||
private lateinit var genre: Genre
|
private lateinit var genre: Genre
|
||||||
private lateinit var songAdapter: SongAdapter
|
private lateinit var songAdapter: SongAdapter
|
||||||
|
private var _binding: FragmentPlaylistDetailBinding? = null
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
private val binding get() = _binding!!
|
||||||
super.onCreate(savedInstanceState)
|
|
||||||
sharedElementEnterTransition = MaterialContainerTransform().apply {
|
|
||||||
drawingViewId = R.id.fragment_container
|
|
||||||
duration = 300L
|
|
||||||
scrimColor = Color.TRANSPARENT
|
|
||||||
setAllContainerColors(requireContext().resolveColor(R.attr.colorSurface))
|
|
||||||
setPathMotion(MaterialArcMotion())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
enterTransition = MaterialSharedAxis(MaterialSharedAxis.Z, true).addTarget(view)
|
||||||
|
returnTransition = MaterialSharedAxis(MaterialSharedAxis.Z, false)
|
||||||
|
_binding = FragmentPlaylistDetailBinding.bind(view)
|
||||||
setHasOptionsMenu(true)
|
setHasOptionsMenu(true)
|
||||||
mainActivity.setBottomBarVisibility(false)
|
|
||||||
mainActivity.addMusicServiceEventListener(detailsViewModel)
|
mainActivity.addMusicServiceEventListener(detailsViewModel)
|
||||||
mainActivity.setSupportActionBar(toolbar)
|
mainActivity.setSupportActionBar(binding.toolbar)
|
||||||
ViewCompat.setTransitionName(container, "genre")
|
ViewCompat.setTransitionName(binding.container, "genre")
|
||||||
genre = arguments.extraGenre
|
genre = arguments.extraGenre
|
||||||
toolbar?.title = arguments.extraGenre.name
|
binding.toolbar.title = arguments.extraGenre.name
|
||||||
setupRecyclerView()
|
setupRecyclerView()
|
||||||
detailsViewModel.getSongs().observe(viewLifecycleOwner, {
|
detailsViewModel.getSongs().observe(viewLifecycleOwner, {
|
||||||
songs(it)
|
songs(it)
|
||||||
})
|
})
|
||||||
|
postponeEnterTransition()
|
||||||
|
view.doOnPreDraw {
|
||||||
|
startPostponedEnterTransition()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setupRecyclerView() {
|
private fun setupRecyclerView() {
|
||||||
songAdapter = SongAdapter(requireActivity(), ArrayList(), R.layout.item_list, null)
|
songAdapter = SongAdapter(requireActivity(), ArrayList(), R.layout.item_list, null)
|
||||||
recyclerView.apply {
|
binding.recyclerView.apply {
|
||||||
itemAnimator = DefaultItemAnimator()
|
itemAnimator = DefaultItemAnimator()
|
||||||
layoutManager = LinearLayoutManager(requireContext())
|
layoutManager = LinearLayoutManager(requireContext())
|
||||||
adapter = songAdapter
|
adapter = songAdapter
|
||||||
|
@ -91,7 +85,7 @@ class GenreDetailsFragment : AbsMainActivityFragment(R.layout.fragment_playlist_
|
||||||
}
|
}
|
||||||
|
|
||||||
fun songs(songs: List<Song>) {
|
fun songs(songs: List<Song>) {
|
||||||
progressIndicator.hide()
|
binding.progressIndicator.hide()
|
||||||
if (songs.isNotEmpty()) songAdapter.swapDataSet(songs)
|
if (songs.isNotEmpty()) songAdapter.swapDataSet(songs)
|
||||||
else songAdapter.swapDataSet(emptyList())
|
else songAdapter.swapDataSet(emptyList())
|
||||||
}
|
}
|
||||||
|
@ -102,13 +96,13 @@ class GenreDetailsFragment : AbsMainActivityFragment(R.layout.fragment_playlist_
|
||||||
|
|
||||||
private fun checkIsEmpty() {
|
private fun checkIsEmpty() {
|
||||||
checkForPadding()
|
checkForPadding()
|
||||||
emptyEmoji.text = getEmojiByUnicode(0x1F631)
|
binding.emptyEmoji.text = getEmojiByUnicode(0x1F631)
|
||||||
empty?.visibility = if (songAdapter.itemCount == 0) View.VISIBLE else View.GONE
|
binding.empty.visibility = if (songAdapter.itemCount == 0) View.VISIBLE else View.GONE
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun checkForPadding() {
|
private fun checkForPadding() {
|
||||||
val height = dipToPix(52f).toInt()
|
val height = dipToPix(52f).toInt()
|
||||||
recyclerView.setPadding(0, 0, 0, height)
|
binding.recyclerView.setPadding(0, 0, 0, height)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
||||||
|
@ -119,4 +113,9 @@ class GenreDetailsFragment : AbsMainActivityFragment(R.layout.fragment_playlist_
|
||||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||||
return GenreMenuHelper.handleMenuClick(requireActivity(), genre, item)
|
return GenreMenuHelper.handleMenuClick(requireActivity(), genre, item)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onDestroyView() {
|
||||||
|
super.onDestroyView()
|
||||||
|
_binding = null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -60,4 +60,5 @@ class GenreDetailsViewModel(
|
||||||
override fun onPlayStateChanged() {}
|
override fun onPlayStateChanged() {}
|
||||||
override fun onRepeatModeChanged() {}
|
override fun onRepeatModeChanged() {}
|
||||||
override fun onShuffleModeChanged() {}
|
override fun onShuffleModeChanged() {}
|
||||||
|
override fun onFavoriteStateChanged() {}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,25 +15,31 @@
|
||||||
package code.name.monkey.retromusic.fragments.genres
|
package code.name.monkey.retromusic.fragments.genres
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import android.view.Menu
|
||||||
|
import android.view.MenuInflater
|
||||||
|
import android.view.MenuItem
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import androidx.core.os.bundleOf
|
import androidx.core.os.bundleOf
|
||||||
import androidx.lifecycle.Observer
|
|
||||||
import androidx.navigation.fragment.FragmentNavigatorExtras
|
|
||||||
import androidx.navigation.fragment.findNavController
|
import androidx.navigation.fragment.findNavController
|
||||||
|
import androidx.recyclerview.widget.GridLayoutManager
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import code.name.monkey.retromusic.EXTRA_GENRE
|
import code.name.monkey.retromusic.EXTRA_GENRE
|
||||||
import code.name.monkey.retromusic.R
|
import code.name.monkey.retromusic.R
|
||||||
import code.name.monkey.retromusic.adapter.GenreAdapter
|
import code.name.monkey.retromusic.adapter.GenreAdapter
|
||||||
|
import code.name.monkey.retromusic.fragments.ReloadType
|
||||||
import code.name.monkey.retromusic.fragments.base.AbsRecyclerViewFragment
|
import code.name.monkey.retromusic.fragments.base.AbsRecyclerViewFragment
|
||||||
import code.name.monkey.retromusic.interfaces.IGenreClickListener
|
import code.name.monkey.retromusic.interfaces.IGenreClickListener
|
||||||
import code.name.monkey.retromusic.model.Genre
|
import code.name.monkey.retromusic.model.Genre
|
||||||
import com.google.android.material.transition.MaterialElevationScale
|
import code.name.monkey.retromusic.util.RetroUtil
|
||||||
|
import com.google.android.gms.cast.framework.CastButtonFactory
|
||||||
|
import com.google.android.material.transition.MaterialSharedAxis
|
||||||
|
|
||||||
class GenresFragment : AbsRecyclerViewFragment<GenreAdapter, LinearLayoutManager>(),
|
class
|
||||||
|
GenresFragment : AbsRecyclerViewFragment<GenreAdapter, LinearLayoutManager>(),
|
||||||
IGenreClickListener {
|
IGenreClickListener {
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
libraryViewModel.getGenre().observe(viewLifecycleOwner, Observer {
|
libraryViewModel.getGenre().observe(viewLifecycleOwner, {
|
||||||
if (it.isNotEmpty())
|
if (it.isNotEmpty())
|
||||||
adapter?.swapDataSet(it)
|
adapter?.swapDataSet(it)
|
||||||
else
|
else
|
||||||
|
@ -42,14 +48,37 @@ class GenresFragment : AbsRecyclerViewFragment<GenreAdapter, LinearLayoutManager
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun createLayoutManager(): LinearLayoutManager {
|
override fun createLayoutManager(): LinearLayoutManager {
|
||||||
return LinearLayoutManager(activity)
|
return if (RetroUtil.isLandscape()) {
|
||||||
|
GridLayoutManager(activity, 4)
|
||||||
|
} else {
|
||||||
|
GridLayoutManager(activity, 2)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun createAdapter(): GenreAdapter {
|
override fun createAdapter(): GenreAdapter {
|
||||||
val dataSet = if (adapter == null) ArrayList() else adapter!!.dataSet
|
val dataSet = if (adapter == null) ArrayList() else adapter!!.dataSet
|
||||||
return GenreAdapter(requireActivity(), dataSet, R.layout.item_list_no_image, this)
|
return GenreAdapter(requireActivity(), dataSet, R.layout.item_genre, this)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
||||||
|
super.onCreateOptionsMenu(menu, inflater)
|
||||||
|
menu.removeItem(R.id.action_grid_size)
|
||||||
|
menu.removeItem(R.id.action_layout_type)
|
||||||
|
menu.removeItem(R.id.action_sort_order)
|
||||||
|
menu.findItem(R.id.action_settings).setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM)
|
||||||
|
//Setting up cast button
|
||||||
|
CastButtonFactory.setUpMediaRouteButton(requireContext(), menu, R.id.action_cast)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onResume() {
|
||||||
|
super.onResume()
|
||||||
|
libraryViewModel.forceReload(ReloadType.Genres)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
override val titleRes: Int
|
||||||
|
get() = R.string.genres
|
||||||
|
|
||||||
override val emptyMessage: Int
|
override val emptyMessage: Int
|
||||||
get() = R.string.no_genres
|
get() = R.string.no_genres
|
||||||
|
|
||||||
|
@ -64,19 +93,13 @@ class GenresFragment : AbsRecyclerViewFragment<GenreAdapter, LinearLayoutManager
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onClickGenre(genre: Genre, view: View) {
|
override fun onClickGenre(genre: Genre, view: View) {
|
||||||
exitTransition = MaterialElevationScale(false).apply {
|
exitTransition = MaterialSharedAxis(MaterialSharedAxis.Z, true).addTarget(requireView())
|
||||||
duration = 300L
|
reenterTransition = MaterialSharedAxis(MaterialSharedAxis.Z, false)
|
||||||
}
|
|
||||||
reenterTransition = MaterialElevationScale(true).apply {
|
|
||||||
duration = 300L
|
|
||||||
}
|
|
||||||
findNavController().navigate(
|
findNavController().navigate(
|
||||||
R.id.genreDetailsFragment,
|
R.id.genreDetailsFragment,
|
||||||
bundleOf(EXTRA_GENRE to genre),
|
bundleOf(EXTRA_GENRE to genre),
|
||||||
null,
|
null,
|
||||||
FragmentNavigatorExtras(
|
null
|
||||||
view to "genre"
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue