mirror of
https://github.com/CappielloAntonio/tempo.git
synced 2026-01-31 14:43:36 +00:00
Compare commits
127 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9cf62c8c0c | ||
|
|
330556ec33 | ||
|
|
4c8c5ce120 | ||
|
|
55ae9a8442 | ||
|
|
f8a53c7db2 | ||
|
|
b58cae1ecd | ||
|
|
e305f20811 | ||
|
|
c8c1bcfd3e | ||
|
|
e1c96d278f | ||
|
|
3783a2f790 | ||
|
|
63e288d147 | ||
|
|
d3ca43ed49 | ||
|
|
f0d31e425a | ||
|
|
609fc70d33 | ||
|
|
0b92c40d51 | ||
|
|
eb6a2b609e | ||
|
|
20ffc960df | ||
|
|
48d9022f9a | ||
|
|
9e6926fc97 | ||
|
|
780f1c3a2e | ||
|
|
0f471a7b9f | ||
|
|
4ec1519063 | ||
|
|
618cf23e6e | ||
|
|
e8e24354ec | ||
|
|
fd0fd0546c | ||
|
|
030ca82c3a | ||
|
|
0e6b860e03 | ||
|
|
8c288e8938 | ||
|
|
4e968f3397 | ||
|
|
b1e0f49ddb | ||
|
|
73a1ab2330 | ||
|
|
8540348670 | ||
|
|
67e4079732 | ||
|
|
6c6d56f451 | ||
|
|
f967df8ef3 | ||
|
|
c5a78bf945 | ||
|
|
6f51fd92bb | ||
|
|
3c2ea38f1e | ||
|
|
edf140fb5d | ||
|
|
049ce7713a | ||
|
|
8c49ceffdb | ||
|
|
052e9d9068 | ||
|
|
4d1213c43d | ||
|
|
1ec0c7b99c | ||
|
|
07f8914a9f | ||
|
|
965a80462c | ||
|
|
349c961f1a | ||
|
|
eb9f824c01 | ||
|
|
e465892013 | ||
|
|
a49c78b9f1 | ||
|
|
436ef21a29 | ||
|
|
be8decfac3 | ||
|
|
7c87ec2cbe | ||
|
|
fb7296b467 | ||
|
|
078aa87521 | ||
|
|
54a4355793 | ||
|
|
e84f62220c | ||
|
|
176db09662 | ||
|
|
2c3aebc83b | ||
|
|
a67571ee4f | ||
|
|
79f5b9b158 | ||
|
|
f6b176a357 | ||
|
|
aa5290c7ee | ||
|
|
0a26f0a7b8 | ||
|
|
c243fa9edc | ||
|
|
f94e5892cd | ||
|
|
477331da6f | ||
|
|
c4e8fe5261 | ||
|
|
92fd6b01e4 | ||
|
|
263d9ebc5f | ||
|
|
f6578afb14 | ||
|
|
41b374ab23 | ||
|
|
4448a632af | ||
|
|
b3b1c5b006 | ||
|
|
25900c848a | ||
|
|
08e9be107b | ||
|
|
6cbb2ee117 | ||
|
|
dacaa03eb7 | ||
|
|
a3d8b75d07 | ||
|
|
d08c113d99 | ||
|
|
2db716a79c | ||
|
|
71b913be9b | ||
|
|
fb353a33d9 | ||
|
|
240498c219 | ||
|
|
1eac053d2d | ||
|
|
160222563c | ||
|
|
4ec3d6bde7 | ||
|
|
e0f276dd2a | ||
|
|
acee7f8fa9 | ||
|
|
499929ad55 | ||
|
|
7b6d2c62a5 | ||
|
|
ff6bf20c30 | ||
|
|
58d540b939 | ||
|
|
4b9eaa8c3d | ||
|
|
03700d9e4c | ||
|
|
374dbb58bb | ||
|
|
a88658ac8f | ||
|
|
0e97eab744 | ||
|
|
309eca0764 | ||
|
|
fd85f36411 | ||
|
|
b4180afa36 | ||
|
|
2712b73dac | ||
|
|
302458e76b | ||
|
|
dd085a2cdb | ||
|
|
7a58ad5494 | ||
|
|
84234849a4 | ||
|
|
6f6f596432 | ||
|
|
3d3d0fa856 | ||
|
|
4ff2ed38c7 | ||
|
|
321994496a | ||
|
|
3e1c3133ca | ||
|
|
10b9d7ec76 | ||
|
|
1980e53a27 | ||
|
|
54e988b70e | ||
|
|
14d6128df0 | ||
|
|
7488346804 | ||
|
|
733102a8a4 | ||
|
|
28fef53590 | ||
|
|
e35aed9cc4 | ||
|
|
111a17350b | ||
|
|
54be869081 | ||
|
|
b9462d7374 | ||
|
|
234c9a10d2 | ||
|
|
817c3b02e5 | ||
|
|
1f65b4c321 | ||
|
|
ab0e1ead45 | ||
|
|
0c2b18326e |
2
.github/workflows/github_release.yml
vendored
2
.github/workflows/github_release.yml
vendored
@@ -52,7 +52,7 @@ jobs:
|
|||||||
BUILD_TOOLS_VERSION: ${{ env.BUILD_TOOL_VERSION }}
|
BUILD_TOOLS_VERSION: ${{ env.BUILD_TOOL_VERSION }}
|
||||||
|
|
||||||
- name: Make artifact
|
- name: Make artifact
|
||||||
uses: actions/upload-artifact@v2
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: app-release-signed
|
name: app-release-signed
|
||||||
path: ${{steps.sign_apk.outputs.signedReleaseFile}}
|
path: ${{steps.sign_apk.outputs.signedReleaseFile}}
|
||||||
|
|||||||
2
.idea/compiler.xml
generated
2
.idea/compiler.xml
generated
@@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="CompilerConfiguration">
|
<component name="CompilerConfiguration">
|
||||||
<bytecodeTargetLevel target="17" />
|
<bytecodeTargetLevel target="21" />
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
||||||
1
.idea/gradle.xml
generated
1
.idea/gradle.xml
generated
@@ -4,6 +4,7 @@
|
|||||||
<component name="GradleSettings">
|
<component name="GradleSettings">
|
||||||
<option name="linkedExternalProjectsSettings">
|
<option name="linkedExternalProjectsSettings">
|
||||||
<GradleProjectSettings>
|
<GradleProjectSettings>
|
||||||
|
<option name="testRunner" value="CHOOSE_PER_TEST" />
|
||||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||||
<option name="gradleJvm" value="#GRADLE_LOCAL_JAVA_HOME" />
|
<option name="gradleJvm" value="#GRADLE_LOCAL_JAVA_HOME" />
|
||||||
<option name="modules">
|
<option name="modules">
|
||||||
|
|||||||
3
.idea/misc.xml
generated
3
.idea/misc.xml
generated
@@ -1,3 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="DesignSurface">
|
<component name="DesignSurface">
|
||||||
<option name="filePathToZoomLevelMap">
|
<option name="filePathToZoomLevelMap">
|
||||||
@@ -191,7 +192,7 @@
|
|||||||
</map>
|
</map>
|
||||||
</option>
|
</option>
|
||||||
</component>
|
</component>
|
||||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="17" project-jdk-type="JavaSDK">
|
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="jbr-21" project-jdk-type="JavaSDK">
|
||||||
<output url="file://$PROJECT_DIR$/build/classes" />
|
<output url="file://$PROJECT_DIR$/build/classes" />
|
||||||
</component>
|
</component>
|
||||||
<component name="ProjectType">
|
<component name="ProjectType">
|
||||||
|
|||||||
11
README.md
11
README.md
@@ -6,12 +6,22 @@
|
|||||||
<b>Access your music library on all your android devices</b>
|
<b>Access your music library on all your android devices</b>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<a href="https://github.com/CappielloAntonio/tempo/releases"><img src="https://i.ibb.co/q0mdc4Z/get-it-on-github.png" width="200"></a>
|
||||||
|
</p>
|
||||||
|
<p align="center">
|
||||||
|
<a href="https://f-droid.org/packages/com.cappielloantonio.notquitemy.tempo"><img src="https://fdroid.gitlab.io/artwork/badge/get-it-on.png" width="200"></a>
|
||||||
|
<a href="https://apt.izzysoft.de/fdroid/index/apk/com.cappielloantonio.tempo"><img src="https://gitlab.com/IzzyOnDroid/repo/-/raw/master/assets/IzzyOnDroid.png" width="200"></a>
|
||||||
|
</p>
|
||||||
|
|
||||||
**Tempo** is an open-source and lightweight music client for Subsonic, designed and built natively for Android. It provides a seamless and intuitive music streaming experience, allowing you to access and play your Subsonic music library directly from your Android device.
|
**Tempo** is an open-source and lightweight music client for Subsonic, designed and built natively for Android. It provides a seamless and intuitive music streaming experience, allowing you to access and play your Subsonic music library directly from your Android device.
|
||||||
|
|
||||||
Tempo does not rely on magic algorithms to decide what you should listen to. Instead, the interface is built around your listening history, randomness, and optionally integrates with services like Last.fm to personalize your music experience.
|
Tempo does not rely on magic algorithms to decide what you should listen to. Instead, the interface is built around your listening history, randomness, and optionally integrates with services like Last.fm to personalize your music experience.
|
||||||
|
|
||||||
**If you find Tempo useful, please consider starring the project on GitHub. It would mean a lot to me and help promote the app to a wider audience.**
|
**If you find Tempo useful, please consider starring the project on GitHub. It would mean a lot to me and help promote the app to a wider audience.**
|
||||||
|
|
||||||
|
**Use the Github version of the app for full Android Auto and Chromecast support.**
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
- **Subsonic Integration**: Tempo seamlessly integrates with your Subsonic server, providing you with easy access to your entire music collection on the go.
|
- **Subsonic Integration**: Tempo seamlessly integrates with your Subsonic server, providing you with easy access to your entire music collection on the go.
|
||||||
- **Sleek and Intuitive UI**: Enjoy a clean and user-friendly interface designed to enhance your music listening experience, tailored to your preferences and listening history.
|
- **Sleek and Intuitive UI**: Enjoy a clean and user-friendly interface designed to enhance your music listening experience, tailored to your preferences and listening history.
|
||||||
@@ -23,6 +33,7 @@ Tempo does not rely on magic algorithms to decide what you should listen to. Ins
|
|||||||
- **Scrobbling Integration**: Optionally integrate Tempo with Last.fm to scrobble your played tracks, gather music insights, and further personalize your music recommendations, if supported by your Subsonic server.
|
- **Scrobbling Integration**: Optionally integrate Tempo with Last.fm to scrobble your played tracks, gather music insights, and further personalize your music recommendations, if supported by your Subsonic server.
|
||||||
- **Podcasts and Radio**: If your Subsonic server supports it, listen to podcasts and radio shows directly within Tempo, expanding your audio entertainment options.
|
- **Podcasts and Radio**: If your Subsonic server supports it, listen to podcasts and radio shows directly within Tempo, expanding your audio entertainment options.
|
||||||
- **Transcoding Support**: Activate transcoding of tracks on your Subsonic server, allowing you to set a transcoding profile for optimized streaming directly from the app. This feature requires support from your Subsonic server.
|
- **Transcoding Support**: Activate transcoding of tracks on your Subsonic server, allowing you to set a transcoding profile for optimized streaming directly from the app. This feature requires support from your Subsonic server.
|
||||||
|
- **Android Auto Support**: Enjoy your favorite music on the go with full Android Auto integration, allowing you to seamlessly control and listen to your tracks directly from your mobile device while driving.
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<img src="mockup/feat/1_screenshot.png" width=200>
|
<img src="mockup/feat/1_screenshot.png" width=200>
|
||||||
|
|||||||
@@ -3,15 +3,15 @@ apply plugin: 'kotlin-android'
|
|||||||
apply plugin: 'kotlin-parcelize'
|
apply plugin: 'kotlin-parcelize'
|
||||||
|
|
||||||
android {
|
android {
|
||||||
compileSdk = 34
|
compileSdk 35
|
||||||
buildToolsVersion = "34.0.0"
|
buildToolsVersion = '35.0.0'
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
minSdkVersion 24
|
minSdkVersion 24
|
||||||
targetSdkVersion 34
|
targetSdk 35
|
||||||
|
|
||||||
versionCode 24
|
versionCode 26
|
||||||
versionName '3.7.0'
|
versionName '3.9.0'
|
||||||
|
|
||||||
testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
|
testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
|
||||||
|
|
||||||
@@ -37,6 +37,11 @@ android {
|
|||||||
dimension = "default"
|
dimension = "default"
|
||||||
applicationId "com.cappielloantonio.notquitemy.tempo"
|
applicationId "com.cappielloantonio.notquitemy.tempo"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
play {
|
||||||
|
dimension = "default"
|
||||||
|
applicationId "com.cappielloantonio.play.tempo"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
buildTypes {
|
buildTypes {
|
||||||
@@ -59,22 +64,25 @@ android {
|
|||||||
|
|
||||||
buildFeatures {
|
buildFeatures {
|
||||||
viewBinding true
|
viewBinding true
|
||||||
|
buildConfig true
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace 'com.cappielloantonio.tempo'
|
namespace 'com.cappielloantonio.tempo'
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
implementation files('../libs/lib-decoder-ffmpeg-release.aar')
|
||||||
|
|
||||||
// AndroidX
|
// AndroidX
|
||||||
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
|
implementation 'androidx.constraintlayout:constraintlayout:2.2.0'
|
||||||
implementation 'androidx.coordinatorlayout:coordinatorlayout:1.2.0'
|
implementation 'androidx.coordinatorlayout:coordinatorlayout:1.2.0'
|
||||||
implementation 'androidx.preference:preference-ktx:1.2.1'
|
implementation 'androidx.preference:preference-ktx:1.2.1'
|
||||||
implementation 'androidx.navigation:navigation-fragment-ktx:2.7.6'
|
implementation 'androidx.navigation:navigation-fragment-ktx:2.8.6'
|
||||||
implementation 'androidx.navigation:navigation-ui-ktx:2.7.6'
|
implementation 'androidx.navigation:navigation-ui-ktx:2.8.6'
|
||||||
implementation 'androidx.recyclerview:recyclerview:1.3.2'
|
implementation 'androidx.recyclerview:recyclerview:1.4.0'
|
||||||
implementation 'androidx.room:room-runtime:2.6.1'
|
implementation 'androidx.room:room-runtime:2.6.1'
|
||||||
implementation 'androidx.core:core-splashscreen:1.0.1'
|
implementation 'androidx.core:core-splashscreen:1.0.1'
|
||||||
implementation "androidx.appcompat:appcompat:1.6.1"
|
implementation 'androidx.appcompat:appcompat:1.7.0'
|
||||||
|
|
||||||
// Android Material
|
// Android Material
|
||||||
implementation 'com.google.android.material:material:1.10.0'
|
implementation 'com.google.android.material:material:1.10.0'
|
||||||
@@ -84,17 +92,19 @@ dependencies {
|
|||||||
implementation 'com.github.bumptech.glide:annotations:4.16.0'
|
implementation 'com.github.bumptech.glide:annotations:4.16.0'
|
||||||
|
|
||||||
// Media3
|
// Media3
|
||||||
implementation 'androidx.media3:media3-session:1.2.1'
|
implementation 'androidx.media3:media3-session:1.5.1'
|
||||||
implementation 'androidx.media3:media3-common:1.2.1'
|
implementation 'androidx.media3:media3-common:1.5.1'
|
||||||
implementation 'androidx.media3:media3-exoplayer:1.2.1'
|
implementation 'androidx.media3:media3-exoplayer:1.5.1'
|
||||||
implementation 'androidx.media3:media3-ui:1.2.1'
|
implementation 'androidx.media3:media3-ui:1.5.1'
|
||||||
tempoImplementation 'androidx.media3:media3-cast:1.2.1'
|
implementation 'androidx.media3:media3-exoplayer-hls:1.5.1'
|
||||||
|
tempoImplementation 'androidx.media3:media3-cast:1.5.1'
|
||||||
|
playImplementation 'androidx.media3:media3-cast:1.5.1'
|
||||||
|
|
||||||
annotationProcessor 'com.github.bumptech.glide:compiler:4.16.0'
|
annotationProcessor 'com.github.bumptech.glide:compiler:4.16.0'
|
||||||
annotationProcessor 'androidx.room:room-compiler:2.6.1'
|
annotationProcessor 'androidx.room:room-compiler:2.6.1'
|
||||||
|
|
||||||
// Retrofit
|
// Retrofit
|
||||||
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
|
implementation 'com.squareup.retrofit2:retrofit:2.11.0'
|
||||||
implementation 'com.squareup.okhttp3:logging-interceptor:5.0.0-alpha.12'
|
implementation 'com.squareup.okhttp3:logging-interceptor:5.0.0-alpha.14'
|
||||||
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
|
implementation 'com.squareup.retrofit2:converter-gson:2.11.0'
|
||||||
}
|
}
|
||||||
5
app/proguard-rules.pro
vendored
5
app/proguard-rules.pro
vendored
@@ -22,4 +22,7 @@
|
|||||||
|
|
||||||
-keepattributes SourceFile, LineNumberTable
|
-keepattributes SourceFile, LineNumberTable
|
||||||
-keep public class * extends java.lang.Exception
|
-keep public class * extends java.lang.Exception
|
||||||
-keep class retrofit2.** { *; }
|
-keep class retrofit2.** { *; }
|
||||||
|
|
||||||
|
-keep class **.reflect.TypeToken { *; }
|
||||||
|
-keep class * extends **.reflect.TypeToken
|
||||||
1065
app/schemas/com.cappielloantonio.tempo.database.AppDatabase/10.json
Normal file
1065
app/schemas/com.cappielloantonio.tempo.database.AppDatabase/10.json
Normal file
File diff suppressed because it is too large
Load Diff
1027
app/schemas/com.cappielloantonio.tempo.database.AppDatabase/9.json
Normal file
1027
app/schemas/com.cappielloantonio.tempo.database.AppDatabase/9.json
Normal file
File diff suppressed because it is too large
Load Diff
@@ -4,8 +4,10 @@ import android.app.Application;
|
|||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
import androidx.preference.PreferenceManager;
|
import androidx.preference.PreferenceManager;
|
||||||
|
|
||||||
|
import com.cappielloantonio.tempo.github.Github;
|
||||||
import com.cappielloantonio.tempo.helper.ThemeHelper;
|
import com.cappielloantonio.tempo.helper.ThemeHelper;
|
||||||
import com.cappielloantonio.tempo.subsonic.Subsonic;
|
import com.cappielloantonio.tempo.subsonic.Subsonic;
|
||||||
import com.cappielloantonio.tempo.subsonic.SubsonicPreferences;
|
import com.cappielloantonio.tempo.subsonic.SubsonicPreferences;
|
||||||
@@ -15,6 +17,7 @@ public class App extends Application {
|
|||||||
private static App instance;
|
private static App instance;
|
||||||
private static Context context;
|
private static Context context;
|
||||||
private static Subsonic subsonic;
|
private static Subsonic subsonic;
|
||||||
|
private static Github github;
|
||||||
private static SharedPreferences preferences;
|
private static SharedPreferences preferences;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -53,6 +56,13 @@ public class App extends Application {
|
|||||||
return subsonic;
|
return subsonic;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Github getGithubClientInstance() {
|
||||||
|
if (github == null) {
|
||||||
|
github = new Github();
|
||||||
|
}
|
||||||
|
return github;
|
||||||
|
}
|
||||||
|
|
||||||
public SharedPreferences getPreferences() {
|
public SharedPreferences getPreferences() {
|
||||||
if (preferences == null) {
|
if (preferences == null) {
|
||||||
preferences = PreferenceManager.getDefaultSharedPreferences(context);
|
preferences = PreferenceManager.getDefaultSharedPreferences(context);
|
||||||
@@ -61,18 +71,12 @@ public class App extends Application {
|
|||||||
return preferences;
|
return preferences;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Subsonic getSubsonicClient() {
|
public static void refreshSubsonicClient() {
|
||||||
String server = Preferences.getServer();
|
subsonic = getSubsonicClient();
|
||||||
String username = Preferences.getUser();
|
}
|
||||||
String password = Preferences.getPassword();
|
|
||||||
String token = Preferences.getToken();
|
|
||||||
String salt = Preferences.getSalt();
|
|
||||||
boolean isLowSecurity = Preferences.isLowScurity();
|
|
||||||
|
|
||||||
SubsonicPreferences preferences = new SubsonicPreferences();
|
private static Subsonic getSubsonicClient() {
|
||||||
preferences.setServerUrl(server);
|
SubsonicPreferences preferences = getSubsonicPreferences();
|
||||||
preferences.setUsername(username);
|
|
||||||
preferences.setAuthentication(password, token, salt, isLowSecurity);
|
|
||||||
|
|
||||||
if (preferences.getAuthentication() != null) {
|
if (preferences.getAuthentication() != null) {
|
||||||
if (preferences.getAuthentication().getPassword() != null)
|
if (preferences.getAuthentication().getPassword() != null)
|
||||||
@@ -85,4 +89,21 @@ public class App extends Application {
|
|||||||
|
|
||||||
return new Subsonic(preferences);
|
return new Subsonic(preferences);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
private static SubsonicPreferences getSubsonicPreferences() {
|
||||||
|
String server = Preferences.getInUseServerAddress();
|
||||||
|
String username = Preferences.getUser();
|
||||||
|
String password = Preferences.getPassword();
|
||||||
|
String token = Preferences.getToken();
|
||||||
|
String salt = Preferences.getSalt();
|
||||||
|
boolean isLowSecurity = Preferences.isLowScurity();
|
||||||
|
|
||||||
|
SubsonicPreferences preferences = new SubsonicPreferences();
|
||||||
|
preferences.setServerUrl(server);
|
||||||
|
preferences.setUsername(username);
|
||||||
|
preferences.setAuthentication(password, token, salt, isLowSecurity);
|
||||||
|
|
||||||
|
return preferences;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package com.cappielloantonio.tempo.database;
|
package com.cappielloantonio.tempo.database;
|
||||||
|
|
||||||
|
import androidx.media3.common.util.UnstableApi;
|
||||||
import androidx.room.AutoMigration;
|
import androidx.room.AutoMigration;
|
||||||
import androidx.room.Database;
|
import androidx.room.Database;
|
||||||
import androidx.room.Room;
|
import androidx.room.Room;
|
||||||
@@ -11,6 +12,7 @@ import com.cappielloantonio.tempo.database.converter.DateConverters;
|
|||||||
import com.cappielloantonio.tempo.database.dao.ChronologyDao;
|
import com.cappielloantonio.tempo.database.dao.ChronologyDao;
|
||||||
import com.cappielloantonio.tempo.database.dao.DownloadDao;
|
import com.cappielloantonio.tempo.database.dao.DownloadDao;
|
||||||
import com.cappielloantonio.tempo.database.dao.FavoriteDao;
|
import com.cappielloantonio.tempo.database.dao.FavoriteDao;
|
||||||
|
import com.cappielloantonio.tempo.database.dao.PlaylistDao;
|
||||||
import com.cappielloantonio.tempo.database.dao.QueueDao;
|
import com.cappielloantonio.tempo.database.dao.QueueDao;
|
||||||
import com.cappielloantonio.tempo.database.dao.RecentSearchDao;
|
import com.cappielloantonio.tempo.database.dao.RecentSearchDao;
|
||||||
import com.cappielloantonio.tempo.database.dao.ServerDao;
|
import com.cappielloantonio.tempo.database.dao.ServerDao;
|
||||||
@@ -22,11 +24,13 @@ import com.cappielloantonio.tempo.model.Queue;
|
|||||||
import com.cappielloantonio.tempo.model.RecentSearch;
|
import com.cappielloantonio.tempo.model.RecentSearch;
|
||||||
import com.cappielloantonio.tempo.model.Server;
|
import com.cappielloantonio.tempo.model.Server;
|
||||||
import com.cappielloantonio.tempo.model.SessionMediaItem;
|
import com.cappielloantonio.tempo.model.SessionMediaItem;
|
||||||
|
import com.cappielloantonio.tempo.subsonic.models.Playlist;
|
||||||
|
|
||||||
|
@UnstableApi
|
||||||
@Database(
|
@Database(
|
||||||
version = 8,
|
version = 10,
|
||||||
entities = {Queue.class, Server.class, RecentSearch.class, Download.class, Chronology.class, Favorite.class, SessionMediaItem.class},
|
entities = {Queue.class, Server.class, RecentSearch.class, Download.class, Chronology.class, Favorite.class, SessionMediaItem.class, Playlist.class},
|
||||||
autoMigrations = {@AutoMigration(from = 7, to = 8)}
|
autoMigrations = {@AutoMigration(from = 9, to = 10)}
|
||||||
)
|
)
|
||||||
@TypeConverters({DateConverters.class})
|
@TypeConverters({DateConverters.class})
|
||||||
public abstract class AppDatabase extends RoomDatabase {
|
public abstract class AppDatabase extends RoomDatabase {
|
||||||
@@ -56,4 +60,6 @@ public abstract class AppDatabase extends RoomDatabase {
|
|||||||
public abstract FavoriteDao favoriteDao();
|
public abstract FavoriteDao favoriteDao();
|
||||||
|
|
||||||
public abstract SessionMediaItemDao sessionMediaItemDao();
|
public abstract SessionMediaItemDao sessionMediaItemDao();
|
||||||
|
|
||||||
|
public abstract PlaylistDao playlistDao();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,10 @@ import java.util.List;
|
|||||||
|
|
||||||
@Dao
|
@Dao
|
||||||
public interface ChronologyDao {
|
public interface ChronologyDao {
|
||||||
@Query("SELECT * FROM chronology WHERE timestamp >= :startDate AND timestamp < :endDate AND server == :server GROUP BY id ORDER BY COUNT(id) DESC LIMIT 9")
|
@Query("SELECT * FROM chronology WHERE server == :server GROUP BY id ORDER BY timestamp DESC LIMIT :count")
|
||||||
|
LiveData<List<Chronology>> getLastPlayed(String server, int count);
|
||||||
|
|
||||||
|
@Query("SELECT * FROM chronology WHERE timestamp >= :endDate AND timestamp < :startDate AND server == :server GROUP BY id ORDER BY COUNT(id) DESC LIMIT 20")
|
||||||
LiveData<List<Chronology>> getAllFrom(long startDate, long endDate, String server);
|
LiveData<List<Chronology>> getAllFrom(long startDate, long endDate, String server);
|
||||||
|
|
||||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import java.util.List;
|
|||||||
|
|
||||||
@Dao
|
@Dao
|
||||||
public interface DownloadDao {
|
public interface DownloadDao {
|
||||||
@Query("SELECT * FROM download WHERE download_state = 1 ORDER BY artist, album, track ASC")
|
@Query("SELECT * FROM download WHERE download_state = 1 ORDER BY artist, album, disc_number, track ASC")
|
||||||
LiveData<List<Download>> getAll();
|
LiveData<List<Download>> getAll();
|
||||||
|
|
||||||
@Query("SELECT * FROM download WHERE id = :id")
|
@Query("SELECT * FROM download WHERE id = :id")
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
package com.cappielloantonio.tempo.github;
|
||||||
|
|
||||||
|
import com.cappielloantonio.tempo.github.api.release.ReleaseClient;
|
||||||
|
|
||||||
|
public class Github {
|
||||||
|
private static final String OWNER = "CappielloAntonio";
|
||||||
|
private static final String REPO = "Tempo";
|
||||||
|
private ReleaseClient releaseClient;
|
||||||
|
|
||||||
|
public ReleaseClient getReleaseClient() {
|
||||||
|
if (releaseClient == null) {
|
||||||
|
releaseClient = new ReleaseClient(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
return releaseClient;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUrl() {
|
||||||
|
return "https://api.github.com/";
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getOwner() {
|
||||||
|
return OWNER;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getRepo() {
|
||||||
|
return REPO;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
package com.cappielloantonio.tempo.github
|
||||||
|
|
||||||
|
import okhttp3.OkHttpClient
|
||||||
|
import okhttp3.logging.HttpLoggingInterceptor
|
||||||
|
import retrofit2.Retrofit
|
||||||
|
import retrofit2.converter.gson.GsonConverterFactory
|
||||||
|
|
||||||
|
class GithubRetrofitClient(github: Github) {
|
||||||
|
var retrofit: Retrofit
|
||||||
|
|
||||||
|
init {
|
||||||
|
retrofit = Retrofit.Builder()
|
||||||
|
.baseUrl(github.url)
|
||||||
|
.addConverterFactory(GsonConverterFactory.create())
|
||||||
|
.client(getOkHttpClient())
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getOkHttpClient(): OkHttpClient {
|
||||||
|
return OkHttpClient.Builder()
|
||||||
|
.addInterceptor(getHttpLoggingInterceptor())
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getHttpLoggingInterceptor(): HttpLoggingInterceptor {
|
||||||
|
val loggingInterceptor = HttpLoggingInterceptor()
|
||||||
|
loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY)
|
||||||
|
return loggingInterceptor
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
package com.cappielloantonio.tempo.github.api.release;
|
||||||
|
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import com.cappielloantonio.tempo.github.Github;
|
||||||
|
import com.cappielloantonio.tempo.github.GithubRetrofitClient;
|
||||||
|
import com.cappielloantonio.tempo.github.models.LatestRelease;
|
||||||
|
|
||||||
|
import retrofit2.Call;
|
||||||
|
|
||||||
|
public class ReleaseClient {
|
||||||
|
private static final String TAG = "ReleaseClient";
|
||||||
|
|
||||||
|
private final ReleaseService releaseService;
|
||||||
|
|
||||||
|
public ReleaseClient(Github github) {
|
||||||
|
this.releaseService = new GithubRetrofitClient(github).getRetrofit().create(ReleaseService.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Call<LatestRelease> getLatestRelease() {
|
||||||
|
Log.d(TAG, "getLatestRelease()");
|
||||||
|
return releaseService.getLatestRelease(Github.getOwner(), Github.getRepo());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
package com.cappielloantonio.tempo.github.api.release;
|
||||||
|
|
||||||
|
import com.cappielloantonio.tempo.github.models.LatestRelease;
|
||||||
|
|
||||||
|
import retrofit2.Call;
|
||||||
|
import retrofit2.http.GET;
|
||||||
|
import retrofit2.http.Path;
|
||||||
|
|
||||||
|
public interface ReleaseService {
|
||||||
|
@GET("repos/{owner}/{repo}/releases/latest")
|
||||||
|
Call<LatestRelease> getLatestRelease(@Path("owner") String owner, @Path("repo") String repo);
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
package com.cappielloantonio.tempo.github.models
|
||||||
|
|
||||||
|
import androidx.annotation.Keep
|
||||||
|
import com.google.gson.annotations.SerializedName
|
||||||
|
|
||||||
|
@Keep
|
||||||
|
data class Assets(
|
||||||
|
@SerializedName("url")
|
||||||
|
var url: String? = null,
|
||||||
|
@SerializedName("id")
|
||||||
|
var id: Int? = null,
|
||||||
|
@SerializedName("node_id")
|
||||||
|
var nodeId: String? = null,
|
||||||
|
@SerializedName("name")
|
||||||
|
var name: String? = null,
|
||||||
|
@SerializedName("label")
|
||||||
|
var label: String? = null,
|
||||||
|
@SerializedName("uploader")
|
||||||
|
var uploader: Uploader? = Uploader(),
|
||||||
|
@SerializedName("content_type")
|
||||||
|
var contentType: String? = null,
|
||||||
|
@SerializedName("state")
|
||||||
|
var state: String? = null,
|
||||||
|
@SerializedName("size")
|
||||||
|
var size: Int? = null,
|
||||||
|
@SerializedName("download_count")
|
||||||
|
var downloadCount: Int? = null,
|
||||||
|
@SerializedName("created_at")
|
||||||
|
var createdAt: String? = null,
|
||||||
|
@SerializedName("updated_at")
|
||||||
|
var updatedAt: String? = null,
|
||||||
|
@SerializedName("browser_download_url")
|
||||||
|
var browserDownloadUrl: String? = null
|
||||||
|
)
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
package com.cappielloantonio.tempo.github.models
|
||||||
|
|
||||||
|
import androidx.annotation.Keep
|
||||||
|
import com.google.gson.annotations.SerializedName
|
||||||
|
|
||||||
|
@Keep
|
||||||
|
data class Author(
|
||||||
|
@SerializedName("login")
|
||||||
|
var login: String? = null,
|
||||||
|
@SerializedName("id")
|
||||||
|
var id: Int? = null,
|
||||||
|
@SerializedName("node_id")
|
||||||
|
var nodeId: String? = null,
|
||||||
|
@SerializedName("avatar_url")
|
||||||
|
var avatarUrl: String? = null,
|
||||||
|
@SerializedName("gravatar_id")
|
||||||
|
var gravatarId: String? = null,
|
||||||
|
@SerializedName("url")
|
||||||
|
var url: String? = null,
|
||||||
|
@SerializedName("html_url")
|
||||||
|
var htmlUrl: String? = null,
|
||||||
|
@SerializedName("followers_url")
|
||||||
|
var followersUrl: String? = null,
|
||||||
|
@SerializedName("following_url")
|
||||||
|
var followingUrl: String? = null,
|
||||||
|
@SerializedName("gists_url")
|
||||||
|
var gistsUrl: String? = null,
|
||||||
|
@SerializedName("starred_url")
|
||||||
|
var starredUrl: String? = null,
|
||||||
|
@SerializedName("subscriptions_url")
|
||||||
|
var subscriptionsUrl: String? = null,
|
||||||
|
@SerializedName("organizations_url")
|
||||||
|
var organizationsUrl: String? = null,
|
||||||
|
@SerializedName("repos_url")
|
||||||
|
var reposUrl: String? = null,
|
||||||
|
@SerializedName("events_url")
|
||||||
|
var eventsUrl: String? = null,
|
||||||
|
@SerializedName("received_events_url")
|
||||||
|
var receivedEventsUrl: String? = null,
|
||||||
|
@SerializedName("type")
|
||||||
|
var type: String? = null,
|
||||||
|
@SerializedName("site_admin")
|
||||||
|
var siteAdmin: Boolean? = null
|
||||||
|
)
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
package com.cappielloantonio.tempo.github.models
|
||||||
|
|
||||||
|
import androidx.annotation.Keep
|
||||||
|
import com.google.gson.annotations.SerializedName
|
||||||
|
|
||||||
|
@Keep
|
||||||
|
data class LatestRelease(
|
||||||
|
@SerializedName("url")
|
||||||
|
var url: String? = null,
|
||||||
|
@SerializedName("assets_url")
|
||||||
|
var assetsUrl: String? = null,
|
||||||
|
@SerializedName("upload_url")
|
||||||
|
var uploadUrl: String? = null,
|
||||||
|
@SerializedName("html_url")
|
||||||
|
var htmlUrl: String? = null,
|
||||||
|
@SerializedName("id")
|
||||||
|
var id: Int? = null,
|
||||||
|
@SerializedName("author")
|
||||||
|
var author: Author? = Author(),
|
||||||
|
@SerializedName("node_id")
|
||||||
|
var nodeId: String? = null,
|
||||||
|
@SerializedName("tag_name")
|
||||||
|
var tagName: String? = null,
|
||||||
|
@SerializedName("target_commitish")
|
||||||
|
var targetCommitish: String? = null,
|
||||||
|
@SerializedName("name")
|
||||||
|
var name: String? = null,
|
||||||
|
@SerializedName("draft")
|
||||||
|
var draft: Boolean? = null,
|
||||||
|
@SerializedName("prerelease")
|
||||||
|
var prerelease: Boolean? = null,
|
||||||
|
@SerializedName("created_at")
|
||||||
|
var createdAt: String? = null,
|
||||||
|
@SerializedName("published_at")
|
||||||
|
var publishedAt: String? = null,
|
||||||
|
@SerializedName("assets")
|
||||||
|
var assets: ArrayList<Assets> = arrayListOf(),
|
||||||
|
@SerializedName("tarball_url")
|
||||||
|
var tarballUrl: String? = null,
|
||||||
|
@SerializedName("zipball_url")
|
||||||
|
var zipballUrl: String? = null,
|
||||||
|
@SerializedName("body")
|
||||||
|
var body: String? = null,
|
||||||
|
@SerializedName("reactions")
|
||||||
|
var reactions: Reactions? = Reactions()
|
||||||
|
)
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
package com.cappielloantonio.tempo.github.models
|
||||||
|
|
||||||
|
import androidx.annotation.Keep
|
||||||
|
import com.google.gson.annotations.SerializedName
|
||||||
|
|
||||||
|
@Keep
|
||||||
|
data class Reactions(
|
||||||
|
@SerializedName("url")
|
||||||
|
var url: String? = null,
|
||||||
|
@SerializedName("total_count")
|
||||||
|
var totalCount: Int? = null,
|
||||||
|
@SerializedName("+1")
|
||||||
|
var like: Int? = null,
|
||||||
|
@SerializedName("-1")
|
||||||
|
var dislike: Int? = null,
|
||||||
|
@SerializedName("laugh")
|
||||||
|
var laugh: Int? = null,
|
||||||
|
@SerializedName("hooray")
|
||||||
|
var hooray: Int? = null,
|
||||||
|
@SerializedName("confused")
|
||||||
|
var confused: Int? = null,
|
||||||
|
@SerializedName("heart")
|
||||||
|
var heart: Int? = null,
|
||||||
|
@SerializedName("rocket")
|
||||||
|
var rocket: Int? = null,
|
||||||
|
@SerializedName("eyes")
|
||||||
|
var eyes: Int? = null
|
||||||
|
)
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
package com.cappielloantonio.tempo.github.models
|
||||||
|
|
||||||
|
import androidx.annotation.Keep
|
||||||
|
import com.google.gson.annotations.SerializedName
|
||||||
|
|
||||||
|
@Keep
|
||||||
|
data class Uploader(
|
||||||
|
@SerializedName("login")
|
||||||
|
var login: String? = null,
|
||||||
|
@SerializedName("id")
|
||||||
|
var id: Int? = null,
|
||||||
|
@SerializedName("node_id")
|
||||||
|
var nodeId: String? = null,
|
||||||
|
@SerializedName("avatar_url")
|
||||||
|
var avatarUrl: String? = null,
|
||||||
|
@SerializedName("gravatar_id")
|
||||||
|
var gravatarId: String? = null,
|
||||||
|
@SerializedName("url")
|
||||||
|
var url: String? = null,
|
||||||
|
@SerializedName("html_url")
|
||||||
|
var htmlUrl: String? = null,
|
||||||
|
@SerializedName("followers_url")
|
||||||
|
var followersUrl: String? = null,
|
||||||
|
@SerializedName("following_url")
|
||||||
|
var followingUrl: String? = null,
|
||||||
|
@SerializedName("gists_url")
|
||||||
|
var gistsUrl: String? = null,
|
||||||
|
@SerializedName("starred_url")
|
||||||
|
var starredUrl: String? = null,
|
||||||
|
@SerializedName("subscriptions_url")
|
||||||
|
var subscriptionsUrl: String? = null,
|
||||||
|
@SerializedName("organizations_url")
|
||||||
|
var organizationsUrl: String? = null,
|
||||||
|
@SerializedName("repos_url")
|
||||||
|
var reposUrl: String? = null,
|
||||||
|
@SerializedName("events_url")
|
||||||
|
var eventsUrl: String? = null,
|
||||||
|
@SerializedName("received_events_url")
|
||||||
|
var receivedEventsUrl: String? = null,
|
||||||
|
@SerializedName("type")
|
||||||
|
var type: String? = null,
|
||||||
|
@SerializedName("site_admin")
|
||||||
|
var siteAdmin: Boolean? = null
|
||||||
|
)
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
package com.cappielloantonio.tempo.github.utils;
|
||||||
|
|
||||||
|
import com.cappielloantonio.tempo.BuildConfig;
|
||||||
|
import com.cappielloantonio.tempo.github.models.LatestRelease;
|
||||||
|
|
||||||
|
public class UpdateUtil {
|
||||||
|
|
||||||
|
public static boolean showUpdateDialog(LatestRelease release) {
|
||||||
|
if (release.getTagName() == null) return false;
|
||||||
|
|
||||||
|
try {
|
||||||
|
String[] local = BuildConfig.VERSION_NAME.split("\\.");
|
||||||
|
String[] remote = release.getTagName().split("\\.");
|
||||||
|
|
||||||
|
for (int i = 0; i < local.length; i++) {
|
||||||
|
int localPart = Integer.parseInt(local[i]);
|
||||||
|
int remotePart = Integer.parseInt(remote[i]);
|
||||||
|
|
||||||
|
if (localPart > remotePart) {
|
||||||
|
return false;
|
||||||
|
} else if (localPart < remotePart) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception exception) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
package com.cappielloantonio.tempo.model
|
||||||
|
|
||||||
|
import androidx.annotation.Keep
|
||||||
|
|
||||||
|
@Keep
|
||||||
|
data class HomeSector(
|
||||||
|
val id: String,
|
||||||
|
val sectorTitle: String,
|
||||||
|
var isVisible: Boolean,
|
||||||
|
val order: Int,
|
||||||
|
)
|
||||||
@@ -2,6 +2,7 @@ package com.cappielloantonio.tempo.model
|
|||||||
|
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
import androidx.annotation.Keep
|
import androidx.annotation.Keep
|
||||||
|
import androidx.annotation.Nullable
|
||||||
import androidx.room.ColumnInfo
|
import androidx.room.ColumnInfo
|
||||||
import androidx.room.Entity
|
import androidx.room.Entity
|
||||||
import androidx.room.PrimaryKey
|
import androidx.room.PrimaryKey
|
||||||
@@ -27,6 +28,9 @@ data class Server(
|
|||||||
@ColumnInfo(name = "address")
|
@ColumnInfo(name = "address")
|
||||||
val address: String,
|
val address: String,
|
||||||
|
|
||||||
|
@ColumnInfo(name = "local_address")
|
||||||
|
val localAddress: String?,
|
||||||
|
|
||||||
@ColumnInfo(name = "timestamp")
|
@ColumnInfo(name = "timestamp")
|
||||||
val timestamp: Long,
|
val timestamp: Long,
|
||||||
|
|
||||||
|
|||||||
@@ -236,12 +236,12 @@ class SessionMediaItem() {
|
|||||||
.setMediaId(id!!)
|
.setMediaId(id!!)
|
||||||
.setMediaMetadata(
|
.setMediaMetadata(
|
||||||
MediaMetadata.Builder()
|
MediaMetadata.Builder()
|
||||||
.setTitle(MusicUtil.getReadableString(title))
|
.setTitle(title)
|
||||||
.setTrackNumber(track ?: 0)
|
.setTrackNumber(track ?: 0)
|
||||||
.setDiscNumber(discNumber ?: 0)
|
.setDiscNumber(discNumber ?: 0)
|
||||||
.setReleaseYear(year ?: 0)
|
.setReleaseYear(year ?: 0)
|
||||||
.setAlbumTitle(MusicUtil.getReadableString(album))
|
.setAlbumTitle(album)
|
||||||
.setArtist(MusicUtil.getReadableString(artist))
|
.setArtist(artist)
|
||||||
.setArtworkUri(artworkUri)
|
.setArtworkUri(artworkUri)
|
||||||
.setExtras(bundle)
|
.setExtras(bundle)
|
||||||
.setIsBrowsable(false)
|
.setIsBrowsable(false)
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import com.cappielloantonio.tempo.interfaces.DecadesCallback;
|
|||||||
import com.cappielloantonio.tempo.interfaces.MediaCallback;
|
import com.cappielloantonio.tempo.interfaces.MediaCallback;
|
||||||
import com.cappielloantonio.tempo.subsonic.base.ApiResponse;
|
import com.cappielloantonio.tempo.subsonic.base.ApiResponse;
|
||||||
import com.cappielloantonio.tempo.subsonic.models.AlbumID3;
|
import com.cappielloantonio.tempo.subsonic.models.AlbumID3;
|
||||||
|
import com.cappielloantonio.tempo.subsonic.models.AlbumInfo;
|
||||||
import com.cappielloantonio.tempo.subsonic.models.Child;
|
import com.cappielloantonio.tempo.subsonic.models.Child;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@@ -131,9 +132,10 @@ public class AlbumRepository {
|
|||||||
.enqueue(new Callback<ApiResponse>() {
|
.enqueue(new Callback<ApiResponse>() {
|
||||||
@Override
|
@Override
|
||||||
public void onResponse(@NonNull Call<ApiResponse> call, @NonNull Response<ApiResponse> response) {
|
public void onResponse(@NonNull Call<ApiResponse> call, @NonNull Response<ApiResponse> response) {
|
||||||
if (response.isSuccessful() && response.body() != null && response.body().getSubsonicResponse().getArtist() != null) {
|
if (response.isSuccessful() && response.body() != null && response.body().getSubsonicResponse().getArtist() != null && response.body().getSubsonicResponse().getArtist().getAlbums() != null) {
|
||||||
List<AlbumID3> albums = response.body().getSubsonicResponse().getArtist().getAlbums();
|
List<AlbumID3> albums = response.body().getSubsonicResponse().getArtist().getAlbums();
|
||||||
albums.sort(Comparator.comparing(AlbumID3::getYear));
|
albums.sort(Comparator.comparing(AlbumID3::getYear));
|
||||||
|
Collections.reverse(albums);
|
||||||
artistsAlbum.setValue(albums);
|
artistsAlbum.setValue(albums);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -170,6 +172,29 @@ public class AlbumRepository {
|
|||||||
return album;
|
return album;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public MutableLiveData<AlbumInfo> getAlbumInfo(String id) {
|
||||||
|
MutableLiveData<AlbumInfo> albumInfo = new MutableLiveData<>();
|
||||||
|
|
||||||
|
App.getSubsonicClientInstance(false)
|
||||||
|
.getBrowsingClient()
|
||||||
|
.getAlbumInfo2(id)
|
||||||
|
.enqueue(new Callback<ApiResponse>() {
|
||||||
|
@Override
|
||||||
|
public void onResponse(@NonNull Call<ApiResponse> call, @NonNull Response<ApiResponse> response) {
|
||||||
|
if (response.isSuccessful() && response.body() != null && response.body().getSubsonicResponse().getAlbumInfo() != null) {
|
||||||
|
albumInfo.setValue(response.body().getSubsonicResponse().getAlbumInfo());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailure(@NonNull Call<ApiResponse> call, @NonNull Throwable t) {
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return albumInfo;
|
||||||
|
}
|
||||||
|
|
||||||
public void getInstantMix(AlbumID3 album, int count, MediaCallback callback) {
|
public void getInstantMix(AlbumID3 album, int count, MediaCallback callback) {
|
||||||
App.getSubsonicClientInstance(false)
|
App.getSubsonicClientInstance(false)
|
||||||
.getBrowsingClient()
|
.getBrowsingClient()
|
||||||
@@ -250,7 +275,7 @@ public class AlbumRepository {
|
|||||||
@Override
|
@Override
|
||||||
public void onResponse(@NonNull Call<ApiResponse> call, @NonNull Response<ApiResponse> response) {
|
public void onResponse(@NonNull Call<ApiResponse> call, @NonNull Response<ApiResponse> response) {
|
||||||
if (response.isSuccessful() && response.body() != null && response.body().getSubsonicResponse().getAlbumList2() != null && response.body().getSubsonicResponse().getAlbumList2().getAlbums() != null) {
|
if (response.isSuccessful() && response.body() != null && response.body().getSubsonicResponse().getAlbumList2() != null && response.body().getSubsonicResponse().getAlbumList2().getAlbums() != null) {
|
||||||
if (response.body().getSubsonicResponse().getAlbumList2().getAlbums().size() > 0 && !response.body().getSubsonicResponse().getAlbumList2().getAlbums().isEmpty()) {
|
if (!response.body().getSubsonicResponse().getAlbumList2().getAlbums().isEmpty() && !response.body().getSubsonicResponse().getAlbumList2().getAlbums().isEmpty()) {
|
||||||
callback.onLoadYear(response.body().getSubsonicResponse().getAlbumList2().getAlbums().get(0).getYear());
|
callback.onLoadYear(response.body().getSubsonicResponse().getAlbumList2().getAlbums().get(0).getYear());
|
||||||
} else {
|
} else {
|
||||||
callback.onLoadYear(-1);
|
callback.onLoadYear(-1);
|
||||||
|
|||||||
@@ -2,9 +2,13 @@ package com.cappielloantonio.tempo.repository;
|
|||||||
|
|
||||||
|
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
|
import android.view.View;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.OptIn;
|
import androidx.annotation.OptIn;
|
||||||
|
import androidx.lifecycle.LifecycleOwner;
|
||||||
|
import androidx.lifecycle.MutableLiveData;
|
||||||
|
import androidx.lifecycle.Observer;
|
||||||
import androidx.media3.common.MediaItem;
|
import androidx.media3.common.MediaItem;
|
||||||
import androidx.media3.common.MediaMetadata;
|
import androidx.media3.common.MediaMetadata;
|
||||||
import androidx.media3.common.util.UnstableApi;
|
import androidx.media3.common.util.UnstableApi;
|
||||||
@@ -12,9 +16,13 @@ import androidx.media3.session.LibraryResult;
|
|||||||
|
|
||||||
import com.cappielloantonio.tempo.App;
|
import com.cappielloantonio.tempo.App;
|
||||||
import com.cappielloantonio.tempo.database.AppDatabase;
|
import com.cappielloantonio.tempo.database.AppDatabase;
|
||||||
|
import com.cappielloantonio.tempo.database.dao.ChronologyDao;
|
||||||
import com.cappielloantonio.tempo.database.dao.SessionMediaItemDao;
|
import com.cappielloantonio.tempo.database.dao.SessionMediaItemDao;
|
||||||
import com.cappielloantonio.tempo.glide.CustomGlideRequest;
|
import com.cappielloantonio.tempo.glide.CustomGlideRequest;
|
||||||
|
import com.cappielloantonio.tempo.model.Chronology;
|
||||||
|
import com.cappielloantonio.tempo.model.Download;
|
||||||
import com.cappielloantonio.tempo.model.SessionMediaItem;
|
import com.cappielloantonio.tempo.model.SessionMediaItem;
|
||||||
|
import com.cappielloantonio.tempo.service.DownloaderManager;
|
||||||
import com.cappielloantonio.tempo.subsonic.base.ApiResponse;
|
import com.cappielloantonio.tempo.subsonic.base.ApiResponse;
|
||||||
import com.cappielloantonio.tempo.subsonic.models.AlbumID3;
|
import com.cappielloantonio.tempo.subsonic.models.AlbumID3;
|
||||||
import com.cappielloantonio.tempo.subsonic.models.Artist;
|
import com.cappielloantonio.tempo.subsonic.models.Artist;
|
||||||
@@ -26,6 +34,7 @@ import com.cappielloantonio.tempo.subsonic.models.InternetRadioStation;
|
|||||||
import com.cappielloantonio.tempo.subsonic.models.MusicFolder;
|
import com.cappielloantonio.tempo.subsonic.models.MusicFolder;
|
||||||
import com.cappielloantonio.tempo.subsonic.models.Playlist;
|
import com.cappielloantonio.tempo.subsonic.models.Playlist;
|
||||||
import com.cappielloantonio.tempo.subsonic.models.PodcastEpisode;
|
import com.cappielloantonio.tempo.subsonic.models.PodcastEpisode;
|
||||||
|
import com.cappielloantonio.tempo.util.DownloadUtil;
|
||||||
import com.cappielloantonio.tempo.util.MappingUtil;
|
import com.cappielloantonio.tempo.util.MappingUtil;
|
||||||
import com.cappielloantonio.tempo.util.MusicUtil;
|
import com.cappielloantonio.tempo.util.MusicUtil;
|
||||||
import com.cappielloantonio.tempo.util.Preferences;
|
import com.cappielloantonio.tempo.util.Preferences;
|
||||||
@@ -44,6 +53,7 @@ import retrofit2.Response;
|
|||||||
|
|
||||||
public class AutomotiveRepository {
|
public class AutomotiveRepository {
|
||||||
private final SessionMediaItemDao sessionMediaItemDao = AppDatabase.getInstance().sessionMediaItemDao();
|
private final SessionMediaItemDao sessionMediaItemDao = AppDatabase.getInstance().sessionMediaItemDao();
|
||||||
|
private final ChronologyDao chronologyDao = AppDatabase.getInstance().chronologyDao();
|
||||||
|
|
||||||
public ListenableFuture<LibraryResult<ImmutableList<MediaItem>>> getAlbums(String prefix, String type, int size) {
|
public ListenableFuture<LibraryResult<ImmutableList<MediaItem>>> getAlbums(String prefix, String type, int size) {
|
||||||
final SettableFuture<LibraryResult<ImmutableList<MediaItem>>> listenableFuture = SettableFuture.create();
|
final SettableFuture<LibraryResult<ImmutableList<MediaItem>>> listenableFuture = SettableFuture.create();
|
||||||
@@ -132,6 +142,66 @@ public class AutomotiveRepository {
|
|||||||
return listenableFuture;
|
return listenableFuture;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ListenableFuture<LibraryResult<ImmutableList<MediaItem>>> getRandomSongs(int count) {
|
||||||
|
final SettableFuture<LibraryResult<ImmutableList<MediaItem>>> listenableFuture = SettableFuture.create();
|
||||||
|
|
||||||
|
App.getSubsonicClientInstance(false)
|
||||||
|
.getAlbumSongListClient()
|
||||||
|
.getRandomSongs(100, null, null)
|
||||||
|
.enqueue(new Callback<ApiResponse>() {
|
||||||
|
@Override
|
||||||
|
public void onResponse(@NonNull Call<ApiResponse> call, @NonNull Response<ApiResponse> response) {
|
||||||
|
if (response.isSuccessful() && response.body() != null && response.body().getSubsonicResponse().getRandomSongs() != null && response.body().getSubsonicResponse().getRandomSongs().getSongs() != null) {
|
||||||
|
List<Child> songs = response.body().getSubsonicResponse().getRandomSongs().getSongs();
|
||||||
|
|
||||||
|
setChildrenMetadata(songs);
|
||||||
|
|
||||||
|
List<MediaItem> mediaItems = MappingUtil.mapMediaItems(songs);
|
||||||
|
|
||||||
|
LibraryResult<ImmutableList<MediaItem>> libraryResult = LibraryResult.ofItemList(ImmutableList.copyOf(mediaItems), null);
|
||||||
|
|
||||||
|
listenableFuture.set(libraryResult);
|
||||||
|
} else {
|
||||||
|
listenableFuture.set(LibraryResult.ofError(LibraryResult.RESULT_ERROR_BAD_VALUE));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailure(@NonNull Call<ApiResponse> call, @NonNull Throwable t) {
|
||||||
|
listenableFuture.setException(t);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return listenableFuture;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ListenableFuture<LibraryResult<ImmutableList<MediaItem>>> getRecentlyPlayedSongs(String server, int count) {
|
||||||
|
final SettableFuture<LibraryResult<ImmutableList<MediaItem>>> listenableFuture = SettableFuture.create();
|
||||||
|
|
||||||
|
chronologyDao.getLastPlayed(server, count).observeForever(new Observer<List<Chronology>>() {
|
||||||
|
@Override
|
||||||
|
public void onChanged(List<Chronology> chronology) {
|
||||||
|
if (chronology != null && !chronology.isEmpty()) {
|
||||||
|
List<Child> songs = new ArrayList<>(chronology);
|
||||||
|
|
||||||
|
setChildrenMetadata(songs);
|
||||||
|
|
||||||
|
List<MediaItem> mediaItems = MappingUtil.mapMediaItems(songs);
|
||||||
|
|
||||||
|
LibraryResult<ImmutableList<MediaItem>> libraryResult = LibraryResult.ofItemList(ImmutableList.copyOf(mediaItems), null);
|
||||||
|
|
||||||
|
listenableFuture.set(libraryResult);
|
||||||
|
} else {
|
||||||
|
listenableFuture.set(LibraryResult.ofError(LibraryResult.RESULT_ERROR_BAD_VALUE));
|
||||||
|
}
|
||||||
|
|
||||||
|
chronologyDao.getLastPlayed(server, count).removeObserver(this);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return listenableFuture;
|
||||||
|
}
|
||||||
|
|
||||||
public ListenableFuture<LibraryResult<ImmutableList<MediaItem>>> getStarredAlbums(String prefix) {
|
public ListenableFuture<LibraryResult<ImmutableList<MediaItem>>> getStarredAlbums(String prefix) {
|
||||||
final SettableFuture<LibraryResult<ImmutableList<MediaItem>>> listenableFuture = SettableFuture.create();
|
final SettableFuture<LibraryResult<ImmutableList<MediaItem>>> listenableFuture = SettableFuture.create();
|
||||||
|
|
||||||
|
|||||||
@@ -12,28 +12,8 @@ import java.util.List;
|
|||||||
public class ChronologyRepository {
|
public class ChronologyRepository {
|
||||||
private final ChronologyDao chronologyDao = AppDatabase.getInstance().chronologyDao();
|
private final ChronologyDao chronologyDao = AppDatabase.getInstance().chronologyDao();
|
||||||
|
|
||||||
public LiveData<List<Chronology>> getThisWeek(String server) {
|
public LiveData<List<Chronology>> getChronology(String server, long start, long end) {
|
||||||
Calendar calendar = Calendar.getInstance();
|
return chronologyDao.getAllFrom(start, end, server);
|
||||||
|
|
||||||
Calendar first = (Calendar) calendar.clone();
|
|
||||||
first.add(Calendar.DAY_OF_WEEK, first.getFirstDayOfWeek() - first.get(Calendar.DAY_OF_WEEK));
|
|
||||||
|
|
||||||
Calendar last = (Calendar) first.clone();
|
|
||||||
last.add(Calendar.DAY_OF_YEAR, 6);
|
|
||||||
|
|
||||||
return chronologyDao.getAllFrom(first.getTime().getTime(), last.getTime().getTime(), server);
|
|
||||||
}
|
|
||||||
|
|
||||||
public LiveData<List<Chronology>> getLastWeek(String server) {
|
|
||||||
Calendar calendar = Calendar.getInstance();
|
|
||||||
|
|
||||||
Calendar first = (Calendar) calendar.clone();
|
|
||||||
first.add(Calendar.DAY_OF_WEEK, first.getFirstDayOfWeek() - first.get(Calendar.DAY_OF_WEEK) - 6);
|
|
||||||
|
|
||||||
Calendar last = (Calendar) first.clone();
|
|
||||||
last.add(Calendar.DAY_OF_YEAR, 6);
|
|
||||||
|
|
||||||
return chronologyDao.getAllFrom(first.getTime().getTime(), last.getTime().getTime(), server);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void insert(Chronology item) {
|
public void insert(Chronology item) {
|
||||||
|
|||||||
@@ -0,0 +1,37 @@
|
|||||||
|
package com.cappielloantonio.tempo.repository;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.lifecycle.MutableLiveData;
|
||||||
|
|
||||||
|
import com.cappielloantonio.tempo.App;
|
||||||
|
import com.cappielloantonio.tempo.subsonic.base.ApiResponse;
|
||||||
|
import com.cappielloantonio.tempo.subsonic.models.LyricsList;
|
||||||
|
|
||||||
|
import retrofit2.Call;
|
||||||
|
import retrofit2.Callback;
|
||||||
|
import retrofit2.Response;
|
||||||
|
|
||||||
|
public class OpenRepository {
|
||||||
|
public MutableLiveData<LyricsList> getLyricsBySongId(String id) {
|
||||||
|
MutableLiveData<LyricsList> lyricsList = new MutableLiveData<>();
|
||||||
|
|
||||||
|
App.getSubsonicClientInstance(false)
|
||||||
|
.getOpenClient()
|
||||||
|
.getLyricsBySongId(id)
|
||||||
|
.enqueue(new Callback<ApiResponse>() {
|
||||||
|
@Override
|
||||||
|
public void onResponse(@NonNull Call<ApiResponse> call, @NonNull Response<ApiResponse> response) {
|
||||||
|
if (response.isSuccessful() && response.body() != null && response.body().getSubsonicResponse().getLyricsList() != null) {
|
||||||
|
lyricsList.setValue(response.body().getSubsonicResponse().getLyricsList());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailure(@NonNull Call<ApiResponse> call, @NonNull Throwable t) {
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return lyricsList;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,9 +1,12 @@
|
|||||||
package com.cappielloantonio.tempo.repository;
|
package com.cappielloantonio.tempo.repository;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.lifecycle.LiveData;
|
||||||
import androidx.lifecycle.MutableLiveData;
|
import androidx.lifecycle.MutableLiveData;
|
||||||
|
|
||||||
import com.cappielloantonio.tempo.App;
|
import com.cappielloantonio.tempo.App;
|
||||||
|
import com.cappielloantonio.tempo.database.AppDatabase;
|
||||||
|
import com.cappielloantonio.tempo.database.dao.PlaylistDao;
|
||||||
import com.cappielloantonio.tempo.subsonic.base.ApiResponse;
|
import com.cappielloantonio.tempo.subsonic.base.ApiResponse;
|
||||||
import com.cappielloantonio.tempo.subsonic.models.Child;
|
import com.cappielloantonio.tempo.subsonic.models.Child;
|
||||||
import com.cappielloantonio.tempo.subsonic.models.Playlist;
|
import com.cappielloantonio.tempo.subsonic.models.Playlist;
|
||||||
@@ -17,6 +20,7 @@ import retrofit2.Callback;
|
|||||||
import retrofit2.Response;
|
import retrofit2.Response;
|
||||||
|
|
||||||
public class PlaylistRepository {
|
public class PlaylistRepository {
|
||||||
|
private final PlaylistDao playlistDao = AppDatabase.getInstance().playlistDao();
|
||||||
public MutableLiveData<List<Playlist>> getPlaylists(boolean random, int size) {
|
public MutableLiveData<List<Playlist>> getPlaylists(boolean random, int size) {
|
||||||
MutableLiveData<List<Playlist>> listLivePlaylists = new MutableLiveData<>(new ArrayList<>());
|
MutableLiveData<List<Playlist>> listLivePlaylists = new MutableLiveData<>(new ArrayList<>());
|
||||||
|
|
||||||
@@ -153,4 +157,50 @@ public class PlaylistRepository {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public LiveData<List<Playlist>> getPinnedPlaylists() {
|
||||||
|
return playlistDao.getAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void insert(Playlist playlist) {
|
||||||
|
InsertThreadSafe insert = new InsertThreadSafe(playlistDao, playlist);
|
||||||
|
Thread thread = new Thread(insert);
|
||||||
|
thread.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void delete(Playlist playlist) {
|
||||||
|
DeleteThreadSafe delete = new DeleteThreadSafe(playlistDao, playlist);
|
||||||
|
Thread thread = new Thread(delete);
|
||||||
|
thread.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class InsertThreadSafe implements Runnable {
|
||||||
|
private final PlaylistDao playlistDao;
|
||||||
|
private final Playlist playlist;
|
||||||
|
|
||||||
|
public InsertThreadSafe(PlaylistDao playlistDao, Playlist playlist) {
|
||||||
|
this.playlistDao = playlistDao;
|
||||||
|
this.playlist = playlist;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
playlistDao.insert(playlist);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class DeleteThreadSafe implements Runnable {
|
||||||
|
private final PlaylistDao playlistDao;
|
||||||
|
private final Playlist playlist;
|
||||||
|
|
||||||
|
public DeleteThreadSafe(PlaylistDao playlistDao, Playlist playlist) {
|
||||||
|
this.playlistDao = playlistDao;
|
||||||
|
this.playlist = playlist;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
playlistDao.delete(playlist);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,9 @@
|
|||||||
package com.cappielloantonio.tempo.repository;
|
package com.cappielloantonio.tempo.repository;
|
||||||
|
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.lifecycle.MutableLiveData;
|
import androidx.lifecycle.MutableLiveData;
|
||||||
|
|
||||||
import com.cappielloantonio.tempo.App;
|
import com.cappielloantonio.tempo.App;
|
||||||
import com.cappielloantonio.tempo.database.AppDatabase;
|
|
||||||
import com.cappielloantonio.tempo.database.dao.FavoriteDao;
|
|
||||||
import com.cappielloantonio.tempo.subsonic.base.ApiResponse;
|
import com.cappielloantonio.tempo.subsonic.base.ApiResponse;
|
||||||
import com.cappielloantonio.tempo.subsonic.models.Child;
|
import com.cappielloantonio.tempo.subsonic.models.Child;
|
||||||
|
|
||||||
@@ -54,12 +50,12 @@ public class SongRepository {
|
|||||||
return starredSongs;
|
return starredSongs;
|
||||||
}
|
}
|
||||||
|
|
||||||
public MutableLiveData<List<Child>> getInstantMix(Child song, int count) {
|
public MutableLiveData<List<Child>> getInstantMix(String id, int count) {
|
||||||
MutableLiveData<List<Child>> instantMix = new MutableLiveData<>();
|
MutableLiveData<List<Child>> instantMix = new MutableLiveData<>();
|
||||||
|
|
||||||
App.getSubsonicClientInstance(false)
|
App.getSubsonicClientInstance(false)
|
||||||
.getBrowsingClient()
|
.getBrowsingClient()
|
||||||
.getSimilarSongs2(song.getId(), count)
|
.getSimilarSongs2(id, count)
|
||||||
.enqueue(new Callback<ApiResponse>() {
|
.enqueue(new Callback<ApiResponse>() {
|
||||||
@Override
|
@Override
|
||||||
public void onResponse(@NonNull Call<ApiResponse> call, @NonNull Response<ApiResponse> response) {
|
public void onResponse(@NonNull Call<ApiResponse> call, @NonNull Response<ApiResponse> response) {
|
||||||
|
|||||||
@@ -1,12 +1,19 @@
|
|||||||
package com.cappielloantonio.tempo.repository;
|
package com.cappielloantonio.tempo.repository;
|
||||||
|
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.lifecycle.MutableLiveData;
|
import androidx.lifecycle.MutableLiveData;
|
||||||
|
|
||||||
import com.cappielloantonio.tempo.App;
|
import com.cappielloantonio.tempo.App;
|
||||||
|
import com.cappielloantonio.tempo.github.models.LatestRelease;
|
||||||
import com.cappielloantonio.tempo.interfaces.SystemCallback;
|
import com.cappielloantonio.tempo.interfaces.SystemCallback;
|
||||||
import com.cappielloantonio.tempo.subsonic.base.ApiResponse;
|
import com.cappielloantonio.tempo.subsonic.base.ApiResponse;
|
||||||
|
import com.cappielloantonio.tempo.subsonic.models.OpenSubsonicExtension;
|
||||||
import com.cappielloantonio.tempo.subsonic.models.ResponseStatus;
|
import com.cappielloantonio.tempo.subsonic.models.ResponseStatus;
|
||||||
|
import com.cappielloantonio.tempo.subsonic.models.SubsonicResponse;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import retrofit2.Call;
|
import retrofit2.Call;
|
||||||
import retrofit2.Callback;
|
import retrofit2.Callback;
|
||||||
@@ -43,8 +50,8 @@ public class SystemRepository {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public MutableLiveData<Boolean> ping() {
|
public MutableLiveData<SubsonicResponse> ping() {
|
||||||
MutableLiveData<Boolean> pingResult = new MutableLiveData<>();
|
MutableLiveData<SubsonicResponse> pingResult = new MutableLiveData<>();
|
||||||
|
|
||||||
App.getSubsonicClientInstance(false)
|
App.getSubsonicClientInstance(false)
|
||||||
.getSystemClient()
|
.getSystemClient()
|
||||||
@@ -53,16 +60,64 @@ public class SystemRepository {
|
|||||||
@Override
|
@Override
|
||||||
public void onResponse(@NonNull Call<ApiResponse> call, @NonNull Response<ApiResponse> response) {
|
public void onResponse(@NonNull Call<ApiResponse> call, @NonNull Response<ApiResponse> response) {
|
||||||
if (response.isSuccessful() && response.body() != null) {
|
if (response.isSuccessful() && response.body() != null) {
|
||||||
pingResult.postValue(true);
|
pingResult.postValue(response.body().getSubsonicResponse());
|
||||||
|
} else {
|
||||||
|
pingResult.postValue(null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onFailure(@NonNull Call<ApiResponse> call, @NonNull Throwable t) {
|
public void onFailure(@NonNull Call<ApiResponse> call, @NonNull Throwable t) {
|
||||||
pingResult.postValue(false);
|
pingResult.postValue(null);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return pingResult;
|
return pingResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public MutableLiveData<List<OpenSubsonicExtension>> getOpenSubsonicExtensions() {
|
||||||
|
MutableLiveData<List<OpenSubsonicExtension>> extensionsResult = new MutableLiveData<>();
|
||||||
|
|
||||||
|
App.getSubsonicClientInstance(false)
|
||||||
|
.getSystemClient()
|
||||||
|
.getOpenSubsonicExtensions()
|
||||||
|
.enqueue(new Callback<ApiResponse>() {
|
||||||
|
@Override
|
||||||
|
public void onResponse(@NonNull Call<ApiResponse> call, @NonNull Response<ApiResponse> response) {
|
||||||
|
if (response.isSuccessful() && response.body() != null) {
|
||||||
|
extensionsResult.postValue(response.body().getSubsonicResponse().getOpenSubsonicExtensions());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailure(@NonNull Call<ApiResponse> call, @NonNull Throwable t) {
|
||||||
|
extensionsResult.postValue(null);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return extensionsResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MutableLiveData<LatestRelease> checkTempoUpdate() {
|
||||||
|
MutableLiveData<LatestRelease> latestRelease = new MutableLiveData<>();
|
||||||
|
|
||||||
|
App.getGithubClientInstance()
|
||||||
|
.getReleaseClient()
|
||||||
|
.getLatestRelease()
|
||||||
|
.enqueue(new Callback<LatestRelease>() {
|
||||||
|
@Override
|
||||||
|
public void onResponse(@NonNull Call<LatestRelease> call, @NonNull Response<LatestRelease> response) {
|
||||||
|
if (response.isSuccessful() && response.body() != null) {
|
||||||
|
latestRelease.postValue(response.body());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailure(@NonNull Call<LatestRelease> call, @NonNull Throwable t) {
|
||||||
|
latestRelease.postValue(null);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return latestRelease;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,16 @@
|
|||||||
package com.cappielloantonio.tempo.service;
|
package com.cappielloantonio.tempo.service;
|
||||||
|
|
||||||
import androidx.media3.common.MediaItem;
|
import android.content.ComponentName;
|
||||||
import androidx.media3.session.MediaBrowser;
|
|
||||||
|
|
||||||
|
import androidx.annotation.OptIn;
|
||||||
|
import androidx.lifecycle.LiveData;
|
||||||
|
import androidx.lifecycle.Observer;
|
||||||
|
import androidx.media3.common.MediaItem;
|
||||||
|
import androidx.media3.common.util.UnstableApi;
|
||||||
|
import androidx.media3.session.MediaBrowser;
|
||||||
|
import androidx.media3.session.SessionToken;
|
||||||
|
|
||||||
|
import com.cappielloantonio.tempo.App;
|
||||||
import com.cappielloantonio.tempo.interfaces.MediaIndexCallback;
|
import com.cappielloantonio.tempo.interfaces.MediaIndexCallback;
|
||||||
import com.cappielloantonio.tempo.model.Chronology;
|
import com.cappielloantonio.tempo.model.Chronology;
|
||||||
import com.cappielloantonio.tempo.repository.ChronologyRepository;
|
import com.cappielloantonio.tempo.repository.ChronologyRepository;
|
||||||
@@ -299,6 +307,30 @@ public class MediaManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OptIn(markerClass = UnstableApi.class)
|
||||||
|
public static void continuousPlay(MediaItem mediaItem) {
|
||||||
|
if (mediaItem != null && Preferences.isContinuousPlayEnabled() && Preferences.isInstantMixUsable()) {
|
||||||
|
Preferences.setLastInstantMix();
|
||||||
|
|
||||||
|
LiveData<List<Child>> instantMix = getSongRepository().getInstantMix(mediaItem.mediaId, 10);
|
||||||
|
instantMix.observeForever(new Observer<List<Child>>() {
|
||||||
|
@Override
|
||||||
|
public void onChanged(List<Child> media) {
|
||||||
|
if (media != null) {
|
||||||
|
ListenableFuture<MediaBrowser> mediaBrowserListenableFuture = new MediaBrowser.Builder(
|
||||||
|
App.getContext(),
|
||||||
|
new SessionToken(App.getContext(), new ComponentName(App.getContext(), MediaService.class))
|
||||||
|
).buildAsync();
|
||||||
|
|
||||||
|
enqueue(mediaBrowserListenableFuture, media, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
instantMix.removeObserver(this);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static void saveChronology(MediaItem mediaItem) {
|
public static void saveChronology(MediaItem mediaItem) {
|
||||||
if (mediaItem != null) {
|
if (mediaItem != null) {
|
||||||
getChronologyRepository().insert(new Chronology(mediaItem));
|
getChronologyRepository().insert(new Chronology(mediaItem));
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import com.cappielloantonio.tempo.subsonic.api.internetradio.InternetRadioClient
|
|||||||
import com.cappielloantonio.tempo.subsonic.api.mediaannotation.MediaAnnotationClient;
|
import com.cappielloantonio.tempo.subsonic.api.mediaannotation.MediaAnnotationClient;
|
||||||
import com.cappielloantonio.tempo.subsonic.api.medialibraryscanning.MediaLibraryScanningClient;
|
import com.cappielloantonio.tempo.subsonic.api.medialibraryscanning.MediaLibraryScanningClient;
|
||||||
import com.cappielloantonio.tempo.subsonic.api.mediaretrieval.MediaRetrievalClient;
|
import com.cappielloantonio.tempo.subsonic.api.mediaretrieval.MediaRetrievalClient;
|
||||||
|
import com.cappielloantonio.tempo.subsonic.api.open.OpenClient;
|
||||||
import com.cappielloantonio.tempo.subsonic.api.playlist.PlaylistClient;
|
import com.cappielloantonio.tempo.subsonic.api.playlist.PlaylistClient;
|
||||||
import com.cappielloantonio.tempo.subsonic.api.podcast.PodcastClient;
|
import com.cappielloantonio.tempo.subsonic.api.podcast.PodcastClient;
|
||||||
import com.cappielloantonio.tempo.subsonic.api.searching.SearchingClient;
|
import com.cappielloantonio.tempo.subsonic.api.searching.SearchingClient;
|
||||||
@@ -35,6 +36,7 @@ public class Subsonic {
|
|||||||
private BookmarksClient bookmarksClient;
|
private BookmarksClient bookmarksClient;
|
||||||
private InternetRadioClient internetRadioClient;
|
private InternetRadioClient internetRadioClient;
|
||||||
private SharingClient sharingClient;
|
private SharingClient sharingClient;
|
||||||
|
private OpenClient openClient;
|
||||||
|
|
||||||
public Subsonic(SubsonicPreferences preferences) {
|
public Subsonic(SubsonicPreferences preferences) {
|
||||||
this.preferences = preferences;
|
this.preferences = preferences;
|
||||||
@@ -128,6 +130,13 @@ public class Subsonic {
|
|||||||
return sharingClient;
|
return sharingClient;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public OpenClient getOpenClient() {
|
||||||
|
if (openClient == null) {
|
||||||
|
openClient = new OpenClient(this);
|
||||||
|
}
|
||||||
|
return openClient;
|
||||||
|
}
|
||||||
|
|
||||||
public String getUrl() {
|
public String getUrl() {
|
||||||
String url = preferences.getServerUrl() + "/rest/";
|
String url = preferences.getServerUrl() + "/rest/";
|
||||||
return url.replace("//rest", "/rest");
|
return url.replace("//rest", "/rest");
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import com.cappielloantonio.tempo.subsonic.base.ApiResponse;
|
|||||||
import retrofit2.Call;
|
import retrofit2.Call;
|
||||||
|
|
||||||
public class MediaRetrievalClient {
|
public class MediaRetrievalClient {
|
||||||
private static final String TAG = "BrowsingClient";
|
private static final String TAG = "MediaRetrievalClient";
|
||||||
|
|
||||||
private final Subsonic subsonic;
|
private final Subsonic subsonic;
|
||||||
private final MediaRetrievalService mediaRetrievalService;
|
private final MediaRetrievalService mediaRetrievalService;
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
package com.cappielloantonio.tempo.subsonic.api.open;
|
||||||
|
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import com.cappielloantonio.tempo.subsonic.RetrofitClient;
|
||||||
|
import com.cappielloantonio.tempo.subsonic.Subsonic;
|
||||||
|
import com.cappielloantonio.tempo.subsonic.base.ApiResponse;
|
||||||
|
|
||||||
|
import retrofit2.Call;
|
||||||
|
|
||||||
|
public class OpenClient {
|
||||||
|
private static final String TAG = "OpenClient";
|
||||||
|
|
||||||
|
private final Subsonic subsonic;
|
||||||
|
private final OpenService openService;
|
||||||
|
|
||||||
|
public OpenClient(Subsonic subsonic) {
|
||||||
|
this.subsonic = subsonic;
|
||||||
|
this.openService = new RetrofitClient(subsonic).getRetrofit().create(OpenService.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Call<ApiResponse> getLyricsBySongId(String id) {
|
||||||
|
Log.d(TAG, "getLyricsBySongId()");
|
||||||
|
return openService.getLyricsBySongId(subsonic.getParams(), id);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
package com.cappielloantonio.tempo.subsonic.api.open;
|
||||||
|
|
||||||
|
import com.cappielloantonio.tempo.subsonic.base.ApiResponse;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import retrofit2.Call;
|
||||||
|
import retrofit2.http.GET;
|
||||||
|
import retrofit2.http.Query;
|
||||||
|
import retrofit2.http.QueryMap;
|
||||||
|
|
||||||
|
public interface OpenService {
|
||||||
|
@GET("getLyricsBySongId")
|
||||||
|
Call<ApiResponse> getLyricsBySongId(@QueryMap Map<String, String> params, @Query("id") String id);
|
||||||
|
}
|
||||||
@@ -28,4 +28,9 @@ public class SystemClient {
|
|||||||
Log.d(TAG, "getLicense()");
|
Log.d(TAG, "getLicense()");
|
||||||
return systemService.getLicense(subsonic.getParams());
|
return systemService.getLicense(subsonic.getParams());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Call<ApiResponse> getOpenSubsonicExtensions() {
|
||||||
|
Log.d(TAG, "getOpenSubsonicExtensions()");
|
||||||
|
return systemService.getOpenSubsonicExtensions(subsonic.getParams());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,4 +14,7 @@ public interface SystemService {
|
|||||||
|
|
||||||
@GET("getLicense")
|
@GET("getLicense")
|
||||||
Call<ApiResponse> getLicense(@QueryMap Map<String, String> params);
|
Call<ApiResponse> getLicense(@QueryMap Map<String, String> params);
|
||||||
|
|
||||||
|
@GET("getOpenSubsonicExtensions")
|
||||||
|
Call<ApiResponse> getOpenSubsonicExtensions(@QueryMap Map<String, String> params);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ import android.os.Parcelable
|
|||||||
import androidx.annotation.Keep
|
import androidx.annotation.Keep
|
||||||
import com.google.gson.annotations.SerializedName
|
import com.google.gson.annotations.SerializedName
|
||||||
import kotlinx.parcelize.Parcelize
|
import kotlinx.parcelize.Parcelize
|
||||||
|
import java.time.Instant
|
||||||
|
import java.time.LocalDate
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
@Keep
|
@Keep
|
||||||
@@ -17,9 +19,23 @@ open class AlbumID3 : Parcelable {
|
|||||||
var coverArtId: String? = null
|
var coverArtId: String? = null
|
||||||
var songCount: Int? = 0
|
var songCount: Int? = 0
|
||||||
var duration: Int? = 0
|
var duration: Int? = 0
|
||||||
var playCount: Long? = null
|
var playCount: Long? = 0
|
||||||
var created: Date? = null
|
var created: Date? = null
|
||||||
var starred: Date? = null
|
var starred: Date? = null
|
||||||
var year: Int = 0
|
var year: Int = 0
|
||||||
var genre: String? = null
|
var genre: String? = null
|
||||||
|
var played: Date? = Date(0)
|
||||||
|
var userRating: Int? = 0
|
||||||
|
var recordLabels: List<RecordLabel>? = null
|
||||||
|
var musicBrainzId: String? = null
|
||||||
|
var genres: List<ItemGenre>? = null
|
||||||
|
var artists: List<ArtistID3>? = null
|
||||||
|
var displayArtist: String? = null
|
||||||
|
var releaseTypes: List<String>? = null
|
||||||
|
var moods: List<String>? = null
|
||||||
|
var sortName: String? = null
|
||||||
|
var originalReleaseDate: ItemDate? = null
|
||||||
|
var releaseDate: ItemDate? = null
|
||||||
|
var isCompilation: Boolean? = null
|
||||||
|
var discTitles: List<DiscTitle>? = null
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
package com.cappielloantonio.tempo.subsonic.models
|
||||||
|
|
||||||
|
import android.os.Parcelable
|
||||||
|
import androidx.annotation.Keep
|
||||||
|
import kotlinx.parcelize.Parcelize
|
||||||
|
|
||||||
|
@Keep
|
||||||
|
@Parcelize
|
||||||
|
open class DiscTitle : Parcelable {
|
||||||
|
var disc: Int? = null
|
||||||
|
var title: String? = null
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
package com.cappielloantonio.tempo.subsonic.models
|
||||||
|
|
||||||
|
import android.os.Parcelable
|
||||||
|
import androidx.annotation.Keep
|
||||||
|
import kotlinx.parcelize.Parcelize
|
||||||
|
import java.text.SimpleDateFormat
|
||||||
|
import java.util.Calendar
|
||||||
|
import java.util.Locale
|
||||||
|
|
||||||
|
@Keep
|
||||||
|
@Parcelize
|
||||||
|
open class ItemDate : Parcelable {
|
||||||
|
var year: Int? = null
|
||||||
|
var month: Int? = null
|
||||||
|
var day: Int? = null
|
||||||
|
|
||||||
|
fun getFormattedDate(): String {
|
||||||
|
val calendar = Calendar.getInstance()
|
||||||
|
val dateFormat = SimpleDateFormat("MMMM dd, yyyy", Locale.getDefault())
|
||||||
|
|
||||||
|
calendar.set(year ?: 0, month ?: 0, day ?: 0)
|
||||||
|
|
||||||
|
return dateFormat.format(calendar.time)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
package com.cappielloantonio.tempo.subsonic.models
|
||||||
|
|
||||||
|
import android.os.Parcelable
|
||||||
|
import androidx.annotation.Keep
|
||||||
|
import kotlinx.parcelize.Parcelize
|
||||||
|
|
||||||
|
@Keep
|
||||||
|
@Parcelize
|
||||||
|
open class ItemGenre : Parcelable {
|
||||||
|
var name: String? = null
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package com.cappielloantonio.tempo.subsonic.models
|
||||||
|
|
||||||
|
import androidx.annotation.Keep
|
||||||
|
|
||||||
|
@Keep
|
||||||
|
class Line {
|
||||||
|
var start: Int? = null
|
||||||
|
lateinit var value: String
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
package com.cappielloantonio.tempo.subsonic.models
|
||||||
|
|
||||||
|
import androidx.annotation.Keep
|
||||||
|
|
||||||
|
@Keep
|
||||||
|
class LyricsList {
|
||||||
|
var structuredLyrics: List<StructuredLyrics>? = null
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package com.cappielloantonio.tempo.subsonic.models
|
||||||
|
|
||||||
|
import androidx.annotation.Keep
|
||||||
|
|
||||||
|
@Keep
|
||||||
|
class OpenSubsonicExtension {
|
||||||
|
var name: String? = null
|
||||||
|
var versions: List<Int>? = null
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
package com.cappielloantonio.tempo.subsonic.models
|
||||||
|
|
||||||
|
import android.os.Parcelable
|
||||||
|
import androidx.annotation.Keep
|
||||||
|
import kotlinx.parcelize.Parcelize
|
||||||
|
|
||||||
|
@Keep
|
||||||
|
@Parcelize
|
||||||
|
open class RecordLabel : Parcelable {
|
||||||
|
var name: String? = null
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package com.cappielloantonio.tempo.subsonic.models
|
||||||
|
|
||||||
|
import androidx.annotation.Keep
|
||||||
|
|
||||||
|
@Keep
|
||||||
|
class StructuredLyrics {
|
||||||
|
var displayArtist: String? = null
|
||||||
|
var displayTitle: String? = null
|
||||||
|
var lang: String? = null
|
||||||
|
var offset: Int = 0
|
||||||
|
var synced: Boolean = false
|
||||||
|
var line: List<Line>? = null
|
||||||
|
}
|
||||||
@@ -51,4 +51,7 @@ class SubsonicResponse {
|
|||||||
var version: String? = null
|
var version: String? = null
|
||||||
var type: String? = null
|
var type: String? = null
|
||||||
var serverVersion: String? = null
|
var serverVersion: String? = null
|
||||||
|
var openSubsonic: Boolean? = null
|
||||||
|
var openSubsonicExtensions: List<OpenSubsonicExtension>? = null
|
||||||
|
var lyricsList: LyricsList? = null
|
||||||
}
|
}
|
||||||
@@ -2,7 +2,8 @@ package com.cappielloantonio.tempo.subsonic.utils;
|
|||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.net.ConnectivityManager;
|
import android.net.ConnectivityManager;
|
||||||
import android.net.NetworkInfo;
|
import android.net.Network;
|
||||||
|
import android.net.NetworkCapabilities;
|
||||||
|
|
||||||
import com.cappielloantonio.tempo.App;
|
import com.cappielloantonio.tempo.App;
|
||||||
|
|
||||||
@@ -39,7 +40,19 @@ public class CacheUtil {
|
|||||||
|
|
||||||
private boolean isConnected() {
|
private boolean isConnected() {
|
||||||
ConnectivityManager connectivityManager = (ConnectivityManager) App.getContext().getSystemService(Context.CONNECTIVITY_SERVICE);
|
ConnectivityManager connectivityManager = (ConnectivityManager) App.getContext().getSystemService(Context.CONNECTIVITY_SERVICE);
|
||||||
NetworkInfo netInfo = connectivityManager.getActiveNetworkInfo();
|
|
||||||
return (netInfo != null && netInfo.isConnected());
|
if (connectivityManager != null) {
|
||||||
|
Network network = connectivityManager.getActiveNetwork();
|
||||||
|
|
||||||
|
if (network != null) {
|
||||||
|
NetworkCapabilities capabilities = connectivityManager.getNetworkCapabilities(network);
|
||||||
|
|
||||||
|
if (capabilities != null) {
|
||||||
|
return capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) && capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import android.net.ConnectivityManager;
|
|||||||
import android.net.NetworkInfo;
|
import android.net.NetworkInfo;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
|
import android.util.Log;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
@@ -18,12 +19,16 @@ import androidx.navigation.NavController;
|
|||||||
import androidx.navigation.fragment.NavHostFragment;
|
import androidx.navigation.fragment.NavHostFragment;
|
||||||
import androidx.navigation.ui.NavigationUI;
|
import androidx.navigation.ui.NavigationUI;
|
||||||
|
|
||||||
|
import com.cappielloantonio.tempo.App;
|
||||||
|
import com.cappielloantonio.tempo.BuildConfig;
|
||||||
import com.cappielloantonio.tempo.R;
|
import com.cappielloantonio.tempo.R;
|
||||||
import com.cappielloantonio.tempo.broadcast.receiver.ConnectivityStatusBroadcastReceiver;
|
import com.cappielloantonio.tempo.broadcast.receiver.ConnectivityStatusBroadcastReceiver;
|
||||||
import com.cappielloantonio.tempo.databinding.ActivityMainBinding;
|
import com.cappielloantonio.tempo.databinding.ActivityMainBinding;
|
||||||
|
import com.cappielloantonio.tempo.github.utils.UpdateUtil;
|
||||||
import com.cappielloantonio.tempo.service.MediaManager;
|
import com.cappielloantonio.tempo.service.MediaManager;
|
||||||
import com.cappielloantonio.tempo.ui.activity.base.BaseActivity;
|
import com.cappielloantonio.tempo.ui.activity.base.BaseActivity;
|
||||||
import com.cappielloantonio.tempo.ui.dialog.ConnectionAlertDialog;
|
import com.cappielloantonio.tempo.ui.dialog.ConnectionAlertDialog;
|
||||||
|
import com.cappielloantonio.tempo.ui.dialog.GithubTempoUpdateDialog;
|
||||||
import com.cappielloantonio.tempo.ui.dialog.ServerUnreachableDialog;
|
import com.cappielloantonio.tempo.ui.dialog.ServerUnreachableDialog;
|
||||||
import com.cappielloantonio.tempo.ui.fragment.PlayerBottomSheetFragment;
|
import com.cappielloantonio.tempo.ui.fragment.PlayerBottomSheetFragment;
|
||||||
import com.cappielloantonio.tempo.util.Constants;
|
import com.cappielloantonio.tempo.util.Constants;
|
||||||
@@ -39,7 +44,7 @@ import java.util.concurrent.ExecutionException;
|
|||||||
|
|
||||||
@UnstableApi
|
@UnstableApi
|
||||||
public class MainActivity extends BaseActivity {
|
public class MainActivity extends BaseActivity {
|
||||||
private static final String TAG = "MainActivity";
|
private static final String TAG = "MainActivityLogs";
|
||||||
|
|
||||||
public ActivityMainBinding bind;
|
public ActivityMainBinding bind;
|
||||||
private MainViewModel mainViewModel;
|
private MainViewModel mainViewModel;
|
||||||
@@ -70,6 +75,8 @@ public class MainActivity extends BaseActivity {
|
|||||||
|
|
||||||
init();
|
init();
|
||||||
checkConnectionType();
|
checkConnectionType();
|
||||||
|
getOpenSubsonicExtensions();
|
||||||
|
checkTempoUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -300,10 +307,11 @@ public class MainActivity extends BaseActivity {
|
|||||||
Preferences.setToken(null);
|
Preferences.setToken(null);
|
||||||
Preferences.setPassword(null);
|
Preferences.setPassword(null);
|
||||||
Preferences.setServer(null);
|
Preferences.setServer(null);
|
||||||
|
Preferences.setLocalAddress(null);
|
||||||
Preferences.setUser(null);
|
Preferences.setUser(null);
|
||||||
|
|
||||||
// TODO Enter all settings to be reset
|
// TODO Enter all settings to be reset
|
||||||
Preferences.setServerId(null);
|
Preferences.setOpenSubsonic(false);
|
||||||
Preferences.setPlaybackSpeed(Constants.MEDIA_PLAYBACK_SPEED_100);
|
Preferences.setPlaybackSpeed(Constants.MEDIA_PLAYBACK_SPEED_100);
|
||||||
Preferences.setSkipSilenceMode(false);
|
Preferences.setSkipSilenceMode(false);
|
||||||
Preferences.setDataSavingMode(false);
|
Preferences.setDataSavingMode(false);
|
||||||
@@ -333,10 +341,55 @@ public class MainActivity extends BaseActivity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void pingServer() {
|
private void pingServer() {
|
||||||
|
if (Preferences.getToken() == null) return;
|
||||||
|
|
||||||
|
if (Preferences.isInUseServerAddressLocal()) {
|
||||||
|
mainViewModel.ping().observe(this, subsonicResponse -> {
|
||||||
|
if (subsonicResponse == null) {
|
||||||
|
Preferences.setServerSwitchableTimer();
|
||||||
|
Preferences.switchInUseServerAddress();
|
||||||
|
App.refreshSubsonicClient();
|
||||||
|
pingServer();
|
||||||
|
} else {
|
||||||
|
Preferences.setOpenSubsonic(subsonicResponse.getOpenSubsonic() != null && subsonicResponse.getOpenSubsonic());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
if (Preferences.isServerSwitchable()) {
|
||||||
|
Preferences.setServerSwitchableTimer();
|
||||||
|
Preferences.switchInUseServerAddress();
|
||||||
|
App.refreshSubsonicClient();
|
||||||
|
pingServer();
|
||||||
|
} else {
|
||||||
|
mainViewModel.ping().observe(this, subsonicResponse -> {
|
||||||
|
if (subsonicResponse == null) {
|
||||||
|
if (Preferences.showServerUnreachableDialog()) {
|
||||||
|
ServerUnreachableDialog dialog = new ServerUnreachableDialog();
|
||||||
|
dialog.show(getSupportFragmentManager(), null);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Preferences.setOpenSubsonic(subsonicResponse.getOpenSubsonic() != null && subsonicResponse.getOpenSubsonic());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void getOpenSubsonicExtensions() {
|
||||||
if (Preferences.getToken() != null) {
|
if (Preferences.getToken() != null) {
|
||||||
mainViewModel.ping().observe(this, isPingSuccessfull -> {
|
mainViewModel.getOpenSubsonicExtensions().observe(this, openSubsonicExtensions -> {
|
||||||
if (!isPingSuccessfull && Preferences.showServerUnreachableDialog()) {
|
if (openSubsonicExtensions != null) {
|
||||||
ServerUnreachableDialog dialog = new ServerUnreachableDialog();
|
Preferences.setOpenSubsonicExtensions(openSubsonicExtensions);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkTempoUpdate() {
|
||||||
|
if (BuildConfig.FLAVOR.equals("tempo") && Preferences.showTempoUpdateDialog()) {
|
||||||
|
mainViewModel.checkTempoUpdate().observe(this, latestRelease -> {
|
||||||
|
if (latestRelease != null && UpdateUtil.showUpdateDialog(latestRelease)) {
|
||||||
|
GithubTempoUpdateDialog dialog = new GithubTempoUpdateDialog(latestRelease);
|
||||||
dialog.show(getSupportFragmentManager(), null);
|
dialog.show(getSupportFragmentManager(), null);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -38,8 +38,8 @@ public class AlbumAdapter extends RecyclerView.Adapter<AlbumAdapter.ViewHolder>
|
|||||||
public void onBindViewHolder(ViewHolder holder, int position) {
|
public void onBindViewHolder(ViewHolder holder, int position) {
|
||||||
AlbumID3 album = albums.get(position);
|
AlbumID3 album = albums.get(position);
|
||||||
|
|
||||||
holder.item.albumNameLabel.setText(MusicUtil.getReadableString(album.getName()));
|
holder.item.albumNameLabel.setText(album.getName());
|
||||||
holder.item.artistNameLabel.setText(MusicUtil.getReadableString(album.getArtist()));
|
holder.item.artistNameLabel.setText(album.getArtist());
|
||||||
|
|
||||||
CustomGlideRequest.Builder
|
CustomGlideRequest.Builder
|
||||||
.from(holder.itemView.getContext(), album.getCoverArtId(), CustomGlideRequest.ResourceType.Album)
|
.from(holder.itemView.getContext(), album.getCoverArtId(), CustomGlideRequest.ResourceType.Album)
|
||||||
|
|||||||
@@ -38,8 +38,8 @@ public class AlbumArtistPageOrSimilarAdapter extends RecyclerView.Adapter<AlbumA
|
|||||||
public void onBindViewHolder(ViewHolder holder, int position) {
|
public void onBindViewHolder(ViewHolder holder, int position) {
|
||||||
AlbumID3 album = albums.get(position);
|
AlbumID3 album = albums.get(position);
|
||||||
|
|
||||||
holder.item.albumNameLabel.setText(MusicUtil.getReadableString(album.getName()));
|
holder.item.albumNameLabel.setText(album.getName());
|
||||||
holder.item.artistNameLabel.setText(MusicUtil.getReadableString(album.getArtist()));
|
holder.item.artistNameLabel.setText(album.getArtist());
|
||||||
|
|
||||||
CustomGlideRequest.Builder
|
CustomGlideRequest.Builder
|
||||||
.from(holder.itemView.getContext(), album.getCoverArtId(), CustomGlideRequest.ResourceType.Album)
|
.from(holder.itemView.getContext(), album.getCoverArtId(), CustomGlideRequest.ResourceType.Album)
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package com.cappielloantonio.tempo.ui.adapter;
|
|||||||
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.Filter;
|
import android.widget.Filter;
|
||||||
import android.widget.Filterable;
|
import android.widget.Filterable;
|
||||||
@@ -24,6 +25,7 @@ import java.util.List;
|
|||||||
public class AlbumCatalogueAdapter extends RecyclerView.Adapter<AlbumCatalogueAdapter.ViewHolder> implements Filterable {
|
public class AlbumCatalogueAdapter extends RecyclerView.Adapter<AlbumCatalogueAdapter.ViewHolder> implements Filterable {
|
||||||
private final ClickCallback click;
|
private final ClickCallback click;
|
||||||
private String currentFilter;
|
private String currentFilter;
|
||||||
|
private boolean showArtist;
|
||||||
|
|
||||||
private final Filter filtering = new Filter() {
|
private final Filter filtering = new Filter() {
|
||||||
@Override
|
@Override
|
||||||
@@ -59,11 +61,12 @@ public class AlbumCatalogueAdapter extends RecyclerView.Adapter<AlbumCatalogueAd
|
|||||||
private List<AlbumID3> albums;
|
private List<AlbumID3> albums;
|
||||||
private List<AlbumID3> albumsFull;
|
private List<AlbumID3> albumsFull;
|
||||||
|
|
||||||
public AlbumCatalogueAdapter(ClickCallback click) {
|
public AlbumCatalogueAdapter(ClickCallback click, boolean showArtist) {
|
||||||
this.click = click;
|
this.click = click;
|
||||||
this.albums = Collections.emptyList();
|
this.albums = Collections.emptyList();
|
||||||
this.albumsFull = Collections.emptyList();
|
this.albumsFull = Collections.emptyList();
|
||||||
this.currentFilter = "";
|
this.currentFilter = "";
|
||||||
|
this.showArtist = showArtist;
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
@@ -77,8 +80,9 @@ public class AlbumCatalogueAdapter extends RecyclerView.Adapter<AlbumCatalogueAd
|
|||||||
public void onBindViewHolder(ViewHolder holder, int position) {
|
public void onBindViewHolder(ViewHolder holder, int position) {
|
||||||
AlbumID3 album = albums.get(position);
|
AlbumID3 album = albums.get(position);
|
||||||
|
|
||||||
holder.item.albumNameLabel.setText(MusicUtil.getReadableString(album.getName()));
|
holder.item.albumNameLabel.setText(album.getName());
|
||||||
holder.item.artistNameLabel.setText(MusicUtil.getReadableString(album.getArtist()));
|
holder.item.artistNameLabel.setText(album.getArtist());
|
||||||
|
holder.item.artistNameLabel.setVisibility(showArtist ? View.VISIBLE : View.GONE);
|
||||||
|
|
||||||
CustomGlideRequest.Builder
|
CustomGlideRequest.Builder
|
||||||
.from(holder.itemView.getContext(), album.getCoverArtId(), CustomGlideRequest.ResourceType.Album)
|
.from(holder.itemView.getContext(), album.getCoverArtId(), CustomGlideRequest.ResourceType.Album)
|
||||||
@@ -161,6 +165,18 @@ public class AlbumCatalogueAdapter extends RecyclerView.Adapter<AlbumCatalogueAd
|
|||||||
case Constants.ALBUM_ORDER_BY_RANDOM:
|
case Constants.ALBUM_ORDER_BY_RANDOM:
|
||||||
Collections.shuffle(albums);
|
Collections.shuffle(albums);
|
||||||
break;
|
break;
|
||||||
|
case Constants.ALBUM_ORDER_BY_RECENTLY_ADDED:
|
||||||
|
albums.sort(Comparator.comparing(AlbumID3::getCreated));
|
||||||
|
Collections.reverse(albums);
|
||||||
|
break;
|
||||||
|
case Constants.ALBUM_ORDER_BY_RECENTLY_PLAYED:
|
||||||
|
albums.sort(Comparator.comparing(AlbumID3::getPlayed));
|
||||||
|
Collections.reverse(albums);
|
||||||
|
break;
|
||||||
|
case Constants.ALBUM_ORDER_BY_MOST_PLAYED:
|
||||||
|
albums.sort(Comparator.comparing(AlbumID3::getPlayCount));
|
||||||
|
Collections.reverse(albums);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
notifyDataSetChanged();
|
notifyDataSetChanged();
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ package com.cappielloantonio.tempo.ui.adapter;
|
|||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.Filter;
|
||||||
|
import android.widget.Filterable;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
@@ -11,22 +13,60 @@ import com.cappielloantonio.tempo.databinding.ItemHorizontalAlbumBinding;
|
|||||||
import com.cappielloantonio.tempo.glide.CustomGlideRequest;
|
import com.cappielloantonio.tempo.glide.CustomGlideRequest;
|
||||||
import com.cappielloantonio.tempo.interfaces.ClickCallback;
|
import com.cappielloantonio.tempo.interfaces.ClickCallback;
|
||||||
import com.cappielloantonio.tempo.subsonic.models.AlbumID3;
|
import com.cappielloantonio.tempo.subsonic.models.AlbumID3;
|
||||||
|
import com.cappielloantonio.tempo.subsonic.models.Child;
|
||||||
import com.cappielloantonio.tempo.util.Constants;
|
import com.cappielloantonio.tempo.util.Constants;
|
||||||
import com.cappielloantonio.tempo.util.MusicUtil;
|
import com.cappielloantonio.tempo.util.MusicUtil;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.Comparator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class AlbumHorizontalAdapter extends RecyclerView.Adapter<AlbumHorizontalAdapter.ViewHolder> {
|
public class AlbumHorizontalAdapter extends RecyclerView.Adapter<AlbumHorizontalAdapter.ViewHolder> implements Filterable {
|
||||||
private final ClickCallback click;
|
private final ClickCallback click;
|
||||||
private final boolean isOffline;
|
private final boolean isOffline;
|
||||||
|
|
||||||
|
private List<AlbumID3> albumsFull;
|
||||||
private List<AlbumID3> albums;
|
private List<AlbumID3> albums;
|
||||||
|
private String currentFilter;
|
||||||
|
|
||||||
|
private final Filter filtering = new Filter() {
|
||||||
|
@Override
|
||||||
|
protected FilterResults performFiltering(CharSequence constraint) {
|
||||||
|
List<AlbumID3> filteredList = new ArrayList<>();
|
||||||
|
|
||||||
|
if (constraint == null || constraint.length() == 0) {
|
||||||
|
filteredList.addAll(albumsFull);
|
||||||
|
} else {
|
||||||
|
String filterPattern = constraint.toString().toLowerCase().trim();
|
||||||
|
currentFilter = filterPattern;
|
||||||
|
|
||||||
|
for (AlbumID3 item : albumsFull) {
|
||||||
|
if (item.getName().toLowerCase().contains(filterPattern)) {
|
||||||
|
filteredList.add(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FilterResults results = new FilterResults();
|
||||||
|
results.values = filteredList;
|
||||||
|
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void publishResults(CharSequence constraint, FilterResults results) {
|
||||||
|
albums = (List<AlbumID3>) results.values;
|
||||||
|
notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
public AlbumHorizontalAdapter(ClickCallback click, boolean isOffline) {
|
public AlbumHorizontalAdapter(ClickCallback click, boolean isOffline) {
|
||||||
this.click = click;
|
this.click = click;
|
||||||
this.isOffline = isOffline;
|
this.isOffline = isOffline;
|
||||||
this.albums = Collections.emptyList();
|
this.albums = Collections.emptyList();
|
||||||
|
this.albumsFull = Collections.emptyList();
|
||||||
|
this.currentFilter = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
@@ -40,8 +80,8 @@ public class AlbumHorizontalAdapter extends RecyclerView.Adapter<AlbumHorizontal
|
|||||||
public void onBindViewHolder(ViewHolder holder, int position) {
|
public void onBindViewHolder(ViewHolder holder, int position) {
|
||||||
AlbumID3 album = albums.get(position);
|
AlbumID3 album = albums.get(position);
|
||||||
|
|
||||||
holder.item.albumTitleTextView.setText(MusicUtil.getReadableString(album.getName()));
|
holder.item.albumTitleTextView.setText(album.getName());
|
||||||
holder.item.albumArtistTextView.setText(MusicUtil.getReadableString(album.getArtist()));
|
holder.item.albumArtistTextView.setText(album.getArtist());
|
||||||
|
|
||||||
CustomGlideRequest.Builder
|
CustomGlideRequest.Builder
|
||||||
.from(holder.itemView.getContext(), album.getCoverArtId(), CustomGlideRequest.ResourceType.Album)
|
.from(holder.itemView.getContext(), album.getCoverArtId(), CustomGlideRequest.ResourceType.Album)
|
||||||
@@ -55,10 +95,16 @@ public class AlbumHorizontalAdapter extends RecyclerView.Adapter<AlbumHorizontal
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void setItems(List<AlbumID3> albums) {
|
public void setItems(List<AlbumID3> albums) {
|
||||||
this.albums = albums;
|
this.albumsFull = albums != null ? albums : Collections.emptyList();
|
||||||
|
filtering.filter(currentFilter);
|
||||||
notifyDataSetChanged();
|
notifyDataSetChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Filter getFilter() {
|
||||||
|
return filtering;
|
||||||
|
}
|
||||||
|
|
||||||
public AlbumID3 getItem(int id) {
|
public AlbumID3 getItem(int id) {
|
||||||
return albums.get(id);
|
return albums.get(id);
|
||||||
}
|
}
|
||||||
@@ -95,4 +141,21 @@ public class AlbumHorizontalAdapter extends RecyclerView.Adapter<AlbumHorizontal
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void sort(String order) {
|
||||||
|
switch (order) {
|
||||||
|
case Constants.ALBUM_ORDER_BY_NAME:
|
||||||
|
albums.sort(Comparator.comparing(AlbumID3::getName));
|
||||||
|
break;
|
||||||
|
case Constants.ALBUM_ORDER_BY_MOST_RECENTLY_STARRED:
|
||||||
|
albums.sort(Comparator.comparing(AlbumID3::getStarred, Comparator.nullsLast(Comparator.reverseOrder())));
|
||||||
|
break;
|
||||||
|
case Constants.ALBUM_ORDER_BY_LEAST_RECENTLY_STARRED:
|
||||||
|
albums.sort(Comparator.comparing(AlbumID3::getStarred, Comparator.nullsLast(Comparator.naturalOrder())));
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
notifyDataSetChanged();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ public class ArtistAdapter extends RecyclerView.Adapter<ArtistAdapter.ViewHolder
|
|||||||
public void onBindViewHolder(ViewHolder holder, int position) {
|
public void onBindViewHolder(ViewHolder holder, int position) {
|
||||||
ArtistID3 artist = artists.get(position);
|
ArtistID3 artist = artists.get(position);
|
||||||
|
|
||||||
holder.item.artistNameLabel.setText(MusicUtil.getReadableString(artist.getName()));
|
holder.item.artistNameLabel.setText(artist.getName());
|
||||||
|
|
||||||
CustomGlideRequest.Builder
|
CustomGlideRequest.Builder
|
||||||
.from(holder.itemView.getContext(), artist.getCoverArtId(), CustomGlideRequest.ResourceType.Artist)
|
.from(holder.itemView.getContext(), artist.getCoverArtId(), CustomGlideRequest.ResourceType.Artist)
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ public class ArtistCatalogueAdapter extends RecyclerView.Adapter<ArtistCatalogue
|
|||||||
@Override
|
@Override
|
||||||
protected void publishResults(CharSequence constraint, FilterResults results) {
|
protected void publishResults(CharSequence constraint, FilterResults results) {
|
||||||
artists.clear();
|
artists.clear();
|
||||||
artists.addAll((List) results.values);
|
if (results.count > 0) artists.addAll((List) results.values);
|
||||||
notifyDataSetChanged();
|
notifyDataSetChanged();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -74,7 +74,7 @@ public class ArtistCatalogueAdapter extends RecyclerView.Adapter<ArtistCatalogue
|
|||||||
public void onBindViewHolder(ViewHolder holder, int position) {
|
public void onBindViewHolder(ViewHolder holder, int position) {
|
||||||
ArtistID3 artist = artists.get(position);
|
ArtistID3 artist = artists.get(position);
|
||||||
|
|
||||||
holder.item.artistNameLabel.setText(MusicUtil.getReadableString(artist.getName()));
|
holder.item.artistNameLabel.setText(artist.getName());
|
||||||
|
|
||||||
CustomGlideRequest.Builder
|
CustomGlideRequest.Builder
|
||||||
.from(holder.itemView.getContext(), artist.getCoverArtId(), CustomGlideRequest.ResourceType.Artist)
|
.from(holder.itemView.getContext(), artist.getCoverArtId(), CustomGlideRequest.ResourceType.Artist)
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ import android.os.Bundle;
|
|||||||
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 android.widget.Filter;
|
||||||
|
import android.widget.Filterable;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
@@ -11,21 +13,59 @@ import androidx.recyclerview.widget.RecyclerView;
|
|||||||
import com.cappielloantonio.tempo.databinding.ItemHorizontalArtistBinding;
|
import com.cappielloantonio.tempo.databinding.ItemHorizontalArtistBinding;
|
||||||
import com.cappielloantonio.tempo.glide.CustomGlideRequest;
|
import com.cappielloantonio.tempo.glide.CustomGlideRequest;
|
||||||
import com.cappielloantonio.tempo.interfaces.ClickCallback;
|
import com.cappielloantonio.tempo.interfaces.ClickCallback;
|
||||||
|
import com.cappielloantonio.tempo.subsonic.models.AlbumID3;
|
||||||
import com.cappielloantonio.tempo.subsonic.models.ArtistID3;
|
import com.cappielloantonio.tempo.subsonic.models.ArtistID3;
|
||||||
import com.cappielloantonio.tempo.util.Constants;
|
import com.cappielloantonio.tempo.util.Constants;
|
||||||
import com.cappielloantonio.tempo.util.MusicUtil;
|
import com.cappielloantonio.tempo.util.MusicUtil;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.Comparator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class ArtistHorizontalAdapter extends RecyclerView.Adapter<ArtistHorizontalAdapter.ViewHolder> {
|
public class ArtistHorizontalAdapter extends RecyclerView.Adapter<ArtistHorizontalAdapter.ViewHolder> implements Filterable {
|
||||||
private final ClickCallback click;
|
private final ClickCallback click;
|
||||||
|
|
||||||
|
private List<ArtistID3> artistsFull;
|
||||||
private List<ArtistID3> artists;
|
private List<ArtistID3> artists;
|
||||||
|
private String currentFilter;
|
||||||
|
|
||||||
|
private final Filter filtering = new Filter() {
|
||||||
|
@Override
|
||||||
|
protected FilterResults performFiltering(CharSequence constraint) {
|
||||||
|
List<ArtistID3> filteredList = new ArrayList<>();
|
||||||
|
|
||||||
|
if (constraint == null || constraint.length() == 0) {
|
||||||
|
filteredList.addAll(artistsFull);
|
||||||
|
} else {
|
||||||
|
String filterPattern = constraint.toString().toLowerCase().trim();
|
||||||
|
currentFilter = filterPattern;
|
||||||
|
|
||||||
|
for (ArtistID3 item : artistsFull) {
|
||||||
|
if (item.getName().toLowerCase().contains(filterPattern)) {
|
||||||
|
filteredList.add(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FilterResults results = new FilterResults();
|
||||||
|
results.values = filteredList;
|
||||||
|
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void publishResults(CharSequence constraint, FilterResults results) {
|
||||||
|
artists = (List<ArtistID3>) results.values;
|
||||||
|
notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
public ArtistHorizontalAdapter(ClickCallback click) {
|
public ArtistHorizontalAdapter(ClickCallback click) {
|
||||||
this.click = click;
|
this.click = click;
|
||||||
this.artists = Collections.emptyList();
|
this.artists = Collections.emptyList();
|
||||||
|
this.artistsFull = Collections.emptyList();
|
||||||
|
this.currentFilter = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
@@ -39,7 +79,7 @@ public class ArtistHorizontalAdapter extends RecyclerView.Adapter<ArtistHorizont
|
|||||||
public void onBindViewHolder(ViewHolder holder, int position) {
|
public void onBindViewHolder(ViewHolder holder, int position) {
|
||||||
ArtistID3 artist = artists.get(position);
|
ArtistID3 artist = artists.get(position);
|
||||||
|
|
||||||
holder.item.artistNameTextView.setText(MusicUtil.getReadableString(artist.getName()));
|
holder.item.artistNameTextView.setText(artist.getName());
|
||||||
|
|
||||||
if (artist.getAlbumCount() > 0) {
|
if (artist.getAlbumCount() > 0) {
|
||||||
holder.item.artistInfoTextView.setText("Album count: " + artist.getAlbumCount());
|
holder.item.artistInfoTextView.setText("Album count: " + artist.getAlbumCount());
|
||||||
@@ -59,10 +99,16 @@ public class ArtistHorizontalAdapter extends RecyclerView.Adapter<ArtistHorizont
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void setItems(List<ArtistID3> artists) {
|
public void setItems(List<ArtistID3> artists) {
|
||||||
this.artists = artists;
|
this.artistsFull = artists != null ? artists : Collections.emptyList();
|
||||||
|
filtering.filter(currentFilter);
|
||||||
notifyDataSetChanged();
|
notifyDataSetChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Filter getFilter() {
|
||||||
|
return filtering;
|
||||||
|
}
|
||||||
|
|
||||||
public ArtistID3 getItem(int id) {
|
public ArtistID3 getItem(int id) {
|
||||||
return artists.get(id);
|
return artists.get(id);
|
||||||
}
|
}
|
||||||
@@ -109,4 +155,21 @@ public class ArtistHorizontalAdapter extends RecyclerView.Adapter<ArtistHorizont
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void sort(String order) {
|
||||||
|
switch (order) {
|
||||||
|
case Constants.ARTIST_ORDER_BY_NAME:
|
||||||
|
artists.sort(Comparator.comparing(ArtistID3::getName));
|
||||||
|
break;
|
||||||
|
case Constants.ARTIST_ORDER_BY_MOST_RECENTLY_STARRED:
|
||||||
|
artists.sort(Comparator.comparing(ArtistID3::getStarred, Comparator.nullsLast(Comparator.reverseOrder())));
|
||||||
|
break;
|
||||||
|
case Constants.ARTIST_ORDER_BY_LEAST_RECENTLY_STARRED:
|
||||||
|
artists.sort(Comparator.comparing(ArtistID3::getStarred, Comparator.nullsLast(Comparator.naturalOrder())));
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
notifyDataSetChanged();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ public class ArtistSimilarAdapter extends RecyclerView.Adapter<ArtistSimilarAdap
|
|||||||
public void onBindViewHolder(ViewHolder holder, int position) {
|
public void onBindViewHolder(ViewHolder holder, int position) {
|
||||||
SimilarArtistID3 artist = artists.get(position);
|
SimilarArtistID3 artist = artists.get(position);
|
||||||
|
|
||||||
holder.item.artistNameLabel.setText(MusicUtil.getReadableString(artist.getName()));
|
holder.item.artistNameLabel.setText(artist.getName());
|
||||||
|
|
||||||
CustomGlideRequest.Builder
|
CustomGlideRequest.Builder
|
||||||
.from(holder.itemView.getContext(), artist.getCoverArtId(), CustomGlideRequest.ResourceType.Artist)
|
.from(holder.itemView.getContext(), artist.getCoverArtId(), CustomGlideRequest.ResourceType.Artist)
|
||||||
|
|||||||
@@ -39,8 +39,8 @@ public class DiscoverSongAdapter extends RecyclerView.Adapter<DiscoverSongAdapte
|
|||||||
public void onBindViewHolder(ViewHolder holder, int position) {
|
public void onBindViewHolder(ViewHolder holder, int position) {
|
||||||
Child song = songs.get(position);
|
Child song = songs.get(position);
|
||||||
|
|
||||||
holder.item.titleDiscoverSongLabel.setText(MusicUtil.getReadableString(song.getTitle()));
|
holder.item.titleDiscoverSongLabel.setText(song.getTitle());
|
||||||
holder.item.albumDiscoverSongLabel.setText(MusicUtil.getReadableString(song.getAlbum()));
|
holder.item.albumDiscoverSongLabel.setText(song.getAlbum());
|
||||||
|
|
||||||
CustomGlideRequest.Builder
|
CustomGlideRequest.Builder
|
||||||
.from(holder.itemView.getContext(), song.getCoverArtId(), CustomGlideRequest.ResourceType.Song)
|
.from(holder.itemView.getContext(), song.getCoverArtId(), CustomGlideRequest.ResourceType.Song)
|
||||||
|
|||||||
@@ -185,9 +185,17 @@ public class DownloadHorizontalAdapter extends RecyclerView.Adapter<DownloadHori
|
|||||||
private void initTrackLayout(ViewHolder holder, int position) {
|
private void initTrackLayout(ViewHolder holder, int position) {
|
||||||
Child song = grouped.get(position);
|
Child song = grouped.get(position);
|
||||||
|
|
||||||
holder.item.downloadedItemTitleTextView.setText(MusicUtil.getReadableString(song.getTitle()));
|
holder.item.downloadedItemTitleTextView.setText(song.getTitle());
|
||||||
holder.item.downloadedItemSubtitleTextView.setText(holder.itemView.getContext().getString(R.string.song_subtitle_formatter, MusicUtil.getReadableString(song.getArtist()), MusicUtil.getReadableDurationString(song.getDuration(), false)));
|
holder.item.downloadedItemSubtitleTextView.setText(
|
||||||
holder.item.downloadedItemPreTextView.setText(MusicUtil.getReadableString(song.getAlbum()));
|
holder.itemView.getContext().getString(
|
||||||
|
R.string.song_subtitle_formatter,
|
||||||
|
song.getArtist(),
|
||||||
|
MusicUtil.getReadableDurationString(song.getDuration(), false),
|
||||||
|
""
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
holder.item.downloadedItemPreTextView.setText(song.getAlbum());
|
||||||
|
|
||||||
CustomGlideRequest.Builder
|
CustomGlideRequest.Builder
|
||||||
.from(holder.itemView.getContext(), song.getCoverArtId(), CustomGlideRequest.ResourceType.Song)
|
.from(holder.itemView.getContext(), song.getCoverArtId(), CustomGlideRequest.ResourceType.Song)
|
||||||
@@ -208,9 +216,9 @@ public class DownloadHorizontalAdapter extends RecyclerView.Adapter<DownloadHori
|
|||||||
private void initAlbumLayout(ViewHolder holder, int position) {
|
private void initAlbumLayout(ViewHolder holder, int position) {
|
||||||
Child song = grouped.get(position);
|
Child song = grouped.get(position);
|
||||||
|
|
||||||
holder.item.downloadedItemTitleTextView.setText(MusicUtil.getReadableString(song.getAlbum()));
|
holder.item.downloadedItemTitleTextView.setText(song.getAlbum());
|
||||||
holder.item.downloadedItemSubtitleTextView.setText(holder.itemView.getContext().getString(R.string.download_item_single_subtitle_formatter, countSong(Constants.DOWNLOAD_TYPE_ALBUM, song.getAlbumId(), songs)));
|
holder.item.downloadedItemSubtitleTextView.setText(holder.itemView.getContext().getString(R.string.download_item_single_subtitle_formatter, countSong(Constants.DOWNLOAD_TYPE_ALBUM, song.getAlbumId(), songs)));
|
||||||
holder.item.downloadedItemPreTextView.setText(MusicUtil.getReadableString(song.getArtist()));
|
holder.item.downloadedItemPreTextView.setText(song.getArtist());
|
||||||
|
|
||||||
CustomGlideRequest.Builder
|
CustomGlideRequest.Builder
|
||||||
.from(holder.itemView.getContext(), song.getCoverArtId(), CustomGlideRequest.ResourceType.Song)
|
.from(holder.itemView.getContext(), song.getCoverArtId(), CustomGlideRequest.ResourceType.Song)
|
||||||
@@ -231,7 +239,7 @@ public class DownloadHorizontalAdapter extends RecyclerView.Adapter<DownloadHori
|
|||||||
private void initArtistLayout(ViewHolder holder, int position) {
|
private void initArtistLayout(ViewHolder holder, int position) {
|
||||||
Child song = grouped.get(position);
|
Child song = grouped.get(position);
|
||||||
|
|
||||||
holder.item.downloadedItemTitleTextView.setText(MusicUtil.getReadableString(song.getArtist()));
|
holder.item.downloadedItemTitleTextView.setText(song.getArtist());
|
||||||
holder.item.downloadedItemSubtitleTextView.setText(holder.itemView.getContext().getString(R.string.download_item_single_subtitle_formatter, countSong(Constants.DOWNLOAD_TYPE_ARTIST, song.getArtistId(), songs)));
|
holder.item.downloadedItemSubtitleTextView.setText(holder.itemView.getContext().getString(R.string.download_item_single_subtitle_formatter, countSong(Constants.DOWNLOAD_TYPE_ARTIST, song.getArtistId(), songs)));
|
||||||
|
|
||||||
CustomGlideRequest.Builder
|
CustomGlideRequest.Builder
|
||||||
@@ -247,7 +255,7 @@ public class DownloadHorizontalAdapter extends RecyclerView.Adapter<DownloadHori
|
|||||||
private void initGenreLayout(ViewHolder holder, int position) {
|
private void initGenreLayout(ViewHolder holder, int position) {
|
||||||
Child song = grouped.get(position);
|
Child song = grouped.get(position);
|
||||||
|
|
||||||
holder.item.downloadedItemTitleTextView.setText(MusicUtil.getReadableString(song.getGenre()));
|
holder.item.downloadedItemTitleTextView.setText(song.getGenre());
|
||||||
holder.item.downloadedItemSubtitleTextView.setText(holder.itemView.getContext().getString(R.string.download_item_single_subtitle_formatter, countSong(Constants.DOWNLOAD_TYPE_GENRE, song.getGenre(), songs)));
|
holder.item.downloadedItemSubtitleTextView.setText(holder.itemView.getContext().getString(R.string.download_item_single_subtitle_formatter, countSong(Constants.DOWNLOAD_TYPE_GENRE, song.getGenre(), songs)));
|
||||||
|
|
||||||
holder.item.itemCoverImageView.setVisibility(View.GONE);
|
holder.item.itemCoverImageView.setVisibility(View.GONE);
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ public class GenreAdapter extends RecyclerView.Adapter<GenreAdapter.ViewHolder>
|
|||||||
public void onBindViewHolder(ViewHolder holder, int position) {
|
public void onBindViewHolder(ViewHolder holder, int position) {
|
||||||
Genre genre = genres.get(position);
|
Genre genre = genres.get(position);
|
||||||
|
|
||||||
holder.item.genreLabel.setText(MusicUtil.getReadableString(genre.getGenre()));
|
holder.item.genreLabel.setText(genre.getGenre());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ import com.cappielloantonio.tempo.databinding.ItemLibraryCatalogueGenreBinding;
|
|||||||
import com.cappielloantonio.tempo.interfaces.ClickCallback;
|
import com.cappielloantonio.tempo.interfaces.ClickCallback;
|
||||||
import com.cappielloantonio.tempo.subsonic.models.Genre;
|
import com.cappielloantonio.tempo.subsonic.models.Genre;
|
||||||
import com.cappielloantonio.tempo.util.Constants;
|
import com.cappielloantonio.tempo.util.Constants;
|
||||||
import com.cappielloantonio.tempo.util.MusicUtil;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
@@ -49,7 +48,7 @@ public class GenreCatalogueAdapter extends RecyclerView.Adapter<GenreCatalogueAd
|
|||||||
@Override
|
@Override
|
||||||
protected void publishResults(CharSequence constraint, FilterResults results) {
|
protected void publishResults(CharSequence constraint, FilterResults results) {
|
||||||
genres.clear();
|
genres.clear();
|
||||||
genres.addAll((List) results.values);
|
if (results.count > 0) genres.addAll((List) results.values);
|
||||||
notifyDataSetChanged();
|
notifyDataSetChanged();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -73,7 +72,7 @@ public class GenreCatalogueAdapter extends RecyclerView.Adapter<GenreCatalogueAd
|
|||||||
public void onBindViewHolder(ViewHolder holder, int position) {
|
public void onBindViewHolder(ViewHolder holder, int position) {
|
||||||
Genre genre = genres.get(position);
|
Genre genre = genres.get(position);
|
||||||
|
|
||||||
holder.item.genreLabel.setText(MusicUtil.getReadableString(genre.getGenre()));
|
holder.item.genreLabel.setText(genre.getGenre());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -0,0 +1,76 @@
|
|||||||
|
package com.cappielloantonio.tempo.ui.adapter;
|
||||||
|
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
|
import com.cappielloantonio.tempo.databinding.ItemHorizontalHomeSectorBinding;
|
||||||
|
import com.cappielloantonio.tempo.databinding.ItemHorizontalPlaylistDialogTrackBinding;
|
||||||
|
import com.cappielloantonio.tempo.glide.CustomGlideRequest;
|
||||||
|
import com.cappielloantonio.tempo.model.HomeSector;
|
||||||
|
import com.cappielloantonio.tempo.subsonic.models.Child;
|
||||||
|
import com.cappielloantonio.tempo.util.Constants;
|
||||||
|
import com.cappielloantonio.tempo.util.MusicUtil;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class HomeSectorHorizontalAdapter extends RecyclerView.Adapter<HomeSectorHorizontalAdapter.ViewHolder> {
|
||||||
|
private List<HomeSector> sectors;
|
||||||
|
|
||||||
|
public HomeSectorHorizontalAdapter() {
|
||||||
|
this.sectors = Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||||
|
ItemHorizontalHomeSectorBinding view = ItemHorizontalHomeSectorBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false);
|
||||||
|
return new ViewHolder(view);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBindViewHolder(ViewHolder holder, int position) {
|
||||||
|
HomeSector sector = sectors.get(position);
|
||||||
|
|
||||||
|
holder.item.homeSectorTitleCheckBox.setText(sector.getSectorTitle());
|
||||||
|
holder.item.homeSectorTitleCheckBox.setChecked(sector.isVisible());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getItemCount() {
|
||||||
|
return sectors.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<HomeSector> getItems() {
|
||||||
|
return this.sectors;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setItems(List<HomeSector> sectors) {
|
||||||
|
this.sectors = sectors;
|
||||||
|
notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
public HomeSector getItem(int id) {
|
||||||
|
return sectors.get(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ViewHolder extends RecyclerView.ViewHolder {
|
||||||
|
ItemHorizontalHomeSectorBinding item;
|
||||||
|
|
||||||
|
ViewHolder(ItemHorizontalHomeSectorBinding item) {
|
||||||
|
super(item.getRoot());
|
||||||
|
|
||||||
|
this.item = item;
|
||||||
|
|
||||||
|
this.item.homeSectorTitleCheckBox.setOnCheckedChangeListener((buttonView, isChecked) -> onCheck(isChecked));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onCheck(boolean isChecked) {
|
||||||
|
sectors.get(getBindingAdapterPosition()).setVisible(isChecked);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,9 +2,11 @@ package com.cappielloantonio.tempo.ui.adapter;
|
|||||||
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.appcompat.content.res.AppCompatResources;
|
||||||
import androidx.media3.session.MediaBrowser;
|
import androidx.media3.session.MediaBrowser;
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
@@ -17,6 +19,7 @@ import com.cappielloantonio.tempo.service.MediaManager;
|
|||||||
import com.cappielloantonio.tempo.subsonic.models.Child;
|
import com.cappielloantonio.tempo.subsonic.models.Child;
|
||||||
import com.cappielloantonio.tempo.util.Constants;
|
import com.cappielloantonio.tempo.util.Constants;
|
||||||
import com.cappielloantonio.tempo.util.MusicUtil;
|
import com.cappielloantonio.tempo.util.MusicUtil;
|
||||||
|
import com.cappielloantonio.tempo.util.Preferences;
|
||||||
import com.google.common.util.concurrent.ListenableFuture;
|
import com.google.common.util.concurrent.ListenableFuture;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@@ -45,8 +48,15 @@ public class PlayerSongQueueAdapter extends RecyclerView.Adapter<PlayerSongQueue
|
|||||||
public void onBindViewHolder(ViewHolder holder, int position) {
|
public void onBindViewHolder(ViewHolder holder, int position) {
|
||||||
Child song = songs.get(position);
|
Child song = songs.get(position);
|
||||||
|
|
||||||
holder.item.queueSongTitleTextView.setText(MusicUtil.getReadableString(song.getTitle()));
|
holder.item.queueSongTitleTextView.setText(song.getTitle());
|
||||||
holder.item.queueSongSubtitleTextView.setText(holder.itemView.getContext().getString(R.string.song_subtitle_formatter, MusicUtil.getReadableString(song.getArtist()), MusicUtil.getReadableDurationString(song.getDuration(), false)));
|
holder.item.queueSongSubtitleTextView.setText(
|
||||||
|
holder.itemView.getContext().getString(
|
||||||
|
R.string.song_subtitle_formatter,
|
||||||
|
song.getArtist(),
|
||||||
|
MusicUtil.getReadableDurationString(song.getDuration(), false),
|
||||||
|
MusicUtil.getReadableAudioQualityString(song)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
CustomGlideRequest.Builder
|
CustomGlideRequest.Builder
|
||||||
.from(holder.itemView.getContext(), song.getCoverArtId(), CustomGlideRequest.ResourceType.Song)
|
.from(holder.itemView.getContext(), song.getCoverArtId(), CustomGlideRequest.ResourceType.Song)
|
||||||
@@ -59,12 +69,33 @@ public class PlayerSongQueueAdapter extends RecyclerView.Adapter<PlayerSongQueue
|
|||||||
if (position < index) {
|
if (position < index) {
|
||||||
holder.item.queueSongTitleTextView.setAlpha(0.2f);
|
holder.item.queueSongTitleTextView.setAlpha(0.2f);
|
||||||
holder.item.queueSongSubtitleTextView.setAlpha(0.2f);
|
holder.item.queueSongSubtitleTextView.setAlpha(0.2f);
|
||||||
|
holder.item.ratingIndicatorImageView.setAlpha(0.2f);
|
||||||
} else {
|
} else {
|
||||||
holder.item.queueSongTitleTextView.setAlpha(1.0f);
|
holder.item.queueSongTitleTextView.setAlpha(1.0f);
|
||||||
holder.item.queueSongSubtitleTextView.setAlpha(1.0f);
|
holder.item.queueSongSubtitleTextView.setAlpha(1.0f);
|
||||||
|
holder.item.ratingIndicatorImageView.setAlpha(1.0f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (Preferences.showItemRating()) {
|
||||||
|
if (song.getStarred() == null && song.getUserRating() == null) {
|
||||||
|
holder.item.ratingIndicatorImageView.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
holder.item.preferredIcon.setVisibility(song.getStarred() != null ? View.VISIBLE : View.GONE);
|
||||||
|
holder.item.ratingBarLayout.setVisibility(song.getUserRating() != null ? View.VISIBLE : View.GONE);
|
||||||
|
|
||||||
|
if (song.getUserRating() != null) {
|
||||||
|
holder.item.oneStarIcon.setImageDrawable(AppCompatResources.getDrawable(holder.itemView.getContext(), song.getUserRating() >= 1 ? R.drawable.ic_star : R.drawable.ic_star_outlined));
|
||||||
|
holder.item.twoStarIcon.setImageDrawable(AppCompatResources.getDrawable(holder.itemView.getContext(), song.getUserRating() >= 2 ? R.drawable.ic_star : R.drawable.ic_star_outlined));
|
||||||
|
holder.item.threeStarIcon.setImageDrawable(AppCompatResources.getDrawable(holder.itemView.getContext(), song.getUserRating() >= 3 ? R.drawable.ic_star : R.drawable.ic_star_outlined));
|
||||||
|
holder.item.fourStarIcon.setImageDrawable(AppCompatResources.getDrawable(holder.itemView.getContext(), song.getUserRating() >= 4 ? R.drawable.ic_star : R.drawable.ic_star_outlined));
|
||||||
|
holder.item.fiveStarIcon.setImageDrawable(AppCompatResources.getDrawable(holder.itemView.getContext(), song.getUserRating() >= 5 ? R.drawable.ic_star : R.drawable.ic_star_outlined));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
holder.item.ratingIndicatorImageView.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Child> getItems() {
|
public List<Child> getItems() {
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ public class PlaylistDialogHorizontalAdapter extends RecyclerView.Adapter<Playli
|
|||||||
public void onBindViewHolder(ViewHolder holder, int position) {
|
public void onBindViewHolder(ViewHolder holder, int position) {
|
||||||
Playlist playlist = playlists.get(position);
|
Playlist playlist = playlists.get(position);
|
||||||
|
|
||||||
holder.item.playlistDialogTitleTextView.setText(MusicUtil.getReadableString(playlist.getName()));
|
holder.item.playlistDialogTitleTextView.setText(playlist.getName());
|
||||||
holder.item.playlistDialogCountTextView.setText(holder.itemView.getContext().getString(R.string.playlist_counted_tracks, playlist.getSongCount(), MusicUtil.getReadableDurationString(playlist.getDuration(), false)));
|
holder.item.playlistDialogCountTextView.setText(holder.itemView.getContext().getString(R.string.playlist_counted_tracks, playlist.getSongCount(), MusicUtil.getReadableDurationString(playlist.getDuration(), false)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -32,8 +32,8 @@ public class PlaylistDialogSongHorizontalAdapter extends RecyclerView.Adapter<Pl
|
|||||||
public void onBindViewHolder(ViewHolder holder, int position) {
|
public void onBindViewHolder(ViewHolder holder, int position) {
|
||||||
Child song = songs.get(position);
|
Child song = songs.get(position);
|
||||||
|
|
||||||
holder.item.playlistDialogSongTitleTextView.setText(MusicUtil.getReadableString(song.getTitle()));
|
holder.item.playlistDialogSongTitleTextView.setText(song.getTitle());
|
||||||
holder.item.playlistDialogAlbumArtistTextView.setText(MusicUtil.getReadableString(song.getArtist()));
|
holder.item.playlistDialogAlbumArtistTextView.setText(song.getArtist());
|
||||||
holder.item.playlistDialogSongDurationTextView.setText(MusicUtil.getReadableDurationString(song.getDuration(), false));
|
holder.item.playlistDialogSongDurationTextView.setText(MusicUtil.getReadableDurationString(song.getDuration(), false));
|
||||||
|
|
||||||
CustomGlideRequest.Builder
|
CustomGlideRequest.Builder
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ public class PlaylistHorizontalAdapter extends RecyclerView.Adapter<PlaylistHori
|
|||||||
@Override
|
@Override
|
||||||
protected void publishResults(CharSequence constraint, FilterResults results) {
|
protected void publishResults(CharSequence constraint, FilterResults results) {
|
||||||
playlists.clear();
|
playlists.clear();
|
||||||
playlists.addAll((List) results.values);
|
if (results.count > 0) playlists.addAll((List) results.values);
|
||||||
notifyDataSetChanged();
|
notifyDataSetChanged();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -75,7 +75,7 @@ public class PlaylistHorizontalAdapter extends RecyclerView.Adapter<PlaylistHori
|
|||||||
public void onBindViewHolder(ViewHolder holder, int position) {
|
public void onBindViewHolder(ViewHolder holder, int position) {
|
||||||
Playlist playlist = playlists.get(position);
|
Playlist playlist = playlists.get(position);
|
||||||
|
|
||||||
holder.item.playlistTitleTextView.setText(MusicUtil.getReadableString(playlist.getName()));
|
holder.item.playlistTitleTextView.setText(playlist.getName());
|
||||||
holder.item.playlistSubtitleTextView.setText(holder.itemView.getContext().getString(R.string.playlist_counted_tracks, playlist.getSongCount(), MusicUtil.getReadableDurationString(playlist.getDuration(), false)));
|
holder.item.playlistSubtitleTextView.setText(holder.itemView.getContext().getString(R.string.playlist_counted_tracks, playlist.getSongCount(), MusicUtil.getReadableDurationString(playlist.getDuration(), false)));
|
||||||
|
|
||||||
CustomGlideRequest.Builder
|
CustomGlideRequest.Builder
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ public class PodcastChannelCatalogueAdapter extends RecyclerView.Adapter<Podcast
|
|||||||
@Override
|
@Override
|
||||||
protected void publishResults(CharSequence constraint, FilterResults results) {
|
protected void publishResults(CharSequence constraint, FilterResults results) {
|
||||||
podcastChannels.clear();
|
podcastChannels.clear();
|
||||||
podcastChannels.addAll((List) results.values);
|
if (results.count > 0) podcastChannels.addAll((List) results.values);
|
||||||
notifyDataSetChanged();
|
notifyDataSetChanged();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -72,7 +72,7 @@ public class PodcastChannelCatalogueAdapter extends RecyclerView.Adapter<Podcast
|
|||||||
public void onBindViewHolder(ViewHolder holder, int position) {
|
public void onBindViewHolder(ViewHolder holder, int position) {
|
||||||
PodcastChannel podcastChannel = podcastChannels.get(position);
|
PodcastChannel podcastChannel = podcastChannels.get(position);
|
||||||
|
|
||||||
holder.item.podcastChannelTitleLabel.setText(MusicUtil.getReadableString(podcastChannel.getTitle()));
|
holder.item.podcastChannelTitleLabel.setText(podcastChannel.getTitle());
|
||||||
|
|
||||||
CustomGlideRequest.Builder
|
CustomGlideRequest.Builder
|
||||||
.from(holder.itemView.getContext(), podcastChannel.getCoverArtId(), CustomGlideRequest.ResourceType.Podcast)
|
.from(holder.itemView.getContext(), podcastChannel.getCoverArtId(), CustomGlideRequest.ResourceType.Podcast)
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ public class PodcastChannelHorizontalAdapter extends RecyclerView.Adapter<Podcas
|
|||||||
public void onBindViewHolder(ViewHolder holder, int position) {
|
public void onBindViewHolder(ViewHolder holder, int position) {
|
||||||
PodcastChannel podcastChannel = podcastChannels.get(position);
|
PodcastChannel podcastChannel = podcastChannels.get(position);
|
||||||
|
|
||||||
holder.item.podcastChannelTitleTextView.setText(MusicUtil.getReadableString(podcastChannel.getTitle()));
|
holder.item.podcastChannelTitleTextView.setText(podcastChannel.getTitle());
|
||||||
holder.item.podcastChannelDescriptionTextView.setText(MusicUtil.getReadableString(podcastChannel.getDescription()));
|
holder.item.podcastChannelDescriptionTextView.setText(MusicUtil.getReadableString(podcastChannel.getDescription()));
|
||||||
|
|
||||||
CustomGlideRequest.Builder
|
CustomGlideRequest.Builder
|
||||||
|
|||||||
@@ -45,8 +45,8 @@ public class PodcastEpisodeAdapter extends RecyclerView.Adapter<PodcastEpisodeAd
|
|||||||
PodcastEpisode podcastEpisode = podcastEpisodes.get(position);
|
PodcastEpisode podcastEpisode = podcastEpisodes.get(position);
|
||||||
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("MMM d");
|
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("MMM d");
|
||||||
|
|
||||||
holder.item.podcastTitleLabel.setText(MusicUtil.getReadableString(podcastEpisode.getTitle()));
|
holder.item.podcastTitleLabel.setText(podcastEpisode.getTitle());
|
||||||
holder.item.podcastSubtitleLabel.setText(MusicUtil.getReadableString(podcastEpisode.getArtist()));
|
holder.item.podcastSubtitleLabel.setText(podcastEpisode.getArtist());
|
||||||
holder.item.podcastReleasesAndDurationLabel.setText(holder.itemView.getContext().getString(R.string.podcast_release_date_duration_formatter, simpleDateFormat.format(podcastEpisode.getPublishDate()), MusicUtil.getReadablePodcastDurationString(podcastEpisode.getDuration())));
|
holder.item.podcastReleasesAndDurationLabel.setText(holder.itemView.getContext().getString(R.string.podcast_release_date_duration_formatter, simpleDateFormat.format(podcastEpisode.getPublishDate()), MusicUtil.getReadablePodcastDurationString(podcastEpisode.getDuration())));
|
||||||
holder.item.podcastDescriptionText.setText(MusicUtil.getReadableString(podcastEpisode.getDescription()));
|
holder.item.podcastDescriptionText.setText(MusicUtil.getReadableString(podcastEpisode.getDescription()));
|
||||||
|
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ public class ShareHorizontalAdapter extends RecyclerView.Adapter<ShareHorizontal
|
|||||||
public void onBindViewHolder(ViewHolder holder, int position) {
|
public void onBindViewHolder(ViewHolder holder, int position) {
|
||||||
Share share = shares.get(position);
|
Share share = shares.get(position);
|
||||||
|
|
||||||
holder.item.shareTitleTextView.setText(MusicUtil.getReadableString(share.getDescription()));
|
holder.item.shareTitleTextView.setText(share.getDescription());
|
||||||
holder.item.shareSubtitleTextView.setText(holder.itemView.getContext().getString(R.string.share_subtitle_item, UIUtil.getReadableDate(share.getExpires())));
|
holder.item.shareSubtitleTextView.setText(holder.itemView.getContext().getString(R.string.share_subtitle_item, UIUtil.getReadableDate(share.getExpires())));
|
||||||
|
|
||||||
if (share.getEntries() != null && !share.getEntries().isEmpty()) CustomGlideRequest.Builder
|
if (share.getEntries() != null && !share.getEntries().isEmpty()) CustomGlideRequest.Builder
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ public class SimilarTrackAdapter extends RecyclerView.Adapter<SimilarTrackAdapte
|
|||||||
public void onBindViewHolder(ViewHolder holder, int position) {
|
public void onBindViewHolder(ViewHolder holder, int position) {
|
||||||
Child song = songs.get(position);
|
Child song = songs.get(position);
|
||||||
|
|
||||||
holder.item.titleTrackLabel.setText(MusicUtil.getReadableString(song.getTitle()));
|
holder.item.titleTrackLabel.setText(song.getTitle());
|
||||||
|
|
||||||
CustomGlideRequest.Builder
|
CustomGlideRequest.Builder
|
||||||
.from(holder.itemView.getContext(), song.getCoverArtId(), CustomGlideRequest.ResourceType.Song)
|
.from(holder.itemView.getContext(), song.getCoverArtId(), CustomGlideRequest.ResourceType.Song)
|
||||||
|
|||||||
@@ -4,8 +4,11 @@ import android.os.Bundle;
|
|||||||
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 android.widget.Filter;
|
||||||
|
import android.widget.Filterable;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.appcompat.content.res.AppCompatResources;
|
||||||
import androidx.media3.common.util.UnstableApi;
|
import androidx.media3.common.util.UnstableApi;
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
@@ -13,28 +16,71 @@ import com.cappielloantonio.tempo.R;
|
|||||||
import com.cappielloantonio.tempo.databinding.ItemHorizontalTrackBinding;
|
import com.cappielloantonio.tempo.databinding.ItemHorizontalTrackBinding;
|
||||||
import com.cappielloantonio.tempo.glide.CustomGlideRequest;
|
import com.cappielloantonio.tempo.glide.CustomGlideRequest;
|
||||||
import com.cappielloantonio.tempo.interfaces.ClickCallback;
|
import com.cappielloantonio.tempo.interfaces.ClickCallback;
|
||||||
|
import com.cappielloantonio.tempo.subsonic.models.AlbumID3;
|
||||||
import com.cappielloantonio.tempo.subsonic.models.Child;
|
import com.cappielloantonio.tempo.subsonic.models.Child;
|
||||||
|
import com.cappielloantonio.tempo.subsonic.models.DiscTitle;
|
||||||
import com.cappielloantonio.tempo.util.Constants;
|
import com.cappielloantonio.tempo.util.Constants;
|
||||||
import com.cappielloantonio.tempo.util.DownloadUtil;
|
import com.cappielloantonio.tempo.util.DownloadUtil;
|
||||||
import com.cappielloantonio.tempo.util.MusicUtil;
|
import com.cappielloantonio.tempo.util.MusicUtil;
|
||||||
|
import com.cappielloantonio.tempo.util.Preferences;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.Comparator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
@UnstableApi
|
@UnstableApi
|
||||||
public class SongHorizontalAdapter extends RecyclerView.Adapter<SongHorizontalAdapter.ViewHolder> {
|
public class SongHorizontalAdapter extends RecyclerView.Adapter<SongHorizontalAdapter.ViewHolder> implements Filterable {
|
||||||
private final ClickCallback click;
|
private final ClickCallback click;
|
||||||
private final boolean showCoverArt;
|
private final boolean showCoverArt;
|
||||||
private final boolean showAlbum;
|
private final boolean showAlbum;
|
||||||
|
private final AlbumID3 album;
|
||||||
|
|
||||||
|
private List<Child> songsFull;
|
||||||
private List<Child> songs;
|
private List<Child> songs;
|
||||||
|
private String currentFilter;
|
||||||
|
|
||||||
public SongHorizontalAdapter(ClickCallback click, boolean showCoverArt, boolean showAlbum) {
|
private final Filter filtering = new Filter() {
|
||||||
|
@Override
|
||||||
|
protected FilterResults performFiltering(CharSequence constraint) {
|
||||||
|
List<Child> filteredList = new ArrayList<>();
|
||||||
|
|
||||||
|
if (constraint == null || constraint.length() == 0) {
|
||||||
|
filteredList.addAll(songsFull);
|
||||||
|
} else {
|
||||||
|
String filterPattern = constraint.toString().toLowerCase().trim();
|
||||||
|
currentFilter = filterPattern;
|
||||||
|
|
||||||
|
for (Child item : songsFull) {
|
||||||
|
if (item.getTitle().toLowerCase().contains(filterPattern)) {
|
||||||
|
filteredList.add(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FilterResults results = new FilterResults();
|
||||||
|
results.values = filteredList;
|
||||||
|
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void publishResults(CharSequence constraint, FilterResults results) {
|
||||||
|
songs = (List<Child>) results.values;
|
||||||
|
notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public SongHorizontalAdapter(ClickCallback click, boolean showCoverArt, boolean showAlbum, AlbumID3 album) {
|
||||||
this.click = click;
|
this.click = click;
|
||||||
this.showCoverArt = showCoverArt;
|
this.showCoverArt = showCoverArt;
|
||||||
this.showAlbum = showAlbum;
|
this.showAlbum = showAlbum;
|
||||||
this.songs = Collections.emptyList();
|
this.songs = Collections.emptyList();
|
||||||
|
this.songsFull = Collections.emptyList();
|
||||||
|
this.currentFilter = "";
|
||||||
|
this.album = album;
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
@@ -48,14 +94,25 @@ public class SongHorizontalAdapter extends RecyclerView.Adapter<SongHorizontalAd
|
|||||||
public void onBindViewHolder(ViewHolder holder, int position) {
|
public void onBindViewHolder(ViewHolder holder, int position) {
|
||||||
Child song = songs.get(position);
|
Child song = songs.get(position);
|
||||||
|
|
||||||
holder.item.searchResultSongTitleTextView.setText(MusicUtil.getReadableString(song.getTitle()));
|
holder.item.searchResultSongTitleTextView.setText(song.getTitle());
|
||||||
holder.item.searchResultSongSubtitleTextView.setText(holder.itemView.getContext().getString(R.string.song_subtitle_formatter, MusicUtil.getReadableString(this.showAlbum ? song.getAlbum() : song.getArtist()), MusicUtil.getReadableDurationString(song.getDuration(), false)));
|
|
||||||
|
holder.item.searchResultSongSubtitleTextView.setText(
|
||||||
|
holder.itemView.getContext().getString(
|
||||||
|
R.string.song_subtitle_formatter,
|
||||||
|
this.showAlbum ?
|
||||||
|
song.getAlbum() :
|
||||||
|
song.getArtist(),
|
||||||
|
MusicUtil.getReadableDurationString(song.getDuration(), false),
|
||||||
|
MusicUtil.getReadableAudioQualityString(song)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
holder.item.trackNumberTextView.setText(MusicUtil.getReadableTrackNumber(holder.itemView.getContext(), song.getTrack()));
|
holder.item.trackNumberTextView.setText(MusicUtil.getReadableTrackNumber(holder.itemView.getContext(), song.getTrack()));
|
||||||
|
|
||||||
if (DownloadUtil.getDownloadTracker(holder.itemView.getContext()).isDownloaded(song.getId())) {
|
if (DownloadUtil.getDownloadTracker(holder.itemView.getContext()).isDownloaded(song.getId())) {
|
||||||
holder.item.searchResultDowanloadIndicatorImageView.setVisibility(View.VISIBLE);
|
holder.item.searchResultDownloadIndicatorImageView.setVisibility(View.VISIBLE);
|
||||||
} else {
|
} else {
|
||||||
holder.item.searchResultDowanloadIndicatorImageView.setVisibility(View.GONE);
|
holder.item.searchResultDownloadIndicatorImageView.setVisibility(View.GONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (showCoverArt) CustomGlideRequest.Builder
|
if (showCoverArt) CustomGlideRequest.Builder
|
||||||
@@ -66,8 +123,47 @@ public class SongHorizontalAdapter extends RecyclerView.Adapter<SongHorizontalAd
|
|||||||
holder.item.trackNumberTextView.setVisibility(showCoverArt ? View.INVISIBLE : View.VISIBLE);
|
holder.item.trackNumberTextView.setVisibility(showCoverArt ? View.INVISIBLE : View.VISIBLE);
|
||||||
holder.item.songCoverImageView.setVisibility(showCoverArt ? View.VISIBLE : View.INVISIBLE);
|
holder.item.songCoverImageView.setVisibility(showCoverArt ? View.VISIBLE : View.INVISIBLE);
|
||||||
|
|
||||||
if (!showCoverArt && (position > 0 && songs.get(position - 1) != null && songs.get(position - 1).getDiscNumber() != null && songs.get(position).getDiscNumber() != null && songs.get(position - 1).getDiscNumber() < songs.get(position).getDiscNumber())) {
|
if (!showCoverArt &&
|
||||||
holder.item.differentDiskDivider.setVisibility(View.VISIBLE);
|
(position == 0 ||
|
||||||
|
(position > 0 && songs.get(position - 1) != null &&
|
||||||
|
songs.get(position - 1).getDiscNumber() != null &&
|
||||||
|
songs.get(position).getDiscNumber() != null &&
|
||||||
|
songs.get(position - 1).getDiscNumber() < songs.get(position).getDiscNumber()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
holder.item.differentDiskDividerSector.setVisibility(View.VISIBLE);
|
||||||
|
|
||||||
|
if (songs.get(position).getDiscNumber() != null && !Objects.requireNonNull(songs.get(position).getDiscNumber()).toString().isBlank()) {
|
||||||
|
holder.item.discTitleTextView.setText(holder.itemView.getContext().getString(R.string.disc_titleless, songs.get(position).getDiscNumber().toString()));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (album.getDiscTitles() != null) {
|
||||||
|
Optional<DiscTitle> discTitle = album.getDiscTitles().stream().filter(title -> Objects.equals(title.getDisc(), songs.get(position).getDiscNumber())).findFirst();
|
||||||
|
|
||||||
|
if (discTitle.isPresent() && discTitle.get().getDisc() != null && discTitle.get().getTitle() != null && !discTitle.get().getTitle().isEmpty()) {
|
||||||
|
holder.item.discTitleTextView.setText(holder.itemView.getContext().getString(R.string.disc_titlefull, discTitle.get().getDisc().toString() , discTitle.get().getTitle()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Preferences.showItemRating()) {
|
||||||
|
if (song.getStarred() == null && song.getUserRating() == null) {
|
||||||
|
holder.item.ratingIndicatorImageView.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
holder.item.preferredIcon.setVisibility(song.getStarred() != null ? View.VISIBLE : View.GONE);
|
||||||
|
holder.item.ratingBarLayout.setVisibility(song.getUserRating() != null ? View.VISIBLE : View.GONE);
|
||||||
|
|
||||||
|
if (song.getUserRating() != null) {
|
||||||
|
holder.item.oneStarIcon.setImageDrawable(AppCompatResources.getDrawable(holder.itemView.getContext(), song.getUserRating() >= 1 ? R.drawable.ic_star : R.drawable.ic_star_outlined));
|
||||||
|
holder.item.twoStarIcon.setImageDrawable(AppCompatResources.getDrawable(holder.itemView.getContext(), song.getUserRating() >= 2 ? R.drawable.ic_star : R.drawable.ic_star_outlined));
|
||||||
|
holder.item.threeStarIcon.setImageDrawable(AppCompatResources.getDrawable(holder.itemView.getContext(), song.getUserRating() >= 3 ? R.drawable.ic_star : R.drawable.ic_star_outlined));
|
||||||
|
holder.item.fourStarIcon.setImageDrawable(AppCompatResources.getDrawable(holder.itemView.getContext(), song.getUserRating() >= 4 ? R.drawable.ic_star : R.drawable.ic_star_outlined));
|
||||||
|
holder.item.fiveStarIcon.setImageDrawable(AppCompatResources.getDrawable(holder.itemView.getContext(), song.getUserRating() >= 5 ? R.drawable.ic_star : R.drawable.ic_star_outlined));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
holder.item.ratingIndicatorImageView.setVisibility(View.GONE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -77,7 +173,8 @@ public class SongHorizontalAdapter extends RecyclerView.Adapter<SongHorizontalAd
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void setItems(List<Child> songs) {
|
public void setItems(List<Child> songs) {
|
||||||
this.songs = songs != null ? songs : Collections.emptyList();
|
this.songsFull = songs != null ? songs : Collections.emptyList();
|
||||||
|
filtering.filter(currentFilter);
|
||||||
notifyDataSetChanged();
|
notifyDataSetChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -91,6 +188,11 @@ public class SongHorizontalAdapter extends RecyclerView.Adapter<SongHorizontalAd
|
|||||||
return position;
|
return position;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Filter getFilter() {
|
||||||
|
return filtering;
|
||||||
|
}
|
||||||
|
|
||||||
public Child getItem(int id) {
|
public Child getItem(int id) {
|
||||||
return songs.get(id);
|
return songs.get(id);
|
||||||
}
|
}
|
||||||
@@ -129,4 +231,20 @@ public class SongHorizontalAdapter extends RecyclerView.Adapter<SongHorizontalAd
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void sort(String order) {
|
||||||
|
switch (order) {
|
||||||
|
case Constants.MEDIA_BY_TITLE:
|
||||||
|
songs.sort(Comparator.comparing(Child::getTitle));
|
||||||
|
break;
|
||||||
|
case Constants.MEDIA_MOST_RECENTLY_STARRED:
|
||||||
|
songs.sort(Comparator.comparing(Child::getStarred, Comparator.nullsLast(Comparator.reverseOrder())));
|
||||||
|
break;
|
||||||
|
case Constants.MEDIA_LEAST_RECENTLY_STARRED:
|
||||||
|
songs.sort(Comparator.comparing(Child::getStarred, Comparator.nullsLast(Comparator.naturalOrder())));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
notifyDataSetChanged();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,73 @@
|
|||||||
|
package com.cappielloantonio.tempo.ui.dialog;
|
||||||
|
|
||||||
|
import android.app.Dialog;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.Bundle;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.appcompat.app.AlertDialog;
|
||||||
|
import androidx.fragment.app.DialogFragment;
|
||||||
|
|
||||||
|
import com.cappielloantonio.tempo.R;
|
||||||
|
import com.cappielloantonio.tempo.databinding.DialogGithubTempoUpdateBinding;
|
||||||
|
import com.cappielloantonio.tempo.github.models.LatestRelease;
|
||||||
|
import com.cappielloantonio.tempo.util.Preferences;
|
||||||
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
public class GithubTempoUpdateDialog extends DialogFragment {
|
||||||
|
private final LatestRelease latestRelease;
|
||||||
|
|
||||||
|
public GithubTempoUpdateDialog(LatestRelease latestRelease) {
|
||||||
|
this.latestRelease = latestRelease;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||||
|
DialogGithubTempoUpdateBinding bind = DialogGithubTempoUpdateBinding.inflate(getLayoutInflater());
|
||||||
|
|
||||||
|
MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(requireActivity())
|
||||||
|
.setView(bind.getRoot())
|
||||||
|
.setTitle(R.string.github_update_dialog_title)
|
||||||
|
.setPositiveButton(R.string.github_update_dialog_positive_button, (dialog, id) -> { })
|
||||||
|
.setNegativeButton(R.string.github_update_dialog_negative_button, (dialog, id) -> { })
|
||||||
|
.setNeutralButton(R.string.github_update_dialog_neutral_button, (dialog, id) -> { });
|
||||||
|
|
||||||
|
return builder.create();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStart() {
|
||||||
|
super.onStart();
|
||||||
|
|
||||||
|
setButtonAction();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setButtonAction() {
|
||||||
|
AlertDialog alertDialog = (AlertDialog) Objects.requireNonNull(getDialog());
|
||||||
|
|
||||||
|
alertDialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener(v -> {
|
||||||
|
openLink(latestRelease.getHtmlUrl());
|
||||||
|
Objects.requireNonNull(getDialog()).dismiss();
|
||||||
|
});
|
||||||
|
|
||||||
|
alertDialog.getButton(AlertDialog.BUTTON_NEGATIVE).setOnClickListener(v -> {
|
||||||
|
Preferences.setTempoUpdateReminder();
|
||||||
|
Objects.requireNonNull(getDialog()).dismiss();
|
||||||
|
});
|
||||||
|
|
||||||
|
alertDialog.getButton(AlertDialog.BUTTON_NEUTRAL).setOnClickListener(v -> {
|
||||||
|
openLink(getString(R.string.support_url));
|
||||||
|
Objects.requireNonNull(getDialog()).dismiss();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void openLink(String link) {
|
||||||
|
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(link));
|
||||||
|
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||||
|
startActivity(intent);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,115 @@
|
|||||||
|
package com.cappielloantonio.tempo.ui.dialog;
|
||||||
|
|
||||||
|
import android.app.Dialog;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.fragment.app.DialogFragment;
|
||||||
|
import androidx.lifecycle.ViewModelProvider;
|
||||||
|
import androidx.recyclerview.widget.ItemTouchHelper;
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
|
import com.cappielloantonio.tempo.R;
|
||||||
|
import com.cappielloantonio.tempo.databinding.DialogHomeRearrangementBinding;
|
||||||
|
import com.cappielloantonio.tempo.ui.adapter.HomeSectorHorizontalAdapter;
|
||||||
|
import com.cappielloantonio.tempo.viewmodel.HomeRearrangementViewModel;
|
||||||
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
public class HomeRearrangementDialog extends DialogFragment {
|
||||||
|
private DialogHomeRearrangementBinding bind;
|
||||||
|
private HomeRearrangementViewModel homeRearrangementViewModel;
|
||||||
|
private HomeSectorHorizontalAdapter homeSectorHorizontalAdapter;
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||||
|
bind = DialogHomeRearrangementBinding.inflate(getLayoutInflater());
|
||||||
|
|
||||||
|
homeRearrangementViewModel = new ViewModelProvider(requireActivity()).get(HomeRearrangementViewModel.class);
|
||||||
|
|
||||||
|
return new MaterialAlertDialogBuilder(requireContext())
|
||||||
|
.setView(bind.getRoot())
|
||||||
|
.setTitle(R.string.home_rearrangement_dialog_title)
|
||||||
|
.setPositiveButton(R.string.home_rearrangement_dialog_positive_button, (dialog, id) -> { })
|
||||||
|
.setNeutralButton(R.string.home_rearrangement_dialog_neutral_button, (dialog, id) -> { })
|
||||||
|
.setNegativeButton(R.string.home_rearrangement_dialog_negative_button, (dialog, id) -> dialog.cancel())
|
||||||
|
.create();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStart() {
|
||||||
|
super.onStart();
|
||||||
|
|
||||||
|
setButtonAction();
|
||||||
|
initSectorView();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDestroyView() {
|
||||||
|
super.onDestroyView();
|
||||||
|
homeRearrangementViewModel.closeDialog();
|
||||||
|
bind = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setButtonAction() {
|
||||||
|
androidx.appcompat.app.AlertDialog alertDialog = (androidx.appcompat.app.AlertDialog) Objects.requireNonNull(getDialog());
|
||||||
|
|
||||||
|
alertDialog.getButton(androidx.appcompat.app.AlertDialog.BUTTON_POSITIVE).setOnClickListener(v -> {
|
||||||
|
homeRearrangementViewModel.saveHomeSectorList(homeSectorHorizontalAdapter.getItems());
|
||||||
|
dismiss();
|
||||||
|
});
|
||||||
|
|
||||||
|
alertDialog.getButton(androidx.appcompat.app.AlertDialog.BUTTON_NEUTRAL).setOnClickListener(v -> {
|
||||||
|
homeRearrangementViewModel.resetHomeSectorList();
|
||||||
|
dismiss();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initSectorView() {
|
||||||
|
bind.homeSectorItemRecyclerView.setLayoutManager(new LinearLayoutManager(requireContext()));
|
||||||
|
bind.homeSectorItemRecyclerView.setHasFixedSize(true);
|
||||||
|
|
||||||
|
homeSectorHorizontalAdapter = new HomeSectorHorizontalAdapter();
|
||||||
|
bind.homeSectorItemRecyclerView.setAdapter(homeSectorHorizontalAdapter);
|
||||||
|
homeSectorHorizontalAdapter.setItems(homeRearrangementViewModel.getHomeSectorList());
|
||||||
|
|
||||||
|
new ItemTouchHelper(new ItemTouchHelper.SimpleCallback(ItemTouchHelper.UP | ItemTouchHelper.DOWN, 0) {
|
||||||
|
int originalPosition = -1;
|
||||||
|
int fromPosition = -1;
|
||||||
|
int toPosition = -1;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onMove(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, @NonNull RecyclerView.ViewHolder target) {
|
||||||
|
if (originalPosition == -1) originalPosition = viewHolder.getBindingAdapterPosition();
|
||||||
|
|
||||||
|
fromPosition = viewHolder.getBindingAdapterPosition();
|
||||||
|
toPosition = target.getBindingAdapterPosition();
|
||||||
|
|
||||||
|
Collections.swap(homeSectorHorizontalAdapter.getItems(), fromPosition, toPosition);
|
||||||
|
Objects.requireNonNull(recyclerView.getAdapter()).notifyItemMoved(fromPosition, toPosition);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clearView(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder) {
|
||||||
|
super.clearView(recyclerView, viewHolder);
|
||||||
|
|
||||||
|
homeRearrangementViewModel.orderSectorLiveListAfterSwap(homeSectorHorizontalAdapter.getItems());
|
||||||
|
|
||||||
|
originalPosition = -1;
|
||||||
|
fromPosition = -1;
|
||||||
|
toPosition = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int direction) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
).attachToRecyclerView(bind.homeSectorItemRecyclerView);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -83,7 +83,7 @@ public class PlaylistChooserDialog extends DialogFragment implements ClickCallba
|
|||||||
|
|
||||||
playlistChooserViewModel.getPlaylistList(requireActivity()).observe(requireActivity(), playlists -> {
|
playlistChooserViewModel.getPlaylistList(requireActivity()).observe(requireActivity(), playlists -> {
|
||||||
if (playlists != null) {
|
if (playlists != null) {
|
||||||
if (playlists.size() > 0) {
|
if (!playlists.isEmpty()) {
|
||||||
if (bind != null) bind.noPlaylistsCreatedTextView.setVisibility(View.GONE);
|
if (bind != null) bind.noPlaylistsCreatedTextView.setVisibility(View.GONE);
|
||||||
if (bind != null) bind.playlistDialogRecyclerView.setVisibility(View.VISIBLE);
|
if (bind != null) bind.playlistDialogRecyclerView.setVisibility(View.VISIBLE);
|
||||||
playlistDialogHorizontalAdapter.setItems(playlists);
|
playlistDialogHorizontalAdapter.setItems(playlists);
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import android.content.Context;
|
|||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.fragment.app.DialogFragment;
|
import androidx.fragment.app.DialogFragment;
|
||||||
@@ -81,7 +82,7 @@ public class PlaylistEditorDialog extends DialogFragment {
|
|||||||
playlistEditorViewModel.setPlaylistToEdit(requireArguments().getParcelable(Constants.PLAYLIST_OBJECT));
|
playlistEditorViewModel.setPlaylistToEdit(requireArguments().getParcelable(Constants.PLAYLIST_OBJECT));
|
||||||
|
|
||||||
if (playlistEditorViewModel.getPlaylistToEdit() != null) {
|
if (playlistEditorViewModel.getPlaylistToEdit() != null) {
|
||||||
bind.playlistNameTextView.setText(MusicUtil.getReadableString(playlistEditorViewModel.getPlaylistToEdit().getName()));
|
bind.playlistNameTextView.setText(playlistEditorViewModel.getPlaylistToEdit().getName());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -101,9 +102,12 @@ public class PlaylistEditorDialog extends DialogFragment {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
alertDialog.getButton(androidx.appcompat.app.AlertDialog.BUTTON_NEUTRAL).setOnClickListener(v -> {
|
alertDialog.getButton(androidx.appcompat.app.AlertDialog.BUTTON_NEUTRAL).setOnClickListener(v -> Toast.makeText(requireContext(), R.string.playlist_editor_dialog_action_delete_toast, Toast.LENGTH_SHORT).show());
|
||||||
|
|
||||||
|
alertDialog.getButton(androidx.appcompat.app.AlertDialog.BUTTON_NEUTRAL).setOnLongClickListener(v -> {
|
||||||
playlistEditorViewModel.deletePlaylist();
|
playlistEditorViewModel.deletePlaylist();
|
||||||
dialogDismiss();
|
dialogDismiss();
|
||||||
|
return false;
|
||||||
});
|
});
|
||||||
|
|
||||||
bind.playlistShareButton.setOnClickListener(view -> {
|
bind.playlistShareButton.setOnClickListener(view -> {
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ package com.cappielloantonio.tempo.ui.dialog;
|
|||||||
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.View;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.fragment.app.DialogFragment;
|
import androidx.fragment.app.DialogFragment;
|
||||||
@@ -28,6 +30,7 @@ public class ServerSignupDialog extends DialogFragment {
|
|||||||
private String username;
|
private String username;
|
||||||
private String password;
|
private String password;
|
||||||
private String server;
|
private String server;
|
||||||
|
private String localAddress;
|
||||||
private boolean lowSecurity = false;
|
private boolean lowSecurity = false;
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
@@ -69,6 +72,7 @@ public class ServerSignupDialog extends DialogFragment {
|
|||||||
bind.usernameTextView.setText(loginViewModel.getServerToEdit().getUsername());
|
bind.usernameTextView.setText(loginViewModel.getServerToEdit().getUsername());
|
||||||
bind.passwordTextView.setText("");
|
bind.passwordTextView.setText("");
|
||||||
bind.serverTextView.setText(loginViewModel.getServerToEdit().getAddress());
|
bind.serverTextView.setText(loginViewModel.getServerToEdit().getAddress());
|
||||||
|
bind.localAddressTextView.setText(loginViewModel.getServerToEdit().getLocalAddress());
|
||||||
bind.lowSecurityCheckbox.setChecked(loginViewModel.getServerToEdit().isLowSecurity());
|
bind.lowSecurityCheckbox.setChecked(loginViewModel.getServerToEdit().isLowSecurity());
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -86,9 +90,12 @@ public class ServerSignupDialog extends DialogFragment {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
alertDialog.getButton(androidx.appcompat.app.AlertDialog.BUTTON_NEUTRAL).setOnClickListener(v -> {
|
alertDialog.getButton(androidx.appcompat.app.AlertDialog.BUTTON_NEUTRAL).setOnClickListener(v -> Toast.makeText(requireContext(), R.string.server_signup_dialog_action_delete_toast, Toast.LENGTH_SHORT).show());
|
||||||
|
|
||||||
|
alertDialog.getButton(androidx.appcompat.app.AlertDialog.BUTTON_NEUTRAL).setOnLongClickListener(v -> {
|
||||||
loginViewModel.deleteServer(null);
|
loginViewModel.deleteServer(null);
|
||||||
Objects.requireNonNull(getDialog()).dismiss();
|
Objects.requireNonNull(getDialog()).dismiss();
|
||||||
|
return true;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -96,7 +103,8 @@ public class ServerSignupDialog extends DialogFragment {
|
|||||||
serverName = Objects.requireNonNull(bind.serverNameTextView.getText()).toString().trim();
|
serverName = Objects.requireNonNull(bind.serverNameTextView.getText()).toString().trim();
|
||||||
username = Objects.requireNonNull(bind.usernameTextView.getText()).toString().trim();
|
username = Objects.requireNonNull(bind.usernameTextView.getText()).toString().trim();
|
||||||
password = bind.lowSecurityCheckbox.isChecked() ? MusicUtil.passwordHexEncoding(Objects.requireNonNull(bind.passwordTextView.getText()).toString()) : Objects.requireNonNull(bind.passwordTextView.getText()).toString();
|
password = bind.lowSecurityCheckbox.isChecked() ? MusicUtil.passwordHexEncoding(Objects.requireNonNull(bind.passwordTextView.getText()).toString()) : Objects.requireNonNull(bind.passwordTextView.getText()).toString();
|
||||||
server = Objects.requireNonNull(bind.serverTextView.getText()).toString().trim();
|
server = bind.serverTextView.getText() != null && !bind.serverTextView.getText().toString().trim().isBlank() ? bind.serverTextView.getText().toString().trim() : null;
|
||||||
|
localAddress = bind.localAddressTextView.getText() != null && !bind.localAddressTextView.getText().toString().trim().isBlank() ? bind.localAddressTextView.getText().toString().trim() : null;
|
||||||
lowSecurity = bind.lowSecurityCheckbox.isChecked();
|
lowSecurity = bind.lowSecurityCheckbox.isChecked();
|
||||||
|
|
||||||
if (TextUtils.isEmpty(serverName)) {
|
if (TextUtils.isEmpty(serverName)) {
|
||||||
@@ -114,6 +122,11 @@ public class ServerSignupDialog extends DialogFragment {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!TextUtils.isEmpty(localAddress) && !localAddress.matches("^https?://(.*)")) {
|
||||||
|
bind.localAddressTextView.setError(getString(R.string.error_server_prefix));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (!server.matches("^https?://(.*)")) {
|
if (!server.matches("^https?://(.*)")) {
|
||||||
bind.serverTextView.setError(getString(R.string.error_server_prefix));
|
bind.serverTextView.setError(getString(R.string.error_server_prefix));
|
||||||
return false;
|
return false;
|
||||||
@@ -124,6 +137,6 @@ public class ServerSignupDialog extends DialogFragment {
|
|||||||
|
|
||||||
private void saveServerPreference() {
|
private void saveServerPreference() {
|
||||||
String serverID = loginViewModel.getServerToEdit() != null ? loginViewModel.getServerToEdit().getServerId() : UUID.randomUUID().toString();
|
String serverID = loginViewModel.getServerToEdit() != null ? loginViewModel.getServerToEdit().getServerId() : UUID.randomUUID().toString();
|
||||||
loginViewModel.addServer(new Server(serverID, this.serverName, this.username, this.password, this.server, System.currentTimeMillis(), this.lowSecurity));
|
loginViewModel.addServer(new Server(serverID, this.serverName, this.username, this.password, this.server, this.localAddress, System.currentTimeMillis(), this.lowSecurity));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ public class ServerUnreachableDialog extends DialogFragment {
|
|||||||
});
|
});
|
||||||
|
|
||||||
alertDialog.getButton(androidx.appcompat.app.AlertDialog.BUTTON_POSITIVE).setOnClickListener(v -> {
|
alertDialog.getButton(androidx.appcompat.app.AlertDialog.BUTTON_POSITIVE).setOnClickListener(v -> {
|
||||||
Preferences.setServerUnreachableDatetime(System.currentTimeMillis());
|
Preferences.setServerUnreachableDatetime();
|
||||||
alertDialog.dismiss();
|
alertDialog.dismiss();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,76 @@
|
|||||||
|
package com.cappielloantonio.tempo.ui.dialog;
|
||||||
|
|
||||||
|
import android.app.Dialog;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.widget.Button;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.OptIn;
|
||||||
|
import androidx.fragment.app.DialogFragment;
|
||||||
|
import androidx.media3.common.util.UnstableApi;
|
||||||
|
|
||||||
|
import com.cappielloantonio.tempo.R;
|
||||||
|
import com.cappielloantonio.tempo.databinding.DialogStreamingCacheStorageBinding;
|
||||||
|
import com.cappielloantonio.tempo.interfaces.DialogClickCallback;
|
||||||
|
import com.cappielloantonio.tempo.util.Preferences;
|
||||||
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||||
|
|
||||||
|
@OptIn(markerClass = UnstableApi.class)
|
||||||
|
public class StreamingCacheStorageDialog extends DialogFragment {
|
||||||
|
private final DialogClickCallback dialogClickCallback;
|
||||||
|
|
||||||
|
public StreamingCacheStorageDialog(DialogClickCallback dialogClickCallback) {
|
||||||
|
this.dialogClickCallback = dialogClickCallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||||
|
DialogStreamingCacheStorageBinding bind = DialogStreamingCacheStorageBinding.inflate(getLayoutInflater());
|
||||||
|
|
||||||
|
return new MaterialAlertDialogBuilder(getActivity())
|
||||||
|
.setView(bind.getRoot())
|
||||||
|
.setTitle(R.string.streaming_cache_storage_dialog_title)
|
||||||
|
.setPositiveButton(R.string.streaming_cache_storage_external_dialog_positive_button, null)
|
||||||
|
.setNegativeButton(R.string.streaming_cache_storage_internal_dialog_negative_button, null)
|
||||||
|
.create();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onResume() {
|
||||||
|
super.onResume();
|
||||||
|
setButtonAction();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setButtonAction() {
|
||||||
|
androidx.appcompat.app.AlertDialog dialog = (androidx.appcompat.app.AlertDialog) getDialog();
|
||||||
|
|
||||||
|
if (dialog != null) {
|
||||||
|
Button positiveButton = dialog.getButton(Dialog.BUTTON_POSITIVE);
|
||||||
|
positiveButton.setOnClickListener(v -> {
|
||||||
|
int currentPreference = Preferences.getStreamingCacheStoragePreference();
|
||||||
|
int newPreference = 1;
|
||||||
|
|
||||||
|
if (currentPreference != newPreference) {
|
||||||
|
Preferences.setStreamingCacheStoragePreference(newPreference);
|
||||||
|
dialogClickCallback.onPositiveClick();
|
||||||
|
}
|
||||||
|
|
||||||
|
dialog.dismiss();
|
||||||
|
});
|
||||||
|
|
||||||
|
Button negativeButton = dialog.getButton(Dialog.BUTTON_NEGATIVE);
|
||||||
|
negativeButton.setOnClickListener(v -> {
|
||||||
|
int currentPreference = Preferences.getStreamingCacheStoragePreference();
|
||||||
|
int newPreference = 0;
|
||||||
|
|
||||||
|
if (currentPreference != newPreference) {
|
||||||
|
Preferences.setStreamingCacheStoragePreference(newPreference);
|
||||||
|
dialogClickCallback.onNegativeClick();
|
||||||
|
}
|
||||||
|
|
||||||
|
dialog.dismiss();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -67,6 +67,7 @@ public class AlbumCatalogueFragment extends Fragment implements ClickCallback {
|
|||||||
|
|
||||||
initAppBar();
|
initAppBar();
|
||||||
initAlbumCatalogueView();
|
initAlbumCatalogueView();
|
||||||
|
initProgressLoader();
|
||||||
|
|
||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
@@ -111,7 +112,7 @@ public class AlbumCatalogueFragment extends Fragment implements ClickCallback {
|
|||||||
bind.albumCatalogueRecyclerView.addItemDecoration(new GridItemDecoration(2, 20, false));
|
bind.albumCatalogueRecyclerView.addItemDecoration(new GridItemDecoration(2, 20, false));
|
||||||
bind.albumCatalogueRecyclerView.setHasFixedSize(true);
|
bind.albumCatalogueRecyclerView.setHasFixedSize(true);
|
||||||
|
|
||||||
albumAdapter = new AlbumCatalogueAdapter(this);
|
albumAdapter = new AlbumCatalogueAdapter(this, true);
|
||||||
albumAdapter.setStateRestorationPolicy(RecyclerView.Adapter.StateRestorationPolicy.PREVENT_WHEN_EMPTY);
|
albumAdapter.setStateRestorationPolicy(RecyclerView.Adapter.StateRestorationPolicy.PREVENT_WHEN_EMPTY);
|
||||||
bind.albumCatalogueRecyclerView.setAdapter(albumAdapter);
|
bind.albumCatalogueRecyclerView.setAdapter(albumAdapter);
|
||||||
albumCatalogueViewModel.getAlbumList().observe(getViewLifecycleOwner(), albums -> albumAdapter.setItems(albums));
|
albumCatalogueViewModel.getAlbumList().observe(getViewLifecycleOwner(), albums -> albumAdapter.setItems(albums));
|
||||||
@@ -124,6 +125,18 @@ public class AlbumCatalogueFragment extends Fragment implements ClickCallback {
|
|||||||
bind.albumListSortImageView.setOnClickListener(view -> showPopupMenu(view, R.menu.sort_album_popup_menu));
|
bind.albumListSortImageView.setOnClickListener(view -> showPopupMenu(view, R.menu.sort_album_popup_menu));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void initProgressLoader() {
|
||||||
|
albumCatalogueViewModel.getLoadingStatus().observe(getViewLifecycleOwner(), isLoading -> {
|
||||||
|
if (isLoading) {
|
||||||
|
bind.albumListSortImageView.setEnabled(false);
|
||||||
|
bind.albumListProgressLoader.setVisibility(View.VISIBLE);
|
||||||
|
} else {
|
||||||
|
bind.albumListSortImageView.setEnabled(true);
|
||||||
|
bind.albumListProgressLoader.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) {
|
public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) {
|
||||||
inflater.inflate(R.menu.toolbar_menu, menu);
|
inflater.inflate(R.menu.toolbar_menu, menu);
|
||||||
@@ -171,6 +184,15 @@ public class AlbumCatalogueFragment extends Fragment implements ClickCallback {
|
|||||||
} else if (menuItem.getItemId() == R.id.menu_album_sort_random) {
|
} else if (menuItem.getItemId() == R.id.menu_album_sort_random) {
|
||||||
albumAdapter.sort(Constants.ALBUM_ORDER_BY_RANDOM);
|
albumAdapter.sort(Constants.ALBUM_ORDER_BY_RANDOM);
|
||||||
return true;
|
return true;
|
||||||
|
} else if (menuItem.getItemId() == R.id.menu_album_sort_recently_added) {
|
||||||
|
albumAdapter.sort(Constants.ALBUM_ORDER_BY_RECENTLY_ADDED);
|
||||||
|
return true;
|
||||||
|
} else if (menuItem.getItemId() == R.id.menu_album_sort_recently_played) {
|
||||||
|
albumAdapter.sort(Constants.ALBUM_ORDER_BY_RECENTLY_PLAYED);
|
||||||
|
return true;
|
||||||
|
} else if (menuItem.getItemId() == R.id.menu_album_sort_most_played) {
|
||||||
|
albumAdapter.sort(Constants.ALBUM_ORDER_BY_MOST_PLAYED);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -1,11 +1,21 @@
|
|||||||
package com.cappielloantonio.tempo.ui.fragment;
|
package com.cappielloantonio.tempo.ui.fragment;
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint;
|
||||||
|
import android.content.Context;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.Menu;
|
||||||
|
import android.view.MenuInflater;
|
||||||
|
import android.view.MenuItem;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
import android.view.inputmethod.EditorInfo;
|
||||||
|
import android.view.inputmethod.InputMethodManager;
|
||||||
|
import android.widget.PopupMenu;
|
||||||
|
import android.widget.SearchView;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
import androidx.annotation.OptIn;
|
import androidx.annotation.OptIn;
|
||||||
import androidx.core.view.ViewCompat;
|
import androidx.core.view.ViewCompat;
|
||||||
import androidx.fragment.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
@@ -17,12 +27,14 @@ import androidx.recyclerview.widget.LinearLayoutManager;
|
|||||||
import com.cappielloantonio.tempo.R;
|
import com.cappielloantonio.tempo.R;
|
||||||
import com.cappielloantonio.tempo.databinding.FragmentAlbumListPageBinding;
|
import com.cappielloantonio.tempo.databinding.FragmentAlbumListPageBinding;
|
||||||
import com.cappielloantonio.tempo.interfaces.ClickCallback;
|
import com.cappielloantonio.tempo.interfaces.ClickCallback;
|
||||||
|
import com.cappielloantonio.tempo.subsonic.models.AlbumID3;
|
||||||
import com.cappielloantonio.tempo.ui.activity.MainActivity;
|
import com.cappielloantonio.tempo.ui.activity.MainActivity;
|
||||||
import com.cappielloantonio.tempo.ui.adapter.AlbumHorizontalAdapter;
|
import com.cappielloantonio.tempo.ui.adapter.AlbumHorizontalAdapter;
|
||||||
import com.cappielloantonio.tempo.util.Constants;
|
import com.cappielloantonio.tempo.util.Constants;
|
||||||
import com.cappielloantonio.tempo.util.MusicUtil;
|
|
||||||
import com.cappielloantonio.tempo.viewmodel.AlbumListPageViewModel;
|
import com.cappielloantonio.tempo.viewmodel.AlbumListPageViewModel;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
@OptIn(markerClass = UnstableApi.class)
|
@OptIn(markerClass = UnstableApi.class)
|
||||||
public class AlbumListPageFragment extends Fragment implements ClickCallback {
|
public class AlbumListPageFragment extends Fragment implements ClickCallback {
|
||||||
private FragmentAlbumListPageBinding bind;
|
private FragmentAlbumListPageBinding bind;
|
||||||
@@ -31,6 +43,12 @@ public class AlbumListPageFragment extends Fragment implements ClickCallback {
|
|||||||
private AlbumListPageViewModel albumListPageViewModel;
|
private AlbumListPageViewModel albumListPageViewModel;
|
||||||
private AlbumHorizontalAdapter albumHorizontalAdapter;
|
private AlbumHorizontalAdapter albumHorizontalAdapter;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
setHasOptionsMenu(true);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||||
activity = (MainActivity) getActivity();
|
activity = (MainActivity) getActivity();
|
||||||
@@ -74,7 +92,7 @@ public class AlbumListPageFragment extends Fragment implements ClickCallback {
|
|||||||
} else if (requireArguments().getParcelable(Constants.ARTIST_OBJECT) != null) {
|
} else if (requireArguments().getParcelable(Constants.ARTIST_OBJECT) != null) {
|
||||||
albumListPageViewModel.artist = requireArguments().getParcelable(Constants.ARTIST_OBJECT);
|
albumListPageViewModel.artist = requireArguments().getParcelable(Constants.ARTIST_OBJECT);
|
||||||
albumListPageViewModel.title = Constants.ALBUM_FROM_ARTIST;
|
albumListPageViewModel.title = Constants.ALBUM_FROM_ARTIST;
|
||||||
bind.pageTitleLabel.setText(MusicUtil.getReadableString(albumListPageViewModel.artist.getName()));
|
bind.pageTitleLabel.setText(albumListPageViewModel.artist.getName());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -86,7 +104,10 @@ public class AlbumListPageFragment extends Fragment implements ClickCallback {
|
|||||||
activity.getSupportActionBar().setDisplayShowHomeEnabled(true);
|
activity.getSupportActionBar().setDisplayShowHomeEnabled(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
bind.toolbar.setNavigationOnClickListener(v -> activity.navController.navigateUp());
|
bind.toolbar.setNavigationOnClickListener(v -> {
|
||||||
|
hideKeyboard(v);
|
||||||
|
activity.navController.navigateUp();
|
||||||
|
});
|
||||||
|
|
||||||
bind.appBarLayout.addOnOffsetChangedListener((appBarLayout, verticalOffset) -> {
|
bind.appBarLayout.addOnOffsetChangedListener((appBarLayout, verticalOffset) -> {
|
||||||
if ((bind.albumInfoSector.getHeight() + verticalOffset) < (2 * ViewCompat.getMinimumHeight(bind.toolbar))) {
|
if ((bind.albumInfoSector.getHeight() + verticalOffset) < (2 * ViewCompat.getMinimumHeight(bind.toolbar))) {
|
||||||
@@ -97,6 +118,7 @@ public class AlbumListPageFragment extends Fragment implements ClickCallback {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressLint("ClickableViewAccessibility")
|
||||||
private void initAlbumListView() {
|
private void initAlbumListView() {
|
||||||
bind.albumListRecyclerView.setLayoutManager(new LinearLayoutManager(requireContext()));
|
bind.albumListRecyclerView.setLayoutManager(new LinearLayoutManager(requireContext()));
|
||||||
bind.albumListRecyclerView.setHasFixedSize(true);
|
bind.albumListRecyclerView.setHasFixedSize(true);
|
||||||
@@ -107,7 +129,99 @@ public class AlbumListPageFragment extends Fragment implements ClickCallback {
|
|||||||
);
|
);
|
||||||
|
|
||||||
bind.albumListRecyclerView.setAdapter(albumHorizontalAdapter);
|
bind.albumListRecyclerView.setAdapter(albumHorizontalAdapter);
|
||||||
albumListPageViewModel.getAlbumList(getViewLifecycleOwner()).observe(getViewLifecycleOwner(), albums -> albumHorizontalAdapter.setItems(albums));
|
albumListPageViewModel.getAlbumList(getViewLifecycleOwner()).observe(getViewLifecycleOwner(), albums -> {
|
||||||
|
albumHorizontalAdapter.setItems(albums);
|
||||||
|
setAlbumListPageSubtitle(albums);
|
||||||
|
setAlbumListPageSorter();
|
||||||
|
});
|
||||||
|
|
||||||
|
bind.albumListRecyclerView.setOnTouchListener((v, event) -> {
|
||||||
|
hideKeyboard(v);
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
bind.albumListSortImageView.setOnClickListener(view -> showPopupMenu(view, R.menu.sort_horizontal_album_popup_menu));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) {
|
||||||
|
inflater.inflate(R.menu.toolbar_menu, menu);
|
||||||
|
|
||||||
|
MenuItem searchItem = menu.findItem(R.id.action_search);
|
||||||
|
|
||||||
|
SearchView searchView = (SearchView) searchItem.getActionView();
|
||||||
|
searchView.setImeOptions(EditorInfo.IME_ACTION_DONE);
|
||||||
|
searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
|
||||||
|
@Override
|
||||||
|
public boolean onQueryTextSubmit(String query) {
|
||||||
|
searchView.clearFocus();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onQueryTextChange(String newText) {
|
||||||
|
albumHorizontalAdapter.getFilter().filter(newText);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
searchView.setPadding(-32, 0, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void hideKeyboard(View view) {
|
||||||
|
InputMethodManager imm = (InputMethodManager) requireActivity().getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||||
|
imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showPopupMenu(View view, int menuResource) {
|
||||||
|
PopupMenu popup = new PopupMenu(requireContext(), view);
|
||||||
|
popup.getMenuInflater().inflate(menuResource, popup.getMenu());
|
||||||
|
|
||||||
|
popup.setOnMenuItemClickListener(menuItem -> {
|
||||||
|
if (menuItem.getItemId() == R.id.menu_horizontal_album_sort_name) {
|
||||||
|
albumHorizontalAdapter.sort(Constants.ALBUM_ORDER_BY_NAME);
|
||||||
|
return true;
|
||||||
|
} else if (menuItem.getItemId() == R.id.menu_horizontal_album_sort_most_recently_starred) {
|
||||||
|
albumHorizontalAdapter.sort(Constants.ALBUM_ORDER_BY_MOST_RECENTLY_STARRED);
|
||||||
|
return true;
|
||||||
|
} else if (menuItem.getItemId() == R.id.menu_horizontal_album_sort_least_recently_starred) {
|
||||||
|
albumHorizontalAdapter.sort(Constants.ALBUM_ORDER_BY_LEAST_RECENTLY_STARRED);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
popup.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setAlbumListPageSubtitle(List<AlbumID3> albums) {
|
||||||
|
switch (albumListPageViewModel.title) {
|
||||||
|
case Constants.ALBUM_RECENTLY_PLAYED:
|
||||||
|
case Constants.ALBUM_MOST_PLAYED:
|
||||||
|
case Constants.ALBUM_RECENTLY_ADDED:
|
||||||
|
bind.pageSubtitleLabel.setText(albums.size() < albumListPageViewModel.maxNumber ?
|
||||||
|
getString(R.string.generic_list_page_count, albums.size()) :
|
||||||
|
getString(R.string.generic_list_page_count_unknown, albumListPageViewModel.maxNumber)
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
case Constants.ALBUM_STARRED:
|
||||||
|
bind.pageSubtitleLabel.setText(getString(R.string.generic_list_page_count, albums.size()));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setAlbumListPageSorter() {
|
||||||
|
switch (albumListPageViewModel.title) {
|
||||||
|
case Constants.ALBUM_RECENTLY_PLAYED:
|
||||||
|
case Constants.ALBUM_MOST_PLAYED:
|
||||||
|
case Constants.ALBUM_RECENTLY_ADDED:
|
||||||
|
bind.albumListSortImageView.setVisibility(View.GONE);
|
||||||
|
break;
|
||||||
|
case Constants.ALBUM_STARRED:
|
||||||
|
bind.albumListSortImageView.setVisibility(View.VISIBLE);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
package com.cappielloantonio.tempo.ui.fragment;
|
package com.cappielloantonio.tempo.ui.fragment;
|
||||||
|
|
||||||
import android.content.ComponentName;
|
import android.content.ComponentName;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.net.Uri;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
@@ -45,9 +47,7 @@ public class AlbumPageFragment extends Fragment implements ClickCallback {
|
|||||||
private FragmentAlbumPageBinding bind;
|
private FragmentAlbumPageBinding bind;
|
||||||
private MainActivity activity;
|
private MainActivity activity;
|
||||||
private AlbumPageViewModel albumPageViewModel;
|
private AlbumPageViewModel albumPageViewModel;
|
||||||
|
|
||||||
private SongHorizontalAdapter songHorizontalAdapter;
|
private SongHorizontalAdapter songHorizontalAdapter;
|
||||||
|
|
||||||
private ListenableFuture<MediaBrowser> mediaBrowserListenableFuture;
|
private ListenableFuture<MediaBrowser> mediaBrowserListenableFuture;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -73,6 +73,7 @@ public class AlbumPageFragment extends Fragment implements ClickCallback {
|
|||||||
init();
|
init();
|
||||||
initAppBar();
|
initAppBar();
|
||||||
initAlbumInfoTextButton();
|
initAlbumInfoTextButton();
|
||||||
|
initAlbumNotes();
|
||||||
initMusicButton();
|
initMusicButton();
|
||||||
initBackCover();
|
initBackCover();
|
||||||
initSongsView();
|
initSongsView();
|
||||||
@@ -103,10 +104,7 @@ public class AlbumPageFragment extends Fragment implements ClickCallback {
|
|||||||
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
|
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
|
||||||
if (item.getItemId() == R.id.action_download_album) {
|
if (item.getItemId() == R.id.action_download_album) {
|
||||||
albumPageViewModel.getAlbumSongLiveList().observe(getViewLifecycleOwner(), songs -> {
|
albumPageViewModel.getAlbumSongLiveList().observe(getViewLifecycleOwner(), songs -> {
|
||||||
DownloadUtil.getDownloadTracker(requireContext()).download(
|
DownloadUtil.getDownloadTracker(requireContext()).download(MappingUtil.mapDownloads(songs), songs.stream().map(Download::new).collect(Collectors.toList()));
|
||||||
MappingUtil.mapDownloads(songs),
|
|
||||||
songs.stream().map(Download::new).collect(Collectors.toList())
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -115,7 +113,7 @@ public class AlbumPageFragment extends Fragment implements ClickCallback {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void init() {
|
private void init() {
|
||||||
albumPageViewModel.setAlbum(requireArguments().getParcelable(Constants.ALBUM_OBJECT));
|
albumPageViewModel.setAlbum(getViewLifecycleOwner(), requireArguments().getParcelable(Constants.ALBUM_OBJECT));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initAppBar() {
|
private void initAppBar() {
|
||||||
@@ -124,17 +122,48 @@ public class AlbumPageFragment extends Fragment implements ClickCallback {
|
|||||||
if (activity.getSupportActionBar() != null) {
|
if (activity.getSupportActionBar() != null) {
|
||||||
activity.getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
activity.getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||||
activity.getSupportActionBar().setDisplayShowHomeEnabled(true);
|
activity.getSupportActionBar().setDisplayShowHomeEnabled(true);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bind.animToolbar.setTitle(MusicUtil.getReadableString(albumPageViewModel.getAlbum().getName()));
|
albumPageViewModel.getAlbum().observe(getViewLifecycleOwner(), album -> {
|
||||||
|
if (bind != null && album != null) {
|
||||||
|
bind.animToolbar.setTitle(album.getName());
|
||||||
|
|
||||||
bind.albumNameLabel.setText(MusicUtil.getReadableString(albumPageViewModel.getAlbum().getName()));
|
bind.albumNameLabel.setText(album.getName());
|
||||||
bind.albumArtistLabel.setText(MusicUtil.getReadableString(albumPageViewModel.getAlbum().getArtist()));
|
bind.albumArtistLabel.setText(album.getArtist());
|
||||||
bind.albumReleaseYearLabel.setText(albumPageViewModel.getAlbum().getYear() != 0 ? String.valueOf(albumPageViewModel.getAlbum().getYear()) : "");
|
bind.albumReleaseYearLabel.setText(album.getYear() != 0 ? String.valueOf(album.getYear()) : "");
|
||||||
|
bind.albumSongCountDurationTextview.setText(getString(R.string.album_page_tracks_count_and_duration, album.getSongCount(), album.getDuration() != null ? album.getDuration() / 60 : 0));
|
||||||
|
bind.albumGenresTextview.setText(album.getGenre());
|
||||||
|
|
||||||
|
if (album.getReleaseDate() != null && album.getOriginalReleaseDate() != null) {
|
||||||
|
bind.albumReleaseYearsTextview.setVisibility(View.VISIBLE);
|
||||||
|
|
||||||
|
if (album.getReleaseDate() == null || album.getOriginalReleaseDate() == null) {
|
||||||
|
bind.albumReleaseYearsTextview.setText(getString(R.string.album_page_release_date_label, album.getReleaseDate() != null ? album.getReleaseDate().getFormattedDate() : album.getOriginalReleaseDate().getFormattedDate()));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (album.getReleaseDate() != null && album.getOriginalReleaseDate() != null) {
|
||||||
|
if (Objects.equals(album.getReleaseDate().getYear(), album.getOriginalReleaseDate().getYear()) && Objects.equals(album.getReleaseDate().getMonth(), album.getOriginalReleaseDate().getMonth()) && Objects.equals(album.getReleaseDate().getDay(), album.getOriginalReleaseDate().getDay())) {
|
||||||
|
bind.albumReleaseYearsTextview.setText(getString(R.string.album_page_release_date_label, album.getReleaseDate().getFormattedDate()));
|
||||||
|
} else {
|
||||||
|
bind.albumReleaseYearsTextview.setText(getString(R.string.album_page_release_dates_label, album.getReleaseDate().getFormattedDate(), album.getOriginalReleaseDate().getFormattedDate()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
bind.animToolbar.setNavigationOnClickListener(v -> activity.navController.navigateUp());
|
bind.animToolbar.setNavigationOnClickListener(v -> activity.navController.navigateUp());
|
||||||
|
|
||||||
Objects.requireNonNull(bind.animToolbar.getOverflowIcon()).setTint(requireContext().getResources().getColor(R.color.titleTextColor, null));
|
Objects.requireNonNull(bind.animToolbar.getOverflowIcon()).setTint(requireContext().getResources().getColor(R.color.titleTextColor, null));
|
||||||
|
|
||||||
|
bind.albumOtherInfoButton.setOnClickListener(v -> {
|
||||||
|
if (bind.albumDetailView.getVisibility() == View.GONE) {
|
||||||
|
bind.albumDetailView.setVisibility(View.VISIBLE);
|
||||||
|
} else if (bind.albumDetailView.getVisibility() == View.VISIBLE) {
|
||||||
|
bind.albumDetailView.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initAlbumInfoTextButton() {
|
private void initAlbumInfoTextButton() {
|
||||||
@@ -148,6 +177,26 @@ public class AlbumPageFragment extends Fragment implements ClickCallback {
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void initAlbumNotes() {
|
||||||
|
albumPageViewModel.getAlbumInfo().observe(getViewLifecycleOwner(), albumInfo -> {
|
||||||
|
if (albumInfo != null) {
|
||||||
|
if (bind != null) bind.albumNotesTextview.setVisibility(View.VISIBLE);
|
||||||
|
if (bind != null)
|
||||||
|
bind.albumNotesTextview.setText(MusicUtil.forceReadableString(albumInfo.getNotes()));
|
||||||
|
|
||||||
|
if (bind != null && albumInfo.getLastFmUrl() != null && !albumInfo.getLastFmUrl().isEmpty()) {
|
||||||
|
bind.albumNotesTextview.setOnClickListener(v -> {
|
||||||
|
Intent intent = new Intent(Intent.ACTION_VIEW);
|
||||||
|
intent.setData(Uri.parse(albumInfo.getLastFmUrl()));
|
||||||
|
startActivity(intent);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (bind != null) bind.albumNotesTextview.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private void initMusicButton() {
|
private void initMusicButton() {
|
||||||
albumPageViewModel.getAlbumSongLiveList().observe(getViewLifecycleOwner(), songs -> {
|
albumPageViewModel.getAlbumSongLiveList().observe(getViewLifecycleOwner(), songs -> {
|
||||||
if (bind != null && !songs.isEmpty()) {
|
if (bind != null && !songs.isEmpty()) {
|
||||||
@@ -171,20 +220,25 @@ public class AlbumPageFragment extends Fragment implements ClickCallback {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void initBackCover() {
|
private void initBackCover() {
|
||||||
CustomGlideRequest.Builder
|
albumPageViewModel.getAlbum().observe(getViewLifecycleOwner(), album -> {
|
||||||
.from(requireContext(), albumPageViewModel.getAlbum().getCoverArtId(), CustomGlideRequest.ResourceType.Album)
|
if (bind != null && album != null) {
|
||||||
.build()
|
CustomGlideRequest.Builder.from(requireContext(), album.getCoverArtId(), CustomGlideRequest.ResourceType.Album).build().into(bind.albumCoverImageView);
|
||||||
.into(bind.albumCoverImageView);
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initSongsView() {
|
private void initSongsView() {
|
||||||
bind.songRecyclerView.setLayoutManager(new LinearLayoutManager(requireContext()));
|
albumPageViewModel.getAlbum().observe(getViewLifecycleOwner(), album -> {
|
||||||
bind.songRecyclerView.setHasFixedSize(true);
|
if (bind != null && album != null) {
|
||||||
|
bind.songRecyclerView.setLayoutManager(new LinearLayoutManager(requireContext()));
|
||||||
|
bind.songRecyclerView.setHasFixedSize(true);
|
||||||
|
|
||||||
songHorizontalAdapter = new SongHorizontalAdapter(this, false, false);
|
songHorizontalAdapter = new SongHorizontalAdapter(this, false, false, album);
|
||||||
bind.songRecyclerView.setAdapter(songHorizontalAdapter);
|
bind.songRecyclerView.setAdapter(songHorizontalAdapter);
|
||||||
|
|
||||||
albumPageViewModel.getAlbumSongLiveList().observe(getViewLifecycleOwner(), songs -> songHorizontalAdapter.setItems(songs));
|
albumPageViewModel.getAlbumSongLiveList().observe(getViewLifecycleOwner(), songs -> songHorizontalAdapter.setItems(songs));
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initializeMediaBrowser() {
|
private void initializeMediaBrowser() {
|
||||||
|
|||||||
@@ -1,11 +1,21 @@
|
|||||||
package com.cappielloantonio.tempo.ui.fragment;
|
package com.cappielloantonio.tempo.ui.fragment;
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint;
|
||||||
|
import android.content.Context;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.Menu;
|
||||||
|
import android.view.MenuInflater;
|
||||||
|
import android.view.MenuItem;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
import android.view.inputmethod.EditorInfo;
|
||||||
|
import android.view.inputmethod.InputMethodManager;
|
||||||
|
import android.widget.PopupMenu;
|
||||||
|
import android.widget.SearchView;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
import androidx.core.view.ViewCompat;
|
import androidx.core.view.ViewCompat;
|
||||||
import androidx.fragment.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
import androidx.lifecycle.ViewModelProvider;
|
import androidx.lifecycle.ViewModelProvider;
|
||||||
@@ -16,11 +26,15 @@ import androidx.recyclerview.widget.LinearLayoutManager;
|
|||||||
import com.cappielloantonio.tempo.R;
|
import com.cappielloantonio.tempo.R;
|
||||||
import com.cappielloantonio.tempo.databinding.FragmentArtistListPageBinding;
|
import com.cappielloantonio.tempo.databinding.FragmentArtistListPageBinding;
|
||||||
import com.cappielloantonio.tempo.interfaces.ClickCallback;
|
import com.cappielloantonio.tempo.interfaces.ClickCallback;
|
||||||
|
import com.cappielloantonio.tempo.subsonic.models.AlbumID3;
|
||||||
|
import com.cappielloantonio.tempo.subsonic.models.ArtistID3;
|
||||||
import com.cappielloantonio.tempo.ui.activity.MainActivity;
|
import com.cappielloantonio.tempo.ui.activity.MainActivity;
|
||||||
import com.cappielloantonio.tempo.ui.adapter.ArtistHorizontalAdapter;
|
import com.cappielloantonio.tempo.ui.adapter.ArtistHorizontalAdapter;
|
||||||
import com.cappielloantonio.tempo.util.Constants;
|
import com.cappielloantonio.tempo.util.Constants;
|
||||||
import com.cappielloantonio.tempo.viewmodel.ArtistListPageViewModel;
|
import com.cappielloantonio.tempo.viewmodel.ArtistListPageViewModel;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
@UnstableApi
|
@UnstableApi
|
||||||
public class ArtistListPageFragment extends Fragment implements ClickCallback {
|
public class ArtistListPageFragment extends Fragment implements ClickCallback {
|
||||||
private FragmentArtistListPageBinding bind;
|
private FragmentArtistListPageBinding bind;
|
||||||
@@ -30,6 +44,12 @@ public class ArtistListPageFragment extends Fragment implements ClickCallback {
|
|||||||
|
|
||||||
private ArtistHorizontalAdapter artistHorizontalAdapter;
|
private ArtistHorizontalAdapter artistHorizontalAdapter;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
setHasOptionsMenu(true);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||||
activity = (MainActivity) getActivity();
|
activity = (MainActivity) getActivity();
|
||||||
@@ -69,7 +89,10 @@ public class ArtistListPageFragment extends Fragment implements ClickCallback {
|
|||||||
activity.getSupportActionBar().setDisplayShowHomeEnabled(true);
|
activity.getSupportActionBar().setDisplayShowHomeEnabled(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
bind.toolbar.setNavigationOnClickListener(v -> activity.navController.navigateUp());
|
bind.toolbar.setNavigationOnClickListener(v -> {
|
||||||
|
hideKeyboard(v);
|
||||||
|
activity.navController.navigateUp();
|
||||||
|
});
|
||||||
|
|
||||||
bind.appBarLayout.addOnOffsetChangedListener((appBarLayout, verticalOffset) -> {
|
bind.appBarLayout.addOnOffsetChangedListener((appBarLayout, verticalOffset) -> {
|
||||||
if ((bind.artistInfoSector.getHeight() + verticalOffset) < (2 * ViewCompat.getMinimumHeight(bind.toolbar))) {
|
if ((bind.artistInfoSector.getHeight() + verticalOffset) < (2 * ViewCompat.getMinimumHeight(bind.toolbar))) {
|
||||||
@@ -80,18 +103,100 @@ public class ArtistListPageFragment extends Fragment implements ClickCallback {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressLint("ClickableViewAccessibility")
|
||||||
private void initArtistListView() {
|
private void initArtistListView() {
|
||||||
bind.artistListRecyclerView.setLayoutManager(new LinearLayoutManager(requireContext()));
|
bind.artistListRecyclerView.setLayoutManager(new LinearLayoutManager(requireContext()));
|
||||||
bind.artistListRecyclerView.setHasFixedSize(true);
|
bind.artistListRecyclerView.setHasFixedSize(true);
|
||||||
|
|
||||||
artistHorizontalAdapter = new ArtistHorizontalAdapter(this);
|
artistHorizontalAdapter = new ArtistHorizontalAdapter(this);
|
||||||
bind.artistListRecyclerView.setAdapter(artistHorizontalAdapter);
|
bind.artistListRecyclerView.setAdapter(artistHorizontalAdapter);
|
||||||
artistListPageViewModel.getArtistList(getViewLifecycleOwner()).observe(getViewLifecycleOwner(), artists -> artistHorizontalAdapter.setItems(artists));
|
artistListPageViewModel.getArtistList(getViewLifecycleOwner()).observe(getViewLifecycleOwner(), artists -> {
|
||||||
|
artistHorizontalAdapter.setItems(artists);
|
||||||
|
setArtistListPageSubtitle(artists);
|
||||||
|
setArtistListPageSorter();
|
||||||
|
});
|
||||||
|
|
||||||
|
bind.artistListRecyclerView.setOnTouchListener((v, event) -> {
|
||||||
|
hideKeyboard(v);
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
bind.artistListSortImageView.setOnClickListener(view -> showPopupMenu(view, R.menu.sort_horizontal_artist_popup_menu));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) {
|
||||||
|
inflater.inflate(R.menu.toolbar_menu, menu);
|
||||||
|
|
||||||
|
MenuItem searchItem = menu.findItem(R.id.action_search);
|
||||||
|
|
||||||
|
SearchView searchView = (SearchView) searchItem.getActionView();
|
||||||
|
searchView.setImeOptions(EditorInfo.IME_ACTION_DONE);
|
||||||
|
searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
|
||||||
|
@Override
|
||||||
|
public boolean onQueryTextSubmit(String query) {
|
||||||
|
searchView.clearFocus();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onQueryTextChange(String newText) {
|
||||||
|
artistHorizontalAdapter.getFilter().filter(newText);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
searchView.setPadding(-32, 0, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void hideKeyboard(View view) {
|
||||||
|
InputMethodManager imm = (InputMethodManager) requireActivity().getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||||
|
imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showPopupMenu(View view, int menuResource) {
|
||||||
|
PopupMenu popup = new PopupMenu(requireContext(), view);
|
||||||
|
popup.getMenuInflater().inflate(menuResource, popup.getMenu());
|
||||||
|
|
||||||
|
popup.setOnMenuItemClickListener(menuItem -> {
|
||||||
|
if (menuItem.getItemId() == R.id.menu_horizontal_artist_sort_name) {
|
||||||
|
artistHorizontalAdapter.sort(Constants.ARTIST_ORDER_BY_NAME);
|
||||||
|
return true;
|
||||||
|
} else if (menuItem.getItemId() == R.id.menu_horizontal_artist_sort_most_recently_starred) {
|
||||||
|
artistHorizontalAdapter.sort(Constants.ARTIST_ORDER_BY_MOST_RECENTLY_STARRED);
|
||||||
|
return true;
|
||||||
|
} else if (menuItem.getItemId() == R.id.menu_horizontal_artist_sort_least_recently_starred) {
|
||||||
|
artistHorizontalAdapter.sort(Constants.ARTIST_ORDER_BY_LEAST_RECENTLY_STARRED);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
popup.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setArtistListPageSubtitle(List<ArtistID3> artists) {
|
||||||
|
switch (artistListPageViewModel.title) {
|
||||||
|
case Constants.ARTIST_STARRED:
|
||||||
|
case Constants.ARTIST_DOWNLOADED:
|
||||||
|
bind.pageSubtitleLabel.setText(getString(R.string.generic_list_page_count, artists.size()));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setArtistListPageSorter() {
|
||||||
|
switch (artistListPageViewModel.title) {
|
||||||
|
case Constants.ARTIST_STARRED:
|
||||||
|
case Constants.ARTIST_DOWNLOADED:
|
||||||
|
bind.artistListSortImageView.setVisibility(View.VISIBLE);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onArtistClick(Bundle bundle) {
|
public void onArtistClick(Bundle bundle) {
|
||||||
Navigation.findNavController(requireView()).navigate(R.id.albumListPageFragment, bundle);
|
Navigation.findNavController(requireView()).navigate(R.id.artistPageFragment, bundle);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -27,9 +27,11 @@ import com.cappielloantonio.tempo.helper.recyclerview.GridItemDecoration;
|
|||||||
import com.cappielloantonio.tempo.interfaces.ClickCallback;
|
import com.cappielloantonio.tempo.interfaces.ClickCallback;
|
||||||
import com.cappielloantonio.tempo.service.MediaManager;
|
import com.cappielloantonio.tempo.service.MediaManager;
|
||||||
import com.cappielloantonio.tempo.service.MediaService;
|
import com.cappielloantonio.tempo.service.MediaService;
|
||||||
|
import com.cappielloantonio.tempo.subsonic.models.ArtistID3;
|
||||||
import com.cappielloantonio.tempo.ui.activity.MainActivity;
|
import com.cappielloantonio.tempo.ui.activity.MainActivity;
|
||||||
import com.cappielloantonio.tempo.ui.adapter.AlbumArtistPageOrSimilarAdapter;
|
import com.cappielloantonio.tempo.ui.adapter.AlbumArtistPageOrSimilarAdapter;
|
||||||
import com.cappielloantonio.tempo.ui.adapter.AlbumCatalogueAdapter;
|
import com.cappielloantonio.tempo.ui.adapter.AlbumCatalogueAdapter;
|
||||||
|
import com.cappielloantonio.tempo.ui.adapter.ArtistCatalogueAdapter;
|
||||||
import com.cappielloantonio.tempo.ui.adapter.ArtistSimilarAdapter;
|
import com.cappielloantonio.tempo.ui.adapter.ArtistSimilarAdapter;
|
||||||
import com.cappielloantonio.tempo.ui.adapter.SongHorizontalAdapter;
|
import com.cappielloantonio.tempo.ui.adapter.SongHorizontalAdapter;
|
||||||
import com.cappielloantonio.tempo.util.Constants;
|
import com.cappielloantonio.tempo.util.Constants;
|
||||||
@@ -38,6 +40,10 @@ import com.cappielloantonio.tempo.util.Preferences;
|
|||||||
import com.cappielloantonio.tempo.viewmodel.ArtistPageViewModel;
|
import com.cappielloantonio.tempo.viewmodel.ArtistPageViewModel;
|
||||||
import com.google.common.util.concurrent.ListenableFuture;
|
import com.google.common.util.concurrent.ListenableFuture;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
@UnstableApi
|
@UnstableApi
|
||||||
public class ArtistPageFragment extends Fragment implements ClickCallback {
|
public class ArtistPageFragment extends Fragment implements ClickCallback {
|
||||||
private FragmentArtistPageBinding bind;
|
private FragmentArtistPageBinding bind;
|
||||||
@@ -45,9 +51,8 @@ public class ArtistPageFragment extends Fragment implements ClickCallback {
|
|||||||
private ArtistPageViewModel artistPageViewModel;
|
private ArtistPageViewModel artistPageViewModel;
|
||||||
|
|
||||||
private SongHorizontalAdapter songHorizontalAdapter;
|
private SongHorizontalAdapter songHorizontalAdapter;
|
||||||
private AlbumArtistPageOrSimilarAdapter albumArtistPageOrSimilarAdapter;
|
|
||||||
private AlbumCatalogueAdapter albumCatalogueAdapter;
|
private AlbumCatalogueAdapter albumCatalogueAdapter;
|
||||||
private ArtistSimilarAdapter artistSimilarAdapter;
|
private ArtistCatalogueAdapter artistCatalogueAdapter;
|
||||||
|
|
||||||
private ListenableFuture<MediaBrowser> mediaBrowserListenableFuture;
|
private ListenableFuture<MediaBrowser> mediaBrowserListenableFuture;
|
||||||
|
|
||||||
@@ -64,8 +69,7 @@ public class ArtistPageFragment extends Fragment implements ClickCallback {
|
|||||||
initArtistInfo();
|
initArtistInfo();
|
||||||
initPlayButtons();
|
initPlayButtons();
|
||||||
initTopSongsView();
|
initTopSongsView();
|
||||||
initHorizontalAlbumsView();
|
initAlbumsView();
|
||||||
initVerticalAlbumsView();
|
|
||||||
initSimilarArtistsView();
|
initSimilarArtistsView();
|
||||||
|
|
||||||
return view;
|
return view;
|
||||||
@@ -99,18 +103,6 @@ public class ArtistPageFragment extends Fragment implements ClickCallback {
|
|||||||
bundle.putParcelable(Constants.ARTIST_OBJECT, artistPageViewModel.getArtist());
|
bundle.putParcelable(Constants.ARTIST_OBJECT, artistPageViewModel.getArtist());
|
||||||
activity.navController.navigate(R.id.action_artistPageFragment_to_songListPageFragment, bundle);
|
activity.navController.navigate(R.id.action_artistPageFragment_to_songListPageFragment, bundle);
|
||||||
});
|
});
|
||||||
|
|
||||||
bind.artistPageAlbumsSwitchLayoutTextViewClickable.setOnClickListener(view -> {
|
|
||||||
boolean isHorizontalRecyclerViewVisible = bind.albumsHorizontalRecyclerView.getVisibility() == View.VISIBLE;
|
|
||||||
|
|
||||||
bind.albumsHorizontalRecyclerView.setVisibility(isHorizontalRecyclerViewVisible ? View.GONE : View.VISIBLE);
|
|
||||||
bind.albumsVerticalRecyclerView.setVisibility(isHorizontalRecyclerViewVisible ? View.VISIBLE : View.GONE);
|
|
||||||
|
|
||||||
Preferences.setArtistAlbumLayout(!isHorizontalRecyclerViewVisible);
|
|
||||||
});
|
|
||||||
|
|
||||||
bind.albumsHorizontalRecyclerView.setVisibility(Preferences.isArtistAlbumLayoutHorizontal() ? View.VISIBLE : View.GONE);
|
|
||||||
bind.albumsVerticalRecyclerView.setVisibility(Preferences.isArtistAlbumLayoutHorizontal() ? View.GONE : View.VISIBLE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initAppBar() {
|
private void initAppBar() {
|
||||||
@@ -118,7 +110,7 @@ public class ArtistPageFragment extends Fragment implements ClickCallback {
|
|||||||
if (activity.getSupportActionBar() != null)
|
if (activity.getSupportActionBar() != null)
|
||||||
activity.getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
activity.getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||||
|
|
||||||
bind.collapsingToolbar.setTitle(MusicUtil.getReadableString(artistPageViewModel.getArtist().getName()));
|
bind.collapsingToolbar.setTitle(artistPageViewModel.getArtist().getName());
|
||||||
bind.animToolbar.setNavigationOnClickListener(v -> activity.navController.navigateUp());
|
bind.animToolbar.setNavigationOnClickListener(v -> activity.navController.navigateUp());
|
||||||
bind.collapsingToolbar.setExpandedTitleColor(getResources().getColor(R.color.white, null));
|
bind.collapsingToolbar.setExpandedTitleColor(getResources().getColor(R.color.white, null));
|
||||||
}
|
}
|
||||||
@@ -126,8 +118,6 @@ public class ArtistPageFragment extends Fragment implements ClickCallback {
|
|||||||
private void initArtistInfo() {
|
private void initArtistInfo() {
|
||||||
artistPageViewModel.getArtistInfo(artistPageViewModel.getArtist().getId()).observe(getViewLifecycleOwner(), artistInfo -> {
|
artistPageViewModel.getArtistInfo(artistPageViewModel.getArtist().getId()).observe(getViewLifecycleOwner(), artistInfo -> {
|
||||||
if (artistInfo == null) {
|
if (artistInfo == null) {
|
||||||
if (bind != null)
|
|
||||||
bind.artistPageBioPlaceholder.placeholder.setVisibility(View.VISIBLE);
|
|
||||||
if (bind != null) bind.artistPageBioSector.setVisibility(View.GONE);
|
if (bind != null) bind.artistPageBioSector.setVisibility(View.GONE);
|
||||||
} else {
|
} else {
|
||||||
String normalizedBio = MusicUtil.forceReadableString(artistInfo.getBiography());
|
String normalizedBio = MusicUtil.forceReadableString(artistInfo.getBiography());
|
||||||
@@ -150,8 +140,6 @@ public class ArtistPageFragment extends Fragment implements ClickCallback {
|
|||||||
startActivity(intent);
|
startActivity(intent);
|
||||||
});
|
});
|
||||||
|
|
||||||
if (bind != null)
|
|
||||||
bind.artistPageBioPlaceholder.placeholder.setVisibility(View.GONE);
|
|
||||||
if (bind != null) bind.artistPageBioSector.setVisibility(View.VISIBLE);
|
if (bind != null) bind.artistPageBioSector.setVisibility(View.VISIBLE);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -160,7 +148,7 @@ public class ArtistPageFragment extends Fragment implements ClickCallback {
|
|||||||
private void initPlayButtons() {
|
private void initPlayButtons() {
|
||||||
bind.artistPageShuffleButton.setOnClickListener(v -> {
|
bind.artistPageShuffleButton.setOnClickListener(v -> {
|
||||||
artistPageViewModel.getArtistShuffleList().observe(getViewLifecycleOwner(), songs -> {
|
artistPageViewModel.getArtistShuffleList().observe(getViewLifecycleOwner(), songs -> {
|
||||||
if (songs.size() > 0) {
|
if (!songs.isEmpty()) {
|
||||||
MediaManager.startQueue(mediaBrowserListenableFuture, songs, 0);
|
MediaManager.startQueue(mediaBrowserListenableFuture, songs, 0);
|
||||||
activity.setBottomSheetInPeek(true);
|
activity.setBottomSheetInPeek(true);
|
||||||
} else {
|
} else {
|
||||||
@@ -171,7 +159,7 @@ public class ArtistPageFragment extends Fragment implements ClickCallback {
|
|||||||
|
|
||||||
bind.artistPageRadioButton.setOnClickListener(v -> {
|
bind.artistPageRadioButton.setOnClickListener(v -> {
|
||||||
artistPageViewModel.getArtistInstantMix().observe(getViewLifecycleOwner(), songs -> {
|
artistPageViewModel.getArtistInstantMix().observe(getViewLifecycleOwner(), songs -> {
|
||||||
if (songs.size() > 0) {
|
if (!songs.isEmpty()) {
|
||||||
MediaManager.startQueue(mediaBrowserListenableFuture, songs, 0);
|
MediaManager.startQueue(mediaBrowserListenableFuture, songs, 0);
|
||||||
activity.setBottomSheetInPeek(true);
|
activity.setBottomSheetInPeek(true);
|
||||||
} else {
|
} else {
|
||||||
@@ -184,62 +172,33 @@ public class ArtistPageFragment extends Fragment implements ClickCallback {
|
|||||||
private void initTopSongsView() {
|
private void initTopSongsView() {
|
||||||
bind.mostStreamedSongRecyclerView.setLayoutManager(new LinearLayoutManager(requireContext()));
|
bind.mostStreamedSongRecyclerView.setLayoutManager(new LinearLayoutManager(requireContext()));
|
||||||
|
|
||||||
songHorizontalAdapter = new SongHorizontalAdapter(this, true, true);
|
songHorizontalAdapter = new SongHorizontalAdapter(this, true, true, null);
|
||||||
bind.mostStreamedSongRecyclerView.setAdapter(songHorizontalAdapter);
|
bind.mostStreamedSongRecyclerView.setAdapter(songHorizontalAdapter);
|
||||||
artistPageViewModel.getArtistTopSongList().observe(getViewLifecycleOwner(), songs -> {
|
artistPageViewModel.getArtistTopSongList().observe(getViewLifecycleOwner(), songs -> {
|
||||||
if (songs == null) {
|
if (songs == null) {
|
||||||
if (bind != null)
|
|
||||||
bind.artistPageTopTracksPlaceholder.placeholder.setVisibility(View.VISIBLE);
|
|
||||||
if (bind != null) bind.artistPageTopSongsSector.setVisibility(View.GONE);
|
if (bind != null) bind.artistPageTopSongsSector.setVisibility(View.GONE);
|
||||||
} else {
|
} else {
|
||||||
if (bind != null)
|
|
||||||
bind.artistPageTopTracksPlaceholder.placeholder.setVisibility(View.GONE);
|
|
||||||
if (bind != null)
|
if (bind != null)
|
||||||
bind.artistPageTopSongsSector.setVisibility(!songs.isEmpty() ? View.VISIBLE : View.GONE);
|
bind.artistPageTopSongsSector.setVisibility(!songs.isEmpty() ? View.VISIBLE : View.GONE);
|
||||||
|
if (bind != null)
|
||||||
|
bind.artistPageShuffleButton.setEnabled(!songs.isEmpty());
|
||||||
songHorizontalAdapter.setItems(songs);
|
songHorizontalAdapter.setItems(songs);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initHorizontalAlbumsView() {
|
private void initAlbumsView() {
|
||||||
bind.albumsHorizontalRecyclerView.setLayoutManager(new LinearLayoutManager(requireContext(), LinearLayoutManager.HORIZONTAL, false));
|
bind.albumsRecyclerView.setLayoutManager(new GridLayoutManager(requireContext(), 2));
|
||||||
|
bind.albumsRecyclerView.addItemDecoration(new GridItemDecoration(2, 20, false));
|
||||||
|
bind.albumsRecyclerView.setHasFixedSize(true);
|
||||||
|
|
||||||
albumArtistPageOrSimilarAdapter = new AlbumArtistPageOrSimilarAdapter(this);
|
albumCatalogueAdapter = new AlbumCatalogueAdapter(this, false);
|
||||||
bind.albumsHorizontalRecyclerView.setAdapter(albumArtistPageOrSimilarAdapter);
|
bind.albumsRecyclerView.setAdapter(albumCatalogueAdapter);
|
||||||
artistPageViewModel.getAlbumList().observe(getViewLifecycleOwner(), albums -> {
|
|
||||||
if (albums == null) {
|
|
||||||
if (bind != null)
|
|
||||||
bind.artistPageAlbumPlaceholder.placeholder.setVisibility(View.VISIBLE);
|
|
||||||
if (bind != null) bind.artistPageAlbumsSector.setVisibility(View.GONE);
|
|
||||||
} else {
|
|
||||||
if (bind != null)
|
|
||||||
bind.artistPageAlbumPlaceholder.placeholder.setVisibility(View.GONE);
|
|
||||||
if (bind != null)
|
|
||||||
bind.artistPageAlbumsSector.setVisibility(!albums.isEmpty() ? View.VISIBLE : View.GONE);
|
|
||||||
albumArtistPageOrSimilarAdapter.setItems(albums);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
CustomLinearSnapHelper albumSnapHelper = new CustomLinearSnapHelper();
|
|
||||||
albumSnapHelper.attachToRecyclerView(bind.albumsHorizontalRecyclerView);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initVerticalAlbumsView() {
|
|
||||||
bind.albumsVerticalRecyclerView.setLayoutManager(new GridLayoutManager(requireContext(), 2));
|
|
||||||
bind.albumsVerticalRecyclerView.addItemDecoration(new GridItemDecoration(2, 20, false));
|
|
||||||
bind.albumsVerticalRecyclerView.setHasFixedSize(true);
|
|
||||||
|
|
||||||
albumCatalogueAdapter = new AlbumCatalogueAdapter(this);
|
|
||||||
bind.albumsVerticalRecyclerView.setAdapter(albumCatalogueAdapter);
|
|
||||||
|
|
||||||
artistPageViewModel.getAlbumList().observe(getViewLifecycleOwner(), albums -> {
|
artistPageViewModel.getAlbumList().observe(getViewLifecycleOwner(), albums -> {
|
||||||
if (albums == null) {
|
if (albums == null) {
|
||||||
if (bind != null)
|
|
||||||
bind.artistPageAlbumPlaceholder.placeholder.setVisibility(View.VISIBLE);
|
|
||||||
if (bind != null) bind.artistPageAlbumsSector.setVisibility(View.GONE);
|
if (bind != null) bind.artistPageAlbumsSector.setVisibility(View.GONE);
|
||||||
} else {
|
} else {
|
||||||
if (bind != null)
|
|
||||||
bind.artistPageAlbumPlaceholder.placeholder.setVisibility(View.GONE);
|
|
||||||
if (bind != null)
|
if (bind != null)
|
||||||
bind.artistPageAlbumsSector.setVisibility(!albums.isEmpty() ? View.VISIBLE : View.GONE);
|
bind.artistPageAlbumsSector.setVisibility(!albums.isEmpty() ? View.VISIBLE : View.GONE);
|
||||||
albumCatalogueAdapter.setItems(albums);
|
albumCatalogueAdapter.setItems(albums);
|
||||||
@@ -248,22 +207,27 @@ public class ArtistPageFragment extends Fragment implements ClickCallback {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void initSimilarArtistsView() {
|
private void initSimilarArtistsView() {
|
||||||
bind.similarArtistsRecyclerView.setLayoutManager(new LinearLayoutManager(requireContext(), LinearLayoutManager.HORIZONTAL, false));
|
bind.similarArtistsRecyclerView.setLayoutManager(new GridLayoutManager(requireContext(), 2));
|
||||||
|
bind.similarArtistsRecyclerView.addItemDecoration(new GridItemDecoration(2, 20, false));
|
||||||
bind.similarArtistsRecyclerView.setHasFixedSize(true);
|
bind.similarArtistsRecyclerView.setHasFixedSize(true);
|
||||||
|
|
||||||
artistSimilarAdapter = new ArtistSimilarAdapter(this);
|
artistCatalogueAdapter = new ArtistCatalogueAdapter(this);
|
||||||
bind.similarArtistsRecyclerView.setAdapter(artistSimilarAdapter);
|
bind.similarArtistsRecyclerView.setAdapter(artistCatalogueAdapter);
|
||||||
|
|
||||||
artistPageViewModel.getArtistInfo(artistPageViewModel.getArtist().getId()).observe(getViewLifecycleOwner(), artist -> {
|
artistPageViewModel.getArtistInfo(artistPageViewModel.getArtist().getId()).observe(getViewLifecycleOwner(), artist -> {
|
||||||
if (artist == null) {
|
if (artist == null) {
|
||||||
if (bind != null)
|
|
||||||
bind.artistPageSimilarArtistPlaceholder.placeholder.setVisibility(View.VISIBLE);
|
|
||||||
if (bind != null) bind.similarArtistSector.setVisibility(View.GONE);
|
if (bind != null) bind.similarArtistSector.setVisibility(View.GONE);
|
||||||
} else {
|
} else {
|
||||||
if (bind != null)
|
if (bind != null && artist.getSimilarArtists() != null)
|
||||||
bind.artistPageSimilarArtistPlaceholder.placeholder.setVisibility(View.GONE);
|
|
||||||
if (bind != null)
|
|
||||||
bind.similarArtistSector.setVisibility(!artist.getSimilarArtists().isEmpty() ? View.VISIBLE : View.GONE);
|
bind.similarArtistSector.setVisibility(!artist.getSimilarArtists().isEmpty() ? View.VISIBLE : View.GONE);
|
||||||
artistSimilarAdapter.setItems(artist.getSimilarArtists());
|
|
||||||
|
List<ArtistID3> artists = new ArrayList<>();
|
||||||
|
|
||||||
|
if (artist.getSimilarArtists() != null) {
|
||||||
|
artists.addAll(artist.getSimilarArtists());
|
||||||
|
}
|
||||||
|
|
||||||
|
artistCatalogueAdapter.setItems(artists);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -111,20 +111,14 @@ public class DownloadFragment extends Fragment implements ClickCallback {
|
|||||||
if (bind != null) {
|
if (bind != null) {
|
||||||
bind.emptyDownloadLayout.setVisibility(View.VISIBLE);
|
bind.emptyDownloadLayout.setVisibility(View.VISIBLE);
|
||||||
bind.fragmentDownloadNestedScrollView.setVisibility(View.GONE);
|
bind.fragmentDownloadNestedScrollView.setVisibility(View.GONE);
|
||||||
|
|
||||||
bind.downloadDownloadedPlaceholder.placeholder.setVisibility(View.VISIBLE);
|
|
||||||
bind.downloadDownloadedSector.setVisibility(View.GONE);
|
bind.downloadDownloadedSector.setVisibility(View.GONE);
|
||||||
|
|
||||||
bind.downloadedGroupByImageView.setVisibility(View.GONE);
|
bind.downloadedGroupByImageView.setVisibility(View.GONE);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (bind != null) {
|
if (bind != null) {
|
||||||
bind.emptyDownloadLayout.setVisibility(View.GONE);
|
bind.emptyDownloadLayout.setVisibility(View.GONE);
|
||||||
bind.fragmentDownloadNestedScrollView.setVisibility(View.VISIBLE);
|
bind.fragmentDownloadNestedScrollView.setVisibility(View.VISIBLE);
|
||||||
|
|
||||||
bind.downloadDownloadedPlaceholder.placeholder.setVisibility(View.GONE);
|
|
||||||
bind.downloadDownloadedSector.setVisibility(View.VISIBLE);
|
bind.downloadDownloadedSector.setVisibility(View.VISIBLE);
|
||||||
|
|
||||||
bind.downloadedGroupByImageView.setVisibility(View.VISIBLE);
|
bind.downloadedGroupByImageView.setVisibility(View.VISIBLE);
|
||||||
|
|
||||||
finishDownloadView(songs);
|
finishDownloadView(songs);
|
||||||
|
|||||||
@@ -91,7 +91,7 @@ public class FilterFragment extends Fragment {
|
|||||||
bind.filterContainer.setVisibility(View.VISIBLE);
|
bind.filterContainer.setVisibility(View.VISIBLE);
|
||||||
for (Genre genre : genres) {
|
for (Genre genre : genres) {
|
||||||
Chip chip = (Chip) requireActivity().getLayoutInflater().inflate(R.layout.chip_search_filter_genre, null, false);
|
Chip chip = (Chip) requireActivity().getLayoutInflater().inflate(R.layout.chip_search_filter_genre, null, false);
|
||||||
chip.setText(MusicUtil.getReadableString(genre.getGenre()));
|
chip.setText(genre.getGenre());
|
||||||
chip.setChecked(filterViewModel.getFilters().contains(genre.getGenre()));
|
chip.setChecked(filterViewModel.getFilters().contains(genre.getGenre()));
|
||||||
chip.setOnCheckedChangeListener((buttonView, isChecked) -> {
|
chip.setOnCheckedChangeListener((buttonView, isChecked) -> {
|
||||||
if (isChecked)
|
if (isChecked)
|
||||||
|
|||||||
@@ -105,7 +105,7 @@ public class GenreCatalogueFragment extends Fragment implements ClickCallback {
|
|||||||
genreCatalogueAdapter.setStateRestorationPolicy(RecyclerView.Adapter.StateRestorationPolicy.PREVENT_WHEN_EMPTY);
|
genreCatalogueAdapter.setStateRestorationPolicy(RecyclerView.Adapter.StateRestorationPolicy.PREVENT_WHEN_EMPTY);
|
||||||
bind.genreCatalogueRecyclerView.setAdapter(genreCatalogueAdapter);
|
bind.genreCatalogueRecyclerView.setAdapter(genreCatalogueAdapter);
|
||||||
|
|
||||||
genreCatalogueViewModel.getGenreList().observe(getViewLifecycleOwner(), genres -> genreCatalogueAdapter.setItems(genres));
|
genreCatalogueViewModel.getGenreList().observe(getViewLifecycleOwner(), genres -> genreCatalogueAdapter.setItems(genres) );
|
||||||
|
|
||||||
bind.genreCatalogueRecyclerView.setOnTouchListener((v, event) -> {
|
bind.genreCatalogueRecyclerView.setOnTouchListener((v, event) -> {
|
||||||
hideKeyboard(v);
|
hideKeyboard(v);
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import android.os.Handler;
|
|||||||
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 android.widget.PopupMenu;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
@@ -29,9 +30,10 @@ import com.cappielloantonio.tempo.R;
|
|||||||
import com.cappielloantonio.tempo.databinding.FragmentHomeTabMusicBinding;
|
import com.cappielloantonio.tempo.databinding.FragmentHomeTabMusicBinding;
|
||||||
import com.cappielloantonio.tempo.helper.recyclerview.CustomLinearSnapHelper;
|
import com.cappielloantonio.tempo.helper.recyclerview.CustomLinearSnapHelper;
|
||||||
import com.cappielloantonio.tempo.helper.recyclerview.DotsIndicatorDecoration;
|
import com.cappielloantonio.tempo.helper.recyclerview.DotsIndicatorDecoration;
|
||||||
import com.cappielloantonio.tempo.helper.recyclerview.GridItemDecoration;
|
|
||||||
import com.cappielloantonio.tempo.interfaces.ClickCallback;
|
import com.cappielloantonio.tempo.interfaces.ClickCallback;
|
||||||
|
import com.cappielloantonio.tempo.interfaces.PlaylistCallback;
|
||||||
import com.cappielloantonio.tempo.model.Download;
|
import com.cappielloantonio.tempo.model.Download;
|
||||||
|
import com.cappielloantonio.tempo.model.HomeSector;
|
||||||
import com.cappielloantonio.tempo.service.DownloaderManager;
|
import com.cappielloantonio.tempo.service.DownloaderManager;
|
||||||
import com.cappielloantonio.tempo.service.MediaManager;
|
import com.cappielloantonio.tempo.service.MediaManager;
|
||||||
import com.cappielloantonio.tempo.service.MediaService;
|
import com.cappielloantonio.tempo.service.MediaService;
|
||||||
@@ -43,11 +45,13 @@ import com.cappielloantonio.tempo.ui.adapter.AlbumHorizontalAdapter;
|
|||||||
import com.cappielloantonio.tempo.ui.adapter.ArtistAdapter;
|
import com.cappielloantonio.tempo.ui.adapter.ArtistAdapter;
|
||||||
import com.cappielloantonio.tempo.ui.adapter.ArtistHorizontalAdapter;
|
import com.cappielloantonio.tempo.ui.adapter.ArtistHorizontalAdapter;
|
||||||
import com.cappielloantonio.tempo.ui.adapter.DiscoverSongAdapter;
|
import com.cappielloantonio.tempo.ui.adapter.DiscoverSongAdapter;
|
||||||
import com.cappielloantonio.tempo.ui.adapter.GridTrackAdapter;
|
import com.cappielloantonio.tempo.ui.adapter.PlaylistHorizontalAdapter;
|
||||||
import com.cappielloantonio.tempo.ui.adapter.ShareHorizontalAdapter;
|
import com.cappielloantonio.tempo.ui.adapter.ShareHorizontalAdapter;
|
||||||
import com.cappielloantonio.tempo.ui.adapter.SimilarTrackAdapter;
|
import com.cappielloantonio.tempo.ui.adapter.SimilarTrackAdapter;
|
||||||
import com.cappielloantonio.tempo.ui.adapter.SongHorizontalAdapter;
|
import com.cappielloantonio.tempo.ui.adapter.SongHorizontalAdapter;
|
||||||
import com.cappielloantonio.tempo.ui.adapter.YearAdapter;
|
import com.cappielloantonio.tempo.ui.adapter.YearAdapter;
|
||||||
|
import com.cappielloantonio.tempo.ui.dialog.HomeRearrangementDialog;
|
||||||
|
import com.cappielloantonio.tempo.ui.dialog.PlaylistEditorDialog;
|
||||||
import com.cappielloantonio.tempo.util.Constants;
|
import com.cappielloantonio.tempo.util.Constants;
|
||||||
import com.cappielloantonio.tempo.util.DownloadUtil;
|
import com.cappielloantonio.tempo.util.DownloadUtil;
|
||||||
import com.cappielloantonio.tempo.util.MappingUtil;
|
import com.cappielloantonio.tempo.util.MappingUtil;
|
||||||
@@ -60,6 +64,7 @@ import com.google.common.util.concurrent.ListenableFuture;
|
|||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
@UnstableApi
|
@UnstableApi
|
||||||
public class HomeTabMusicFragment extends Fragment implements ClickCallback {
|
public class HomeTabMusicFragment extends Fragment implements ClickCallback {
|
||||||
@@ -74,6 +79,7 @@ public class HomeTabMusicFragment extends Fragment implements ClickCallback {
|
|||||||
private ArtistAdapter radioArtistAdapter;
|
private ArtistAdapter radioArtistAdapter;
|
||||||
private ArtistAdapter bestOfArtistAdapter;
|
private ArtistAdapter bestOfArtistAdapter;
|
||||||
private SongHorizontalAdapter starredSongAdapter;
|
private SongHorizontalAdapter starredSongAdapter;
|
||||||
|
private SongHorizontalAdapter topSongAdapter;
|
||||||
private AlbumHorizontalAdapter starredAlbumAdapter;
|
private AlbumHorizontalAdapter starredAlbumAdapter;
|
||||||
private ArtistHorizontalAdapter starredArtistAdapter;
|
private ArtistHorizontalAdapter starredArtistAdapter;
|
||||||
private AlbumAdapter recentlyAddedAlbumAdapter;
|
private AlbumAdapter recentlyAddedAlbumAdapter;
|
||||||
@@ -81,7 +87,7 @@ public class HomeTabMusicFragment extends Fragment implements ClickCallback {
|
|||||||
private AlbumAdapter mostPlayedAlbumAdapter;
|
private AlbumAdapter mostPlayedAlbumAdapter;
|
||||||
private AlbumHorizontalAdapter newReleasesAlbumAdapter;
|
private AlbumHorizontalAdapter newReleasesAlbumAdapter;
|
||||||
private YearAdapter yearAdapter;
|
private YearAdapter yearAdapter;
|
||||||
private GridTrackAdapter gridTrackAdapter;
|
private PlaylistHorizontalAdapter playlistHorizontalAdapter;
|
||||||
private ShareHorizontalAdapter shareHorizontalAdapter;
|
private ShareHorizontalAdapter shareHorizontalAdapter;
|
||||||
|
|
||||||
private ListenableFuture<MediaBrowser> mediaBrowserListenableFuture;
|
private ListenableFuture<MediaBrowser> mediaBrowserListenableFuture;
|
||||||
@@ -117,8 +123,12 @@ public class HomeTabMusicFragment extends Fragment implements ClickCallback {
|
|||||||
initNewReleasesView();
|
initNewReleasesView();
|
||||||
initYearSongView();
|
initYearSongView();
|
||||||
initRecentAddedAlbumView();
|
initRecentAddedAlbumView();
|
||||||
initGridView();
|
initTopSongsView();
|
||||||
|
initPinnedPlaylistsView();
|
||||||
initSharesView();
|
initSharesView();
|
||||||
|
initHomeReorganizer();
|
||||||
|
|
||||||
|
reorder();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -156,7 +166,7 @@ public class HomeTabMusicFragment extends Fragment implements ClickCallback {
|
|||||||
homeViewModel.getRandomShuffleSample().observe(getViewLifecycleOwner(), songs -> {
|
homeViewModel.getRandomShuffleSample().observe(getViewLifecycleOwner(), songs -> {
|
||||||
MusicUtil.ratingFilter(songs);
|
MusicUtil.ratingFilter(songs);
|
||||||
|
|
||||||
if (songs.size() > 0) {
|
if (!songs.isEmpty()) {
|
||||||
MediaManager.startQueue(mediaBrowserListenableFuture, songs, 0);
|
MediaManager.startQueue(mediaBrowserListenableFuture, songs, 0);
|
||||||
activity.setBottomSheetInPeek(true);
|
activity.setBottomSheetInPeek(true);
|
||||||
}
|
}
|
||||||
@@ -248,6 +258,8 @@ public class HomeTabMusicFragment extends Fragment implements ClickCallback {
|
|||||||
homeViewModel.refreshShares(getViewLifecycleOwner());
|
homeViewModel.refreshShares(getViewLifecycleOwner());
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
bind.gridTracksPreTextView.setOnClickListener(view -> showPopupMenu(view, R.menu.filter_top_songs_popup_menu));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initSyncStarredView() {
|
private void initSyncStarredView() {
|
||||||
@@ -303,6 +315,8 @@ public class HomeTabMusicFragment extends Fragment implements ClickCallback {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void initDiscoverSongSlideView() {
|
private void initDiscoverSongSlideView() {
|
||||||
|
if (homeViewModel.checkHomeSectorVisibility(Constants.HOME_SECTOR_DISCOVERY)) return;
|
||||||
|
|
||||||
bind.discoverSongViewPager.setOrientation(ViewPager2.ORIENTATION_HORIZONTAL);
|
bind.discoverSongViewPager.setOrientation(ViewPager2.ORIENTATION_HORIZONTAL);
|
||||||
|
|
||||||
discoverSongAdapter = new DiscoverSongAdapter(this);
|
discoverSongAdapter = new DiscoverSongAdapter(this);
|
||||||
@@ -312,12 +326,8 @@ public class HomeTabMusicFragment extends Fragment implements ClickCallback {
|
|||||||
MusicUtil.ratingFilter(songs);
|
MusicUtil.ratingFilter(songs);
|
||||||
|
|
||||||
if (songs == null) {
|
if (songs == null) {
|
||||||
if (bind != null)
|
|
||||||
bind.homeDiscoveryPlaceholder.placeholder.setVisibility(View.VISIBLE);
|
|
||||||
if (bind != null) bind.homeDiscoverSector.setVisibility(View.GONE);
|
if (bind != null) bind.homeDiscoverSector.setVisibility(View.GONE);
|
||||||
} else {
|
} else {
|
||||||
if (bind != null)
|
|
||||||
bind.homeDiscoveryPlaceholder.placeholder.setVisibility(View.GONE);
|
|
||||||
if (bind != null)
|
if (bind != null)
|
||||||
bind.homeDiscoverSector.setVisibility(!songs.isEmpty() ? View.VISIBLE : View.GONE);
|
bind.homeDiscoverSector.setVisibility(!songs.isEmpty() ? View.VISIBLE : View.GONE);
|
||||||
|
|
||||||
@@ -329,6 +339,8 @@ public class HomeTabMusicFragment extends Fragment implements ClickCallback {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void initSimilarSongView() {
|
private void initSimilarSongView() {
|
||||||
|
if (homeViewModel.checkHomeSectorVisibility(Constants.HOME_SECTOR_MADE_FOR_YOU)) return;
|
||||||
|
|
||||||
bind.similarTracksRecyclerView.setLayoutManager(new LinearLayoutManager(requireContext(), LinearLayoutManager.HORIZONTAL, false));
|
bind.similarTracksRecyclerView.setLayoutManager(new LinearLayoutManager(requireContext(), LinearLayoutManager.HORIZONTAL, false));
|
||||||
bind.similarTracksRecyclerView.setHasFixedSize(true);
|
bind.similarTracksRecyclerView.setHasFixedSize(true);
|
||||||
|
|
||||||
@@ -338,12 +350,8 @@ public class HomeTabMusicFragment extends Fragment implements ClickCallback {
|
|||||||
MusicUtil.ratingFilter(songs);
|
MusicUtil.ratingFilter(songs);
|
||||||
|
|
||||||
if (songs == null) {
|
if (songs == null) {
|
||||||
if (bind != null)
|
|
||||||
bind.homeSimilarTracksPlaceholder.placeholder.setVisibility(View.VISIBLE);
|
|
||||||
if (bind != null) bind.homeSimilarTracksSector.setVisibility(View.GONE);
|
if (bind != null) bind.homeSimilarTracksSector.setVisibility(View.GONE);
|
||||||
} else {
|
} else {
|
||||||
if (bind != null)
|
|
||||||
bind.homeSimilarTracksPlaceholder.placeholder.setVisibility(View.GONE);
|
|
||||||
if (bind != null)
|
if (bind != null)
|
||||||
bind.homeSimilarTracksSector.setVisibility(!songs.isEmpty() ? View.VISIBLE : View.GONE);
|
bind.homeSimilarTracksSector.setVisibility(!songs.isEmpty() ? View.VISIBLE : View.GONE);
|
||||||
|
|
||||||
@@ -356,6 +364,8 @@ public class HomeTabMusicFragment extends Fragment implements ClickCallback {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void initArtistBestOf() {
|
private void initArtistBestOf() {
|
||||||
|
if (homeViewModel.checkHomeSectorVisibility(Constants.HOME_SECTOR_BEST_OF)) return;
|
||||||
|
|
||||||
bind.bestOfArtistRecyclerView.setLayoutManager(new LinearLayoutManager(requireContext(), LinearLayoutManager.HORIZONTAL, false));
|
bind.bestOfArtistRecyclerView.setLayoutManager(new LinearLayoutManager(requireContext(), LinearLayoutManager.HORIZONTAL, false));
|
||||||
bind.bestOfArtistRecyclerView.setHasFixedSize(true);
|
bind.bestOfArtistRecyclerView.setHasFixedSize(true);
|
||||||
|
|
||||||
@@ -363,12 +373,8 @@ public class HomeTabMusicFragment extends Fragment implements ClickCallback {
|
|||||||
bind.bestOfArtistRecyclerView.setAdapter(bestOfArtistAdapter);
|
bind.bestOfArtistRecyclerView.setAdapter(bestOfArtistAdapter);
|
||||||
homeViewModel.getBestOfArtists(getViewLifecycleOwner()).observe(getViewLifecycleOwner(), artists -> {
|
homeViewModel.getBestOfArtists(getViewLifecycleOwner()).observe(getViewLifecycleOwner(), artists -> {
|
||||||
if (artists == null) {
|
if (artists == null) {
|
||||||
if (bind != null)
|
|
||||||
bind.homeBestOfArtistPlaceholder.placeholder.setVisibility(View.VISIBLE);
|
|
||||||
if (bind != null) bind.homeBestOfArtistSector.setVisibility(View.GONE);
|
if (bind != null) bind.homeBestOfArtistSector.setVisibility(View.GONE);
|
||||||
} else {
|
} else {
|
||||||
if (bind != null)
|
|
||||||
bind.homeBestOfArtistPlaceholder.placeholder.setVisibility(View.GONE);
|
|
||||||
if (bind != null)
|
if (bind != null)
|
||||||
bind.homeBestOfArtistSector.setVisibility(!artists.isEmpty() ? View.VISIBLE : View.GONE);
|
bind.homeBestOfArtistSector.setVisibility(!artists.isEmpty() ? View.VISIBLE : View.GONE);
|
||||||
|
|
||||||
@@ -381,6 +387,8 @@ public class HomeTabMusicFragment extends Fragment implements ClickCallback {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void initArtistRadio() {
|
private void initArtistRadio() {
|
||||||
|
if (homeViewModel.checkHomeSectorVisibility(Constants.HOME_SECTOR_RADIO_STATION)) return;
|
||||||
|
|
||||||
bind.radioArtistRecyclerView.setLayoutManager(new LinearLayoutManager(requireContext(), LinearLayoutManager.HORIZONTAL, false));
|
bind.radioArtistRecyclerView.setLayoutManager(new LinearLayoutManager(requireContext(), LinearLayoutManager.HORIZONTAL, false));
|
||||||
bind.radioArtistRecyclerView.setHasFixedSize(true);
|
bind.radioArtistRecyclerView.setHasFixedSize(true);
|
||||||
|
|
||||||
@@ -388,12 +396,8 @@ public class HomeTabMusicFragment extends Fragment implements ClickCallback {
|
|||||||
bind.radioArtistRecyclerView.setAdapter(radioArtistAdapter);
|
bind.radioArtistRecyclerView.setAdapter(radioArtistAdapter);
|
||||||
homeViewModel.getStarredArtistsSample(getViewLifecycleOwner()).observe(getViewLifecycleOwner(), artists -> {
|
homeViewModel.getStarredArtistsSample(getViewLifecycleOwner()).observe(getViewLifecycleOwner(), artists -> {
|
||||||
if (artists == null) {
|
if (artists == null) {
|
||||||
if (bind != null)
|
|
||||||
bind.homeRadioArtistPlaceholder.placeholder.setVisibility(View.VISIBLE);
|
|
||||||
if (bind != null) bind.homeRadioArtistSector.setVisibility(View.GONE);
|
if (bind != null) bind.homeRadioArtistSector.setVisibility(View.GONE);
|
||||||
} else {
|
} else {
|
||||||
if (bind != null)
|
|
||||||
bind.homeRadioArtistPlaceholder.placeholder.setVisibility(View.GONE);
|
|
||||||
if (bind != null)
|
if (bind != null)
|
||||||
bind.homeRadioArtistSector.setVisibility(!artists.isEmpty() ? View.VISIBLE : View.GONE);
|
bind.homeRadioArtistSector.setVisibility(!artists.isEmpty() ? View.VISIBLE : View.GONE);
|
||||||
if (bind != null)
|
if (bind != null)
|
||||||
@@ -407,43 +411,55 @@ public class HomeTabMusicFragment extends Fragment implements ClickCallback {
|
|||||||
artistRadioSnapHelper.attachToRecyclerView(bind.radioArtistRecyclerView);
|
artistRadioSnapHelper.attachToRecyclerView(bind.radioArtistRecyclerView);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initGridView() {
|
private void initTopSongsView() {
|
||||||
bind.gridTracksRecyclerView.setLayoutManager(new GridLayoutManager(requireContext(), 3));
|
if (homeViewModel.checkHomeSectorVisibility(Constants.HOME_SECTOR_TOP_SONGS)) return;
|
||||||
bind.gridTracksRecyclerView.addItemDecoration(new GridItemDecoration(3, 8, false));
|
|
||||||
bind.gridTracksRecyclerView.setHasFixedSize(true);
|
|
||||||
|
|
||||||
gridTrackAdapter = new GridTrackAdapter(this);
|
bind.topSongsRecyclerView.setHasFixedSize(true);
|
||||||
bind.gridTracksRecyclerView.setAdapter(gridTrackAdapter);
|
|
||||||
|
|
||||||
homeViewModel.getDiscoverSongSample(getViewLifecycleOwner()).observe(getViewLifecycleOwner(), music -> {
|
topSongAdapter = new SongHorizontalAdapter(this, true, false, null);
|
||||||
if (music != null) {
|
bind.topSongsRecyclerView.setAdapter(topSongAdapter);
|
||||||
homeViewModel.getGridSongSample(getViewLifecycleOwner()).observe(getViewLifecycleOwner(), chronologies -> {
|
homeViewModel.getChronologySample(getViewLifecycleOwner()).observe(getViewLifecycleOwner(), chronologies -> {
|
||||||
if (chronologies == null || chronologies.size() == 0) {
|
if (chronologies == null || chronologies.isEmpty()) {
|
||||||
if (bind != null) bind.homeGridTracksSector.setVisibility(View.GONE);
|
if (bind != null) bind.homeGridTracksSector.setVisibility(View.GONE);
|
||||||
if (bind != null) bind.afterGridDivider.setVisibility(View.GONE);
|
if (bind != null) bind.afterGridDivider.setVisibility(View.GONE);
|
||||||
} else {
|
} else {
|
||||||
if (bind != null) bind.homeGridTracksSector.setVisibility(View.VISIBLE);
|
if (bind != null) bind.homeGridTracksSector.setVisibility(View.VISIBLE);
|
||||||
if (bind != null) bind.afterGridDivider.setVisibility(View.VISIBLE);
|
if (bind != null) bind.afterGridDivider.setVisibility(View.VISIBLE);
|
||||||
gridTrackAdapter.setItems(chronologies);
|
if (bind != null)
|
||||||
}
|
bind.topSongsRecyclerView.setLayoutManager(new GridLayoutManager(requireContext(), UIUtil.getSpanCount(chronologies.size(), 5), GridLayoutManager.HORIZONTAL, false));
|
||||||
});
|
|
||||||
|
List<Child> topSongs = chronologies.stream()
|
||||||
|
.map(cronologia -> (Child) cronologia)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
topSongAdapter.setItems(topSongs);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
SnapHelper topTrackSnapHelper = new PagerSnapHelper();
|
||||||
|
topTrackSnapHelper.attachToRecyclerView(bind.topSongsRecyclerView);
|
||||||
|
|
||||||
|
bind.topSongsRecyclerView.addItemDecoration(
|
||||||
|
new DotsIndicatorDecoration(
|
||||||
|
getResources().getDimensionPixelSize(R.dimen.radius),
|
||||||
|
getResources().getDimensionPixelSize(R.dimen.radius) * 4,
|
||||||
|
getResources().getDimensionPixelSize(R.dimen.dots_height),
|
||||||
|
requireContext().getResources().getColor(R.color.titleTextColor, null),
|
||||||
|
requireContext().getResources().getColor(R.color.titleTextColor, null))
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initStarredTracksView() {
|
private void initStarredTracksView() {
|
||||||
|
if (homeViewModel.checkHomeSectorVisibility(Constants.HOME_SECTOR_STARRED_TRACKS)) return;
|
||||||
|
|
||||||
bind.starredTracksRecyclerView.setHasFixedSize(true);
|
bind.starredTracksRecyclerView.setHasFixedSize(true);
|
||||||
|
|
||||||
starredSongAdapter = new SongHorizontalAdapter(this, true, false);
|
starredSongAdapter = new SongHorizontalAdapter(this, true, false, null);
|
||||||
bind.starredTracksRecyclerView.setAdapter(starredSongAdapter);
|
bind.starredTracksRecyclerView.setAdapter(starredSongAdapter);
|
||||||
homeViewModel.getStarredTracks(getViewLifecycleOwner()).observe(getViewLifecycleOwner(), songs -> {
|
homeViewModel.getStarredTracks(getViewLifecycleOwner()).observe(getViewLifecycleOwner(), songs -> {
|
||||||
if (songs == null) {
|
if (songs == null) {
|
||||||
if (bind != null)
|
|
||||||
bind.starredTracksPlaceholder.placeholder.setVisibility(View.VISIBLE);
|
|
||||||
if (bind != null) bind.starredTracksSector.setVisibility(View.GONE);
|
if (bind != null) bind.starredTracksSector.setVisibility(View.GONE);
|
||||||
} else {
|
} else {
|
||||||
if (bind != null)
|
|
||||||
bind.starredTracksPlaceholder.placeholder.setVisibility(View.GONE);
|
|
||||||
if (bind != null)
|
if (bind != null)
|
||||||
bind.starredTracksSector.setVisibility(!songs.isEmpty() ? View.VISIBLE : View.GONE);
|
bind.starredTracksSector.setVisibility(!songs.isEmpty() ? View.VISIBLE : View.GONE);
|
||||||
if (bind != null)
|
if (bind != null)
|
||||||
@@ -467,18 +483,16 @@ public class HomeTabMusicFragment extends Fragment implements ClickCallback {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void initStarredAlbumsView() {
|
private void initStarredAlbumsView() {
|
||||||
|
if (homeViewModel.checkHomeSectorVisibility(Constants.HOME_SECTOR_STARRED_ALBUMS)) return;
|
||||||
|
|
||||||
bind.starredAlbumsRecyclerView.setHasFixedSize(true);
|
bind.starredAlbumsRecyclerView.setHasFixedSize(true);
|
||||||
|
|
||||||
starredAlbumAdapter = new AlbumHorizontalAdapter(this, false);
|
starredAlbumAdapter = new AlbumHorizontalAdapter(this, false);
|
||||||
bind.starredAlbumsRecyclerView.setAdapter(starredAlbumAdapter);
|
bind.starredAlbumsRecyclerView.setAdapter(starredAlbumAdapter);
|
||||||
homeViewModel.getStarredAlbums(getViewLifecycleOwner()).observe(getViewLifecycleOwner(), albums -> {
|
homeViewModel.getStarredAlbums(getViewLifecycleOwner()).observe(getViewLifecycleOwner(), albums -> {
|
||||||
if (albums == null) {
|
if (albums == null) {
|
||||||
if (bind != null)
|
|
||||||
bind.starredAlbumsPlaceholder.placeholder.setVisibility(View.VISIBLE);
|
|
||||||
if (bind != null) bind.starredAlbumsSector.setVisibility(View.GONE);
|
if (bind != null) bind.starredAlbumsSector.setVisibility(View.GONE);
|
||||||
} else {
|
} else {
|
||||||
if (bind != null)
|
|
||||||
bind.starredAlbumsPlaceholder.placeholder.setVisibility(View.GONE);
|
|
||||||
if (bind != null)
|
if (bind != null)
|
||||||
bind.starredAlbumsSector.setVisibility(!albums.isEmpty() ? View.VISIBLE : View.GONE);
|
bind.starredAlbumsSector.setVisibility(!albums.isEmpty() ? View.VISIBLE : View.GONE);
|
||||||
if (bind != null)
|
if (bind != null)
|
||||||
@@ -502,18 +516,16 @@ public class HomeTabMusicFragment extends Fragment implements ClickCallback {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void initStarredArtistsView() {
|
private void initStarredArtistsView() {
|
||||||
|
if (homeViewModel.checkHomeSectorVisibility(Constants.HOME_SECTOR_STARRED_ARTISTS)) return;
|
||||||
|
|
||||||
bind.starredArtistsRecyclerView.setHasFixedSize(true);
|
bind.starredArtistsRecyclerView.setHasFixedSize(true);
|
||||||
|
|
||||||
starredArtistAdapter = new ArtistHorizontalAdapter(this);
|
starredArtistAdapter = new ArtistHorizontalAdapter(this);
|
||||||
bind.starredArtistsRecyclerView.setAdapter(starredArtistAdapter);
|
bind.starredArtistsRecyclerView.setAdapter(starredArtistAdapter);
|
||||||
homeViewModel.getStarredArtists(getViewLifecycleOwner()).observe(getViewLifecycleOwner(), artists -> {
|
homeViewModel.getStarredArtists(getViewLifecycleOwner()).observe(getViewLifecycleOwner(), artists -> {
|
||||||
if (artists == null) {
|
if (artists == null) {
|
||||||
if (bind != null)
|
|
||||||
bind.starredArtistsPlaceholder.placeholder.setVisibility(View.VISIBLE);
|
|
||||||
if (bind != null) bind.starredArtistsSector.setVisibility(View.GONE);
|
if (bind != null) bind.starredArtistsSector.setVisibility(View.GONE);
|
||||||
} else {
|
} else {
|
||||||
if (bind != null)
|
|
||||||
bind.starredArtistsPlaceholder.placeholder.setVisibility(View.GONE);
|
|
||||||
if (bind != null)
|
if (bind != null)
|
||||||
bind.starredArtistsSector.setVisibility(!artists.isEmpty() ? View.VISIBLE : View.GONE);
|
bind.starredArtistsSector.setVisibility(!artists.isEmpty() ? View.VISIBLE : View.GONE);
|
||||||
if (bind != null)
|
if (bind != null)
|
||||||
@@ -539,18 +551,16 @@ public class HomeTabMusicFragment extends Fragment implements ClickCallback {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void initNewReleasesView() {
|
private void initNewReleasesView() {
|
||||||
|
if (homeViewModel.checkHomeSectorVisibility(Constants.HOME_SECTOR_NEW_RELEASES)) return;
|
||||||
|
|
||||||
bind.newReleasesRecyclerView.setHasFixedSize(true);
|
bind.newReleasesRecyclerView.setHasFixedSize(true);
|
||||||
|
|
||||||
newReleasesAlbumAdapter = new AlbumHorizontalAdapter(this, false);
|
newReleasesAlbumAdapter = new AlbumHorizontalAdapter(this, false);
|
||||||
bind.newReleasesRecyclerView.setAdapter(newReleasesAlbumAdapter);
|
bind.newReleasesRecyclerView.setAdapter(newReleasesAlbumAdapter);
|
||||||
homeViewModel.getRecentlyReleasedAlbums(getViewLifecycleOwner()).observe(getViewLifecycleOwner(), albums -> {
|
homeViewModel.getRecentlyReleasedAlbums(getViewLifecycleOwner()).observe(getViewLifecycleOwner(), albums -> {
|
||||||
if (albums == null) {
|
if (albums == null) {
|
||||||
if (bind != null)
|
|
||||||
bind.homeNewReleasesPlaceholder.placeholder.setVisibility(View.VISIBLE);
|
|
||||||
if (bind != null) bind.homeNewReleasesSector.setVisibility(View.GONE);
|
if (bind != null) bind.homeNewReleasesSector.setVisibility(View.GONE);
|
||||||
} else {
|
} else {
|
||||||
if (bind != null)
|
|
||||||
bind.homeNewReleasesPlaceholder.placeholder.setVisibility(View.GONE);
|
|
||||||
if (bind != null)
|
if (bind != null)
|
||||||
bind.homeNewReleasesSector.setVisibility(!albums.isEmpty() ? View.VISIBLE : View.GONE);
|
bind.homeNewReleasesSector.setVisibility(!albums.isEmpty() ? View.VISIBLE : View.GONE);
|
||||||
if (bind != null)
|
if (bind != null)
|
||||||
@@ -574,6 +584,8 @@ public class HomeTabMusicFragment extends Fragment implements ClickCallback {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void initYearSongView() {
|
private void initYearSongView() {
|
||||||
|
if (homeViewModel.checkHomeSectorVisibility(Constants.HOME_SECTOR_FLASHBACK)) return;
|
||||||
|
|
||||||
bind.yearsRecyclerView.setLayoutManager(new LinearLayoutManager(requireContext(), LinearLayoutManager.HORIZONTAL, false));
|
bind.yearsRecyclerView.setLayoutManager(new LinearLayoutManager(requireContext(), LinearLayoutManager.HORIZONTAL, false));
|
||||||
bind.yearsRecyclerView.setHasFixedSize(true);
|
bind.yearsRecyclerView.setHasFixedSize(true);
|
||||||
|
|
||||||
@@ -581,12 +593,8 @@ public class HomeTabMusicFragment extends Fragment implements ClickCallback {
|
|||||||
bind.yearsRecyclerView.setAdapter(yearAdapter);
|
bind.yearsRecyclerView.setAdapter(yearAdapter);
|
||||||
homeViewModel.getYearList(getViewLifecycleOwner()).observe(getViewLifecycleOwner(), years -> {
|
homeViewModel.getYearList(getViewLifecycleOwner()).observe(getViewLifecycleOwner(), years -> {
|
||||||
if (years == null) {
|
if (years == null) {
|
||||||
if (bind != null)
|
|
||||||
bind.homeFlashbackPlaceholder.placeholder.setVisibility(View.VISIBLE);
|
|
||||||
if (bind != null) bind.homeFlashbackSector.setVisibility(View.GONE);
|
if (bind != null) bind.homeFlashbackSector.setVisibility(View.GONE);
|
||||||
} else {
|
} else {
|
||||||
if (bind != null)
|
|
||||||
bind.homeFlashbackPlaceholder.placeholder.setVisibility(View.GONE);
|
|
||||||
if (bind != null)
|
if (bind != null)
|
||||||
bind.homeFlashbackSector.setVisibility(!years.isEmpty() ? View.VISIBLE : View.GONE);
|
bind.homeFlashbackSector.setVisibility(!years.isEmpty() ? View.VISIBLE : View.GONE);
|
||||||
|
|
||||||
@@ -599,6 +607,8 @@ public class HomeTabMusicFragment extends Fragment implements ClickCallback {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void initMostPlayedAlbumView() {
|
private void initMostPlayedAlbumView() {
|
||||||
|
if (homeViewModel.checkHomeSectorVisibility(Constants.HOME_SECTOR_MOST_PLAYED)) return;
|
||||||
|
|
||||||
bind.mostPlayedAlbumsRecyclerView.setLayoutManager(new LinearLayoutManager(requireContext(), LinearLayoutManager.HORIZONTAL, false));
|
bind.mostPlayedAlbumsRecyclerView.setLayoutManager(new LinearLayoutManager(requireContext(), LinearLayoutManager.HORIZONTAL, false));
|
||||||
bind.mostPlayedAlbumsRecyclerView.setHasFixedSize(true);
|
bind.mostPlayedAlbumsRecyclerView.setHasFixedSize(true);
|
||||||
|
|
||||||
@@ -606,15 +616,10 @@ public class HomeTabMusicFragment extends Fragment implements ClickCallback {
|
|||||||
bind.mostPlayedAlbumsRecyclerView.setAdapter(mostPlayedAlbumAdapter);
|
bind.mostPlayedAlbumsRecyclerView.setAdapter(mostPlayedAlbumAdapter);
|
||||||
homeViewModel.getMostPlayedAlbums(getViewLifecycleOwner()).observe(getViewLifecycleOwner(), albums -> {
|
homeViewModel.getMostPlayedAlbums(getViewLifecycleOwner()).observe(getViewLifecycleOwner(), albums -> {
|
||||||
if (albums == null) {
|
if (albums == null) {
|
||||||
if (bind != null)
|
|
||||||
bind.homeMostPlayedAlbumsPlaceholder.placeholder.setVisibility(View.VISIBLE);
|
|
||||||
if (bind != null) bind.homeMostPlayedAlbumsSector.setVisibility(View.GONE);
|
if (bind != null) bind.homeMostPlayedAlbumsSector.setVisibility(View.GONE);
|
||||||
} else {
|
} else {
|
||||||
if (bind != null)
|
|
||||||
bind.homeMostPlayedAlbumsPlaceholder.placeholder.setVisibility(View.GONE);
|
|
||||||
if (bind != null)
|
if (bind != null)
|
||||||
bind.homeMostPlayedAlbumsSector.setVisibility(!albums.isEmpty() ? View.VISIBLE : View.GONE);
|
bind.homeMostPlayedAlbumsSector.setVisibility(!albums.isEmpty() ? View.VISIBLE : View.GONE);
|
||||||
// if (albums.size() < 5) reorder();
|
|
||||||
|
|
||||||
mostPlayedAlbumAdapter.setItems(albums);
|
mostPlayedAlbumAdapter.setItems(albums);
|
||||||
}
|
}
|
||||||
@@ -625,6 +630,8 @@ public class HomeTabMusicFragment extends Fragment implements ClickCallback {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void initRecentPlayedAlbumView() {
|
private void initRecentPlayedAlbumView() {
|
||||||
|
if (homeViewModel.checkHomeSectorVisibility(Constants.HOME_SECTOR_LAST_PLAYED)) return;
|
||||||
|
|
||||||
bind.recentlyPlayedAlbumsRecyclerView.setLayoutManager(new LinearLayoutManager(requireContext(), LinearLayoutManager.HORIZONTAL, false));
|
bind.recentlyPlayedAlbumsRecyclerView.setLayoutManager(new LinearLayoutManager(requireContext(), LinearLayoutManager.HORIZONTAL, false));
|
||||||
bind.recentlyPlayedAlbumsRecyclerView.setHasFixedSize(true);
|
bind.recentlyPlayedAlbumsRecyclerView.setHasFixedSize(true);
|
||||||
|
|
||||||
@@ -632,12 +639,8 @@ public class HomeTabMusicFragment extends Fragment implements ClickCallback {
|
|||||||
bind.recentlyPlayedAlbumsRecyclerView.setAdapter(recentlyPlayedAlbumAdapter);
|
bind.recentlyPlayedAlbumsRecyclerView.setAdapter(recentlyPlayedAlbumAdapter);
|
||||||
homeViewModel.getRecentlyPlayedAlbumList(getViewLifecycleOwner()).observe(getViewLifecycleOwner(), albums -> {
|
homeViewModel.getRecentlyPlayedAlbumList(getViewLifecycleOwner()).observe(getViewLifecycleOwner(), albums -> {
|
||||||
if (albums == null) {
|
if (albums == null) {
|
||||||
if (bind != null)
|
|
||||||
bind.homeRecentlyPlayedAlbumsPlaceholder.placeholder.setVisibility(View.VISIBLE);
|
|
||||||
if (bind != null) bind.homeRecentlyPlayedAlbumsSector.setVisibility(View.GONE);
|
if (bind != null) bind.homeRecentlyPlayedAlbumsSector.setVisibility(View.GONE);
|
||||||
} else {
|
} else {
|
||||||
if (bind != null)
|
|
||||||
bind.homeRecentlyPlayedAlbumsPlaceholder.placeholder.setVisibility(View.GONE);
|
|
||||||
if (bind != null)
|
if (bind != null)
|
||||||
bind.homeRecentlyPlayedAlbumsSector.setVisibility(!albums.isEmpty() ? View.VISIBLE : View.GONE);
|
bind.homeRecentlyPlayedAlbumsSector.setVisibility(!albums.isEmpty() ? View.VISIBLE : View.GONE);
|
||||||
|
|
||||||
@@ -650,6 +653,8 @@ public class HomeTabMusicFragment extends Fragment implements ClickCallback {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void initRecentAddedAlbumView() {
|
private void initRecentAddedAlbumView() {
|
||||||
|
if (homeViewModel.checkHomeSectorVisibility(Constants.HOME_SECTOR_RECENTLY_ADDED)) return;
|
||||||
|
|
||||||
bind.recentlyAddedAlbumsRecyclerView.setLayoutManager(new LinearLayoutManager(requireContext(), LinearLayoutManager.HORIZONTAL, false));
|
bind.recentlyAddedAlbumsRecyclerView.setLayoutManager(new LinearLayoutManager(requireContext(), LinearLayoutManager.HORIZONTAL, false));
|
||||||
bind.recentlyAddedAlbumsRecyclerView.setHasFixedSize(true);
|
bind.recentlyAddedAlbumsRecyclerView.setHasFixedSize(true);
|
||||||
|
|
||||||
@@ -657,12 +662,8 @@ public class HomeTabMusicFragment extends Fragment implements ClickCallback {
|
|||||||
bind.recentlyAddedAlbumsRecyclerView.setAdapter(recentlyAddedAlbumAdapter);
|
bind.recentlyAddedAlbumsRecyclerView.setAdapter(recentlyAddedAlbumAdapter);
|
||||||
homeViewModel.getMostRecentlyAddedAlbums(getViewLifecycleOwner()).observe(getViewLifecycleOwner(), albums -> {
|
homeViewModel.getMostRecentlyAddedAlbums(getViewLifecycleOwner()).observe(getViewLifecycleOwner(), albums -> {
|
||||||
if (albums == null) {
|
if (albums == null) {
|
||||||
if (bind != null)
|
|
||||||
bind.homeRecentlyAddedAlbumsPlaceholder.placeholder.setVisibility(View.VISIBLE);
|
|
||||||
if (bind != null) bind.homeRecentlyAddedAlbumsSector.setVisibility(View.GONE);
|
if (bind != null) bind.homeRecentlyAddedAlbumsSector.setVisibility(View.GONE);
|
||||||
} else {
|
} else {
|
||||||
if (bind != null)
|
|
||||||
bind.homeRecentlyAddedAlbumsPlaceholder.placeholder.setVisibility(View.GONE);
|
|
||||||
if (bind != null)
|
if (bind != null)
|
||||||
bind.homeRecentlyAddedAlbumsSector.setVisibility(!albums.isEmpty() ? View.VISIBLE : View.GONE);
|
bind.homeRecentlyAddedAlbumsSector.setVisibility(!albums.isEmpty() ? View.VISIBLE : View.GONE);
|
||||||
|
|
||||||
@@ -674,7 +675,29 @@ public class HomeTabMusicFragment extends Fragment implements ClickCallback {
|
|||||||
recentAddedAlbumSnapHelper.attachToRecyclerView(bind.recentlyAddedAlbumsRecyclerView);
|
recentAddedAlbumSnapHelper.attachToRecyclerView(bind.recentlyAddedAlbumsRecyclerView);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void initPinnedPlaylistsView() {
|
||||||
|
if (homeViewModel.checkHomeSectorVisibility(Constants.HOME_SECTOR_PINNED_PLAYLISTS)) return;
|
||||||
|
|
||||||
|
bind.pinnedPlaylistsRecyclerView.setLayoutManager(new LinearLayoutManager(requireContext()));
|
||||||
|
bind.pinnedPlaylistsRecyclerView.setHasFixedSize(true);
|
||||||
|
|
||||||
|
playlistHorizontalAdapter = new PlaylistHorizontalAdapter(this);
|
||||||
|
bind.pinnedPlaylistsRecyclerView.setAdapter(playlistHorizontalAdapter);
|
||||||
|
homeViewModel.getPinnedPlaylists(getViewLifecycleOwner()).observe(getViewLifecycleOwner(), playlists -> {
|
||||||
|
if (playlists == null) {
|
||||||
|
if (bind != null) bind.pinnedPlaylistsSector.setVisibility(View.GONE);
|
||||||
|
} else {
|
||||||
|
if (bind != null)
|
||||||
|
bind.pinnedPlaylistsSector.setVisibility(!playlists.isEmpty() ? View.VISIBLE : View.GONE);
|
||||||
|
|
||||||
|
playlistHorizontalAdapter.setItems(playlists);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private void initSharesView() {
|
private void initSharesView() {
|
||||||
|
if (homeViewModel.checkHomeSectorVisibility(Constants.HOME_SECTOR_SHARED)) return;
|
||||||
|
|
||||||
bind.sharesRecyclerView.setHasFixedSize(true);
|
bind.sharesRecyclerView.setHasFixedSize(true);
|
||||||
|
|
||||||
shareHorizontalAdapter = new ShareHorizontalAdapter(this);
|
shareHorizontalAdapter = new ShareHorizontalAdapter(this);
|
||||||
@@ -682,11 +705,8 @@ public class HomeTabMusicFragment extends Fragment implements ClickCallback {
|
|||||||
if (Preferences.isSharingEnabled()) {
|
if (Preferences.isSharingEnabled()) {
|
||||||
homeViewModel.getShares(getViewLifecycleOwner()).observe(getViewLifecycleOwner(), shares -> {
|
homeViewModel.getShares(getViewLifecycleOwner()).observe(getViewLifecycleOwner(), shares -> {
|
||||||
if (shares == null) {
|
if (shares == null) {
|
||||||
if (bind != null)
|
|
||||||
bind.sharesPlaceholder.placeholder.setVisibility(View.VISIBLE);
|
|
||||||
if (bind != null) bind.sharesSector.setVisibility(View.GONE);
|
if (bind != null) bind.sharesSector.setVisibility(View.GONE);
|
||||||
} else {
|
} else {
|
||||||
if (bind != null) bind.sharesPlaceholder.placeholder.setVisibility(View.GONE);
|
|
||||||
if (bind != null)
|
if (bind != null)
|
||||||
bind.sharesSector.setVisibility(!shares.isEmpty() ? View.VISIBLE : View.GONE);
|
bind.sharesSector.setVisibility(!shares.isEmpty() ? View.VISIBLE : View.GONE);
|
||||||
if (bind != null)
|
if (bind != null)
|
||||||
@@ -710,6 +730,19 @@ public class HomeTabMusicFragment extends Fragment implements ClickCallback {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void initHomeReorganizer() {
|
||||||
|
final Handler handler = new Handler();
|
||||||
|
final Runnable runnable = () -> {
|
||||||
|
if (bind != null) bind.homeSectorRearrangementButton.setVisibility(View.VISIBLE);
|
||||||
|
};
|
||||||
|
handler.postDelayed(runnable, 5000);
|
||||||
|
|
||||||
|
bind.homeSectorRearrangementButton.setOnClickListener(v -> {
|
||||||
|
HomeRearrangementDialog dialog = new HomeRearrangementDialog();
|
||||||
|
dialog.show(requireActivity().getSupportFragmentManager(), null);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private void refreshSharesView() {
|
private void refreshSharesView() {
|
||||||
final Handler handler = new Handler();
|
final Handler handler = new Handler();
|
||||||
final Runnable runnable = () -> {
|
final Runnable runnable = () -> {
|
||||||
@@ -735,6 +768,102 @@ public class HomeTabMusicFragment extends Fragment implements ClickCallback {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void reorder() {
|
||||||
|
if (bind != null && homeViewModel.getHomeSectorList() != null) {
|
||||||
|
bind.homeLinearLayoutContainer.removeAllViews();
|
||||||
|
|
||||||
|
for (HomeSector sector : homeViewModel.getHomeSectorList()) {
|
||||||
|
if (!sector.isVisible()) continue;
|
||||||
|
|
||||||
|
switch (sector.getId()) {
|
||||||
|
case Constants.HOME_SECTOR_DISCOVERY:
|
||||||
|
bind.homeLinearLayoutContainer.addView(bind.homeDiscoverSector);
|
||||||
|
break;
|
||||||
|
case Constants.HOME_SECTOR_MADE_FOR_YOU:
|
||||||
|
bind.homeLinearLayoutContainer.addView(bind.homeSimilarTracksSector);
|
||||||
|
break;
|
||||||
|
case Constants.HOME_SECTOR_BEST_OF:
|
||||||
|
bind.homeLinearLayoutContainer.addView(bind.homeBestOfArtistSector);
|
||||||
|
break;
|
||||||
|
case Constants.HOME_SECTOR_RADIO_STATION:
|
||||||
|
bind.homeLinearLayoutContainer.addView(bind.homeRadioArtistSector);
|
||||||
|
break;
|
||||||
|
case Constants.HOME_SECTOR_TOP_SONGS:
|
||||||
|
bind.homeLinearLayoutContainer.addView(bind.homeGridTracksSector);
|
||||||
|
break;
|
||||||
|
case Constants.HOME_SECTOR_STARRED_TRACKS:
|
||||||
|
bind.homeLinearLayoutContainer.addView(bind.starredTracksSector);
|
||||||
|
break;
|
||||||
|
case Constants.HOME_SECTOR_STARRED_ALBUMS:
|
||||||
|
bind.homeLinearLayoutContainer.addView(bind.starredAlbumsSector);
|
||||||
|
break;
|
||||||
|
case Constants.HOME_SECTOR_STARRED_ARTISTS:
|
||||||
|
bind.homeLinearLayoutContainer.addView(bind.starredArtistsSector);
|
||||||
|
break;
|
||||||
|
case Constants.HOME_SECTOR_NEW_RELEASES:
|
||||||
|
bind.homeLinearLayoutContainer.addView(bind.homeNewReleasesSector);
|
||||||
|
break;
|
||||||
|
case Constants.HOME_SECTOR_FLASHBACK:
|
||||||
|
bind.homeLinearLayoutContainer.addView(bind.homeFlashbackSector);
|
||||||
|
break;
|
||||||
|
case Constants.HOME_SECTOR_MOST_PLAYED:
|
||||||
|
bind.homeLinearLayoutContainer.addView(bind.homeMostPlayedAlbumsSector);
|
||||||
|
break;
|
||||||
|
case Constants.HOME_SECTOR_LAST_PLAYED:
|
||||||
|
bind.homeLinearLayoutContainer.addView(bind.homeRecentlyPlayedAlbumsSector);
|
||||||
|
break;
|
||||||
|
case Constants.HOME_SECTOR_RECENTLY_ADDED:
|
||||||
|
bind.homeLinearLayoutContainer.addView(bind.homeRecentlyAddedAlbumsSector);
|
||||||
|
break;
|
||||||
|
case Constants.HOME_SECTOR_PINNED_PLAYLISTS:
|
||||||
|
bind.homeLinearLayoutContainer.addView(bind.pinnedPlaylistsSector);
|
||||||
|
break;
|
||||||
|
case Constants.HOME_SECTOR_SHARED:
|
||||||
|
bind.homeLinearLayoutContainer.addView(bind.sharesSector);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bind.homeLinearLayoutContainer.addView(bind.homeSectorRearrangementButton);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showPopupMenu(View view, int menuResource) {
|
||||||
|
PopupMenu popup = new PopupMenu(requireContext(), view);
|
||||||
|
popup.getMenuInflater().inflate(menuResource, popup.getMenu());
|
||||||
|
|
||||||
|
popup.setOnMenuItemClickListener(menuItem -> {
|
||||||
|
if (menuItem.getItemId() == R.id.menu_last_week_name) {
|
||||||
|
homeViewModel.changeChronologyPeriod(getViewLifecycleOwner(), 0);
|
||||||
|
bind.gridTracksPreTextView.setText(getString(R.string.home_title_last_week));
|
||||||
|
return true;
|
||||||
|
} else if (menuItem.getItemId() == R.id.menu_last_month_name) {
|
||||||
|
homeViewModel.changeChronologyPeriod(getViewLifecycleOwner(), 1);
|
||||||
|
bind.gridTracksPreTextView.setText(getString(R.string.home_title_last_month));
|
||||||
|
return true;
|
||||||
|
} else if (menuItem.getItemId() == R.id.menu_last_year_name) {
|
||||||
|
homeViewModel.changeChronologyPeriod(getViewLifecycleOwner(), 2);
|
||||||
|
bind.gridTracksPreTextView.setText(getString(R.string.home_title_last_year));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
popup.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void refreshPlaylistView() {
|
||||||
|
final Handler handler = new Handler();
|
||||||
|
|
||||||
|
final Runnable runnable = () -> {
|
||||||
|
if (getView() != null && bind != null && homeViewModel != null)
|
||||||
|
homeViewModel.getPinnedPlaylists(getViewLifecycleOwner());
|
||||||
|
};
|
||||||
|
|
||||||
|
handler.postDelayed(runnable, 100);
|
||||||
|
}
|
||||||
|
|
||||||
private void initializeMediaBrowser() {
|
private void initializeMediaBrowser() {
|
||||||
mediaBrowserListenableFuture = new MediaBrowser.Builder(requireContext(), new SessionToken(requireContext(), new ComponentName(requireContext(), MediaService.class))).buildAsync();
|
mediaBrowserListenableFuture = new MediaBrowser.Builder(requireContext(), new SessionToken(requireContext(), new ComponentName(requireContext(), MediaService.class))).buildAsync();
|
||||||
}
|
}
|
||||||
@@ -753,7 +882,7 @@ public class HomeTabMusicFragment extends Fragment implements ClickCallback {
|
|||||||
homeViewModel.getMediaInstantMix(getViewLifecycleOwner(), bundle.getParcelable(Constants.TRACK_OBJECT)).observe(getViewLifecycleOwner(), songs -> {
|
homeViewModel.getMediaInstantMix(getViewLifecycleOwner(), bundle.getParcelable(Constants.TRACK_OBJECT)).observe(getViewLifecycleOwner(), songs -> {
|
||||||
MusicUtil.ratingFilter(songs);
|
MusicUtil.ratingFilter(songs);
|
||||||
|
|
||||||
if (songs != null && songs.size() > 0) {
|
if (songs != null && !songs.isEmpty()) {
|
||||||
MediaManager.enqueue(mediaBrowserListenableFuture, songs, true);
|
MediaManager.enqueue(mediaBrowserListenableFuture, songs, true);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -794,7 +923,7 @@ public class HomeTabMusicFragment extends Fragment implements ClickCallback {
|
|||||||
homeViewModel.getArtistInstantMix(getViewLifecycleOwner(), bundle.getParcelable(Constants.ARTIST_OBJECT)).observe(getViewLifecycleOwner(), songs -> {
|
homeViewModel.getArtistInstantMix(getViewLifecycleOwner(), bundle.getParcelable(Constants.ARTIST_OBJECT)).observe(getViewLifecycleOwner(), songs -> {
|
||||||
MusicUtil.ratingFilter(songs);
|
MusicUtil.ratingFilter(songs);
|
||||||
|
|
||||||
if (songs.size() > 0) {
|
if (!songs.isEmpty()) {
|
||||||
MediaManager.startQueue(mediaBrowserListenableFuture, songs, 0);
|
MediaManager.startQueue(mediaBrowserListenableFuture, songs, 0);
|
||||||
activity.setBottomSheetInPeek(true);
|
activity.setBottomSheetInPeek(true);
|
||||||
}
|
}
|
||||||
@@ -805,7 +934,7 @@ public class HomeTabMusicFragment extends Fragment implements ClickCallback {
|
|||||||
homeViewModel.getArtistBestOf(getViewLifecycleOwner(), bundle.getParcelable(Constants.ARTIST_OBJECT)).observe(getViewLifecycleOwner(), songs -> {
|
homeViewModel.getArtistBestOf(getViewLifecycleOwner(), bundle.getParcelable(Constants.ARTIST_OBJECT)).observe(getViewLifecycleOwner(), songs -> {
|
||||||
MusicUtil.ratingFilter(songs);
|
MusicUtil.ratingFilter(songs);
|
||||||
|
|
||||||
if (songs.size() > 0) {
|
if (!songs.isEmpty()) {
|
||||||
MediaManager.startQueue(mediaBrowserListenableFuture, songs, 0);
|
MediaManager.startQueue(mediaBrowserListenableFuture, songs, 0);
|
||||||
activity.setBottomSheetInPeek(true);
|
activity.setBottomSheetInPeek(true);
|
||||||
}
|
}
|
||||||
@@ -833,6 +962,24 @@ public class HomeTabMusicFragment extends Fragment implements ClickCallback {
|
|||||||
startActivity(intent);
|
startActivity(intent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPlaylistClick(Bundle bundle) {
|
||||||
|
Navigation.findNavController(requireView()).navigate(R.id.playlistPageFragment, bundle);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPlaylistLongClick(Bundle bundle) {
|
||||||
|
PlaylistEditorDialog dialog = new PlaylistEditorDialog(new PlaylistCallback() {
|
||||||
|
@Override
|
||||||
|
public void onDismiss() {
|
||||||
|
refreshPlaylistView();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
dialog.setArguments(bundle);
|
||||||
|
dialog.show(activity.getSupportFragmentManager(), null);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onShareLongClick(Bundle bundle) {
|
public void onShareLongClick(Bundle bundle) {
|
||||||
Navigation.findNavController(requireView()).navigate(R.id.shareBottomSheetDialog, bundle);
|
Navigation.findNavController(requireView()).navigate(R.id.shareBottomSheetDialog, bundle);
|
||||||
|
|||||||
@@ -142,12 +142,8 @@ public class LibraryFragment extends Fragment implements ClickCallback {
|
|||||||
bind.musicFolderRecyclerView.setAdapter(musicFolderAdapter);
|
bind.musicFolderRecyclerView.setAdapter(musicFolderAdapter);
|
||||||
libraryViewModel.getMusicFolders(getViewLifecycleOwner()).observe(getViewLifecycleOwner(), musicFolders -> {
|
libraryViewModel.getMusicFolders(getViewLifecycleOwner()).observe(getViewLifecycleOwner(), musicFolders -> {
|
||||||
if (musicFolders == null) {
|
if (musicFolders == null) {
|
||||||
if (bind != null)
|
|
||||||
bind.libraryMusicFolderPlaceholder.placeholder.setVisibility(View.VISIBLE);
|
|
||||||
if (bind != null) bind.libraryMusicFolderSector.setVisibility(View.GONE);
|
if (bind != null) bind.libraryMusicFolderSector.setVisibility(View.GONE);
|
||||||
} else {
|
} else {
|
||||||
if (bind != null)
|
|
||||||
bind.libraryMusicFolderPlaceholder.placeholder.setVisibility(View.GONE);
|
|
||||||
if (bind != null)
|
if (bind != null)
|
||||||
bind.libraryMusicFolderSector.setVisibility(!musicFolders.isEmpty() ? View.VISIBLE : View.GONE);
|
bind.libraryMusicFolderSector.setVisibility(!musicFolders.isEmpty() ? View.VISIBLE : View.GONE);
|
||||||
|
|
||||||
@@ -164,11 +160,8 @@ public class LibraryFragment extends Fragment implements ClickCallback {
|
|||||||
bind.albumRecyclerView.setAdapter(albumAdapter);
|
bind.albumRecyclerView.setAdapter(albumAdapter);
|
||||||
libraryViewModel.getAlbumSample(getViewLifecycleOwner()).observe(getViewLifecycleOwner(), albums -> {
|
libraryViewModel.getAlbumSample(getViewLifecycleOwner()).observe(getViewLifecycleOwner(), albums -> {
|
||||||
if (albums == null) {
|
if (albums == null) {
|
||||||
if (bind != null)
|
|
||||||
bind.libraryAlbumPlaceholder.placeholder.setVisibility(View.VISIBLE);
|
|
||||||
if (bind != null) bind.libraryAlbumSector.setVisibility(View.GONE);
|
if (bind != null) bind.libraryAlbumSector.setVisibility(View.GONE);
|
||||||
} else {
|
} else {
|
||||||
if (bind != null) bind.libraryAlbumPlaceholder.placeholder.setVisibility(View.GONE);
|
|
||||||
if (bind != null)
|
if (bind != null)
|
||||||
bind.libraryAlbumSector.setVisibility(!albums.isEmpty() ? View.VISIBLE : View.GONE);
|
bind.libraryAlbumSector.setVisibility(!albums.isEmpty() ? View.VISIBLE : View.GONE);
|
||||||
|
|
||||||
@@ -188,12 +181,8 @@ public class LibraryFragment extends Fragment implements ClickCallback {
|
|||||||
bind.artistRecyclerView.setAdapter(artistAdapter);
|
bind.artistRecyclerView.setAdapter(artistAdapter);
|
||||||
libraryViewModel.getArtistSample(getViewLifecycleOwner()).observe(getViewLifecycleOwner(), artists -> {
|
libraryViewModel.getArtistSample(getViewLifecycleOwner()).observe(getViewLifecycleOwner(), artists -> {
|
||||||
if (artists == null) {
|
if (artists == null) {
|
||||||
if (bind != null)
|
|
||||||
bind.libraryArtistPlaceholder.placeholder.setVisibility(View.VISIBLE);
|
|
||||||
if (bind != null) bind.libraryArtistSector.setVisibility(View.GONE);
|
if (bind != null) bind.libraryArtistSector.setVisibility(View.GONE);
|
||||||
} else {
|
} else {
|
||||||
if (bind != null)
|
|
||||||
bind.libraryArtistPlaceholder.placeholder.setVisibility(View.GONE);
|
|
||||||
if (bind != null)
|
if (bind != null)
|
||||||
bind.libraryArtistSector.setVisibility(!artists.isEmpty() ? View.VISIBLE : View.GONE);
|
bind.libraryArtistSector.setVisibility(!artists.isEmpty() ? View.VISIBLE : View.GONE);
|
||||||
|
|
||||||
@@ -214,11 +203,8 @@ public class LibraryFragment extends Fragment implements ClickCallback {
|
|||||||
|
|
||||||
libraryViewModel.getGenreSample(getViewLifecycleOwner()).observe(getViewLifecycleOwner(), genres -> {
|
libraryViewModel.getGenreSample(getViewLifecycleOwner()).observe(getViewLifecycleOwner(), genres -> {
|
||||||
if (genres == null) {
|
if (genres == null) {
|
||||||
if (bind != null)
|
|
||||||
bind.libraryGenrePlaceholder.placeholder.setVisibility(View.VISIBLE);
|
|
||||||
if (bind != null) bind.libraryGenresSector.setVisibility(View.GONE);
|
if (bind != null) bind.libraryGenresSector.setVisibility(View.GONE);
|
||||||
} else {
|
} else {
|
||||||
if (bind != null) bind.libraryGenrePlaceholder.placeholder.setVisibility(View.GONE);
|
|
||||||
if (bind != null)
|
if (bind != null)
|
||||||
bind.libraryGenresSector.setVisibility(!genres.isEmpty() ? View.VISIBLE : View.GONE);
|
bind.libraryGenresSector.setVisibility(!genres.isEmpty() ? View.VISIBLE : View.GONE);
|
||||||
|
|
||||||
@@ -238,12 +224,8 @@ public class LibraryFragment extends Fragment implements ClickCallback {
|
|||||||
bind.playlistRecyclerView.setAdapter(playlistHorizontalAdapter);
|
bind.playlistRecyclerView.setAdapter(playlistHorizontalAdapter);
|
||||||
libraryViewModel.getPlaylistSample(getViewLifecycleOwner()).observe(getViewLifecycleOwner(), playlists -> {
|
libraryViewModel.getPlaylistSample(getViewLifecycleOwner()).observe(getViewLifecycleOwner(), playlists -> {
|
||||||
if (playlists == null) {
|
if (playlists == null) {
|
||||||
if (bind != null)
|
|
||||||
bind.libraryPlaylistPlaceholder.placeholder.setVisibility(View.VISIBLE);
|
|
||||||
if (bind != null) bind.libraryPlaylistSector.setVisibility(View.GONE);
|
if (bind != null) bind.libraryPlaylistSector.setVisibility(View.GONE);
|
||||||
} else {
|
} else {
|
||||||
if (bind != null)
|
|
||||||
bind.libraryPlaylistPlaceholder.placeholder.setVisibility(View.GONE);
|
|
||||||
if (bind != null)
|
if (bind != null)
|
||||||
bind.libraryPlaylistSector.setVisibility(!playlists.isEmpty() ? View.VISIBLE : View.GONE);
|
bind.libraryPlaylistSector.setVisibility(!playlists.isEmpty() ? View.VISIBLE : View.GONE);
|
||||||
|
|
||||||
|
|||||||
@@ -92,7 +92,7 @@ public class LoginFragment extends Fragment implements ClickCallback {
|
|||||||
serverAdapter = new ServerAdapter(this);
|
serverAdapter = new ServerAdapter(this);
|
||||||
bind.serverListRecyclerView.setAdapter(serverAdapter);
|
bind.serverListRecyclerView.setAdapter(serverAdapter);
|
||||||
loginViewModel.getServerList().observe(getViewLifecycleOwner(), servers -> {
|
loginViewModel.getServerList().observe(getViewLifecycleOwner(), servers -> {
|
||||||
if (servers.size() > 0) {
|
if (!servers.isEmpty()) {
|
||||||
if (bind != null) bind.noServerAddedTextView.setVisibility(View.GONE);
|
if (bind != null) bind.noServerAddedTextView.setVisibility(View.GONE);
|
||||||
if (bind != null) bind.serverListRecyclerView.setVisibility(View.VISIBLE);
|
if (bind != null) bind.serverListRecyclerView.setVisibility(View.VISIBLE);
|
||||||
serverAdapter.setItems(servers);
|
serverAdapter.setItems(servers);
|
||||||
@@ -117,12 +117,13 @@ public class LoginFragment extends Fragment implements ClickCallback {
|
|||||||
@Override
|
@Override
|
||||||
public void onServerClick(Bundle bundle) {
|
public void onServerClick(Bundle bundle) {
|
||||||
Server server = bundle.getParcelable("server_object");
|
Server server = bundle.getParcelable("server_object");
|
||||||
saveServerPreference(server.getServerId(), server.getAddress(), server.getUsername(), server.getPassword(), server.isLowSecurity());
|
saveServerPreference(server.getServerId(), server.getAddress(), server.getLocalAddress(), server.getUsername(), server.getPassword(), server.isLowSecurity());
|
||||||
|
|
||||||
SystemRepository systemRepository = new SystemRepository();
|
SystemRepository systemRepository = new SystemRepository();
|
||||||
systemRepository.checkUserCredential(new SystemCallback() {
|
systemRepository.checkUserCredential(new SystemCallback() {
|
||||||
@Override
|
@Override
|
||||||
public void onError(Exception exception) {
|
public void onError(Exception exception) {
|
||||||
|
Preferences.switchInUseServerAddress();
|
||||||
resetServerPreference();
|
resetServerPreference();
|
||||||
Toast.makeText(requireContext(), exception.getMessage(), Toast.LENGTH_SHORT).show();
|
Toast.makeText(requireContext(), exception.getMessage(), Toast.LENGTH_SHORT).show();
|
||||||
}
|
}
|
||||||
@@ -141,9 +142,10 @@ public class LoginFragment extends Fragment implements ClickCallback {
|
|||||||
dialog.show(activity.getSupportFragmentManager(), null);
|
dialog.show(activity.getSupportFragmentManager(), null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void saveServerPreference(String serverId, String server, String user, String password, boolean isLowSecurity) {
|
private void saveServerPreference(String serverId, String server, String localAddress, String user, String password, boolean isLowSecurity) {
|
||||||
Preferences.setServerId(serverId);
|
Preferences.setServerId(serverId);
|
||||||
Preferences.setServer(server);
|
Preferences.setServer(server);
|
||||||
|
Preferences.setLocalAddress(localAddress);
|
||||||
Preferences.setUser(user);
|
Preferences.setUser(user);
|
||||||
Preferences.setPassword(password);
|
Preferences.setPassword(password);
|
||||||
Preferences.setLowSecurity(isLowSecurity);
|
Preferences.setLowSecurity(isLowSecurity);
|
||||||
|
|||||||
@@ -156,11 +156,12 @@ public class PlayerBottomSheetFragment extends Fragment {
|
|||||||
private void setMetadata(MediaMetadata mediaMetadata) {
|
private void setMetadata(MediaMetadata mediaMetadata) {
|
||||||
if (mediaMetadata.extras != null) {
|
if (mediaMetadata.extras != null) {
|
||||||
playerBottomSheetViewModel.setLiveMedia(getViewLifecycleOwner(), mediaMetadata.extras.getString("type"), mediaMetadata.extras.getString("id"));
|
playerBottomSheetViewModel.setLiveMedia(getViewLifecycleOwner(), mediaMetadata.extras.getString("type"), mediaMetadata.extras.getString("id"));
|
||||||
|
playerBottomSheetViewModel.setLiveAlbum(getViewLifecycleOwner(), mediaMetadata.extras.getString("type"), mediaMetadata.extras.getString("albumId"));
|
||||||
playerBottomSheetViewModel.setLiveArtist(getViewLifecycleOwner(), mediaMetadata.extras.getString("type"), mediaMetadata.extras.getString("artistId"));
|
playerBottomSheetViewModel.setLiveArtist(getViewLifecycleOwner(), mediaMetadata.extras.getString("type"), mediaMetadata.extras.getString("artistId"));
|
||||||
playerBottomSheetViewModel.setLiveDescription(mediaMetadata.extras.getString("description", null));
|
playerBottomSheetViewModel.setLiveDescription(mediaMetadata.extras.getString("description", null));
|
||||||
|
|
||||||
bind.playerHeaderLayout.playerHeaderMediaTitleLabel.setText(MusicUtil.getReadableString(mediaMetadata.extras.getString("title")));
|
bind.playerHeaderLayout.playerHeaderMediaTitleLabel.setText(mediaMetadata.extras.getString("title"));
|
||||||
bind.playerHeaderLayout.playerHeaderMediaArtistLabel.setText(MusicUtil.getReadableString(mediaMetadata.extras.getString("artist")));
|
bind.playerHeaderLayout.playerHeaderMediaArtistLabel.setText(mediaMetadata.extras.getString("artist"));
|
||||||
|
|
||||||
CustomGlideRequest.Builder
|
CustomGlideRequest.Builder
|
||||||
.from(requireContext(), mediaMetadata.extras.getString("coverArtId"), CustomGlideRequest.ResourceType.Song)
|
.from(requireContext(), mediaMetadata.extras.getString("coverArtId"), CustomGlideRequest.ResourceType.Song)
|
||||||
@@ -249,6 +250,11 @@ public class PlayerBottomSheetFragment extends Fragment {
|
|||||||
bind.playerBodyLayout.playerBodyBottomSheetViewPager.setCurrentItem(1, true);
|
bind.playerBodyLayout.playerBodyBottomSheetViewPager.setCurrentItem(1, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setPlayerControllerVerticalPagerDraggableState(Boolean isDraggable) {
|
||||||
|
ViewPager2 playerControllerVerticalPager = (ViewPager2) bind.playerBodyLayout.playerBodyBottomSheetViewPager;
|
||||||
|
playerControllerVerticalPager.setUserInputEnabled(isDraggable);
|
||||||
|
}
|
||||||
|
|
||||||
private void defineProgressBarHandler(MediaBrowser mediaBrowser) {
|
private void defineProgressBarHandler(MediaBrowser mediaBrowser) {
|
||||||
progressBarHandler = new Handler();
|
progressBarHandler = new Handler();
|
||||||
progressBarRunnable = () -> {
|
progressBarRunnable = () -> {
|
||||||
|
|||||||
@@ -76,6 +76,7 @@ public class PlayerControllerFragment extends Fragment {
|
|||||||
initQuickActionView();
|
initQuickActionView();
|
||||||
initCoverLyricsSlideView();
|
initCoverLyricsSlideView();
|
||||||
initMediaListenable();
|
initMediaListenable();
|
||||||
|
initMediaLabelButton();
|
||||||
initArtistLabelButton();
|
initArtistLabelButton();
|
||||||
|
|
||||||
return view;
|
return view;
|
||||||
@@ -163,8 +164,8 @@ public class PlayerControllerFragment extends Fragment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void setMetadata(MediaMetadata mediaMetadata) {
|
private void setMetadata(MediaMetadata mediaMetadata) {
|
||||||
playerMediaTitleLabel.setText(MusicUtil.getReadableString(String.valueOf(mediaMetadata.title)));
|
playerMediaTitleLabel.setText(String.valueOf(mediaMetadata.title));
|
||||||
playerArtistNameLabel.setText(MusicUtil.getReadableString(String.valueOf(mediaMetadata.artist)));
|
playerArtistNameLabel.setText(String.valueOf(mediaMetadata.artist));
|
||||||
|
|
||||||
playerMediaTitleLabel.setSelected(true);
|
playerMediaTitleLabel.setSelected(true);
|
||||||
playerArtistNameLabel.setSelected(true);
|
playerArtistNameLabel.setSelected(true);
|
||||||
@@ -257,10 +258,20 @@ public class PlayerControllerFragment extends Fragment {
|
|||||||
public void onPageSelected(int position) {
|
public void onPageSelected(int position) {
|
||||||
super.onPageSelected(position);
|
super.onPageSelected(position);
|
||||||
|
|
||||||
|
PlayerBottomSheetFragment playerBottomSheetFragment = (PlayerBottomSheetFragment) requireActivity().getSupportFragmentManager().findFragmentByTag("PlayerBottomSheet");
|
||||||
|
|
||||||
if (position == 0) {
|
if (position == 0) {
|
||||||
activity.setBottomSheetDraggableState(true);
|
activity.setBottomSheetDraggableState(true);
|
||||||
|
|
||||||
|
if (playerBottomSheetFragment != null) {
|
||||||
|
playerBottomSheetFragment.setPlayerControllerVerticalPagerDraggableState(true);
|
||||||
|
}
|
||||||
} else if (position == 1) {
|
} else if (position == 1) {
|
||||||
activity.setBottomSheetDraggableState(false);
|
activity.setBottomSheetDraggableState(false);
|
||||||
|
|
||||||
|
if (playerBottomSheetFragment != null) {
|
||||||
|
playerBottomSheetFragment.setPlayerControllerVerticalPagerDraggableState(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -289,6 +300,19 @@ public class PlayerControllerFragment extends Fragment {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void initMediaLabelButton() {
|
||||||
|
playerBottomSheetViewModel.getLiveAlbum().observe(getViewLifecycleOwner(), album -> {
|
||||||
|
if (album != null) {
|
||||||
|
playerMediaTitleLabel.setOnClickListener(view -> {
|
||||||
|
Bundle bundle = new Bundle();
|
||||||
|
bundle.putParcelable(Constants.ALBUM_OBJECT, album);
|
||||||
|
NavHostFragment.findNavController(this).navigate(R.id.albumPageFragment, bundle);
|
||||||
|
activity.collapseBottomSheetDelayed();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private void initArtistLabelButton() {
|
private void initArtistLabelButton() {
|
||||||
playerBottomSheetViewModel.getLiveArtist().observe(getViewLifecycleOwner(), artist -> {
|
playerBottomSheetViewModel.getLiveArtist().observe(getViewLifecycleOwner(), artist -> {
|
||||||
if (artist != null) {
|
if (artist != null) {
|
||||||
|
|||||||
@@ -1,31 +1,60 @@
|
|||||||
package com.cappielloantonio.tempo.ui.fragment;
|
package com.cappielloantonio.tempo.ui.fragment;
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint;
|
||||||
|
import android.content.ComponentName;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.text.Spannable;
|
||||||
|
import android.text.SpannableString;
|
||||||
|
import android.text.style.ForegroundColorSpan;
|
||||||
|
import android.util.Log;
|
||||||
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.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.annotation.OptIn;
|
||||||
import androidx.fragment.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
import androidx.lifecycle.ViewModelProvider;
|
import androidx.lifecycle.ViewModelProvider;
|
||||||
|
import androidx.media3.common.util.UnstableApi;
|
||||||
|
import androidx.media3.session.MediaBrowser;
|
||||||
|
import androidx.media3.session.SessionToken;
|
||||||
|
|
||||||
|
import com.cappielloantonio.tempo.R;
|
||||||
import com.cappielloantonio.tempo.databinding.InnerFragmentPlayerLyricsBinding;
|
import com.cappielloantonio.tempo.databinding.InnerFragmentPlayerLyricsBinding;
|
||||||
|
import com.cappielloantonio.tempo.service.MediaService;
|
||||||
|
import com.cappielloantonio.tempo.subsonic.models.Line;
|
||||||
|
import com.cappielloantonio.tempo.subsonic.models.LyricsList;
|
||||||
import com.cappielloantonio.tempo.util.MusicUtil;
|
import com.cappielloantonio.tempo.util.MusicUtil;
|
||||||
|
import com.cappielloantonio.tempo.util.OpenSubsonicExtensionsUtil;
|
||||||
import com.cappielloantonio.tempo.viewmodel.PlayerBottomSheetViewModel;
|
import com.cappielloantonio.tempo.viewmodel.PlayerBottomSheetViewModel;
|
||||||
|
import com.google.common.util.concurrent.ListenableFuture;
|
||||||
|
import com.google.common.util.concurrent.MoreExecutors;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
|
||||||
|
@OptIn(markerClass = UnstableApi.class)
|
||||||
public class PlayerLyricsFragment extends Fragment {
|
public class PlayerLyricsFragment extends Fragment {
|
||||||
private static final String TAG = "PlayerLyricsFragment";
|
private static final String TAG = "PlayerLyricsFragment";
|
||||||
|
|
||||||
private InnerFragmentPlayerLyricsBinding bind;
|
private InnerFragmentPlayerLyricsBinding bind;
|
||||||
private PlayerBottomSheetViewModel playerBottomSheetViewModel;
|
private PlayerBottomSheetViewModel playerBottomSheetViewModel;
|
||||||
|
private ListenableFuture<MediaBrowser> mediaBrowserListenableFuture;
|
||||||
|
private MediaBrowser mediaBrowser;
|
||||||
|
private Handler syncLyricsHandler;
|
||||||
|
private Runnable syncLyricsRunnable;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||||
bind = InnerFragmentPlayerLyricsBinding.inflate(inflater, container, false);
|
bind = InnerFragmentPlayerLyricsBinding.inflate(inflater, container, false);
|
||||||
View view = bind.getRoot();
|
View view = bind.getRoot();
|
||||||
|
|
||||||
playerBottomSheetViewModel = new ViewModelProvider(requireActivity()).get(PlayerBottomSheetViewModel.class);
|
playerBottomSheetViewModel = new ViewModelProvider(requireActivity()).get(PlayerBottomSheetViewModel.class);
|
||||||
|
|
||||||
|
initOverlay();
|
||||||
|
|
||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -33,7 +62,32 @@ public class PlayerLyricsFragment extends Fragment {
|
|||||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||||
super.onViewCreated(view, savedInstanceState);
|
super.onViewCreated(view, savedInstanceState);
|
||||||
|
|
||||||
initLyrics();
|
initPanelContent();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStart() {
|
||||||
|
super.onStart();
|
||||||
|
initializeBrowser();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onResume() {
|
||||||
|
super.onResume();
|
||||||
|
bindMediaController();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPause() {
|
||||||
|
super.onPause();
|
||||||
|
releaseHandler();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStop() {
|
||||||
|
releaseBrowser();
|
||||||
|
super.onStop();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -42,27 +96,195 @@ public class PlayerLyricsFragment extends Fragment {
|
|||||||
bind = null;
|
bind = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initLyrics() {
|
private void initOverlay() {
|
||||||
playerBottomSheetViewModel.getLiveLyrics().observe(getViewLifecycleOwner(), lyrics -> {
|
bind.syncLyricsTapButton.setOnClickListener(view -> {
|
||||||
playerBottomSheetViewModel.getLiveDescription().observe(getViewLifecycleOwner(), description -> {
|
playerBottomSheetViewModel.changeSyncLyricsState();
|
||||||
if (bind != null) {
|
|
||||||
if (lyrics != null && !lyrics.trim().equals("")) {
|
|
||||||
bind.nowPlayingSongLyricsTextView.setText(MusicUtil.getReadableLyrics(lyrics));
|
|
||||||
bind.nowPlayingSongLyricsTextView.setVisibility(View.VISIBLE);
|
|
||||||
bind.emptyDescriptionImageView.setVisibility(View.GONE);
|
|
||||||
bind.titleEmptyDescriptionLabel.setVisibility(View.GONE);
|
|
||||||
} else if (description != null && !description.trim().equals("")) {
|
|
||||||
bind.nowPlayingSongLyricsTextView.setText(MusicUtil.getReadableLyrics(description));
|
|
||||||
bind.nowPlayingSongLyricsTextView.setVisibility(View.VISIBLE);
|
|
||||||
bind.emptyDescriptionImageView.setVisibility(View.GONE);
|
|
||||||
bind.titleEmptyDescriptionLabel.setVisibility(View.GONE);
|
|
||||||
} else {
|
|
||||||
bind.nowPlayingSongLyricsTextView.setVisibility(View.GONE);
|
|
||||||
bind.emptyDescriptionImageView.setVisibility(View.VISIBLE);
|
|
||||||
bind.titleEmptyDescriptionLabel.setVisibility(View.VISIBLE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void initializeBrowser() {
|
||||||
|
mediaBrowserListenableFuture = new MediaBrowser.Builder(requireContext(), new SessionToken(requireContext(), new ComponentName(requireContext(), MediaService.class))).buildAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void releaseHandler() {
|
||||||
|
if (syncLyricsHandler != null) {
|
||||||
|
syncLyricsHandler.removeCallbacks(syncLyricsRunnable);
|
||||||
|
syncLyricsHandler = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void releaseBrowser() {
|
||||||
|
MediaBrowser.releaseFuture(mediaBrowserListenableFuture);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void bindMediaController() {
|
||||||
|
mediaBrowserListenableFuture.addListener(() -> {
|
||||||
|
try {
|
||||||
|
mediaBrowser = mediaBrowserListenableFuture.get();
|
||||||
|
defineProgressHandler();
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}, MoreExecutors.directExecutor());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initPanelContent() {
|
||||||
|
if (OpenSubsonicExtensionsUtil.isSongLyricsExtensionAvailable()) {
|
||||||
|
playerBottomSheetViewModel.getLiveLyricsList().observe(getViewLifecycleOwner(), lyricsList -> {
|
||||||
|
setPanelContent(null, lyricsList);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
playerBottomSheetViewModel.getLiveLyrics().observe(getViewLifecycleOwner(), lyrics -> {
|
||||||
|
setPanelContent(lyrics, null);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setPanelContent(String lyrics, LyricsList lyricsList) {
|
||||||
|
playerBottomSheetViewModel.getLiveDescription().observe(getViewLifecycleOwner(), description -> {
|
||||||
|
if (bind != null) {
|
||||||
|
bind.nowPlayingSongLyricsSrollView.smoothScrollTo(0, 0);
|
||||||
|
|
||||||
|
if (lyrics != null && !lyrics.trim().equals("")) {
|
||||||
|
bind.nowPlayingSongLyricsTextView.setText(MusicUtil.getReadableLyrics(lyrics));
|
||||||
|
bind.nowPlayingSongLyricsTextView.setVisibility(View.VISIBLE);
|
||||||
|
bind.emptyDescriptionImageView.setVisibility(View.GONE);
|
||||||
|
bind.titleEmptyDescriptionLabel.setVisibility(View.GONE);
|
||||||
|
bind.syncLyricsTapButton.setVisibility(View.GONE);
|
||||||
|
} else if (lyricsList != null && lyricsList.getStructuredLyrics() != null) {
|
||||||
|
setSyncLirics(lyricsList);
|
||||||
|
bind.nowPlayingSongLyricsTextView.setVisibility(View.VISIBLE);
|
||||||
|
bind.emptyDescriptionImageView.setVisibility(View.GONE);
|
||||||
|
bind.titleEmptyDescriptionLabel.setVisibility(View.GONE);
|
||||||
|
bind.syncLyricsTapButton.setVisibility(View.VISIBLE);
|
||||||
|
} else if (description != null && !description.trim().equals("")) {
|
||||||
|
bind.nowPlayingSongLyricsTextView.setText(MusicUtil.getReadableLyrics(description));
|
||||||
|
bind.nowPlayingSongLyricsTextView.setVisibility(View.VISIBLE);
|
||||||
|
bind.emptyDescriptionImageView.setVisibility(View.GONE);
|
||||||
|
bind.titleEmptyDescriptionLabel.setVisibility(View.GONE);
|
||||||
|
bind.syncLyricsTapButton.setVisibility(View.GONE);
|
||||||
|
} else {
|
||||||
|
bind.nowPlayingSongLyricsTextView.setVisibility(View.GONE);
|
||||||
|
bind.emptyDescriptionImageView.setVisibility(View.VISIBLE);
|
||||||
|
bind.titleEmptyDescriptionLabel.setVisibility(View.VISIBLE);
|
||||||
|
bind.syncLyricsTapButton.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("DefaultLocale")
|
||||||
|
private void setSyncLirics(LyricsList lyricsList) {
|
||||||
|
if (lyricsList.getStructuredLyrics() != null && !lyricsList.getStructuredLyrics().isEmpty() && lyricsList.getStructuredLyrics().get(0).getLine() != null) {
|
||||||
|
StringBuilder lyricsBuilder = new StringBuilder();
|
||||||
|
List<Line> lines = lyricsList.getStructuredLyrics().get(0).getLine();
|
||||||
|
|
||||||
|
if (lines != null) {
|
||||||
|
for (Line line : lines) {
|
||||||
|
lyricsBuilder.append(line.getValue().trim()).append("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bind.nowPlayingSongLyricsTextView.setText(lyricsBuilder.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void defineProgressHandler() {
|
||||||
|
playerBottomSheetViewModel.getLiveLyricsList().observe(getViewLifecycleOwner(), lyricsList -> {
|
||||||
|
if (lyricsList != null) {
|
||||||
|
|
||||||
|
if (lyricsList.getStructuredLyrics() != null && lyricsList.getStructuredLyrics().get(0) != null && !lyricsList.getStructuredLyrics().get(0).getSynced()) {
|
||||||
|
releaseHandler();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
syncLyricsHandler = new Handler();
|
||||||
|
syncLyricsRunnable = () -> {
|
||||||
|
if (syncLyricsHandler != null) {
|
||||||
|
if (bind != null) {
|
||||||
|
displaySyncedLyrics();
|
||||||
|
}
|
||||||
|
|
||||||
|
syncLyricsHandler.postDelayed(syncLyricsRunnable, 250);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
syncLyricsHandler.postDelayed(syncLyricsRunnable, 250);
|
||||||
|
} else {
|
||||||
|
releaseHandler();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void displaySyncedLyrics() {
|
||||||
|
LyricsList lyricsList = playerBottomSheetViewModel.getLiveLyricsList().getValue();
|
||||||
|
int timestamp = (int) (mediaBrowser.getCurrentPosition());
|
||||||
|
|
||||||
|
if (lyricsList != null && lyricsList.getStructuredLyrics() != null && !lyricsList.getStructuredLyrics().isEmpty() && lyricsList.getStructuredLyrics().get(0).getLine() != null) {
|
||||||
|
StringBuilder lyricsBuilder = new StringBuilder();
|
||||||
|
List<Line> lines = lyricsList.getStructuredLyrics().get(0).getLine();
|
||||||
|
|
||||||
|
if (lines == null || lines.isEmpty()) return;
|
||||||
|
|
||||||
|
for (Line line : lines) {
|
||||||
|
lyricsBuilder.append(line.getValue().trim()).append("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
Line toHighlight = lines.stream().filter(line -> line != null && line.getStart() != null && line.getStart() < timestamp).reduce((first, second) -> second).orElse(null);
|
||||||
|
|
||||||
|
if (toHighlight != null) {
|
||||||
|
String lyrics = lyricsBuilder.toString();
|
||||||
|
Spannable spannableString = new SpannableString(lyrics);
|
||||||
|
|
||||||
|
int startingPosition = getStartPosition(lines, toHighlight);
|
||||||
|
int endingPosition = startingPosition + toHighlight.getValue().length();
|
||||||
|
|
||||||
|
spannableString.setSpan(new ForegroundColorSpan(requireContext().getResources().getColor(R.color.shadowsLyricsTextColor, null)), 0, lyrics.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||||
|
spannableString.setSpan(new ForegroundColorSpan(requireContext().getResources().getColor(R.color.lyricsTextColor, null)), startingPosition, endingPosition, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||||
|
|
||||||
|
bind.nowPlayingSongLyricsTextView.setText(spannableString);
|
||||||
|
|
||||||
|
if (playerBottomSheetViewModel.getSyncLyricsState()) {
|
||||||
|
bind.nowPlayingSongLyricsSrollView.smoothScrollTo(0, getScroll(lines, toHighlight));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getStartPosition(List<Line> lines, Line toHighlight) {
|
||||||
|
int start = 0;
|
||||||
|
|
||||||
|
for (Line line : lines) {
|
||||||
|
if (line != toHighlight) {
|
||||||
|
start = start + line.getValue().length() + 1;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return start;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getLineCount(List<Line> lines, Line toHighlight) {
|
||||||
|
int start = 0;
|
||||||
|
|
||||||
|
for (Line line : lines) {
|
||||||
|
if (line != toHighlight) {
|
||||||
|
bind.tempLyricsLineTextView.setText(line.getValue());
|
||||||
|
start = start + bind.tempLyricsLineTextView.getLineCount();
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return start;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getScroll(List<Line> lines, Line toHighlight) {
|
||||||
|
int lineHeight = bind.nowPlayingSongLyricsTextView.getLineHeight();
|
||||||
|
int lineCount = getLineCount(lines, toHighlight);
|
||||||
|
int scrollViewHeight = bind.nowPlayingSongLyricsSrollView.getHeight();
|
||||||
|
|
||||||
|
return lineHeight * lineCount < scrollViewHeight / 2 ? 0 : lineHeight * lineCount - scrollViewHeight / 2 + lineHeight;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
package com.cappielloantonio.tempo.ui.fragment;
|
package com.cappielloantonio.tempo.ui.fragment;
|
||||||
|
|
||||||
import android.content.ComponentName;
|
import android.content.ComponentName;
|
||||||
|
import android.content.Context;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
@@ -8,6 +9,9 @@ import android.view.MenuInflater;
|
|||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
import android.view.inputmethod.EditorInfo;
|
||||||
|
import android.view.inputmethod.InputMethodManager;
|
||||||
|
import android.widget.SearchView;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
@@ -58,8 +62,29 @@ public class PlaylistPageFragment extends Fragment implements ClickCallback {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) {
|
public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) {
|
||||||
super.onCreateOptionsMenu(menu, inflater);
|
|
||||||
inflater.inflate(R.menu.playlist_page_menu, menu);
|
inflater.inflate(R.menu.playlist_page_menu, menu);
|
||||||
|
|
||||||
|
MenuItem searchItem = menu.findItem(R.id.action_search);
|
||||||
|
|
||||||
|
SearchView searchView = (SearchView) searchItem.getActionView();
|
||||||
|
searchView.setImeOptions(EditorInfo.IME_ACTION_DONE);
|
||||||
|
searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
|
||||||
|
@Override
|
||||||
|
public boolean onQueryTextSubmit(String query) {
|
||||||
|
searchView.clearFocus();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onQueryTextChange(String newText) {
|
||||||
|
songHorizontalAdapter.getFilter().filter(newText);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
searchView.setPadding(-32, 0, 0, 0);
|
||||||
|
|
||||||
|
initMenuOption(menu);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -115,6 +140,12 @@ public class PlaylistPageFragment extends Fragment implements ClickCallback {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
return true;
|
return true;
|
||||||
|
} else if (item.getItemId() == R.id.action_pin_playlist) {
|
||||||
|
playlistPageViewModel.setPinned(true);
|
||||||
|
return true;
|
||||||
|
} else if (item.getItemId() == R.id.action_unpin_playlist) {
|
||||||
|
playlistPageViewModel.setPinned(false);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
@@ -124,6 +155,13 @@ public class PlaylistPageFragment extends Fragment implements ClickCallback {
|
|||||||
playlistPageViewModel.setPlaylist(requireArguments().getParcelable(Constants.PLAYLIST_OBJECT));
|
playlistPageViewModel.setPlaylist(requireArguments().getParcelable(Constants.PLAYLIST_OBJECT));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void initMenuOption(Menu menu) {
|
||||||
|
playlistPageViewModel.isPinned(getViewLifecycleOwner()).observe(getViewLifecycleOwner(), isPinned -> {
|
||||||
|
menu.findItem(R.id.action_unpin_playlist).setVisible(isPinned);
|
||||||
|
menu.findItem(R.id.action_pin_playlist).setVisible(!isPinned);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private void initAppBar() {
|
private void initAppBar() {
|
||||||
activity.setSupportActionBar(bind.animToolbar);
|
activity.setSupportActionBar(bind.animToolbar);
|
||||||
|
|
||||||
@@ -132,17 +170,25 @@ public class PlaylistPageFragment extends Fragment implements ClickCallback {
|
|||||||
activity.getSupportActionBar().setDisplayShowHomeEnabled(true);
|
activity.getSupportActionBar().setDisplayShowHomeEnabled(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
bind.animToolbar.setTitle(MusicUtil.getReadableString(playlistPageViewModel.getPlaylist().getName()));
|
bind.animToolbar.setTitle(playlistPageViewModel.getPlaylist().getName());
|
||||||
|
|
||||||
bind.playlistNameLabel.setText(MusicUtil.getReadableString(playlistPageViewModel.getPlaylist().getName()));
|
bind.playlistNameLabel.setText(playlistPageViewModel.getPlaylist().getName());
|
||||||
bind.playlistSongCountLabel.setText(getString(R.string.playlist_song_count, playlistPageViewModel.getPlaylist().getSongCount()));
|
bind.playlistSongCountLabel.setText(getString(R.string.playlist_song_count, playlistPageViewModel.getPlaylist().getSongCount()));
|
||||||
bind.playlistDurationLabel.setText(getString(R.string.playlist_duration, MusicUtil.getReadableDurationString(playlistPageViewModel.getPlaylist().getDuration(), false)));
|
bind.playlistDurationLabel.setText(getString(R.string.playlist_duration, MusicUtil.getReadableDurationString(playlistPageViewModel.getPlaylist().getDuration(), false)));
|
||||||
|
|
||||||
bind.animToolbar.setNavigationOnClickListener(v -> activity.navController.navigateUp());
|
bind.animToolbar.setNavigationOnClickListener(v -> {
|
||||||
|
hideKeyboard(v);
|
||||||
|
activity.navController.navigateUp();
|
||||||
|
});
|
||||||
|
|
||||||
Objects.requireNonNull(bind.animToolbar.getOverflowIcon()).setTint(requireContext().getResources().getColor(R.color.titleTextColor, null));
|
Objects.requireNonNull(bind.animToolbar.getOverflowIcon()).setTint(requireContext().getResources().getColor(R.color.titleTextColor, null));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void hideKeyboard(View view) {
|
||||||
|
InputMethodManager imm = (InputMethodManager) requireActivity().getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||||
|
imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
private void initMusicButton() {
|
private void initMusicButton() {
|
||||||
playlistPageViewModel.getPlaylistSongLiveList().observe(getViewLifecycleOwner(), songs -> {
|
playlistPageViewModel.getPlaylistSongLiveList().observe(getViewLifecycleOwner(), songs -> {
|
||||||
if (bind != null) {
|
if (bind != null) {
|
||||||
@@ -167,7 +213,7 @@ public class PlaylistPageFragment extends Fragment implements ClickCallback {
|
|||||||
|
|
||||||
// Pic top-left
|
// Pic top-left
|
||||||
CustomGlideRequest.Builder
|
CustomGlideRequest.Builder
|
||||||
.from(requireContext(), songs.size() > 0 ? songs.get(0).getCoverArtId() : playlistPageViewModel.getPlaylist().getCoverArtId(), CustomGlideRequest.ResourceType.Song)
|
.from(requireContext(), !songs.isEmpty() ? songs.get(0).getCoverArtId() : playlistPageViewModel.getPlaylist().getCoverArtId(), CustomGlideRequest.ResourceType.Song)
|
||||||
.build()
|
.build()
|
||||||
.transform(new GranularRoundedCorners(CustomGlideRequest.CORNER_RADIUS, 0, 0, 0))
|
.transform(new GranularRoundedCorners(CustomGlideRequest.CORNER_RADIUS, 0, 0, 0))
|
||||||
.into(bind.playlistCoverImageViewTopLeft);
|
.into(bind.playlistCoverImageViewTopLeft);
|
||||||
@@ -200,7 +246,7 @@ public class PlaylistPageFragment extends Fragment implements ClickCallback {
|
|||||||
bind.songRecyclerView.setLayoutManager(new LinearLayoutManager(requireContext()));
|
bind.songRecyclerView.setLayoutManager(new LinearLayoutManager(requireContext()));
|
||||||
bind.songRecyclerView.setHasFixedSize(true);
|
bind.songRecyclerView.setHasFixedSize(true);
|
||||||
|
|
||||||
songHorizontalAdapter = new SongHorizontalAdapter(this, true, false);
|
songHorizontalAdapter = new SongHorizontalAdapter(this, true, false, null);
|
||||||
bind.songRecyclerView.setAdapter(songHorizontalAdapter);
|
bind.songRecyclerView.setAdapter(songHorizontalAdapter);
|
||||||
|
|
||||||
playlistPageViewModel.getPlaylistSongLiveList().observe(getViewLifecycleOwner(), songs -> songHorizontalAdapter.setItems(songs));
|
playlistPageViewModel.getPlaylistSongLiveList().observe(getViewLifecycleOwner(), songs -> songHorizontalAdapter.setItems(songs));
|
||||||
|
|||||||
@@ -90,9 +90,9 @@ public class PodcastChannelPageFragment extends Fragment implements ClickCallbac
|
|||||||
activity.getSupportActionBar().setDisplayShowHomeEnabled(true);
|
activity.getSupportActionBar().setDisplayShowHomeEnabled(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
bind.toolbar.setTitle(MusicUtil.getReadableString(podcastChannelPageViewModel.getPodcastChannel().getTitle()));
|
bind.toolbar.setTitle(podcastChannelPageViewModel.getPodcastChannel().getTitle());
|
||||||
bind.toolbar.setNavigationOnClickListener(v -> activity.navController.navigateUp());
|
bind.toolbar.setNavigationOnClickListener(v -> activity.navController.navigateUp());
|
||||||
bind.toolbar.setTitle(MusicUtil.getReadableString(podcastChannelPageViewModel.getPodcastChannel().getTitle()));
|
bind.toolbar.setTitle(podcastChannelPageViewModel.getPodcastChannel().getTitle());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initPodcastChannelInfo() {
|
private void initPodcastChannelInfo() {
|
||||||
|
|||||||
@@ -112,7 +112,7 @@ public class SearchFragment extends Fragment implements ClickCallback {
|
|||||||
bind.searchResultTracksRecyclerView.setLayoutManager(new LinearLayoutManager(requireContext()));
|
bind.searchResultTracksRecyclerView.setLayoutManager(new LinearLayoutManager(requireContext()));
|
||||||
bind.searchResultTracksRecyclerView.setHasFixedSize(true);
|
bind.searchResultTracksRecyclerView.setHasFixedSize(true);
|
||||||
|
|
||||||
songHorizontalAdapter = new SongHorizontalAdapter(this, true, false);
|
songHorizontalAdapter = new SongHorizontalAdapter(this, true, false, null);
|
||||||
bind.searchResultTracksRecyclerView.setAdapter(songHorizontalAdapter);
|
bind.searchResultTracksRecyclerView.setAdapter(songHorizontalAdapter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package com.cappielloantonio.tempo.ui.fragment;
|
|||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.media.audiofx.AudioEffect;
|
import android.media.audiofx.AudioEffect;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.os.Handler;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
@@ -30,6 +31,8 @@ import com.cappielloantonio.tempo.ui.activity.MainActivity;
|
|||||||
import com.cappielloantonio.tempo.ui.dialog.DeleteDownloadStorageDialog;
|
import com.cappielloantonio.tempo.ui.dialog.DeleteDownloadStorageDialog;
|
||||||
import com.cappielloantonio.tempo.ui.dialog.DownloadStorageDialog;
|
import com.cappielloantonio.tempo.ui.dialog.DownloadStorageDialog;
|
||||||
import com.cappielloantonio.tempo.ui.dialog.StarredSyncDialog;
|
import com.cappielloantonio.tempo.ui.dialog.StarredSyncDialog;
|
||||||
|
import com.cappielloantonio.tempo.ui.dialog.StreamingCacheStorageDialog;
|
||||||
|
import com.cappielloantonio.tempo.util.DownloadUtil;
|
||||||
import com.cappielloantonio.tempo.util.Preferences;
|
import com.cappielloantonio.tempo.util.Preferences;
|
||||||
import com.cappielloantonio.tempo.util.UIUtil;
|
import com.cappielloantonio.tempo.util.UIUtil;
|
||||||
import com.cappielloantonio.tempo.viewmodel.SettingViewModel;
|
import com.cappielloantonio.tempo.viewmodel.SettingViewModel;
|
||||||
@@ -82,14 +85,17 @@ public class SettingsFragment extends PreferenceFragmentCompat {
|
|||||||
super.onResume();
|
super.onResume();
|
||||||
|
|
||||||
checkEqualizer();
|
checkEqualizer();
|
||||||
|
checkCacheStorage();
|
||||||
checkStorage();
|
checkStorage();
|
||||||
|
|
||||||
|
setStreamingCacheSize();
|
||||||
setAppLanguage();
|
setAppLanguage();
|
||||||
setVersion();
|
setVersion();
|
||||||
|
|
||||||
actionLogout();
|
actionLogout();
|
||||||
actionScan();
|
actionScan();
|
||||||
actionSyncStarredTracks();
|
actionSyncStarredTracks();
|
||||||
|
actionChangeStreamingCacheStorage();
|
||||||
actionChangeDownloadStorage();
|
actionChangeDownloadStorage();
|
||||||
actionDeleteDownloadStorage();
|
actionDeleteDownloadStorage();
|
||||||
actionKeepScreenOn();
|
actionKeepScreenOn();
|
||||||
@@ -132,6 +138,22 @@ public class SettingsFragment extends PreferenceFragmentCompat {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void checkCacheStorage() {
|
||||||
|
Preference storage = findPreference("streaming_cache_storage");
|
||||||
|
|
||||||
|
if (storage == null) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (requireContext().getExternalFilesDirs(null)[1] == null) {
|
||||||
|
storage.setVisible(false);
|
||||||
|
} else {
|
||||||
|
storage.setSummary(Preferences.getDownloadStoragePreference() == 0 ? R.string.download_storage_internal_dialog_negative_button : R.string.download_storage_external_dialog_positive_button);
|
||||||
|
}
|
||||||
|
} catch (Exception exception) {
|
||||||
|
storage.setVisible(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void checkStorage() {
|
private void checkStorage() {
|
||||||
Preference storage = findPreference("download_storage");
|
Preference storage = findPreference("download_storage");
|
||||||
|
|
||||||
@@ -148,6 +170,26 @@ public class SettingsFragment extends PreferenceFragmentCompat {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void setStreamingCacheSize() {
|
||||||
|
ListPreference streamingCachePreference = findPreference("streaming_cache_size");
|
||||||
|
|
||||||
|
if (streamingCachePreference != null) {
|
||||||
|
streamingCachePreference.setSummaryProvider(new Preference.SummaryProvider<ListPreference>() {
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public CharSequence provideSummary(@NonNull ListPreference preference) {
|
||||||
|
CharSequence entry = preference.getEntry();
|
||||||
|
|
||||||
|
if (entry == null) return null;
|
||||||
|
|
||||||
|
long currentSizeMb = DownloadUtil.getStreamingCacheSize(requireActivity()) / (1024 * 1024);
|
||||||
|
|
||||||
|
return getString(R.string.settings_summary_streaming_cache_size, entry, String.valueOf(currentSizeMb));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void setAppLanguage() {
|
private void setAppLanguage() {
|
||||||
ListPreference localePref = (ListPreference) findPreference("language");
|
ListPreference localePref = (ListPreference) findPreference("language");
|
||||||
|
|
||||||
@@ -190,7 +232,8 @@ public class SettingsFragment extends PreferenceFragmentCompat {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(boolean isScanning, long count) {
|
public void onSuccess(boolean isScanning, long count) {
|
||||||
getScanStatus();
|
findPreference("scan_library").setSummary("Scanning: counting " + count + " tracks");
|
||||||
|
if (isScanning) getScanStatus();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -210,6 +253,24 @@ public class SettingsFragment extends PreferenceFragmentCompat {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void actionChangeStreamingCacheStorage() {
|
||||||
|
findPreference("streaming_cache_storage").setOnPreferenceClickListener(preference -> {
|
||||||
|
StreamingCacheStorageDialog dialog = new StreamingCacheStorageDialog(new DialogClickCallback() {
|
||||||
|
@Override
|
||||||
|
public void onPositiveClick() {
|
||||||
|
findPreference("streaming_cache_storage").setSummary(R.string.streaming_cache_storage_external_dialog_positive_button);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onNegativeClick() {
|
||||||
|
findPreference("streaming_cache_storage").setSummary(R.string.streaming_cache_storage_internal_dialog_negative_button);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
dialog.show(activity.getSupportFragmentManager(), null);
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private void actionChangeDownloadStorage() {
|
private void actionChangeDownloadStorage() {
|
||||||
findPreference("download_storage").setOnPreferenceClickListener(preference -> {
|
findPreference("download_storage").setOnPreferenceClickListener(preference -> {
|
||||||
DownloadStorageDialog dialog = new DownloadStorageDialog(new DialogClickCallback() {
|
DownloadStorageDialog dialog = new DownloadStorageDialog(new DialogClickCallback() {
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user