mirror of
https://github.com/CappielloAntonio/tempo.git
synced 2026-01-31 14:43:36 +00:00
Compare commits
99 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1cb371dc5a | ||
|
|
499001a269 | ||
|
|
4c9e47379d | ||
|
|
a0dbb5c81f | ||
|
|
dab53c6bbf | ||
|
|
c0a665c00a | ||
|
|
41b5c57240 | ||
|
|
1d65a79c20 | ||
|
|
efb6e72636 | ||
|
|
af83ffd608 | ||
|
|
0201077bc4 | ||
|
|
91d91d3024 | ||
|
|
9784a2b6c5 | ||
|
|
87a3301912 | ||
|
|
295795edc9 | ||
|
|
6120ab66ba | ||
|
|
7bea180c58 | ||
|
|
a29cee488e | ||
|
|
74b4b04693 | ||
|
|
b8b9c80bdc | ||
|
|
a50fc74117 | ||
|
|
c1af438a3a | ||
|
|
80b251cddc | ||
|
|
7d9a48818e | ||
|
|
ca3da0839b | ||
|
|
cf463d8fa1 | ||
|
|
635fdc4c5c | ||
|
|
3e913931c4 | ||
|
|
46420da038 | ||
|
|
d5b7619dd1 | ||
|
|
89b39123da | ||
|
|
5a43137984 | ||
|
|
761b07450f | ||
|
|
a0b67a06f4 | ||
|
|
59d7adb66d | ||
|
|
0c2f0d23cd | ||
|
|
dc201e6c8f | ||
|
|
9bf3399371 | ||
|
|
28565b691a | ||
|
|
7c6faf66c1 | ||
|
|
b160274859 | ||
|
|
3a1ced65d5 | ||
|
|
afeb72803d | ||
|
|
600c28c2a3 | ||
|
|
ec799fff96 | ||
|
|
60da08cdbc | ||
|
|
bdda3743db | ||
|
|
59fcf11ae3 | ||
|
|
77c80b7695 | ||
|
|
13f168f78c | ||
|
|
324eed7e02 | ||
|
|
4a99c7e9b1 | ||
|
|
01e5917642 | ||
|
|
ae00f4279e | ||
|
|
642c69eb96 | ||
|
|
ac69361735 | ||
|
|
e24efc4948 | ||
|
|
f8ad18ed5a | ||
|
|
17345372a2 | ||
|
|
db76494525 | ||
|
|
e87eda2757 | ||
|
|
06e2729aca | ||
|
|
37ffb88d67 | ||
|
|
3d4437151a | ||
|
|
af1961b185 | ||
|
|
c983e33522 | ||
|
|
b18daec708 | ||
|
|
6d20995e70 | ||
|
|
14cacd1bbc | ||
|
|
9d7acdb892 | ||
|
|
05325913f7 | ||
|
|
5733dca68a | ||
|
|
838d4496e5 | ||
|
|
4967363116 | ||
|
|
fc61308be5 | ||
|
|
5c4a292542 | ||
|
|
aa68c2c3d8 | ||
|
|
9c0ebca66f | ||
|
|
888f177597 | ||
|
|
c69fbcfa68 | ||
|
|
f044db142c | ||
|
|
9af7bc3ac8 | ||
|
|
cd87fcde26 | ||
|
|
9e1a6c804f | ||
|
|
4c15f6eb01 | ||
|
|
4ad2722e81 | ||
|
|
b267b904cc | ||
|
|
1ebe9ff8ba | ||
|
|
623a4956a5 | ||
|
|
6572b846c8 | ||
|
|
fe3ba9fb89 | ||
|
|
c4b9db303a | ||
|
|
aed52fdbf8 | ||
|
|
f9573b3eab | ||
|
|
7a8880ee68 | ||
|
|
68aae32d06 | ||
|
|
4b07f37378 | ||
|
|
4d573c6b9d | ||
|
|
10dcb2380c |
16
.github/workflows/github_release.yml
vendored
16
.github/workflows/github_release.yml
vendored
@@ -6,19 +6,15 @@ on:
|
|||||||
- '[0-9]+.[0-9]+.[0-9]+'
|
- '[0-9]+.[0-9]+.[0-9]+'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
setup:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Setup JDK 8
|
|
||||||
uses: actions/setup-java@v3
|
|
||||||
with:
|
|
||||||
distribution: 'temurin'
|
|
||||||
java-version: 8
|
|
||||||
|
|
||||||
build:
|
build:
|
||||||
needs: setup
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
|
- name: Setup JDK 17
|
||||||
|
uses: actions/setup-java@v2
|
||||||
|
with:
|
||||||
|
distribution: 'zulu'
|
||||||
|
java-version: '17'
|
||||||
|
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Cache Gradle and wrapper
|
- name: Cache Gradle and wrapper
|
||||||
|
|||||||
2
.idea/gradle.xml
generated
2
.idea/gradle.xml
generated
@@ -7,7 +7,7 @@
|
|||||||
<option name="testRunner" value="GRADLE" />
|
<option name="testRunner" value="GRADLE" />
|
||||||
<option name="distributionType" value="DEFAULT_WRAPPED" />
|
<option name="distributionType" value="DEFAULT_WRAPPED" />
|
||||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||||
<option name="gradleJvm" value="11" />
|
<option name="gradleJvm" value="17" />
|
||||||
<option name="modules">
|
<option name="modules">
|
||||||
<set>
|
<set>
|
||||||
<option value="$PROJECT_DIR$" />
|
<option value="$PROJECT_DIR$" />
|
||||||
|
|||||||
2
.idea/misc.xml
generated
2
.idea/misc.xml
generated
@@ -191,7 +191,7 @@
|
|||||||
</map>
|
</map>
|
||||||
</option>
|
</option>
|
||||||
</component>
|
</component>
|
||||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" project-jdk-name="jbr-17" project-jdk-type="JavaSDK">
|
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="17" 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">
|
||||||
|
|||||||
@@ -33,12 +33,8 @@ Tempo does not rely on magic algorithms to decide what you should listen to. Ins
|
|||||||
<img src="mockup/feat/6_screenshot.png" width=200>
|
<img src="mockup/feat/6_screenshot.png" width=200>
|
||||||
<img src="mockup/feat/7_screenshot.png" width=200>
|
<img src="mockup/feat/7_screenshot.png" width=200>
|
||||||
<img src="mockup/feat/8_screenshot.png" width=200>
|
<img src="mockup/feat/8_screenshot.png" width=200>
|
||||||
<img src="mockup/feat/9_screenshot.png" width=200>
|
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
## Disclaimer
|
|
||||||
Tempo is currently under active development and is in alpha state. This means that the app may contain stability issues, bugs, or incomplete features. While we strive to provide a smooth and reliable experience, please be aware that using Tempo in its current state may not be as stable as a fully released version. I appreciate your understanding and patience as I work towards improving the app.
|
|
||||||
|
|
||||||
## Sponsors
|
## Sponsors
|
||||||
Tempo is an open-source project developed and maintained solely by me. I would like to express my heartfelt thanks to all the users who have shown their love and support for Tempo. Your contributions and encouragement mean a lot to me, and they help drive the development and improvement of the app.
|
Tempo is an open-source project developed and maintained solely by me. I would like to express my heartfelt thanks to all the users who have shown their love and support for Tempo. Your contributions and encouragement mean a lot to me, and they help drive the development and improvement of the app.
|
||||||
|
|
||||||
|
|||||||
@@ -3,8 +3,8 @@ apply plugin: 'kotlin-android'
|
|||||||
apply plugin: 'kotlin-parcelize'
|
apply plugin: 'kotlin-parcelize'
|
||||||
|
|
||||||
android {
|
android {
|
||||||
compileSdkVersion 34
|
compileSdk 34
|
||||||
buildToolsVersion '33.0.0'
|
buildToolsVersion "34.0.0"
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
minSdkVersion 24
|
minSdkVersion 24
|
||||||
@@ -28,8 +28,8 @@ android {
|
|||||||
tempo {
|
tempo {
|
||||||
dimension "default"
|
dimension "default"
|
||||||
applicationId 'com.cappielloantonio.tempo'
|
applicationId 'com.cappielloantonio.tempo'
|
||||||
versionCode 14
|
versionCode 17
|
||||||
versionName '3.4.7'
|
versionName '3.5.2'
|
||||||
}
|
}
|
||||||
|
|
||||||
notquitemy {
|
notquitemy {
|
||||||
@@ -71,28 +71,29 @@ dependencies {
|
|||||||
// AndroidX
|
// AndroidX
|
||||||
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
|
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
|
||||||
implementation 'androidx.coordinatorlayout:coordinatorlayout:1.2.0'
|
implementation 'androidx.coordinatorlayout:coordinatorlayout:1.2.0'
|
||||||
implementation 'androidx.preference:preference-ktx:1.2.0'
|
implementation 'androidx.preference:preference-ktx:1.2.1'
|
||||||
implementation 'androidx.navigation:navigation-fragment-ktx:2.6.0'
|
implementation 'androidx.navigation:navigation-fragment-ktx:2.7.0'
|
||||||
implementation 'androidx.navigation:navigation-ui-ktx:2.6.0'
|
implementation 'androidx.navigation:navigation-ui-ktx:2.7.0'
|
||||||
implementation 'androidx.recyclerview:recyclerview:1.3.0'
|
implementation 'androidx.recyclerview:recyclerview:1.3.1'
|
||||||
implementation 'androidx.room:room-runtime:2.5.2'
|
implementation 'androidx.room:room-runtime:2.5.2'
|
||||||
implementation 'androidx.core:core-splashscreen:1.0.1'
|
implementation 'androidx.core:core-splashscreen:1.0.1'
|
||||||
|
implementation "androidx.appcompat:appcompat:1.6.1"
|
||||||
|
|
||||||
// Android Material
|
// Android Material
|
||||||
implementation 'com.google.android.material:material:1.9.0'
|
implementation 'com.google.android.material:material:1.9.0'
|
||||||
|
|
||||||
// Glide
|
// Glide
|
||||||
implementation 'com.github.bumptech.glide:glide:4.15.1'
|
implementation 'com.github.bumptech.glide:glide:4.16.0'
|
||||||
implementation 'com.github.bumptech.glide:annotations:4.15.1'
|
implementation 'com.github.bumptech.glide:annotations:4.16.0'
|
||||||
|
|
||||||
// Media3
|
// Media3
|
||||||
implementation 'androidx.media3:media3-session:1.1.0'
|
implementation 'androidx.media3:media3-session:1.1.1'
|
||||||
implementation 'androidx.media3:media3-common:1.1.0'
|
implementation 'androidx.media3:media3-common:1.1.1'
|
||||||
implementation 'androidx.media3:media3-exoplayer:1.1.0'
|
implementation 'androidx.media3:media3-exoplayer:1.1.1'
|
||||||
implementation 'androidx.media3:media3-ui:1.1.0'
|
implementation 'androidx.media3:media3-ui:1.1.1'
|
||||||
tempoImplementation 'androidx.media3:media3-cast:1.1.0'
|
tempoImplementation 'androidx.media3:media3-cast:1.1.1'
|
||||||
|
|
||||||
annotationProcessor 'com.github.bumptech.glide:compiler:4.15.1'
|
annotationProcessor 'com.github.bumptech.glide:compiler:4.16.0'
|
||||||
annotationProcessor 'androidx.room:room-compiler:2.5.2'
|
annotationProcessor 'androidx.room:room-compiler:2.5.2'
|
||||||
|
|
||||||
// Retrofit
|
// Retrofit
|
||||||
|
|||||||
@@ -0,0 +1,746 @@
|
|||||||
|
{
|
||||||
|
"formatVersion": 1,
|
||||||
|
"database": {
|
||||||
|
"version": 1,
|
||||||
|
"identityHash": "1f4e50f90f58fb9cb53c89747d142fd9",
|
||||||
|
"entities": [
|
||||||
|
{
|
||||||
|
"tableName": "queue",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `track_order` INTEGER NOT NULL, `last_play` INTEGER NOT NULL, `playing_changed` INTEGER NOT NULL, `stream_id` TEXT, `parent_id` TEXT, `is_dir` INTEGER NOT NULL, `title` TEXT, `album` TEXT, `artist` TEXT, `track` INTEGER, `year` INTEGER, `genre` TEXT, `cover_art_id` TEXT, `size` INTEGER, `content_type` TEXT, `suffix` TEXT, `transcoding_content_type` TEXT, `transcoded_suffix` TEXT, `duration` INTEGER, `bitrate` INTEGER, `path` TEXT, `is_video` INTEGER NOT NULL, `user_rating` INTEGER, `average_rating` REAL, `play_count` INTEGER, `disc_number` INTEGER, `created` INTEGER, `starred` INTEGER, `album_id` TEXT, `artist_id` TEXT, `type` TEXT, `bookmark_position` INTEGER, `original_width` INTEGER, `original_height` INTEGER, PRIMARY KEY(`track_order`))",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "id",
|
||||||
|
"columnName": "id",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "trackOrder",
|
||||||
|
"columnName": "track_order",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "lastPlay",
|
||||||
|
"columnName": "last_play",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "playingChanged",
|
||||||
|
"columnName": "playing_changed",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "streamId",
|
||||||
|
"columnName": "stream_id",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "parentId",
|
||||||
|
"columnName": "parent_id",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "isDir",
|
||||||
|
"columnName": "is_dir",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "title",
|
||||||
|
"columnName": "title",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "album",
|
||||||
|
"columnName": "album",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "artist",
|
||||||
|
"columnName": "artist",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "track",
|
||||||
|
"columnName": "track",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "year",
|
||||||
|
"columnName": "year",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "genre",
|
||||||
|
"columnName": "genre",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "coverArtId",
|
||||||
|
"columnName": "cover_art_id",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "size",
|
||||||
|
"columnName": "size",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "contentType",
|
||||||
|
"columnName": "content_type",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "suffix",
|
||||||
|
"columnName": "suffix",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "transcodedContentType",
|
||||||
|
"columnName": "transcoding_content_type",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "transcodedSuffix",
|
||||||
|
"columnName": "transcoded_suffix",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "duration",
|
||||||
|
"columnName": "duration",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "bitrate",
|
||||||
|
"columnName": "bitrate",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "path",
|
||||||
|
"columnName": "path",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "isVideo",
|
||||||
|
"columnName": "is_video",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "userRating",
|
||||||
|
"columnName": "user_rating",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "averageRating",
|
||||||
|
"columnName": "average_rating",
|
||||||
|
"affinity": "REAL",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "playCount",
|
||||||
|
"columnName": "play_count",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "discNumber",
|
||||||
|
"columnName": "disc_number",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "created",
|
||||||
|
"columnName": "created",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "starred",
|
||||||
|
"columnName": "starred",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "albumId",
|
||||||
|
"columnName": "album_id",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "artistId",
|
||||||
|
"columnName": "artist_id",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "type",
|
||||||
|
"columnName": "type",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "bookmarkPosition",
|
||||||
|
"columnName": "bookmark_position",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "originalWidth",
|
||||||
|
"columnName": "original_width",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "originalHeight",
|
||||||
|
"columnName": "original_height",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": false,
|
||||||
|
"columnNames": [
|
||||||
|
"track_order"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"indices": [],
|
||||||
|
"foreignKeys": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "server",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `server_name` TEXT NOT NULL, `username` TEXT NOT NULL, `password` TEXT NOT NULL, `address` TEXT NOT NULL, `timestamp` INTEGER NOT NULL, `low_security` INTEGER NOT NULL DEFAULT false, PRIMARY KEY(`id`))",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "serverId",
|
||||||
|
"columnName": "id",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "serverName",
|
||||||
|
"columnName": "server_name",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "username",
|
||||||
|
"columnName": "username",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "password",
|
||||||
|
"columnName": "password",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "address",
|
||||||
|
"columnName": "address",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "timestamp",
|
||||||
|
"columnName": "timestamp",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "isLowSecurity",
|
||||||
|
"columnName": "low_security",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true,
|
||||||
|
"defaultValue": "false"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": false,
|
||||||
|
"columnNames": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"indices": [],
|
||||||
|
"foreignKeys": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "recent_search",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`search` TEXT NOT NULL, PRIMARY KEY(`search`))",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "search",
|
||||||
|
"columnName": "search",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": false,
|
||||||
|
"columnNames": [
|
||||||
|
"search"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"indices": [],
|
||||||
|
"foreignKeys": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "download",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `playlist_id` TEXT, `playlist_name` TEXT, `download_state` INTEGER NOT NULL DEFAULT 1, `parent_id` TEXT, `is_dir` INTEGER NOT NULL, `title` TEXT, `album` TEXT, `artist` TEXT, `track` INTEGER, `year` INTEGER, `genre` TEXT, `cover_art_id` TEXT, `size` INTEGER, `content_type` TEXT, `suffix` TEXT, `transcoding_content_type` TEXT, `transcoded_suffix` TEXT, `duration` INTEGER, `bitrate` INTEGER, `path` TEXT, `is_video` INTEGER NOT NULL, `user_rating` INTEGER, `average_rating` REAL, `play_count` INTEGER, `disc_number` INTEGER, `created` INTEGER, `starred` INTEGER, `album_id` TEXT, `artist_id` TEXT, `type` TEXT, `bookmark_position` INTEGER, `original_width` INTEGER, `original_height` INTEGER, PRIMARY KEY(`id`))",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "id",
|
||||||
|
"columnName": "id",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "playlistId",
|
||||||
|
"columnName": "playlist_id",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "playlistName",
|
||||||
|
"columnName": "playlist_name",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "downloadState",
|
||||||
|
"columnName": "download_state",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true,
|
||||||
|
"defaultValue": "1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "parentId",
|
||||||
|
"columnName": "parent_id",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "isDir",
|
||||||
|
"columnName": "is_dir",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "title",
|
||||||
|
"columnName": "title",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "album",
|
||||||
|
"columnName": "album",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "artist",
|
||||||
|
"columnName": "artist",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "track",
|
||||||
|
"columnName": "track",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "year",
|
||||||
|
"columnName": "year",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "genre",
|
||||||
|
"columnName": "genre",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "coverArtId",
|
||||||
|
"columnName": "cover_art_id",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "size",
|
||||||
|
"columnName": "size",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "contentType",
|
||||||
|
"columnName": "content_type",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "suffix",
|
||||||
|
"columnName": "suffix",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "transcodedContentType",
|
||||||
|
"columnName": "transcoding_content_type",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "transcodedSuffix",
|
||||||
|
"columnName": "transcoded_suffix",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "duration",
|
||||||
|
"columnName": "duration",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "bitrate",
|
||||||
|
"columnName": "bitrate",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "path",
|
||||||
|
"columnName": "path",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "isVideo",
|
||||||
|
"columnName": "is_video",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "userRating",
|
||||||
|
"columnName": "user_rating",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "averageRating",
|
||||||
|
"columnName": "average_rating",
|
||||||
|
"affinity": "REAL",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "playCount",
|
||||||
|
"columnName": "play_count",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "discNumber",
|
||||||
|
"columnName": "disc_number",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "created",
|
||||||
|
"columnName": "created",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "starred",
|
||||||
|
"columnName": "starred",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "albumId",
|
||||||
|
"columnName": "album_id",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "artistId",
|
||||||
|
"columnName": "artist_id",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "type",
|
||||||
|
"columnName": "type",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "bookmarkPosition",
|
||||||
|
"columnName": "bookmark_position",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "originalWidth",
|
||||||
|
"columnName": "original_width",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "originalHeight",
|
||||||
|
"columnName": "original_height",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": false,
|
||||||
|
"columnNames": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"indices": [],
|
||||||
|
"foreignKeys": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "chronology",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `timestamp` INTEGER NOT NULL, `server` TEXT, `parent_id` TEXT, `is_dir` INTEGER NOT NULL, `title` TEXT, `album` TEXT, `artist` TEXT, `track` INTEGER, `year` INTEGER, `genre` TEXT, `cover_art_id` TEXT, `size` INTEGER, `content_type` TEXT, `suffix` TEXT, `transcoding_content_type` TEXT, `transcoded_suffix` TEXT, `duration` INTEGER, `bitrate` INTEGER, `path` TEXT, `is_video` INTEGER NOT NULL, `user_rating` INTEGER, `average_rating` REAL, `play_count` INTEGER, `disc_number` INTEGER, `created` INTEGER, `starred` INTEGER, `album_id` TEXT, `artist_id` TEXT, `type` TEXT, `bookmark_position` INTEGER, `original_width` INTEGER, `original_height` INTEGER, PRIMARY KEY(`id`))",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "id",
|
||||||
|
"columnName": "id",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "timestamp",
|
||||||
|
"columnName": "timestamp",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "server",
|
||||||
|
"columnName": "server",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "parentId",
|
||||||
|
"columnName": "parent_id",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "isDir",
|
||||||
|
"columnName": "is_dir",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "title",
|
||||||
|
"columnName": "title",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "album",
|
||||||
|
"columnName": "album",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "artist",
|
||||||
|
"columnName": "artist",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "track",
|
||||||
|
"columnName": "track",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "year",
|
||||||
|
"columnName": "year",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "genre",
|
||||||
|
"columnName": "genre",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "coverArtId",
|
||||||
|
"columnName": "cover_art_id",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "size",
|
||||||
|
"columnName": "size",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "contentType",
|
||||||
|
"columnName": "content_type",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "suffix",
|
||||||
|
"columnName": "suffix",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "transcodedContentType",
|
||||||
|
"columnName": "transcoding_content_type",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "transcodedSuffix",
|
||||||
|
"columnName": "transcoded_suffix",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "duration",
|
||||||
|
"columnName": "duration",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "bitrate",
|
||||||
|
"columnName": "bitrate",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "path",
|
||||||
|
"columnName": "path",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "isVideo",
|
||||||
|
"columnName": "is_video",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "userRating",
|
||||||
|
"columnName": "user_rating",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "averageRating",
|
||||||
|
"columnName": "average_rating",
|
||||||
|
"affinity": "REAL",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "playCount",
|
||||||
|
"columnName": "play_count",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "discNumber",
|
||||||
|
"columnName": "disc_number",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "created",
|
||||||
|
"columnName": "created",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "starred",
|
||||||
|
"columnName": "starred",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "albumId",
|
||||||
|
"columnName": "album_id",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "artistId",
|
||||||
|
"columnName": "artist_id",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "type",
|
||||||
|
"columnName": "type",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "bookmarkPosition",
|
||||||
|
"columnName": "bookmark_position",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "originalWidth",
|
||||||
|
"columnName": "original_width",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "originalHeight",
|
||||||
|
"columnName": "original_height",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": false,
|
||||||
|
"columnNames": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"indices": [],
|
||||||
|
"foreignKeys": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"views": [],
|
||||||
|
"setupQueries": [
|
||||||
|
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
|
||||||
|
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '1f4e50f90f58fb9cb53c89747d142fd9')"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,790 @@
|
|||||||
|
{
|
||||||
|
"formatVersion": 1,
|
||||||
|
"database": {
|
||||||
|
"version": 2,
|
||||||
|
"identityHash": "ff99e331b4c34a82c560588c4dd5735f",
|
||||||
|
"entities": [
|
||||||
|
{
|
||||||
|
"tableName": "queue",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `track_order` INTEGER NOT NULL, `last_play` INTEGER NOT NULL, `playing_changed` INTEGER NOT NULL, `stream_id` TEXT, `parent_id` TEXT, `is_dir` INTEGER NOT NULL, `title` TEXT, `album` TEXT, `artist` TEXT, `track` INTEGER, `year` INTEGER, `genre` TEXT, `cover_art_id` TEXT, `size` INTEGER, `content_type` TEXT, `suffix` TEXT, `transcoding_content_type` TEXT, `transcoded_suffix` TEXT, `duration` INTEGER, `bitrate` INTEGER, `path` TEXT, `is_video` INTEGER NOT NULL, `user_rating` INTEGER, `average_rating` REAL, `play_count` INTEGER, `disc_number` INTEGER, `created` INTEGER, `starred` INTEGER, `album_id` TEXT, `artist_id` TEXT, `type` TEXT, `bookmark_position` INTEGER, `original_width` INTEGER, `original_height` INTEGER, PRIMARY KEY(`track_order`))",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "id",
|
||||||
|
"columnName": "id",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "trackOrder",
|
||||||
|
"columnName": "track_order",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "lastPlay",
|
||||||
|
"columnName": "last_play",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "playingChanged",
|
||||||
|
"columnName": "playing_changed",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "streamId",
|
||||||
|
"columnName": "stream_id",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "parentId",
|
||||||
|
"columnName": "parent_id",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "isDir",
|
||||||
|
"columnName": "is_dir",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "title",
|
||||||
|
"columnName": "title",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "album",
|
||||||
|
"columnName": "album",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "artist",
|
||||||
|
"columnName": "artist",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "track",
|
||||||
|
"columnName": "track",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "year",
|
||||||
|
"columnName": "year",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "genre",
|
||||||
|
"columnName": "genre",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "coverArtId",
|
||||||
|
"columnName": "cover_art_id",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "size",
|
||||||
|
"columnName": "size",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "contentType",
|
||||||
|
"columnName": "content_type",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "suffix",
|
||||||
|
"columnName": "suffix",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "transcodedContentType",
|
||||||
|
"columnName": "transcoding_content_type",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "transcodedSuffix",
|
||||||
|
"columnName": "transcoded_suffix",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "duration",
|
||||||
|
"columnName": "duration",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "bitrate",
|
||||||
|
"columnName": "bitrate",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "path",
|
||||||
|
"columnName": "path",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "isVideo",
|
||||||
|
"columnName": "is_video",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "userRating",
|
||||||
|
"columnName": "user_rating",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "averageRating",
|
||||||
|
"columnName": "average_rating",
|
||||||
|
"affinity": "REAL",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "playCount",
|
||||||
|
"columnName": "play_count",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "discNumber",
|
||||||
|
"columnName": "disc_number",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "created",
|
||||||
|
"columnName": "created",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "starred",
|
||||||
|
"columnName": "starred",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "albumId",
|
||||||
|
"columnName": "album_id",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "artistId",
|
||||||
|
"columnName": "artist_id",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "type",
|
||||||
|
"columnName": "type",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "bookmarkPosition",
|
||||||
|
"columnName": "bookmark_position",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "originalWidth",
|
||||||
|
"columnName": "original_width",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "originalHeight",
|
||||||
|
"columnName": "original_height",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": false,
|
||||||
|
"columnNames": [
|
||||||
|
"track_order"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"indices": [],
|
||||||
|
"foreignKeys": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "server",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `server_name` TEXT NOT NULL, `username` TEXT NOT NULL, `password` TEXT NOT NULL, `address` TEXT NOT NULL, `timestamp` INTEGER NOT NULL, `low_security` INTEGER NOT NULL DEFAULT false, PRIMARY KEY(`id`))",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "serverId",
|
||||||
|
"columnName": "id",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "serverName",
|
||||||
|
"columnName": "server_name",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "username",
|
||||||
|
"columnName": "username",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "password",
|
||||||
|
"columnName": "password",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "address",
|
||||||
|
"columnName": "address",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "timestamp",
|
||||||
|
"columnName": "timestamp",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "isLowSecurity",
|
||||||
|
"columnName": "low_security",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true,
|
||||||
|
"defaultValue": "false"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": false,
|
||||||
|
"columnNames": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"indices": [],
|
||||||
|
"foreignKeys": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "recent_search",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`search` TEXT NOT NULL, PRIMARY KEY(`search`))",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "search",
|
||||||
|
"columnName": "search",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": false,
|
||||||
|
"columnNames": [
|
||||||
|
"search"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"indices": [],
|
||||||
|
"foreignKeys": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "download",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `playlist_id` TEXT, `playlist_name` TEXT, `download_state` INTEGER NOT NULL DEFAULT 1, `parent_id` TEXT, `is_dir` INTEGER NOT NULL, `title` TEXT, `album` TEXT, `artist` TEXT, `track` INTEGER, `year` INTEGER, `genre` TEXT, `cover_art_id` TEXT, `size` INTEGER, `content_type` TEXT, `suffix` TEXT, `transcoding_content_type` TEXT, `transcoded_suffix` TEXT, `duration` INTEGER, `bitrate` INTEGER, `path` TEXT, `is_video` INTEGER NOT NULL, `user_rating` INTEGER, `average_rating` REAL, `play_count` INTEGER, `disc_number` INTEGER, `created` INTEGER, `starred` INTEGER, `album_id` TEXT, `artist_id` TEXT, `type` TEXT, `bookmark_position` INTEGER, `original_width` INTEGER, `original_height` INTEGER, PRIMARY KEY(`id`))",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "id",
|
||||||
|
"columnName": "id",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "playlistId",
|
||||||
|
"columnName": "playlist_id",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "playlistName",
|
||||||
|
"columnName": "playlist_name",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "downloadState",
|
||||||
|
"columnName": "download_state",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true,
|
||||||
|
"defaultValue": "1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "parentId",
|
||||||
|
"columnName": "parent_id",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "isDir",
|
||||||
|
"columnName": "is_dir",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "title",
|
||||||
|
"columnName": "title",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "album",
|
||||||
|
"columnName": "album",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "artist",
|
||||||
|
"columnName": "artist",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "track",
|
||||||
|
"columnName": "track",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "year",
|
||||||
|
"columnName": "year",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "genre",
|
||||||
|
"columnName": "genre",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "coverArtId",
|
||||||
|
"columnName": "cover_art_id",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "size",
|
||||||
|
"columnName": "size",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "contentType",
|
||||||
|
"columnName": "content_type",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "suffix",
|
||||||
|
"columnName": "suffix",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "transcodedContentType",
|
||||||
|
"columnName": "transcoding_content_type",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "transcodedSuffix",
|
||||||
|
"columnName": "transcoded_suffix",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "duration",
|
||||||
|
"columnName": "duration",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "bitrate",
|
||||||
|
"columnName": "bitrate",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "path",
|
||||||
|
"columnName": "path",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "isVideo",
|
||||||
|
"columnName": "is_video",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "userRating",
|
||||||
|
"columnName": "user_rating",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "averageRating",
|
||||||
|
"columnName": "average_rating",
|
||||||
|
"affinity": "REAL",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "playCount",
|
||||||
|
"columnName": "play_count",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "discNumber",
|
||||||
|
"columnName": "disc_number",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "created",
|
||||||
|
"columnName": "created",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "starred",
|
||||||
|
"columnName": "starred",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "albumId",
|
||||||
|
"columnName": "album_id",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "artistId",
|
||||||
|
"columnName": "artist_id",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "type",
|
||||||
|
"columnName": "type",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "bookmarkPosition",
|
||||||
|
"columnName": "bookmark_position",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "originalWidth",
|
||||||
|
"columnName": "original_width",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "originalHeight",
|
||||||
|
"columnName": "original_height",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": false,
|
||||||
|
"columnNames": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"indices": [],
|
||||||
|
"foreignKeys": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "chronology",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `timestamp` INTEGER NOT NULL, `server` TEXT, `parent_id` TEXT, `is_dir` INTEGER NOT NULL, `title` TEXT, `album` TEXT, `artist` TEXT, `track` INTEGER, `year` INTEGER, `genre` TEXT, `cover_art_id` TEXT, `size` INTEGER, `content_type` TEXT, `suffix` TEXT, `transcoding_content_type` TEXT, `transcoded_suffix` TEXT, `duration` INTEGER, `bitrate` INTEGER, `path` TEXT, `is_video` INTEGER NOT NULL, `user_rating` INTEGER, `average_rating` REAL, `play_count` INTEGER, `disc_number` INTEGER, `created` INTEGER, `starred` INTEGER, `album_id` TEXT, `artist_id` TEXT, `type` TEXT, `bookmark_position` INTEGER, `original_width` INTEGER, `original_height` INTEGER, PRIMARY KEY(`id`))",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "id",
|
||||||
|
"columnName": "id",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "timestamp",
|
||||||
|
"columnName": "timestamp",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "server",
|
||||||
|
"columnName": "server",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "parentId",
|
||||||
|
"columnName": "parent_id",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "isDir",
|
||||||
|
"columnName": "is_dir",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "title",
|
||||||
|
"columnName": "title",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "album",
|
||||||
|
"columnName": "album",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "artist",
|
||||||
|
"columnName": "artist",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "track",
|
||||||
|
"columnName": "track",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "year",
|
||||||
|
"columnName": "year",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "genre",
|
||||||
|
"columnName": "genre",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "coverArtId",
|
||||||
|
"columnName": "cover_art_id",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "size",
|
||||||
|
"columnName": "size",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "contentType",
|
||||||
|
"columnName": "content_type",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "suffix",
|
||||||
|
"columnName": "suffix",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "transcodedContentType",
|
||||||
|
"columnName": "transcoding_content_type",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "transcodedSuffix",
|
||||||
|
"columnName": "transcoded_suffix",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "duration",
|
||||||
|
"columnName": "duration",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "bitrate",
|
||||||
|
"columnName": "bitrate",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "path",
|
||||||
|
"columnName": "path",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "isVideo",
|
||||||
|
"columnName": "is_video",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "userRating",
|
||||||
|
"columnName": "user_rating",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "averageRating",
|
||||||
|
"columnName": "average_rating",
|
||||||
|
"affinity": "REAL",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "playCount",
|
||||||
|
"columnName": "play_count",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "discNumber",
|
||||||
|
"columnName": "disc_number",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "created",
|
||||||
|
"columnName": "created",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "starred",
|
||||||
|
"columnName": "starred",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "albumId",
|
||||||
|
"columnName": "album_id",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "artistId",
|
||||||
|
"columnName": "artist_id",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "type",
|
||||||
|
"columnName": "type",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "bookmarkPosition",
|
||||||
|
"columnName": "bookmark_position",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "originalWidth",
|
||||||
|
"columnName": "original_width",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "originalHeight",
|
||||||
|
"columnName": "original_height",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": false,
|
||||||
|
"columnNames": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"indices": [],
|
||||||
|
"foreignKeys": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "favorite",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`timestamp` INTEGER NOT NULL, `songId` TEXT, `albumId` TEXT, `artistId` TEXT, `toStar` INTEGER NOT NULL, PRIMARY KEY(`timestamp`))",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "timestamp",
|
||||||
|
"columnName": "timestamp",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "songId",
|
||||||
|
"columnName": "songId",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "albumId",
|
||||||
|
"columnName": "albumId",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "artistId",
|
||||||
|
"columnName": "artistId",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "toStar",
|
||||||
|
"columnName": "toStar",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": false,
|
||||||
|
"columnNames": [
|
||||||
|
"timestamp"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"indices": [],
|
||||||
|
"foreignKeys": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"views": [],
|
||||||
|
"setupQueries": [
|
||||||
|
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
|
||||||
|
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'ff99e331b4c34a82c560588c4dd5735f')"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,797 @@
|
|||||||
|
{
|
||||||
|
"formatVersion": 1,
|
||||||
|
"database": {
|
||||||
|
"version": 3,
|
||||||
|
"identityHash": "6ea111217793c58d54eabb1190dd92ec",
|
||||||
|
"entities": [
|
||||||
|
{
|
||||||
|
"tableName": "queue",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `track_order` INTEGER NOT NULL, `last_play` INTEGER NOT NULL, `playing_changed` INTEGER NOT NULL, `stream_id` TEXT, `parent_id` TEXT, `is_dir` INTEGER NOT NULL, `title` TEXT, `album` TEXT, `artist` TEXT, `track` INTEGER, `year` INTEGER, `genre` TEXT, `cover_art_id` TEXT, `size` INTEGER, `content_type` TEXT, `suffix` TEXT, `transcoding_content_type` TEXT, `transcoded_suffix` TEXT, `duration` INTEGER, `bitrate` INTEGER, `path` TEXT, `is_video` INTEGER NOT NULL, `user_rating` INTEGER, `average_rating` REAL, `play_count` INTEGER, `disc_number` INTEGER, `created` INTEGER, `starred` INTEGER, `album_id` TEXT, `artist_id` TEXT, `type` TEXT, `bookmark_position` INTEGER, `original_width` INTEGER, `original_height` INTEGER, PRIMARY KEY(`track_order`))",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "id",
|
||||||
|
"columnName": "id",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "trackOrder",
|
||||||
|
"columnName": "track_order",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "lastPlay",
|
||||||
|
"columnName": "last_play",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "playingChanged",
|
||||||
|
"columnName": "playing_changed",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "streamId",
|
||||||
|
"columnName": "stream_id",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "parentId",
|
||||||
|
"columnName": "parent_id",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "isDir",
|
||||||
|
"columnName": "is_dir",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "title",
|
||||||
|
"columnName": "title",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "album",
|
||||||
|
"columnName": "album",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "artist",
|
||||||
|
"columnName": "artist",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "track",
|
||||||
|
"columnName": "track",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "year",
|
||||||
|
"columnName": "year",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "genre",
|
||||||
|
"columnName": "genre",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "coverArtId",
|
||||||
|
"columnName": "cover_art_id",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "size",
|
||||||
|
"columnName": "size",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "contentType",
|
||||||
|
"columnName": "content_type",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "suffix",
|
||||||
|
"columnName": "suffix",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "transcodedContentType",
|
||||||
|
"columnName": "transcoding_content_type",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "transcodedSuffix",
|
||||||
|
"columnName": "transcoded_suffix",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "duration",
|
||||||
|
"columnName": "duration",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "bitrate",
|
||||||
|
"columnName": "bitrate",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "path",
|
||||||
|
"columnName": "path",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "isVideo",
|
||||||
|
"columnName": "is_video",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "userRating",
|
||||||
|
"columnName": "user_rating",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "averageRating",
|
||||||
|
"columnName": "average_rating",
|
||||||
|
"affinity": "REAL",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "playCount",
|
||||||
|
"columnName": "play_count",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "discNumber",
|
||||||
|
"columnName": "disc_number",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "created",
|
||||||
|
"columnName": "created",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "starred",
|
||||||
|
"columnName": "starred",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "albumId",
|
||||||
|
"columnName": "album_id",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "artistId",
|
||||||
|
"columnName": "artist_id",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "type",
|
||||||
|
"columnName": "type",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "bookmarkPosition",
|
||||||
|
"columnName": "bookmark_position",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "originalWidth",
|
||||||
|
"columnName": "original_width",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "originalHeight",
|
||||||
|
"columnName": "original_height",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": false,
|
||||||
|
"columnNames": [
|
||||||
|
"track_order"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"indices": [],
|
||||||
|
"foreignKeys": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "server",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `server_name` TEXT NOT NULL, `username` TEXT NOT NULL, `password` TEXT NOT NULL, `address` TEXT NOT NULL, `timestamp` INTEGER NOT NULL, `low_security` INTEGER NOT NULL DEFAULT false, PRIMARY KEY(`id`))",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "serverId",
|
||||||
|
"columnName": "id",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "serverName",
|
||||||
|
"columnName": "server_name",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "username",
|
||||||
|
"columnName": "username",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "password",
|
||||||
|
"columnName": "password",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "address",
|
||||||
|
"columnName": "address",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "timestamp",
|
||||||
|
"columnName": "timestamp",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "isLowSecurity",
|
||||||
|
"columnName": "low_security",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true,
|
||||||
|
"defaultValue": "false"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": false,
|
||||||
|
"columnNames": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"indices": [],
|
||||||
|
"foreignKeys": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "recent_search",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`search` TEXT NOT NULL, PRIMARY KEY(`search`))",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "search",
|
||||||
|
"columnName": "search",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": false,
|
||||||
|
"columnNames": [
|
||||||
|
"search"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"indices": [],
|
||||||
|
"foreignKeys": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "download",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `playlist_id` TEXT, `playlist_name` TEXT, `download_state` INTEGER NOT NULL DEFAULT 1, `download_uri` TEXT DEFAULT '', `parent_id` TEXT, `is_dir` INTEGER NOT NULL, `title` TEXT, `album` TEXT, `artist` TEXT, `track` INTEGER, `year` INTEGER, `genre` TEXT, `cover_art_id` TEXT, `size` INTEGER, `content_type` TEXT, `suffix` TEXT, `transcoding_content_type` TEXT, `transcoded_suffix` TEXT, `duration` INTEGER, `bitrate` INTEGER, `path` TEXT, `is_video` INTEGER NOT NULL, `user_rating` INTEGER, `average_rating` REAL, `play_count` INTEGER, `disc_number` INTEGER, `created` INTEGER, `starred` INTEGER, `album_id` TEXT, `artist_id` TEXT, `type` TEXT, `bookmark_position` INTEGER, `original_width` INTEGER, `original_height` INTEGER, PRIMARY KEY(`id`))",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "id",
|
||||||
|
"columnName": "id",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "playlistId",
|
||||||
|
"columnName": "playlist_id",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "playlistName",
|
||||||
|
"columnName": "playlist_name",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "downloadState",
|
||||||
|
"columnName": "download_state",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true,
|
||||||
|
"defaultValue": "1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "downloadUri",
|
||||||
|
"columnName": "download_uri",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false,
|
||||||
|
"defaultValue": "''"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "parentId",
|
||||||
|
"columnName": "parent_id",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "isDir",
|
||||||
|
"columnName": "is_dir",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "title",
|
||||||
|
"columnName": "title",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "album",
|
||||||
|
"columnName": "album",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "artist",
|
||||||
|
"columnName": "artist",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "track",
|
||||||
|
"columnName": "track",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "year",
|
||||||
|
"columnName": "year",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "genre",
|
||||||
|
"columnName": "genre",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "coverArtId",
|
||||||
|
"columnName": "cover_art_id",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "size",
|
||||||
|
"columnName": "size",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "contentType",
|
||||||
|
"columnName": "content_type",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "suffix",
|
||||||
|
"columnName": "suffix",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "transcodedContentType",
|
||||||
|
"columnName": "transcoding_content_type",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "transcodedSuffix",
|
||||||
|
"columnName": "transcoded_suffix",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "duration",
|
||||||
|
"columnName": "duration",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "bitrate",
|
||||||
|
"columnName": "bitrate",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "path",
|
||||||
|
"columnName": "path",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "isVideo",
|
||||||
|
"columnName": "is_video",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "userRating",
|
||||||
|
"columnName": "user_rating",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "averageRating",
|
||||||
|
"columnName": "average_rating",
|
||||||
|
"affinity": "REAL",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "playCount",
|
||||||
|
"columnName": "play_count",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "discNumber",
|
||||||
|
"columnName": "disc_number",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "created",
|
||||||
|
"columnName": "created",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "starred",
|
||||||
|
"columnName": "starred",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "albumId",
|
||||||
|
"columnName": "album_id",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "artistId",
|
||||||
|
"columnName": "artist_id",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "type",
|
||||||
|
"columnName": "type",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "bookmarkPosition",
|
||||||
|
"columnName": "bookmark_position",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "originalWidth",
|
||||||
|
"columnName": "original_width",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "originalHeight",
|
||||||
|
"columnName": "original_height",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": false,
|
||||||
|
"columnNames": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"indices": [],
|
||||||
|
"foreignKeys": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "chronology",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `timestamp` INTEGER NOT NULL, `server` TEXT, `parent_id` TEXT, `is_dir` INTEGER NOT NULL, `title` TEXT, `album` TEXT, `artist` TEXT, `track` INTEGER, `year` INTEGER, `genre` TEXT, `cover_art_id` TEXT, `size` INTEGER, `content_type` TEXT, `suffix` TEXT, `transcoding_content_type` TEXT, `transcoded_suffix` TEXT, `duration` INTEGER, `bitrate` INTEGER, `path` TEXT, `is_video` INTEGER NOT NULL, `user_rating` INTEGER, `average_rating` REAL, `play_count` INTEGER, `disc_number` INTEGER, `created` INTEGER, `starred` INTEGER, `album_id` TEXT, `artist_id` TEXT, `type` TEXT, `bookmark_position` INTEGER, `original_width` INTEGER, `original_height` INTEGER, PRIMARY KEY(`id`))",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "id",
|
||||||
|
"columnName": "id",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "timestamp",
|
||||||
|
"columnName": "timestamp",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "server",
|
||||||
|
"columnName": "server",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "parentId",
|
||||||
|
"columnName": "parent_id",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "isDir",
|
||||||
|
"columnName": "is_dir",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "title",
|
||||||
|
"columnName": "title",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "album",
|
||||||
|
"columnName": "album",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "artist",
|
||||||
|
"columnName": "artist",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "track",
|
||||||
|
"columnName": "track",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "year",
|
||||||
|
"columnName": "year",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "genre",
|
||||||
|
"columnName": "genre",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "coverArtId",
|
||||||
|
"columnName": "cover_art_id",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "size",
|
||||||
|
"columnName": "size",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "contentType",
|
||||||
|
"columnName": "content_type",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "suffix",
|
||||||
|
"columnName": "suffix",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "transcodedContentType",
|
||||||
|
"columnName": "transcoding_content_type",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "transcodedSuffix",
|
||||||
|
"columnName": "transcoded_suffix",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "duration",
|
||||||
|
"columnName": "duration",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "bitrate",
|
||||||
|
"columnName": "bitrate",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "path",
|
||||||
|
"columnName": "path",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "isVideo",
|
||||||
|
"columnName": "is_video",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "userRating",
|
||||||
|
"columnName": "user_rating",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "averageRating",
|
||||||
|
"columnName": "average_rating",
|
||||||
|
"affinity": "REAL",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "playCount",
|
||||||
|
"columnName": "play_count",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "discNumber",
|
||||||
|
"columnName": "disc_number",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "created",
|
||||||
|
"columnName": "created",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "starred",
|
||||||
|
"columnName": "starred",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "albumId",
|
||||||
|
"columnName": "album_id",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "artistId",
|
||||||
|
"columnName": "artist_id",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "type",
|
||||||
|
"columnName": "type",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "bookmarkPosition",
|
||||||
|
"columnName": "bookmark_position",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "originalWidth",
|
||||||
|
"columnName": "original_width",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "originalHeight",
|
||||||
|
"columnName": "original_height",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": false,
|
||||||
|
"columnNames": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"indices": [],
|
||||||
|
"foreignKeys": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "favorite",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`timestamp` INTEGER NOT NULL, `songId` TEXT, `albumId` TEXT, `artistId` TEXT, `toStar` INTEGER NOT NULL, PRIMARY KEY(`timestamp`))",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "timestamp",
|
||||||
|
"columnName": "timestamp",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "songId",
|
||||||
|
"columnName": "songId",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "albumId",
|
||||||
|
"columnName": "albumId",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "artistId",
|
||||||
|
"columnName": "artistId",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "toStar",
|
||||||
|
"columnName": "toStar",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": false,
|
||||||
|
"columnNames": [
|
||||||
|
"timestamp"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"indices": [],
|
||||||
|
"foreignKeys": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"views": [],
|
||||||
|
"setupQueries": [
|
||||||
|
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
|
||||||
|
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '6ea111217793c58d54eabb1190dd92ec')"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,50 +1,71 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.INTERNET"/>
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
||||||
<uses-permission android:name="android.permission.WAKE_LOCK"/>
|
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
|
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
||||||
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||||
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK" />
|
||||||
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC" />
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:name="App"
|
android:name="App"
|
||||||
|
android:allowBackup="false"
|
||||||
android:icon="@mipmap/ic_launcher"
|
android:icon="@mipmap/ic_launcher"
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
|
android:localeConfig="@xml/locale_config"
|
||||||
android:roundIcon="@mipmap/ic_launcher"
|
android:roundIcon="@mipmap/ic_launcher"
|
||||||
android:supportsRtl="true"
|
android:supportsRtl="true"
|
||||||
android:theme="@style/AppTheme.SplashScreen"
|
android:theme="@style/AppTheme.SplashScreen"
|
||||||
android:usesCleartextTraffic="true"
|
android:usesCleartextTraffic="true">
|
||||||
android:allowBackup="false">
|
|
||||||
|
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="com.google.android.gms.cast.framework.OPTIONS_PROVIDER_CLASS_NAME"
|
android:name="com.google.android.gms.cast.framework.OPTIONS_PROVIDER_CLASS_NAME"
|
||||||
android:value="androidx.media3.cast.DefaultCastOptionsProvider"/>
|
android:value="androidx.media3.cast.DefaultCastOptionsProvider" />
|
||||||
|
|
||||||
|
<meta-data
|
||||||
|
android:name="androidx.car.app.TintableAttributionIcon"
|
||||||
|
android:resource="@drawable/ic_graphic_eq" />
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".ui.activity.MainActivity"
|
android:name=".ui.activity.MainActivity"
|
||||||
android:windowSoftInputMode="adjustPan|adjustResize"
|
android:exported="true"
|
||||||
android:exported="true">
|
android:windowSoftInputMode="adjustPan|adjustResize">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
<category android:name="android.intent.category.LAUNCHER" />
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<service
|
<service
|
||||||
android:name=".service.MediaService"
|
android:name=".service.MediaService"
|
||||||
android:foregroundServiceType="mediaPlayback"
|
android:exported="true"
|
||||||
android:exported="true">
|
android:foregroundServiceType="mediaPlayback">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="androidx.media3.session.MediaLibraryService" />
|
<action android:name="androidx.media3.session.MediaLibraryService" />
|
||||||
<action android:name="androidx.media3.session.MediaBrowserService" />
|
<action android:name="androidx.media3.session.MediaBrowserService" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</service>
|
</service>
|
||||||
<service android:name=".service.DownloaderService"
|
|
||||||
android:exported="true">
|
<service
|
||||||
|
android:name=".service.DownloaderService"
|
||||||
|
android:exported="true"
|
||||||
|
android:foregroundServiceType="dataSync">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="androidx.media3.exoplayer.downloadService.action.RESTART"/>
|
<action android:name="androidx.media3.exoplayer.downloadService.action.RESTART" />
|
||||||
<category android:name="android.intent.category.DEFAULT"/>
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</service>
|
</service>
|
||||||
|
|
||||||
|
<service
|
||||||
|
android:name="androidx.appcompat.app.AppLocalesMetadataHolderService"
|
||||||
|
android:enabled="false"
|
||||||
|
android:exported="false">
|
||||||
|
<meta-data
|
||||||
|
android:name="autoStoreLocales"
|
||||||
|
android:value="true" />
|
||||||
|
</service>
|
||||||
</application>
|
</application>
|
||||||
</manifest>
|
</manifest>
|
||||||
@@ -10,7 +10,6 @@ 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;
|
||||||
import com.cappielloantonio.tempo.util.Preferences;
|
import com.cappielloantonio.tempo.util.Preferences;
|
||||||
import com.google.android.material.color.DynamicColors;
|
|
||||||
|
|
||||||
public class App extends Application {
|
public class App extends Application {
|
||||||
private static App instance;
|
private static App instance;
|
||||||
@@ -22,7 +21,6 @@ public class App extends Application {
|
|||||||
public void onCreate() {
|
public void onCreate() {
|
||||||
super.onCreate();
|
super.onCreate();
|
||||||
|
|
||||||
DynamicColors.applyToActivitiesIfAvailable(this);
|
|
||||||
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
|
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
|
||||||
String themePref = sharedPreferences.getString(Preferences.THEME, ThemeHelper.DEFAULT_MODE);
|
String themePref = sharedPreferences.getString(Preferences.THEME, ThemeHelper.DEFAULT_MODE);
|
||||||
ThemeHelper.applyTheme(themePref);
|
ThemeHelper.applyTheme(themePref);
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package com.cappielloantonio.tempo.database;
|
package com.cappielloantonio.tempo.database;
|
||||||
|
|
||||||
|
import androidx.room.AutoMigration;
|
||||||
import androidx.room.Database;
|
import androidx.room.Database;
|
||||||
import androidx.room.Room;
|
import androidx.room.Room;
|
||||||
import androidx.room.RoomDatabase;
|
import androidx.room.RoomDatabase;
|
||||||
@@ -9,19 +10,21 @@ import com.cappielloantonio.tempo.App;
|
|||||||
import com.cappielloantonio.tempo.database.converter.DateConverters;
|
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.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;
|
||||||
import com.cappielloantonio.tempo.model.Chronology;
|
import com.cappielloantonio.tempo.model.Chronology;
|
||||||
import com.cappielloantonio.tempo.model.Download;
|
import com.cappielloantonio.tempo.model.Download;
|
||||||
|
import com.cappielloantonio.tempo.model.Favorite;
|
||||||
import com.cappielloantonio.tempo.model.Queue;
|
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;
|
||||||
|
|
||||||
@Database(
|
@Database(
|
||||||
version = 1,
|
version = 3,
|
||||||
entities = {Queue.class, Server.class, RecentSearch.class, Download.class, Chronology.class}
|
entities = {Queue.class, Server.class, RecentSearch.class, Download.class, Chronology.class, Favorite.class},
|
||||||
// autoMigrations = {@AutoMigration(from = 61, to = 62)}
|
autoMigrations = {@AutoMigration(from = 2, to = 3)}
|
||||||
)
|
)
|
||||||
@TypeConverters({DateConverters.class})
|
@TypeConverters({DateConverters.class})
|
||||||
public abstract class AppDatabase extends RoomDatabase {
|
public abstract class AppDatabase extends RoomDatabase {
|
||||||
@@ -47,4 +50,6 @@ public abstract class AppDatabase extends RoomDatabase {
|
|||||||
public abstract DownloadDao downloadDao();
|
public abstract DownloadDao downloadDao();
|
||||||
|
|
||||||
public abstract ChronologyDao chronologyDao();
|
public abstract ChronologyDao chronologyDao();
|
||||||
|
|
||||||
|
public abstract FavoriteDao favoriteDao();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,9 @@ 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, track ASC")
|
||||||
LiveData<List<Download>> getAll();
|
LiveData<List<Download>> getAll();
|
||||||
|
|
||||||
|
@Query("SELECT * FROM download WHERE id = :id")
|
||||||
|
Download getOne(String id);
|
||||||
|
|
||||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||||
void insert(Download download);
|
void insert(Download download);
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
package com.cappielloantonio.tempo.database.dao;
|
||||||
|
|
||||||
|
import androidx.room.Dao;
|
||||||
|
import androidx.room.Delete;
|
||||||
|
import androidx.room.Insert;
|
||||||
|
import androidx.room.OnConflictStrategy;
|
||||||
|
import androidx.room.Query;
|
||||||
|
|
||||||
|
import com.cappielloantonio.tempo.model.Favorite;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Dao
|
||||||
|
public interface FavoriteDao {
|
||||||
|
@Query("SELECT * FROM favorite")
|
||||||
|
List<Favorite> getAll();
|
||||||
|
|
||||||
|
@Insert(onConflict = OnConflictStrategy.IGNORE)
|
||||||
|
void insert(Favorite favorite);
|
||||||
|
|
||||||
|
@Delete
|
||||||
|
void delete(Favorite favorite);
|
||||||
|
|
||||||
|
@Query("DELETE FROM favorite")
|
||||||
|
void deleteAll();
|
||||||
|
}
|
||||||
@@ -16,6 +16,7 @@ import com.bumptech.glide.request.RequestOptions;
|
|||||||
import com.bumptech.glide.signature.ObjectKey;
|
import com.bumptech.glide.signature.ObjectKey;
|
||||||
import com.cappielloantonio.tempo.App;
|
import com.cappielloantonio.tempo.App;
|
||||||
import com.cappielloantonio.tempo.util.Preferences;
|
import com.cappielloantonio.tempo.util.Preferences;
|
||||||
|
import com.cappielloantonio.tempo.util.Util;
|
||||||
import com.google.android.material.elevation.SurfaceColors;
|
import com.google.android.material.elevation.SurfaceColors;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@@ -46,7 +47,7 @@ public class CustomGlideRequest {
|
|||||||
uri.append("getCoverArt");
|
uri.append("getCoverArt");
|
||||||
|
|
||||||
if (params.containsKey("u") && params.get("u") != null)
|
if (params.containsKey("u") && params.get("u") != null)
|
||||||
uri.append("?u=").append(params.get("u"));
|
uri.append("?u=").append(Util.encode(params.get("u")));
|
||||||
if (params.containsKey("p") && params.get("p") != null)
|
if (params.containsKey("p") && params.get("p") != null)
|
||||||
uri.append("&p=").append(params.get("p"));
|
uri.append("&p=").append(params.get("p"));
|
||||||
if (params.containsKey("s") && params.get("s") != null)
|
if (params.containsKey("s") && params.get("s") != null)
|
||||||
|
|||||||
@@ -0,0 +1,197 @@
|
|||||||
|
package com.cappielloantonio.tempo.helper.recyclerview;
|
||||||
|
|
||||||
|
import android.animation.Animator;
|
||||||
|
import android.animation.AnimatorListenerAdapter;
|
||||||
|
import android.animation.ObjectAnimator;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.MotionEvent;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import androidx.annotation.IdRes;
|
||||||
|
import androidx.annotation.LayoutRes;
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.core.view.ViewCompat;
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
|
public class FastScrollbar extends LinearLayout {
|
||||||
|
private static final int BUBBLE_ANIMATION_DURATION = 100;
|
||||||
|
private static final int TRACK_SNAP_RANGE = 5;
|
||||||
|
|
||||||
|
private TextView bubble;
|
||||||
|
private View handle;
|
||||||
|
private RecyclerView recyclerView;
|
||||||
|
private int height;
|
||||||
|
private boolean isInitialized = false;
|
||||||
|
private ObjectAnimator currentAnimator = null;
|
||||||
|
|
||||||
|
private final RecyclerView.OnScrollListener onScrollListener = new RecyclerView.OnScrollListener() {
|
||||||
|
@Override
|
||||||
|
public void onScrolled(@NonNull final RecyclerView recyclerView, final int dx, final int dy) {
|
||||||
|
updateBubbleAndHandlePosition();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public interface BubbleTextGetter {
|
||||||
|
String getTextToShowInBubble(int pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
public FastScrollbar(final Context context, final AttributeSet attrs, final int defStyleAttr) {
|
||||||
|
super(context, attrs, defStyleAttr);
|
||||||
|
init(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
public FastScrollbar(final Context context) {
|
||||||
|
super(context);
|
||||||
|
init(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
public FastScrollbar(final Context context, final AttributeSet attrs) {
|
||||||
|
super(context, attrs);
|
||||||
|
init(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void init(Context context) {
|
||||||
|
if (isInitialized) return;
|
||||||
|
isInitialized = true;
|
||||||
|
setOrientation(HORIZONTAL);
|
||||||
|
setClipChildren(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setViewsToUse(@LayoutRes int layoutResId, @IdRes int bubbleResId, @IdRes int handleResId) {
|
||||||
|
final LayoutInflater inflater = LayoutInflater.from(getContext());
|
||||||
|
inflater.inflate(layoutResId, this, true);
|
||||||
|
bubble = findViewById(bubbleResId);
|
||||||
|
if (bubble != null) bubble.setVisibility(INVISIBLE);
|
||||||
|
handle = findViewById(handleResId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
|
||||||
|
super.onSizeChanged(w, h, oldw, oldh);
|
||||||
|
height = h;
|
||||||
|
updateBubbleAndHandlePosition();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onTouchEvent(@NonNull MotionEvent event) {
|
||||||
|
final int action = event.getAction();
|
||||||
|
switch (action) {
|
||||||
|
case MotionEvent.ACTION_DOWN:
|
||||||
|
if (event.getX() < handle.getX() - ViewCompat.getPaddingStart(handle)) return false;
|
||||||
|
if (currentAnimator != null) currentAnimator.cancel();
|
||||||
|
if (bubble != null && bubble.getVisibility() == INVISIBLE) showBubble();
|
||||||
|
handle.setSelected(true);
|
||||||
|
case MotionEvent.ACTION_MOVE:
|
||||||
|
final float y = event.getY();
|
||||||
|
setBubbleAndHandlePosition(y);
|
||||||
|
setRecyclerViewPosition(y);
|
||||||
|
return true;
|
||||||
|
case MotionEvent.ACTION_UP:
|
||||||
|
case MotionEvent.ACTION_CANCEL:
|
||||||
|
handle.setSelected(false);
|
||||||
|
hideBubble();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return super.onTouchEvent(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRecyclerView(final RecyclerView recyclerView) {
|
||||||
|
if (this.recyclerView != recyclerView) {
|
||||||
|
if (this.recyclerView != null)
|
||||||
|
this.recyclerView.removeOnScrollListener(onScrollListener);
|
||||||
|
this.recyclerView = recyclerView;
|
||||||
|
if (this.recyclerView == null) return;
|
||||||
|
recyclerView.addOnScrollListener(onScrollListener);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onDetachedFromWindow() {
|
||||||
|
super.onDetachedFromWindow();
|
||||||
|
if (recyclerView != null) {
|
||||||
|
recyclerView.removeOnScrollListener(onScrollListener);
|
||||||
|
recyclerView = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setRecyclerViewPosition(float y) {
|
||||||
|
if (recyclerView != null) {
|
||||||
|
final int itemCount = recyclerView.getAdapter().getItemCount();
|
||||||
|
float proportion;
|
||||||
|
if (handle.getY() == 0) proportion = 0f;
|
||||||
|
else if (handle.getY() + handle.getHeight() >= height - TRACK_SNAP_RANGE)
|
||||||
|
proportion = 1f;
|
||||||
|
else proportion = y / (float) height;
|
||||||
|
final int targetPos = getValueInRange(0, itemCount - 1, (int) (proportion * (float) itemCount));
|
||||||
|
((LinearLayoutManager) recyclerView.getLayoutManager()).scrollToPositionWithOffset(targetPos, 0);
|
||||||
|
final String bubbleText = ((BubbleTextGetter) recyclerView.getAdapter()).getTextToShowInBubble(targetPos);
|
||||||
|
if (bubble != null) {
|
||||||
|
bubble.setText(bubbleText);
|
||||||
|
if (TextUtils.isEmpty(bubbleText)) {
|
||||||
|
hideBubble();
|
||||||
|
} else if (bubble.getVisibility() == View.INVISIBLE) {
|
||||||
|
showBubble();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getValueInRange(int min, int max, int value) {
|
||||||
|
int minimum = Math.max(min, value);
|
||||||
|
return Math.min(minimum, max);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateBubbleAndHandlePosition() {
|
||||||
|
if (bubble == null || handle.isSelected()) return;
|
||||||
|
|
||||||
|
final int verticalScrollOffset = recyclerView.computeVerticalScrollOffset();
|
||||||
|
final int verticalScrollRange = recyclerView.computeVerticalScrollRange();
|
||||||
|
float proportion = (float) verticalScrollOffset / ((float) verticalScrollRange - height);
|
||||||
|
setBubbleAndHandlePosition(height * proportion);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setBubbleAndHandlePosition(float y) {
|
||||||
|
final int handleHeight = handle.getHeight();
|
||||||
|
handle.setY(getValueInRange(0, height - handleHeight, (int) (y - handleHeight / 2)));
|
||||||
|
if (bubble != null) {
|
||||||
|
int bubbleHeight = bubble.getHeight();
|
||||||
|
bubble.setY(getValueInRange(0, height - bubbleHeight - handleHeight / 2, (int) (y - bubbleHeight)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showBubble() {
|
||||||
|
if (bubble == null) return;
|
||||||
|
bubble.setVisibility(VISIBLE);
|
||||||
|
if (currentAnimator != null) currentAnimator.cancel();
|
||||||
|
currentAnimator = ObjectAnimator.ofFloat(bubble, "alpha", 0f, 1f).setDuration(BUBBLE_ANIMATION_DURATION);
|
||||||
|
currentAnimator.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void hideBubble() {
|
||||||
|
if (bubble == null) return;
|
||||||
|
if (currentAnimator != null) currentAnimator.cancel();
|
||||||
|
currentAnimator = ObjectAnimator.ofFloat(bubble, "alpha", 1f, 0f).setDuration(BUBBLE_ANIMATION_DURATION);
|
||||||
|
currentAnimator.addListener(new AnimatorListenerAdapter() {
|
||||||
|
@Override
|
||||||
|
public void onAnimationEnd(Animator animation) {
|
||||||
|
super.onAnimationEnd(animation);
|
||||||
|
bubble.setVisibility(INVISIBLE);
|
||||||
|
currentAnimator = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAnimationCancel(Animator animation) {
|
||||||
|
super.onAnimationCancel(animation);
|
||||||
|
bubble.setVisibility(INVISIBLE);
|
||||||
|
currentAnimator = null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
currentAnimator.start();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -20,6 +20,7 @@ public interface ClickCallback {
|
|||||||
default void onServerClick(Bundle bundle) {}
|
default void onServerClick(Bundle bundle) {}
|
||||||
default void onServerLongClick(Bundle bundle) {}
|
default void onServerLongClick(Bundle bundle) {}
|
||||||
default void onPodcastEpisodeClick(Bundle bundle) {}
|
default void onPodcastEpisodeClick(Bundle bundle) {}
|
||||||
|
default void onPodcastEpisodeAltClick(Bundle bundle) {}
|
||||||
default void onPodcastEpisodeLongClick(Bundle bundle) {}
|
default void onPodcastEpisodeLongClick(Bundle bundle) {}
|
||||||
default void onPodcastChannelClick(Bundle bundle) {}
|
default void onPodcastChannelClick(Bundle bundle) {}
|
||||||
default void onPodcastChannelLongClick(Bundle bundle) {}
|
default void onPodcastChannelLongClick(Bundle bundle) {}
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package com.cappielloantonio.tempo.interfaces;
|
||||||
|
|
||||||
|
|
||||||
|
import androidx.annotation.Keep;
|
||||||
|
|
||||||
|
@Keep
|
||||||
|
public interface DialogClickCallback {
|
||||||
|
default void onPositiveClick() {}
|
||||||
|
|
||||||
|
default void onNegativeClick() {}
|
||||||
|
|
||||||
|
default void onNeutralClick() {}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package com.cappielloantonio.tempo.interfaces;
|
||||||
|
|
||||||
|
import androidx.annotation.Keep;
|
||||||
|
|
||||||
|
@Keep
|
||||||
|
public interface StarCallback {
|
||||||
|
default void onError() {}
|
||||||
|
default void onSuccess() {}
|
||||||
|
}
|
||||||
@@ -20,6 +20,9 @@ class Download(@PrimaryKey override val id: String) : Child(id) {
|
|||||||
@ColumnInfo(name = "download_state", defaultValue = "1")
|
@ColumnInfo(name = "download_state", defaultValue = "1")
|
||||||
var downloadState: Int = 0
|
var downloadState: Int = 0
|
||||||
|
|
||||||
|
@ColumnInfo(name = "download_uri", defaultValue = "")
|
||||||
|
var downloadUri: String? = null
|
||||||
|
|
||||||
constructor(child: Child) : this(child.id) {
|
constructor(child: Child) : this(child.id) {
|
||||||
parentId = child.parentId
|
parentId = child.parentId
|
||||||
isDir = child.isDir
|
isDir = child.isDir
|
||||||
@@ -52,4 +55,10 @@ class Download(@PrimaryKey override val id: String) : Child(id) {
|
|||||||
originalWidth = child.originalWidth
|
originalWidth = child.originalWidth
|
||||||
originalHeight = child.originalHeight
|
originalHeight = child.originalHeight
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Keep
|
||||||
|
data class DownloadStack(
|
||||||
|
var id: String,
|
||||||
|
var view: String?
|
||||||
|
)
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
package com.cappielloantonio.tempo.model
|
||||||
|
|
||||||
|
import android.os.Parcelable
|
||||||
|
import androidx.annotation.Keep
|
||||||
|
import androidx.annotation.Nullable
|
||||||
|
import androidx.room.ColumnInfo
|
||||||
|
import androidx.room.Entity
|
||||||
|
import androidx.room.PrimaryKey
|
||||||
|
import kotlinx.parcelize.Parcelize
|
||||||
|
|
||||||
|
@Keep
|
||||||
|
@Parcelize
|
||||||
|
@Entity(tableName = "favorite")
|
||||||
|
data class Favorite(
|
||||||
|
@PrimaryKey
|
||||||
|
@ColumnInfo(name = "timestamp")
|
||||||
|
var timestamp: Long,
|
||||||
|
|
||||||
|
@ColumnInfo(name = "songId")
|
||||||
|
val songId: String?,
|
||||||
|
|
||||||
|
@ColumnInfo(name = "albumId")
|
||||||
|
val albumId: String?,
|
||||||
|
|
||||||
|
@ColumnInfo(name = "artistId")
|
||||||
|
val artistId: String?,
|
||||||
|
|
||||||
|
@ColumnInfo(name = "toStar")
|
||||||
|
val toStar: Boolean,
|
||||||
|
) : Parcelable {
|
||||||
|
override fun toString(): String = (songId ?: "null") + (albumId ?: "null") + (artistId ?: "null")
|
||||||
|
}
|
||||||
@@ -21,8 +21,6 @@ import retrofit2.Callback;
|
|||||||
import retrofit2.Response;
|
import retrofit2.Response;
|
||||||
|
|
||||||
public class AlbumRepository {
|
public class AlbumRepository {
|
||||||
private static final String TAG = "AlbumRepository";
|
|
||||||
|
|
||||||
public MutableLiveData<List<AlbumID3>> getAlbums(String type, int size, Integer fromYear, Integer toYear) {
|
public MutableLiveData<List<AlbumID3>> getAlbums(String type, int size, Integer fromYear, Integer toYear) {
|
||||||
MutableLiveData<List<AlbumID3>> listLiveAlbums = new MutableLiveData<>(new ArrayList<>());
|
MutableLiveData<List<AlbumID3>> listLiveAlbums = new MutableLiveData<>(new ArrayList<>());
|
||||||
|
|
||||||
@@ -78,40 +76,6 @@ public class AlbumRepository {
|
|||||||
return starredAlbums;
|
return starredAlbums;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void star(String id) {
|
|
||||||
App.getSubsonicClientInstance(false)
|
|
||||||
.getMediaAnnotationClient()
|
|
||||||
.star(null, id, null)
|
|
||||||
.enqueue(new Callback<ApiResponse>() {
|
|
||||||
@Override
|
|
||||||
public void onResponse(@NonNull Call<ApiResponse> call, @NonNull Response<ApiResponse> response) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onFailure(@NonNull Call<ApiResponse> call, @NonNull Throwable t) {
|
|
||||||
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public void unstar(String id) {
|
|
||||||
App.getSubsonicClientInstance(false)
|
|
||||||
.getMediaAnnotationClient()
|
|
||||||
.unstar(null, id, null)
|
|
||||||
.enqueue(new Callback<ApiResponse>() {
|
|
||||||
@Override
|
|
||||||
public void onResponse(@NonNull Call<ApiResponse> call, @NonNull Response<ApiResponse> response) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onFailure(@NonNull Call<ApiResponse> call, @NonNull Throwable t) {
|
|
||||||
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setRating(String id, int rating) {
|
public void setRating(String id, int rating) {
|
||||||
App.getSubsonicClientInstance(false)
|
App.getSubsonicClientInstance(false)
|
||||||
.getMediaAnnotationClient()
|
.getMediaAnnotationClient()
|
||||||
|
|||||||
@@ -63,8 +63,10 @@ public class ArtistRepository {
|
|||||||
if (response.isSuccessful() && response.body() != null) {
|
if (response.isSuccessful() && response.body() != null) {
|
||||||
List<ArtistID3> artists = new ArrayList<>();
|
List<ArtistID3> artists = new ArrayList<>();
|
||||||
|
|
||||||
for (IndexID3 index : response.body().getSubsonicResponse().getArtists().getIndices()) {
|
if(response.body().getSubsonicResponse().getArtists() != null) {
|
||||||
artists.addAll(index.getArtists());
|
for (IndexID3 index : response.body().getSubsonicResponse().getArtists().getIndices()) {
|
||||||
|
artists.addAll(index.getArtists());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (random) {
|
if (random) {
|
||||||
@@ -135,9 +137,6 @@ public class ArtistRepository {
|
|||||||
return artist;
|
return artist;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Metodo che mi restituisce le informazioni complete dell'artista (bio, immagini prese da last.fm, artisti simili...)
|
|
||||||
*/
|
|
||||||
public MutableLiveData<ArtistInfo2> getArtistFullInfo(String id) {
|
public MutableLiveData<ArtistInfo2> getArtistFullInfo(String id) {
|
||||||
MutableLiveData<ArtistInfo2> artistFullInfo = new MutableLiveData<>(null);
|
MutableLiveData<ArtistInfo2> artistFullInfo = new MutableLiveData<>(null);
|
||||||
|
|
||||||
@@ -161,40 +160,6 @@ public class ArtistRepository {
|
|||||||
return artistFullInfo;
|
return artistFullInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void star(String id) {
|
|
||||||
App.getSubsonicClientInstance(false)
|
|
||||||
.getMediaAnnotationClient()
|
|
||||||
.star(null, null, id)
|
|
||||||
.enqueue(new Callback<ApiResponse>() {
|
|
||||||
@Override
|
|
||||||
public void onResponse(@NonNull Call<ApiResponse> call, @NonNull Response<ApiResponse> response) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onFailure(@NonNull Call<ApiResponse> call, @NonNull Throwable t) {
|
|
||||||
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public void unstar(String id) {
|
|
||||||
App.getSubsonicClientInstance(false)
|
|
||||||
.getMediaAnnotationClient()
|
|
||||||
.unstar(null, null, id)
|
|
||||||
.enqueue(new Callback<ApiResponse>() {
|
|
||||||
@Override
|
|
||||||
public void onResponse(@NonNull Call<ApiResponse> call, @NonNull Response<ApiResponse> response) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onFailure(@NonNull Call<ApiResponse> call, @NonNull Throwable t) {
|
|
||||||
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setRating(String id, int rating) {
|
public void setRating(String id, int rating) {
|
||||||
App.getSubsonicClientInstance(false)
|
App.getSubsonicClientInstance(false)
|
||||||
.getMediaAnnotationClient()
|
.getMediaAnnotationClient()
|
||||||
|
|||||||
@@ -4,8 +4,11 @@ import androidx.lifecycle.LiveData;
|
|||||||
|
|
||||||
import com.cappielloantonio.tempo.database.AppDatabase;
|
import com.cappielloantonio.tempo.database.AppDatabase;
|
||||||
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.model.Download;
|
import com.cappielloantonio.tempo.model.Download;
|
||||||
|
import com.cappielloantonio.tempo.model.Favorite;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class DownloadRepository {
|
public class DownloadRepository {
|
||||||
@@ -15,6 +18,43 @@ public class DownloadRepository {
|
|||||||
return downloadDao.getAll();
|
return downloadDao.getAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Download getDownload(String id) {
|
||||||
|
Download download = null;
|
||||||
|
|
||||||
|
GetDownloadThreadSafe getDownloadThreadSafe = new GetDownloadThreadSafe(downloadDao, id);
|
||||||
|
Thread thread = new Thread(getDownloadThreadSafe);
|
||||||
|
thread.start();
|
||||||
|
|
||||||
|
try {
|
||||||
|
thread.join();
|
||||||
|
download = getDownloadThreadSafe.getDownload();
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
return download;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class GetDownloadThreadSafe implements Runnable {
|
||||||
|
private final DownloadDao downloadDao;
|
||||||
|
private final String id;
|
||||||
|
private Download download;
|
||||||
|
|
||||||
|
public GetDownloadThreadSafe(DownloadDao downloadDao, String id) {
|
||||||
|
this.downloadDao = downloadDao;
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
download = downloadDao.getOne(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Download getDownload() {
|
||||||
|
return download;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void insert(Download download) {
|
public void insert(Download download) {
|
||||||
InsertThreadSafe insert = new InsertThreadSafe(downloadDao, download);
|
InsertThreadSafe insert = new InsertThreadSafe(downloadDao, download);
|
||||||
Thread thread = new Thread(insert);
|
Thread thread = new Thread(insert);
|
||||||
|
|||||||
@@ -0,0 +1,140 @@
|
|||||||
|
package com.cappielloantonio.tempo.repository;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
|
import com.cappielloantonio.tempo.App;
|
||||||
|
import com.cappielloantonio.tempo.database.AppDatabase;
|
||||||
|
import com.cappielloantonio.tempo.database.dao.FavoriteDao;
|
||||||
|
import com.cappielloantonio.tempo.interfaces.StarCallback;
|
||||||
|
import com.cappielloantonio.tempo.model.Favorite;
|
||||||
|
import com.cappielloantonio.tempo.subsonic.base.ApiResponse;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import retrofit2.Call;
|
||||||
|
import retrofit2.Callback;
|
||||||
|
import retrofit2.Response;
|
||||||
|
|
||||||
|
public class FavoriteRepository {
|
||||||
|
private final FavoriteDao favoriteDao = AppDatabase.getInstance().favoriteDao();
|
||||||
|
|
||||||
|
public void star(String id, String albumId, String artistId, StarCallback starCallback) {
|
||||||
|
App.getSubsonicClientInstance(false)
|
||||||
|
.getMediaAnnotationClient()
|
||||||
|
.star(id, albumId, artistId)
|
||||||
|
.enqueue(new Callback<ApiResponse>() {
|
||||||
|
@Override
|
||||||
|
public void onResponse(@NonNull Call<ApiResponse> call, @NonNull Response<ApiResponse> response) {
|
||||||
|
if (response.isSuccessful()) {
|
||||||
|
starCallback.onSuccess();
|
||||||
|
} else {
|
||||||
|
starCallback.onError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailure(@NonNull Call<ApiResponse> call, @NonNull Throwable t) {
|
||||||
|
starCallback.onError();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void unstar(String id, String albumId, String artistId, StarCallback starCallback) {
|
||||||
|
App.getSubsonicClientInstance(false)
|
||||||
|
.getMediaAnnotationClient()
|
||||||
|
.unstar(id, albumId, artistId)
|
||||||
|
.enqueue(new Callback<ApiResponse>() {
|
||||||
|
@Override
|
||||||
|
public void onResponse(@NonNull Call<ApiResponse> call, @NonNull Response<ApiResponse> response) {
|
||||||
|
if (response.isSuccessful()) {
|
||||||
|
starCallback.onSuccess();
|
||||||
|
} else {
|
||||||
|
starCallback.onError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailure(@NonNull Call<ApiResponse> call, @NonNull Throwable t) {
|
||||||
|
starCallback.onError();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Favorite> getFavorites() {
|
||||||
|
List<Favorite> favorites = new ArrayList<>();
|
||||||
|
|
||||||
|
GetAllThreadSafe getAllThreadSafe = new GetAllThreadSafe(favoriteDao);
|
||||||
|
Thread thread = new Thread(getAllThreadSafe);
|
||||||
|
thread.start();
|
||||||
|
|
||||||
|
try {
|
||||||
|
thread.join();
|
||||||
|
favorites = getAllThreadSafe.getFavorites();
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
return favorites;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class GetAllThreadSafe implements Runnable {
|
||||||
|
private final FavoriteDao favoriteDao;
|
||||||
|
private List<Favorite> favorites = new ArrayList<>();
|
||||||
|
|
||||||
|
public GetAllThreadSafe(FavoriteDao favoriteDao) {
|
||||||
|
this.favoriteDao = favoriteDao;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
favorites = favoriteDao.getAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Favorite> getFavorites() {
|
||||||
|
return favorites;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void starLater(String id, String albumId, String artistId, boolean toStar) {
|
||||||
|
InsertThreadSafe insert = new InsertThreadSafe(favoriteDao, new Favorite(System.currentTimeMillis(), id, albumId, artistId, toStar));
|
||||||
|
Thread thread = new Thread(insert);
|
||||||
|
thread.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class InsertThreadSafe implements Runnable {
|
||||||
|
private final FavoriteDao favoriteDao;
|
||||||
|
private final Favorite favorite;
|
||||||
|
|
||||||
|
public InsertThreadSafe(FavoriteDao favoriteDao, Favorite favorite) {
|
||||||
|
this.favoriteDao = favoriteDao;
|
||||||
|
this.favorite = favorite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
favoriteDao.insert(favorite);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void delete(Favorite favorite) {
|
||||||
|
DeleteThreadSafe delete = new DeleteThreadSafe(favoriteDao, favorite);
|
||||||
|
Thread thread = new Thread(delete);
|
||||||
|
thread.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class DeleteThreadSafe implements Runnable {
|
||||||
|
private final FavoriteDao favoriteDao;
|
||||||
|
private final Favorite favorite;
|
||||||
|
|
||||||
|
public DeleteThreadSafe(FavoriteDao favoriteDao, Favorite favorite) {
|
||||||
|
this.favoriteDao = favoriteDao;
|
||||||
|
this.favorite = favorite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
favoriteDao.delete(favorite);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -133,4 +133,21 @@ public class PodcastRepository {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void downloadPodcastEpisode(String episodeId) {
|
||||||
|
App.getSubsonicClientInstance(false)
|
||||||
|
.getPodcastClient()
|
||||||
|
.downloadPodcastEpisode(episodeId)
|
||||||
|
.enqueue(new Callback<ApiResponse>() {
|
||||||
|
@Override
|
||||||
|
public void onResponse(@NonNull Call<ApiResponse> call, @NonNull Response<ApiResponse> response) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailure(@NonNull Call<ApiResponse> call, @NonNull Throwable t) {
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ 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;
|
||||||
|
|
||||||
@@ -119,40 +121,6 @@ public class SongRepository {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public void star(String id) {
|
|
||||||
App.getSubsonicClientInstance(false)
|
|
||||||
.getMediaAnnotationClient()
|
|
||||||
.star(id, null, null)
|
|
||||||
.enqueue(new Callback<ApiResponse>() {
|
|
||||||
@Override
|
|
||||||
public void onResponse(@NonNull Call<ApiResponse> call, @NonNull Response<ApiResponse> response) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onFailure(@NonNull Call<ApiResponse> call, @NonNull Throwable t) {
|
|
||||||
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public void unstar(String id) {
|
|
||||||
App.getSubsonicClientInstance(false)
|
|
||||||
.getMediaAnnotationClient()
|
|
||||||
.unstar(id, null, null)
|
|
||||||
.enqueue(new Callback<ApiResponse>() {
|
|
||||||
@Override
|
|
||||||
public void onResponse(@NonNull Call<ApiResponse> call, @NonNull Response<ApiResponse> response) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onFailure(@NonNull Call<ApiResponse> call, @NonNull Throwable t) {
|
|
||||||
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setRating(String id, int rating) {
|
public void setRating(String id, int rating) {
|
||||||
App.getSubsonicClientInstance(false)
|
App.getSubsonicClientInstance(false)
|
||||||
.getMediaAnnotationClient()
|
.getMediaAnnotationClient()
|
||||||
@@ -173,8 +141,6 @@ public class SongRepository {
|
|||||||
public MutableLiveData<List<Child>> getSongsByGenre(String id, int page) {
|
public MutableLiveData<List<Child>> getSongsByGenre(String id, int page) {
|
||||||
MutableLiveData<List<Child>> songsByGenre = new MutableLiveData<>();
|
MutableLiveData<List<Child>> songsByGenre = new MutableLiveData<>();
|
||||||
|
|
||||||
Log.d(TAG, "onScrolled PAGE: " + page);
|
|
||||||
|
|
||||||
App.getSubsonicClientInstance(false)
|
App.getSubsonicClientInstance(false)
|
||||||
.getAlbumSongListClient()
|
.getAlbumSongListClient()
|
||||||
.getSongsByGenre(id, 100, 100 * page)
|
.getSongsByGenre(id, 100, 100 * page)
|
||||||
|
|||||||
@@ -77,6 +77,8 @@ public class DownloaderManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void download(MediaItem mediaItem, com.cappielloantonio.tempo.model.Download download) {
|
public void download(MediaItem mediaItem, com.cappielloantonio.tempo.model.Download download) {
|
||||||
|
download.setDownloadUri(mediaItem.requestMetadata.mediaUri.toString());
|
||||||
|
|
||||||
DownloadService.sendAddDownload(context, DownloaderService.class, buildDownloadRequest(mediaItem), false);
|
DownloadService.sendAddDownload(context, DownloaderService.class, buildDownloadRequest(mediaItem), false);
|
||||||
insertDatabase(download);
|
insertDatabase(download);
|
||||||
}
|
}
|
||||||
@@ -89,6 +91,7 @@ public class DownloaderManager {
|
|||||||
|
|
||||||
public void remove(MediaItem mediaItem, com.cappielloantonio.tempo.model.Download download) {
|
public void remove(MediaItem mediaItem, com.cappielloantonio.tempo.model.Download download) {
|
||||||
DownloadService.sendRemoveDownload(context, DownloaderService.class, buildDownloadRequest(mediaItem).id, false);
|
DownloadService.sendRemoveDownload(context, DownloaderService.class, buildDownloadRequest(mediaItem).id, false);
|
||||||
|
deleteDatabase(download.getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void remove(List<MediaItem> mediaItems, List<com.cappielloantonio.tempo.model.Download> downloads) {
|
public void remove(List<MediaItem> mediaItems, List<com.cappielloantonio.tempo.model.Download> downloads) {
|
||||||
@@ -97,6 +100,12 @@ public class DownloaderManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void removeAll() {
|
||||||
|
DownloadService.sendRemoveAllDownloads(context, DownloaderService.class, false);
|
||||||
|
deleteAllDatabase();
|
||||||
|
DownloadUtil.eraseDownloadFolder(context);
|
||||||
|
}
|
||||||
|
|
||||||
private void loadDownloads() {
|
private void loadDownloads() {
|
||||||
try (DownloadCursor loadedDownloads = downloadIndex.getDownloads()) {
|
try (DownloadCursor loadedDownloads = downloadIndex.getDownloads()) {
|
||||||
while (loadedDownloads.moveToNext()) {
|
while (loadedDownloads.moveToNext()) {
|
||||||
@@ -108,6 +117,11 @@ public class DownloaderManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String getDownloadNotificationMessage(String id) {
|
||||||
|
com.cappielloantonio.tempo.model.Download download = getDownloadRepository().getDownload(id);
|
||||||
|
return download != null ? download.getTitle() : null;
|
||||||
|
}
|
||||||
|
|
||||||
private static DownloadRepository getDownloadRepository() {
|
private static DownloadRepository getDownloadRepository() {
|
||||||
return new DownloadRepository();
|
return new DownloadRepository();
|
||||||
}
|
}
|
||||||
@@ -120,6 +134,10 @@ public class DownloaderManager {
|
|||||||
getDownloadRepository().delete(id);
|
getDownloadRepository().delete(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void deleteAllDatabase() {
|
||||||
|
getDownloadRepository().deleteAll();
|
||||||
|
}
|
||||||
|
|
||||||
public static void updateDatabase(String id) {
|
public static void updateDatabase(String id) {
|
||||||
getDownloadRepository().update(id);
|
getDownloadRepository().update(id);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import androidx.annotation.NonNull;
|
|||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.media3.common.util.NotificationUtil;
|
import androidx.media3.common.util.NotificationUtil;
|
||||||
import androidx.media3.common.util.UnstableApi;
|
import androidx.media3.common.util.UnstableApi;
|
||||||
import androidx.media3.common.util.Util;
|
|
||||||
import androidx.media3.exoplayer.offline.Download;
|
import androidx.media3.exoplayer.offline.Download;
|
||||||
import androidx.media3.exoplayer.offline.DownloadManager;
|
import androidx.media3.exoplayer.offline.DownloadManager;
|
||||||
import androidx.media3.exoplayer.offline.DownloadNotificationHelper;
|
import androidx.media3.exoplayer.offline.DownloadNotificationHelper;
|
||||||
@@ -52,6 +51,8 @@ public class DownloaderService extends androidx.media3.exoplayer.offline.Downloa
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static final class TerminalStateNotificationHelper implements DownloadManager.Listener {
|
private static final class TerminalStateNotificationHelper implements DownloadManager.Listener {
|
||||||
|
private static final String TAG = "TerminalStateNotificatinHelper";
|
||||||
|
|
||||||
private final Context context;
|
private final Context context;
|
||||||
private final DownloadNotificationHelper notificationHelper;
|
private final DownloadNotificationHelper notificationHelper;
|
||||||
|
|
||||||
@@ -68,10 +69,10 @@ public class DownloaderService extends androidx.media3.exoplayer.offline.Downloa
|
|||||||
Notification notification;
|
Notification notification;
|
||||||
|
|
||||||
if (download.state == Download.STATE_COMPLETED) {
|
if (download.state == Download.STATE_COMPLETED) {
|
||||||
notification = notificationHelper.buildDownloadCompletedNotification(context, R.drawable.ic_check_circle, null, Util.fromUtf8Bytes(download.request.data));
|
notification = notificationHelper.buildDownloadCompletedNotification(context, R.drawable.ic_check_circle, null, DownloaderManager.getDownloadNotificationMessage(download.request.id));
|
||||||
DownloaderManager.updateDatabase(download.request.id);
|
DownloaderManager.updateDatabase(download.request.id);
|
||||||
} else if (download.state == Download.STATE_FAILED) {
|
} else if (download.state == Download.STATE_FAILED) {
|
||||||
notification = notificationHelper.buildDownloadFailedNotification(context, R.drawable.ic_error, null, Util.fromUtf8Bytes(download.request.data));
|
notification = notificationHelper.buildDownloadFailedNotification(context, R.drawable.ic_error, null, DownloaderManager.getDownloadNotificationMessage(download.request.id));
|
||||||
} else {
|
} else {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,4 +48,9 @@ public class PodcastClient {
|
|||||||
Log.d(TAG, "deletePodcastEpisode()");
|
Log.d(TAG, "deletePodcastEpisode()");
|
||||||
return podcastService.deletePodcastEpisode(subsonic.getParams(), episodeId);
|
return podcastService.deletePodcastEpisode(subsonic.getParams(), episodeId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Call<ApiResponse> downloadPodcastEpisode(String episodeId) {
|
||||||
|
Log.d(TAG, "downloadPodcastEpisode()");
|
||||||
|
return podcastService.downloadPodcastEpisode(subsonic.getParams(), episodeId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,4 +27,7 @@ public interface PodcastService {
|
|||||||
|
|
||||||
@GET("deletePodcastEpisode")
|
@GET("deletePodcastEpisode")
|
||||||
Call<ApiResponse> deletePodcastEpisode(@QueryMap Map<String, String> params, @Query("id") String id);
|
Call<ApiResponse> deletePodcastEpisode(@QueryMap Map<String, String> params, @Query("id") String id);
|
||||||
|
|
||||||
|
@GET("downloadPodcastEpisode")
|
||||||
|
Call<ApiResponse> downloadPodcastEpisode(@QueryMap Map<String, String> params, @Query("id") String id);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package com.cappielloantonio.tempo.subsonic.models
|
|||||||
|
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
import androidx.annotation.Keep
|
import androidx.annotation.Keep
|
||||||
import androidx.room.ColumnInfo
|
|
||||||
import com.google.gson.annotations.SerializedName
|
import com.google.gson.annotations.SerializedName
|
||||||
import kotlinx.parcelize.Parcelize
|
import kotlinx.parcelize.Parcelize
|
||||||
import java.util.*
|
import java.util.*
|
||||||
@@ -29,11 +28,9 @@ class PodcastEpisode : Parcelable {
|
|||||||
var transcodedContentType: String? = null
|
var transcodedContentType: String? = null
|
||||||
var transcodedSuffix: String? = null
|
var transcodedSuffix: String? = null
|
||||||
var duration: Int? = null
|
var duration: Int? = null
|
||||||
@ColumnInfo("bitrate")
|
|
||||||
@SerializedName("bitRate")
|
@SerializedName("bitRate")
|
||||||
var bitrate: Int? = null
|
var bitrate: Int? = null
|
||||||
var path: String? = null
|
var path: String? = null
|
||||||
@ColumnInfo(name = "is_video")
|
|
||||||
@SerializedName("isVideo")
|
@SerializedName("isVideo")
|
||||||
var isVideo: Boolean = false
|
var isVideo: Boolean = false
|
||||||
var userRating: Int? = null
|
var userRating: Int? = null
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package com.cappielloantonio.tempo.ui.activity;
|
|||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.IntentFilter;
|
import android.content.IntentFilter;
|
||||||
import android.net.ConnectivityManager;
|
import android.net.ConnectivityManager;
|
||||||
|
import android.net.NetworkInfo;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
@@ -30,6 +31,7 @@ import com.cappielloantonio.tempo.util.Preferences;
|
|||||||
import com.cappielloantonio.tempo.viewmodel.MainViewModel;
|
import com.cappielloantonio.tempo.viewmodel.MainViewModel;
|
||||||
import com.google.android.material.bottomnavigation.BottomNavigationView;
|
import com.google.android.material.bottomnavigation.BottomNavigationView;
|
||||||
import com.google.android.material.bottomsheet.BottomSheetBehavior;
|
import com.google.android.material.bottomsheet.BottomSheetBehavior;
|
||||||
|
import com.google.android.material.color.DynamicColors;
|
||||||
import com.google.common.util.concurrent.MoreExecutors;
|
import com.google.common.util.concurrent.MoreExecutors;
|
||||||
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
@@ -50,10 +52,10 @@ public class MainActivity extends BaseActivity {
|
|||||||
|
|
||||||
ConnectivityStatusBroadcastReceiver connectivityStatusBroadcastReceiver;
|
ConnectivityStatusBroadcastReceiver connectivityStatusBroadcastReceiver;
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
SplashScreen.installSplashScreen(this);
|
SplashScreen.installSplashScreen(this);
|
||||||
|
DynamicColors.applyToActivityIfAvailable(this);
|
||||||
|
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
@@ -117,6 +119,8 @@ public class MainActivity extends BaseActivity {
|
|||||||
fragmentManager.beginTransaction().replace(R.id.player_bottom_sheet, new PlayerBottomSheetFragment(), "PlayerBottomSheet").commit();
|
fragmentManager.beginTransaction().replace(R.id.player_bottom_sheet, new PlayerBottomSheetFragment(), "PlayerBottomSheet").commit();
|
||||||
|
|
||||||
setBottomSheetInPeek(mainViewModel.isQueueLoaded());
|
setBottomSheetInPeek(mainViewModel.isQueueLoaded());
|
||||||
|
|
||||||
|
collapseBottomSheet();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setBottomSheetInPeek(Boolean isVisible) {
|
public void setBottomSheetInPeek(Boolean isVisible) {
|
||||||
@@ -338,7 +342,9 @@ public class MainActivity extends BaseActivity {
|
|||||||
private void checkConnectionType() {
|
private void checkConnectionType() {
|
||||||
if (Preferences.isWifiOnly()) {
|
if (Preferences.isWifiOnly()) {
|
||||||
ConnectivityManager connectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
|
ConnectivityManager connectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
|
||||||
if (connectivityManager.getActiveNetworkInfo().getType() != ConnectivityManager.TYPE_WIFI) {
|
NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo();
|
||||||
|
|
||||||
|
if (networkInfo != null && networkInfo.getType() != ConnectivityManager.TYPE_WIFI) {
|
||||||
ConnectionAlertDialog dialog = new ConnectionAlertDialog();
|
ConnectionAlertDialog dialog = new ConnectionAlertDialog();
|
||||||
dialog.show(getSupportFragmentManager(), null);
|
dialog.show(getSupportFragmentManager(), null);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ public class BaseActivity extends AppCompatActivity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void checkBatteryOptimization() {
|
private void checkBatteryOptimization() {
|
||||||
if (detectBatteryOptimization() && Boolean.TRUE.equals(Preferences.askForOptimization())) {
|
if (detectBatteryOptimization() && Preferences.askForOptimization()) {
|
||||||
showBatteryOptimizationDialog();
|
showBatteryOptimizationDialog();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -98,6 +98,7 @@ public class BaseActivity extends AppCompatActivity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void setNavigationBarColor() {
|
private void setNavigationBarColor() {
|
||||||
getWindow().setNavigationBarColor(SurfaceColors.getColorForElevation(this, 10));
|
getWindow().setNavigationBarColor(SurfaceColors.getColorForElevation(this, 8));
|
||||||
|
getWindow().setStatusBarColor(SurfaceColors.getColorForElevation(this, 0));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,25 +11,35 @@ import androidx.recyclerview.widget.RecyclerView;
|
|||||||
|
|
||||||
import com.cappielloantonio.tempo.R;
|
import com.cappielloantonio.tempo.R;
|
||||||
import com.cappielloantonio.tempo.databinding.ItemHorizontalDownloadBinding;
|
import com.cappielloantonio.tempo.databinding.ItemHorizontalDownloadBinding;
|
||||||
|
import com.cappielloantonio.tempo.glide.CustomGlideRequest;
|
||||||
import com.cappielloantonio.tempo.interfaces.ClickCallback;
|
import com.cappielloantonio.tempo.interfaces.ClickCallback;
|
||||||
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.Util;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
@UnstableApi
|
@UnstableApi
|
||||||
public class DownloadHorizontalAdapter extends RecyclerView.Adapter<DownloadHorizontalAdapter.ViewHolder> {
|
public class DownloadHorizontalAdapter extends RecyclerView.Adapter<DownloadHorizontalAdapter.ViewHolder> {
|
||||||
private final ClickCallback click;
|
private final ClickCallback click;
|
||||||
|
|
||||||
|
private String view;
|
||||||
|
private String filterKey;
|
||||||
|
private String filterValue;
|
||||||
|
|
||||||
private List<Child> songs;
|
private List<Child> songs;
|
||||||
|
private List<Child> grouped;
|
||||||
|
|
||||||
public DownloadHorizontalAdapter(ClickCallback click) {
|
public DownloadHorizontalAdapter(ClickCallback click) {
|
||||||
this.click = click;
|
this.click = click;
|
||||||
|
this.view = Constants.DOWNLOAD_TYPE_TRACK;
|
||||||
this.songs = Collections.emptyList();
|
this.songs = Collections.emptyList();
|
||||||
|
this.grouped = Collections.emptyList();
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
@@ -41,31 +51,43 @@ public class DownloadHorizontalAdapter extends RecyclerView.Adapter<DownloadHori
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onBindViewHolder(ViewHolder holder, int position) {
|
public void onBindViewHolder(ViewHolder holder, int position) {
|
||||||
Child song = songs.get(position);
|
switch (view) {
|
||||||
|
case Constants.DOWNLOAD_TYPE_TRACK:
|
||||||
holder.item.downloadedSongTitleTextView.setText(MusicUtil.getReadableString(song.getTitle()));
|
initTrackLayout(holder, position);
|
||||||
holder.item.downloadedSongArtistTextView.setText(holder.itemView.getContext().getString(R.string.song_subtitle_formatter, MusicUtil.getReadableString(song.getArtist()), MusicUtil.getReadableDurationString(song.getDuration(), false)));
|
break;
|
||||||
holder.item.downloadedSongAlbumTextView.setText(MusicUtil.getReadableString(song.getAlbum()));
|
case Constants.DOWNLOAD_TYPE_ALBUM:
|
||||||
|
initAlbumLayout(holder, position);
|
||||||
if (position > 0 && songs.get(position - 1) != null && !Objects.equals(songs.get(position - 1).getAlbum(), songs.get(position).getAlbum())) {
|
break;
|
||||||
holder.item.divider.setPadding(0, (int) holder.itemView.getContext().getResources().getDimension(R.dimen.downloaded_item_padding), 0, 0);
|
case Constants.DOWNLOAD_TYPE_ARTIST:
|
||||||
} else {
|
initArtistLayout(holder, position);
|
||||||
if (position > 0) holder.item.divider.setVisibility(View.GONE);
|
break;
|
||||||
|
case Constants.DOWNLOAD_TYPE_GENRE:
|
||||||
|
initGenreLayout(holder, position);
|
||||||
|
break;
|
||||||
|
case Constants.DOWNLOAD_TYPE_YEAR:
|
||||||
|
initYearLayout(holder, position);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getItemCount() {
|
public int getItemCount() {
|
||||||
return songs.size();
|
return grouped.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setItems(List<Child> songs) {
|
public void setItems(String view, String filterKey, String filterValue, List<Child> songs) {
|
||||||
|
this.view = filterValue != null ? view : filterKey;
|
||||||
|
this.filterKey = filterKey;
|
||||||
|
this.filterValue = filterValue;
|
||||||
|
|
||||||
this.songs = songs;
|
this.songs = songs;
|
||||||
|
this.grouped = groupSong(songs);
|
||||||
|
|
||||||
notifyDataSetChanged();
|
notifyDataSetChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Child getItem(int id) {
|
public Child getItem(int id) {
|
||||||
return songs.get(id);
|
return grouped.get(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -78,6 +100,145 @@ public class DownloadHorizontalAdapter extends RecyclerView.Adapter<DownloadHori
|
|||||||
return position;
|
return position;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private List<Child> groupSong(List<Child> songs) {
|
||||||
|
switch (view) {
|
||||||
|
case Constants.DOWNLOAD_TYPE_TRACK:
|
||||||
|
return filterSong(filterKey, filterValue, songs.stream().filter(song -> Objects.nonNull(song.getId())).filter(Util.distinctByKey(Child::getId)).collect(Collectors.toList()));
|
||||||
|
case Constants.DOWNLOAD_TYPE_ALBUM:
|
||||||
|
return filterSong(filterKey, filterValue, songs.stream().filter(song -> Objects.nonNull(song.getAlbumId())).filter(Util.distinctByKey(Child::getAlbumId)).collect(Collectors.toList()));
|
||||||
|
case Constants.DOWNLOAD_TYPE_ARTIST:
|
||||||
|
return filterSong(filterKey, filterValue, songs.stream().filter(song -> Objects.nonNull(song.getArtistId())).filter(Util.distinctByKey(Child::getArtistId)).collect(Collectors.toList()));
|
||||||
|
case Constants.DOWNLOAD_TYPE_GENRE:
|
||||||
|
return filterSong(filterKey, filterValue, songs.stream().filter(song -> Objects.nonNull(song.getGenre())).filter(Util.distinctByKey(Child::getGenre)).collect(Collectors.toList()));
|
||||||
|
case Constants.DOWNLOAD_TYPE_YEAR:
|
||||||
|
return filterSong(filterKey, filterValue, songs.stream().filter(song -> Objects.nonNull(song.getYear())).filter(Util.distinctByKey(Child::getYear)).collect(Collectors.toList()));
|
||||||
|
}
|
||||||
|
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Child> filterSong(String filterKey, String filterValue, List<Child> songs) {
|
||||||
|
if (filterValue != null) {
|
||||||
|
switch (filterKey) {
|
||||||
|
case Constants.DOWNLOAD_TYPE_TRACK:
|
||||||
|
return songs.stream().filter(child -> child.getId().equals(filterValue)).collect(Collectors.toList());
|
||||||
|
case Constants.DOWNLOAD_TYPE_ALBUM:
|
||||||
|
return songs.stream().filter(child -> Objects.equals(child.getAlbumId(), filterValue)).collect(Collectors.toList());
|
||||||
|
case Constants.DOWNLOAD_TYPE_GENRE:
|
||||||
|
return songs.stream().filter(child -> Objects.equals(child.getGenre(), filterValue)).collect(Collectors.toList());
|
||||||
|
case Constants.DOWNLOAD_TYPE_YEAR:
|
||||||
|
return songs.stream().filter(child -> Objects.equals(child.getYear(), Integer.valueOf(filterValue))).collect(Collectors.toList());
|
||||||
|
case Constants.DOWNLOAD_TYPE_ARTIST:
|
||||||
|
return songs.stream().filter(child -> Objects.equals(child.getArtistId(), filterValue)).collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return songs;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String countSong(String filterKey, String filterValue, List<Child> songs) {
|
||||||
|
if (filterValue != null) {
|
||||||
|
switch (filterKey) {
|
||||||
|
case Constants.DOWNLOAD_TYPE_TRACK:
|
||||||
|
return String.valueOf(songs.stream().filter(child -> child.getId().equals(filterValue)).count());
|
||||||
|
case Constants.DOWNLOAD_TYPE_ALBUM:
|
||||||
|
return String.valueOf(songs.stream().filter(child -> Objects.equals(child.getAlbumId(), filterValue)).count());
|
||||||
|
case Constants.DOWNLOAD_TYPE_GENRE:
|
||||||
|
return String.valueOf(songs.stream().filter(child -> Objects.equals(child.getGenre(), filterValue)).count());
|
||||||
|
case Constants.DOWNLOAD_TYPE_YEAR:
|
||||||
|
return String.valueOf(songs.stream().filter(child -> Objects.equals(child.getYear(), Integer.valueOf(filterValue))).count());
|
||||||
|
case Constants.DOWNLOAD_TYPE_ARTIST:
|
||||||
|
return String.valueOf(songs.stream().filter(child -> Objects.equals(child.getArtistId(), filterValue)).count());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return "0";
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initTrackLayout(ViewHolder holder, int position) {
|
||||||
|
Child song = grouped.get(position);
|
||||||
|
|
||||||
|
holder.item.downloadedItemTitleTextView.setText(MusicUtil.getReadableString(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.downloadedItemPreTextView.setText(MusicUtil.getReadableString(song.getAlbum()));
|
||||||
|
|
||||||
|
CustomGlideRequest.Builder
|
||||||
|
.from(holder.itemView.getContext(), song.getCoverArtId())
|
||||||
|
.build()
|
||||||
|
.into(holder.item.itemCoverImageView);
|
||||||
|
|
||||||
|
holder.item.itemCoverImageView.setVisibility(View.VISIBLE);
|
||||||
|
holder.item.downloadedItemMoreButton.setVisibility(View.VISIBLE);
|
||||||
|
holder.item.divider.setVisibility(View.VISIBLE);
|
||||||
|
|
||||||
|
if (position > 0 && grouped.get(position - 1) != null && !Objects.equals(grouped.get(position - 1).getAlbum(), grouped.get(position).getAlbum())) {
|
||||||
|
holder.item.divider.setPadding(0, (int) holder.itemView.getContext().getResources().getDimension(R.dimen.downloaded_item_padding), 0, 0);
|
||||||
|
} else {
|
||||||
|
if (position > 0) holder.item.divider.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initAlbumLayout(ViewHolder holder, int position) {
|
||||||
|
Child song = grouped.get(position);
|
||||||
|
|
||||||
|
holder.item.downloadedItemTitleTextView.setText(MusicUtil.getReadableString(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.downloadedItemPreTextView.setText(MusicUtil.getReadableString(song.getArtist()));
|
||||||
|
|
||||||
|
CustomGlideRequest.Builder
|
||||||
|
.from(holder.itemView.getContext(), song.getCoverArtId())
|
||||||
|
.build()
|
||||||
|
.into(holder.item.itemCoverImageView);
|
||||||
|
|
||||||
|
holder.item.itemCoverImageView.setVisibility(View.VISIBLE);
|
||||||
|
holder.item.downloadedItemMoreButton.setVisibility(View.GONE);
|
||||||
|
holder.item.divider.setVisibility(View.VISIBLE);
|
||||||
|
|
||||||
|
if (position > 0 && grouped.get(position - 1) != null && !Objects.equals(grouped.get(position - 1).getArtist(), grouped.get(position).getArtist())) {
|
||||||
|
holder.item.divider.setPadding(0, (int) holder.itemView.getContext().getResources().getDimension(R.dimen.downloaded_item_padding), 0, 0);
|
||||||
|
} else {
|
||||||
|
if (position > 0) holder.item.divider.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initArtistLayout(ViewHolder holder, int position) {
|
||||||
|
Child song = grouped.get(position);
|
||||||
|
|
||||||
|
holder.item.downloadedItemTitleTextView.setText(MusicUtil.getReadableString(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)));
|
||||||
|
|
||||||
|
CustomGlideRequest.Builder
|
||||||
|
.from(holder.itemView.getContext(), song.getCoverArtId())
|
||||||
|
.build()
|
||||||
|
.into(holder.item.itemCoverImageView);
|
||||||
|
|
||||||
|
holder.item.itemCoverImageView.setVisibility(View.VISIBLE);
|
||||||
|
holder.item.downloadedItemMoreButton.setVisibility(View.GONE);
|
||||||
|
holder.item.divider.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initGenreLayout(ViewHolder holder, int position) {
|
||||||
|
Child song = grouped.get(position);
|
||||||
|
|
||||||
|
holder.item.downloadedItemTitleTextView.setText(MusicUtil.getReadableString(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.itemCoverImageView.setVisibility(View.GONE);
|
||||||
|
holder.item.downloadedItemMoreButton.setVisibility(View.GONE);
|
||||||
|
holder.item.divider.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initYearLayout(ViewHolder holder, int position) {
|
||||||
|
Child song = grouped.get(position);
|
||||||
|
|
||||||
|
holder.item.downloadedItemTitleTextView.setText(String.valueOf(song.getYear()));
|
||||||
|
holder.item.downloadedItemSubtitleTextView.setText(holder.itemView.getContext().getString(R.string.download_item_single_subtitle_formatter, countSong(Constants.DOWNLOAD_TYPE_YEAR, song.getYear().toString(), songs)));
|
||||||
|
|
||||||
|
holder.item.itemCoverImageView.setVisibility(View.GONE);
|
||||||
|
holder.item.downloadedItemMoreButton.setVisibility(View.GONE);
|
||||||
|
holder.item.divider.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
|
||||||
public class ViewHolder extends RecyclerView.ViewHolder {
|
public class ViewHolder extends RecyclerView.ViewHolder {
|
||||||
ItemHorizontalDownloadBinding item;
|
ItemHorizontalDownloadBinding item;
|
||||||
|
|
||||||
@@ -86,30 +247,62 @@ public class DownloadHorizontalAdapter extends RecyclerView.Adapter<DownloadHori
|
|||||||
|
|
||||||
this.item = item;
|
this.item = item;
|
||||||
|
|
||||||
item.downloadedSongTitleTextView.setSelected(true);
|
item.downloadedItemTitleTextView.setSelected(true);
|
||||||
item.downloadedSongArtistTextView.setSelected(true);
|
item.downloadedItemSubtitleTextView.setSelected(true);
|
||||||
|
|
||||||
itemView.setOnClickListener(v -> onClick());
|
itemView.setOnClickListener(v -> onClick());
|
||||||
itemView.setOnLongClickListener(v -> onLongClick());
|
itemView.setOnLongClickListener(v -> onLongClick());
|
||||||
|
|
||||||
item.downloadedSongMoreButton.setOnClickListener(v -> onLongClick());
|
item.downloadedItemMoreButton.setOnClickListener(v -> onLongClick());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onClick() {
|
public void onClick() {
|
||||||
Bundle bundle = new Bundle();
|
Bundle bundle = new Bundle();
|
||||||
bundle.putParcelableArrayList(Constants.TRACKS_OBJECT, new ArrayList<>(songs));
|
|
||||||
bundle.putInt(Constants.ITEM_POSITION, getBindingAdapterPosition());
|
|
||||||
|
|
||||||
click.onMediaClick(bundle);
|
switch (view) {
|
||||||
|
case Constants.DOWNLOAD_TYPE_TRACK:
|
||||||
|
bundle.putParcelableArrayList(Constants.TRACKS_OBJECT, new ArrayList<>(grouped));
|
||||||
|
bundle.putInt(Constants.ITEM_POSITION, getBindingAdapterPosition());
|
||||||
|
click.onMediaClick(bundle);
|
||||||
|
break;
|
||||||
|
case Constants.DOWNLOAD_TYPE_ALBUM:
|
||||||
|
bundle.putString(Constants.DOWNLOAD_TYPE_ALBUM, grouped.get(getBindingAdapterPosition()).getAlbumId());
|
||||||
|
click.onAlbumClick(bundle);
|
||||||
|
break;
|
||||||
|
case Constants.DOWNLOAD_TYPE_ARTIST:
|
||||||
|
bundle.putString(Constants.DOWNLOAD_TYPE_ARTIST, grouped.get(getBindingAdapterPosition()).getArtistId());
|
||||||
|
click.onArtistClick(bundle);
|
||||||
|
break;
|
||||||
|
case Constants.DOWNLOAD_TYPE_GENRE:
|
||||||
|
bundle.putString(Constants.DOWNLOAD_TYPE_GENRE, grouped.get(getBindingAdapterPosition()).getGenre());
|
||||||
|
click.onGenreClick(bundle);
|
||||||
|
break;
|
||||||
|
case Constants.DOWNLOAD_TYPE_YEAR:
|
||||||
|
bundle.putString(Constants.DOWNLOAD_TYPE_YEAR, grouped.get(getBindingAdapterPosition()).getYear().toString());
|
||||||
|
click.onYearClick(bundle);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean onLongClick() {
|
private boolean onLongClick() {
|
||||||
Bundle bundle = new Bundle();
|
Bundle bundle = new Bundle();
|
||||||
bundle.putParcelable(Constants.TRACK_OBJECT, songs.get(getBindingAdapterPosition()));
|
|
||||||
|
|
||||||
click.onMediaLongClick(bundle);
|
switch (view) {
|
||||||
|
case Constants.DOWNLOAD_TYPE_TRACK:
|
||||||
|
bundle.putParcelable(Constants.TRACK_OBJECT, grouped.get(getBindingAdapterPosition()));
|
||||||
|
click.onMediaLongClick(bundle);
|
||||||
|
return true;
|
||||||
|
case Constants.DOWNLOAD_TYPE_ALBUM:
|
||||||
|
bundle.putString(Constants.DOWNLOAD_TYPE_ALBUM, grouped.get(getBindingAdapterPosition()).getAlbumId());
|
||||||
|
click.onAlbumLongClick(bundle);
|
||||||
|
return true;
|
||||||
|
case Constants.DOWNLOAD_TYPE_ARTIST:
|
||||||
|
bundle.putString(Constants.DOWNLOAD_TYPE_ARTIST, grouped.get(getBindingAdapterPosition()).getArtistId());
|
||||||
|
click.onArtistLongClick(bundle);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
@@ -46,6 +47,9 @@ public class MusicDirectoryAdapter extends RecyclerView.Adapter<MusicDirectoryAd
|
|||||||
.from(holder.itemView.getContext(), child.getCoverArtId())
|
.from(holder.itemView.getContext(), child.getCoverArtId())
|
||||||
.build()
|
.build()
|
||||||
.into(holder.item.musicDirectoryCoverImageView);
|
.into(holder.item.musicDirectoryCoverImageView);
|
||||||
|
|
||||||
|
holder.item.musicDirectoryMoreButton.setVisibility(child.isDir() ? View.VISIBLE : View.GONE);
|
||||||
|
holder.item.musicDirectoryPlayButton.setVisibility(child.isDir() ? View.GONE : View.VISIBLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -9,16 +9,17 @@ import androidx.media3.common.util.UnstableApi;
|
|||||||
import androidx.recyclerview.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
import com.cappielloantonio.tempo.databinding.ItemLibraryMusicIndexBinding;
|
import com.cappielloantonio.tempo.databinding.ItemLibraryMusicIndexBinding;
|
||||||
import com.cappielloantonio.tempo.glide.CustomGlideRequest;
|
import com.cappielloantonio.tempo.helper.recyclerview.FastScrollbar;
|
||||||
import com.cappielloantonio.tempo.interfaces.ClickCallback;
|
import com.cappielloantonio.tempo.interfaces.ClickCallback;
|
||||||
import com.cappielloantonio.tempo.subsonic.models.Artist;
|
import com.cappielloantonio.tempo.subsonic.models.Artist;
|
||||||
import com.cappielloantonio.tempo.util.Constants;
|
import com.cappielloantonio.tempo.util.Constants;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
@UnstableApi
|
@UnstableApi
|
||||||
public class MusicIndexAdapter extends RecyclerView.Adapter<MusicIndexAdapter.ViewHolder> {
|
public class MusicIndexAdapter extends RecyclerView.Adapter<MusicIndexAdapter.ViewHolder> implements FastScrollbar.BubbleTextGetter {
|
||||||
private final ClickCallback click;
|
private final ClickCallback click;
|
||||||
|
|
||||||
private List<Artist> artists;
|
private List<Artist> artists;
|
||||||
@@ -41,10 +42,10 @@ public class MusicIndexAdapter extends RecyclerView.Adapter<MusicIndexAdapter.Vi
|
|||||||
|
|
||||||
holder.item.musicIndexTitleTextView.setText(artist.getName());
|
holder.item.musicIndexTitleTextView.setText(artist.getName());
|
||||||
|
|
||||||
CustomGlideRequest.Builder
|
/* CustomGlideRequest.Builder
|
||||||
.from(holder.itemView.getContext(), artist.getName())
|
.from(holder.itemView.getContext(), artist.getName())
|
||||||
.build()
|
.build()
|
||||||
.into(holder.item.musicIndexCoverImageView);
|
.into(holder.item.musicIndexCoverImageView); */
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -57,6 +58,11 @@ public class MusicIndexAdapter extends RecyclerView.Adapter<MusicIndexAdapter.Vi
|
|||||||
notifyDataSetChanged();
|
notifyDataSetChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getTextToShowInBubble(int pos) {
|
||||||
|
return Character.toString(Objects.requireNonNull(artists.get(pos).getName().toUpperCase()).charAt(0));
|
||||||
|
}
|
||||||
|
|
||||||
public class ViewHolder extends RecyclerView.ViewHolder {
|
public class ViewHolder extends RecyclerView.ViewHolder {
|
||||||
ItemLibraryMusicIndexBinding item;
|
ItemLibraryMusicIndexBinding item;
|
||||||
|
|
||||||
|
|||||||
@@ -128,14 +128,14 @@ public class PodcastChannelCatalogueAdapter extends RecyclerView.Adapter<Podcast
|
|||||||
Bundle bundle = new Bundle();
|
Bundle bundle = new Bundle();
|
||||||
bundle.putParcelable(Constants.PODCAST_CHANNEL_OBJECT, podcastChannels.get(getBindingAdapterPosition()));
|
bundle.putParcelable(Constants.PODCAST_CHANNEL_OBJECT, podcastChannels.get(getBindingAdapterPosition()));
|
||||||
|
|
||||||
click.onAlbumClick(bundle);
|
click.onPodcastChannelClick(bundle);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean onLongClick() {
|
private boolean onLongClick() {
|
||||||
Bundle bundle = new Bundle();
|
Bundle bundle = new Bundle();
|
||||||
bundle.putParcelable(Constants.PODCAST_CHANNEL_OBJECT, podcastChannels.get(getBindingAdapterPosition()));
|
bundle.putParcelable(Constants.PODCAST_CHANNEL_OBJECT, podcastChannels.get(getBindingAdapterPosition()));
|
||||||
|
|
||||||
click.onAlbumLongClick(bundle);
|
click.onPodcastChannelLongClick(bundle);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
@@ -18,11 +19,14 @@ import com.cappielloantonio.tempo.util.MusicUtil;
|
|||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
public class PodcastEpisodeAdapter extends RecyclerView.Adapter<PodcastEpisodeAdapter.ViewHolder> {
|
public class PodcastEpisodeAdapter extends RecyclerView.Adapter<PodcastEpisodeAdapter.ViewHolder> {
|
||||||
private final ClickCallback click;
|
private final ClickCallback click;
|
||||||
|
|
||||||
private List<PodcastEpisode> podcastEpisodes;
|
private List<PodcastEpisode> podcastEpisodes;
|
||||||
|
private List<PodcastEpisode> podcastEpisodesFull;
|
||||||
|
|
||||||
public PodcastEpisodeAdapter(ClickCallback click) {
|
public PodcastEpisodeAdapter(ClickCallback click) {
|
||||||
this.click = click;
|
this.click = click;
|
||||||
@@ -50,6 +54,10 @@ public class PodcastEpisodeAdapter extends RecyclerView.Adapter<PodcastEpisodeAd
|
|||||||
.from(holder.itemView.getContext(), podcastEpisode.getCoverArtId())
|
.from(holder.itemView.getContext(), podcastEpisode.getCoverArtId())
|
||||||
.build()
|
.build()
|
||||||
.into(holder.item.podcastCoverImageView);
|
.into(holder.item.podcastCoverImageView);
|
||||||
|
|
||||||
|
holder.item.podcastPlayButton.setEnabled(podcastEpisode.getStatus().equals("completed"));
|
||||||
|
holder.item.podcastMoreButton.setVisibility(podcastEpisode.getStatus().equals("completed") ? View.VISIBLE : View.GONE);
|
||||||
|
holder.item.podcastDownloadRequestButton.setVisibility(podcastEpisode.getStatus().equals("completed") ? View.GONE : View.VISIBLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -58,7 +66,8 @@ public class PodcastEpisodeAdapter extends RecyclerView.Adapter<PodcastEpisodeAd
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void setItems(List<PodcastEpisode> podcastEpisodes) {
|
public void setItems(List<PodcastEpisode> podcastEpisodes) {
|
||||||
this.podcastEpisodes = podcastEpisodes;
|
this.podcastEpisodesFull = podcastEpisodes;
|
||||||
|
this.podcastEpisodes = podcastEpisodesFull.stream().filter(podcastEpisode -> Objects.equals(podcastEpisode.getStatus(), "completed")).collect(Collectors.toList());
|
||||||
notifyDataSetChanged();
|
notifyDataSetChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -85,22 +94,57 @@ public class PodcastEpisodeAdapter extends RecyclerView.Adapter<PodcastEpisodeAd
|
|||||||
|
|
||||||
item.podcastPlayButton.setOnClickListener(v -> onClick());
|
item.podcastPlayButton.setOnClickListener(v -> onClick());
|
||||||
item.podcastMoreButton.setOnClickListener(v -> openMore());
|
item.podcastMoreButton.setOnClickListener(v -> openMore());
|
||||||
|
item.podcastDownloadRequestButton.setOnClickListener(v -> requestDownload());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onClick() {
|
public void onClick() {
|
||||||
Bundle bundle = new Bundle();
|
PodcastEpisode podcastEpisode = podcastEpisodes.get(getBindingAdapterPosition());
|
||||||
bundle.putParcelable(Constants.PODCAST_OBJECT, podcastEpisodes.get(getBindingAdapterPosition()));
|
|
||||||
|
|
||||||
click.onPodcastEpisodeClick(bundle);
|
if (podcastEpisode.getStatus().equals("completed")) {
|
||||||
|
Bundle bundle = new Bundle();
|
||||||
|
bundle.putParcelable(Constants.PODCAST_OBJECT, podcastEpisodes.get(getBindingAdapterPosition()));
|
||||||
|
|
||||||
|
click.onPodcastEpisodeClick(bundle);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean openMore() {
|
private boolean openMore() {
|
||||||
Bundle bundle = new Bundle();
|
PodcastEpisode podcastEpisode = podcastEpisodes.get(getBindingAdapterPosition());
|
||||||
bundle.putParcelable(Constants.PODCAST_OBJECT, podcastEpisodes.get(getBindingAdapterPosition()));
|
|
||||||
|
|
||||||
click.onPodcastEpisodeLongClick(bundle);
|
if (podcastEpisode.getStatus().equals("completed")) {
|
||||||
|
Bundle bundle = new Bundle();
|
||||||
|
bundle.putParcelable(Constants.PODCAST_OBJECT, podcastEpisodes.get(getBindingAdapterPosition()));
|
||||||
|
|
||||||
return true;
|
click.onPodcastEpisodeLongClick(bundle);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void requestDownload() {
|
||||||
|
PodcastEpisode podcastEpisode = podcastEpisodes.get(getBindingAdapterPosition());
|
||||||
|
|
||||||
|
if (!podcastEpisode.getStatus().equals("completed")) {
|
||||||
|
Bundle bundle = new Bundle();
|
||||||
|
bundle.putParcelable(Constants.PODCAST_OBJECT, podcastEpisodes.get(getBindingAdapterPosition()));
|
||||||
|
|
||||||
|
click.onPodcastEpisodeAltClick(bundle);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void sort(String order) {
|
||||||
|
switch (order) {
|
||||||
|
case Constants.PODCAST_FILTER_BY_DOWNLOAD:
|
||||||
|
podcastEpisodes = podcastEpisodesFull.stream().filter(podcastEpisode -> Objects.equals(podcastEpisode.getStatus(), "completed")).collect(Collectors.toList());
|
||||||
|
break;
|
||||||
|
case Constants.PODCAST_FILTER_BY_ALL:
|
||||||
|
podcastEpisodes = podcastEpisodesFull;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
notifyDataSetChanged();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -48,7 +48,7 @@ public class SongHorizontalAdapter extends RecyclerView.Adapter<SongHorizontalAd
|
|||||||
|
|
||||||
holder.item.searchResultSongTitleTextView.setText(MusicUtil.getReadableString(song.getTitle()));
|
holder.item.searchResultSongTitleTextView.setText(MusicUtil.getReadableString(song.getTitle()));
|
||||||
holder.item.searchResultSongSubtitleTextView.setText(holder.itemView.getContext().getString(R.string.song_subtitle_formatter, MusicUtil.getReadableString(song.getArtist()), MusicUtil.getReadableDurationString(song.getDuration() != null ? song.getDuration() : 0, false)));
|
holder.item.searchResultSongSubtitleTextView.setText(holder.itemView.getContext().getString(R.string.song_subtitle_formatter, MusicUtil.getReadableString(song.getArtist()), MusicUtil.getReadableDurationString(song.getDuration() != null ? song.getDuration() : 0, false)));
|
||||||
holder.item.trackNumberTextView.setText(String.valueOf(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.searchResultDowanloadIndicatorImageView.setVisibility(View.VISIBLE);
|
||||||
@@ -80,6 +80,16 @@ public class SongHorizontalAdapter extends RecyclerView.Adapter<SongHorizontalAd
|
|||||||
notifyDataSetChanged();
|
notifyDataSetChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getItemViewType(int position) {
|
||||||
|
return position;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getItemId(int position) {
|
||||||
|
return position;
|
||||||
|
}
|
||||||
|
|
||||||
public Child getItem(int id) {
|
public Child getItem(int id) {
|
||||||
return songs.get(id);
|
return songs.get(id);
|
||||||
}
|
}
|
||||||
@@ -104,7 +114,7 @@ public class SongHorizontalAdapter extends RecyclerView.Adapter<SongHorizontalAd
|
|||||||
public void onClick() {
|
public void onClick() {
|
||||||
Bundle bundle = new Bundle();
|
Bundle bundle = new Bundle();
|
||||||
bundle.putParcelableArrayList(Constants.TRACKS_OBJECT, new ArrayList<>(MusicUtil.limitPlayableMedia(songs, getBindingAdapterPosition())));
|
bundle.putParcelableArrayList(Constants.TRACKS_OBJECT, new ArrayList<>(MusicUtil.limitPlayableMedia(songs, getBindingAdapterPosition())));
|
||||||
bundle.putInt(Constants.ITEM_POSITION, MusicUtil.getPlayableMediaPosition(getBindingAdapterPosition()));
|
bundle.putInt(Constants.ITEM_POSITION, MusicUtil.getPlayableMediaPosition(songs, getBindingAdapterPosition()));
|
||||||
|
|
||||||
click.onMediaClick(bundle);
|
click.onMediaClick(bundle);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,65 @@
|
|||||||
|
package com.cappielloantonio.tempo.ui.dialog;
|
||||||
|
|
||||||
|
import android.app.AlertDialog;
|
||||||
|
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.DialogDeleteDownloadStorageBinding;
|
||||||
|
import com.cappielloantonio.tempo.interfaces.DialogClickCallback;
|
||||||
|
import com.cappielloantonio.tempo.util.DownloadUtil;
|
||||||
|
|
||||||
|
@OptIn(markerClass = UnstableApi.class)
|
||||||
|
public class DeleteDownloadStorageDialog extends DialogFragment {
|
||||||
|
private DialogDeleteDownloadStorageBinding bind;
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||||
|
bind = DialogDeleteDownloadStorageBinding.inflate(getLayoutInflater());
|
||||||
|
|
||||||
|
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
|
||||||
|
|
||||||
|
builder.setView(bind.getRoot())
|
||||||
|
.setTitle(R.string.delete_download_storage_dialog_title)
|
||||||
|
.setPositiveButton(R.string.delete_download_storage_dialog_positive_button, null)
|
||||||
|
.setNegativeButton(R.string.delete_download_storage_dialog_negative_button, null);
|
||||||
|
|
||||||
|
return builder.create();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onResume() {
|
||||||
|
super.onResume();
|
||||||
|
setButtonAction();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDestroyView() {
|
||||||
|
super.onDestroyView();
|
||||||
|
bind = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setButtonAction() {
|
||||||
|
AlertDialog dialog = ((AlertDialog) getDialog());
|
||||||
|
|
||||||
|
if (dialog != null) {
|
||||||
|
Button positiveButton = dialog.getButton(Dialog.BUTTON_POSITIVE);
|
||||||
|
positiveButton.setOnClickListener(v -> {
|
||||||
|
DownloadUtil.getDownloadTracker(requireContext()).removeAll();
|
||||||
|
dialog.dismiss();
|
||||||
|
});
|
||||||
|
|
||||||
|
Button negativeButton = dialog.getButton(Dialog.BUTTON_NEGATIVE);
|
||||||
|
negativeButton.setOnClickListener(v -> {
|
||||||
|
dialog.dismiss();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,89 @@
|
|||||||
|
package com.cappielloantonio.tempo.ui.dialog;
|
||||||
|
|
||||||
|
import android.app.AlertDialog;
|
||||||
|
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.DialogDownloadStorageBinding;
|
||||||
|
import com.cappielloantonio.tempo.interfaces.DialogClickCallback;
|
||||||
|
import com.cappielloantonio.tempo.util.DownloadUtil;
|
||||||
|
import com.cappielloantonio.tempo.util.Preferences;
|
||||||
|
|
||||||
|
@OptIn(markerClass = UnstableApi.class)
|
||||||
|
public class DownloadStorageDialog extends DialogFragment {
|
||||||
|
private DialogDownloadStorageBinding bind;
|
||||||
|
|
||||||
|
private final DialogClickCallback dialogClickCallback;
|
||||||
|
|
||||||
|
public DownloadStorageDialog(DialogClickCallback dialogClickCallback) {
|
||||||
|
this.dialogClickCallback = dialogClickCallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||||
|
bind = DialogDownloadStorageBinding.inflate(getLayoutInflater());
|
||||||
|
|
||||||
|
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
|
||||||
|
|
||||||
|
builder.setView(bind.getRoot())
|
||||||
|
.setTitle(R.string.download_storage_dialog_title)
|
||||||
|
.setPositiveButton(R.string.download_storage_external_dialog_positive_button, null)
|
||||||
|
.setNegativeButton(R.string.download_storage_internal_dialog_negative_button, null);
|
||||||
|
|
||||||
|
return builder.create();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onResume() {
|
||||||
|
super.onResume();
|
||||||
|
setButtonAction();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDestroyView() {
|
||||||
|
super.onDestroyView();
|
||||||
|
bind = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setButtonAction() {
|
||||||
|
AlertDialog dialog = ((AlertDialog) getDialog());
|
||||||
|
|
||||||
|
if (dialog != null) {
|
||||||
|
Button positiveButton = dialog.getButton(Dialog.BUTTON_POSITIVE);
|
||||||
|
positiveButton.setOnClickListener(v -> {
|
||||||
|
int currentPreference = Preferences.getDownloadStoragePreference();
|
||||||
|
int newPreference = 1;
|
||||||
|
|
||||||
|
if (currentPreference != newPreference) {
|
||||||
|
Preferences.setDownloadStoragePreference(newPreference);
|
||||||
|
DownloadUtil.getDownloadTracker(requireContext()).removeAll();
|
||||||
|
dialogClickCallback.onPositiveClick();
|
||||||
|
}
|
||||||
|
|
||||||
|
dialog.dismiss();
|
||||||
|
});
|
||||||
|
|
||||||
|
Button negativeButton = dialog.getButton(Dialog.BUTTON_NEGATIVE);
|
||||||
|
negativeButton.setOnClickListener(v -> {
|
||||||
|
int currentPreference = Preferences.getDownloadStoragePreference();
|
||||||
|
int newPreference = 0;
|
||||||
|
|
||||||
|
if (currentPreference != newPreference) {
|
||||||
|
Preferences.setDownloadStoragePreference(newPreference);
|
||||||
|
DownloadUtil.getDownloadTracker(requireContext()).removeAll();
|
||||||
|
dialogClickCallback.onNegativeClick();
|
||||||
|
}
|
||||||
|
|
||||||
|
dialog.dismiss();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,10 +3,14 @@ package com.cappielloantonio.tempo.ui.fragment;
|
|||||||
import android.content.ComponentName;
|
import android.content.ComponentName;
|
||||||
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 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;
|
||||||
@@ -19,14 +23,21 @@ import androidx.recyclerview.widget.LinearLayoutManager;
|
|||||||
import com.cappielloantonio.tempo.R;
|
import com.cappielloantonio.tempo.R;
|
||||||
import com.cappielloantonio.tempo.databinding.FragmentDirectoryBinding;
|
import com.cappielloantonio.tempo.databinding.FragmentDirectoryBinding;
|
||||||
import com.cappielloantonio.tempo.interfaces.ClickCallback;
|
import com.cappielloantonio.tempo.interfaces.ClickCallback;
|
||||||
|
import com.cappielloantonio.tempo.model.Download;
|
||||||
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.Child;
|
||||||
import com.cappielloantonio.tempo.ui.activity.MainActivity;
|
import com.cappielloantonio.tempo.ui.activity.MainActivity;
|
||||||
import com.cappielloantonio.tempo.ui.adapter.MusicDirectoryAdapter;
|
import com.cappielloantonio.tempo.ui.adapter.MusicDirectoryAdapter;
|
||||||
import com.cappielloantonio.tempo.util.Constants;
|
import com.cappielloantonio.tempo.util.Constants;
|
||||||
|
import com.cappielloantonio.tempo.util.DownloadUtil;
|
||||||
|
import com.cappielloantonio.tempo.util.MappingUtil;
|
||||||
import com.cappielloantonio.tempo.viewmodel.DirectoryViewModel;
|
import com.cappielloantonio.tempo.viewmodel.DirectoryViewModel;
|
||||||
import com.google.common.util.concurrent.ListenableFuture;
|
import com.google.common.util.concurrent.ListenableFuture;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
@UnstableApi
|
@UnstableApi
|
||||||
public class DirectoryFragment extends Fragment implements ClickCallback {
|
public class DirectoryFragment extends Fragment implements ClickCallback {
|
||||||
private static final String TAG = "DirectoryFragment";
|
private static final String TAG = "DirectoryFragment";
|
||||||
@@ -39,6 +50,18 @@ public class DirectoryFragment extends Fragment implements ClickCallback {
|
|||||||
|
|
||||||
private ListenableFuture<MediaBrowser> mediaBrowserListenableFuture;
|
private ListenableFuture<MediaBrowser> mediaBrowserListenableFuture;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
setHasOptionsMenu(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) {
|
||||||
|
super.onCreateOptionsMenu(menu, inflater);
|
||||||
|
inflater.inflate(R.menu.directory_page_menu, menu);
|
||||||
|
}
|
||||||
|
|
||||||
@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();
|
||||||
@@ -71,6 +94,24 @@ public class DirectoryFragment extends Fragment implements ClickCallback {
|
|||||||
bind = null;
|
bind = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
|
||||||
|
if (item.getItemId() == R.id.action_download_directory) {
|
||||||
|
directoryViewModel.loadMusicDirectory(getArguments().getString(Constants.MUSIC_DIRECTORY_ID)).observe(getViewLifecycleOwner(), directory -> {
|
||||||
|
if (isVisible() && getActivity() != null) {
|
||||||
|
List<Child> songs = directory.getChildren().stream().filter(child -> !child.isDir()).collect(Collectors.toList());
|
||||||
|
DownloadUtil.getDownloadTracker(requireContext()).download(
|
||||||
|
MappingUtil.mapDownloads(songs),
|
||||||
|
songs.stream().map(Download::new).collect(Collectors.toList())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
private void initAppBar() {
|
private void initAppBar() {
|
||||||
activity.setSupportActionBar(bind.toolbar);
|
activity.setSupportActionBar(bind.toolbar);
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ 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.PopupMenu;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
@@ -19,15 +20,19 @@ import androidx.recyclerview.widget.LinearLayoutManager;
|
|||||||
import com.cappielloantonio.tempo.R;
|
import com.cappielloantonio.tempo.R;
|
||||||
import com.cappielloantonio.tempo.databinding.FragmentDownloadBinding;
|
import com.cappielloantonio.tempo.databinding.FragmentDownloadBinding;
|
||||||
import com.cappielloantonio.tempo.interfaces.ClickCallback;
|
import com.cappielloantonio.tempo.interfaces.ClickCallback;
|
||||||
|
import com.cappielloantonio.tempo.model.DownloadStack;
|
||||||
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.Child;
|
||||||
import com.cappielloantonio.tempo.ui.activity.MainActivity;
|
import com.cappielloantonio.tempo.ui.activity.MainActivity;
|
||||||
import com.cappielloantonio.tempo.ui.adapter.DownloadHorizontalAdapter;
|
import com.cappielloantonio.tempo.ui.adapter.DownloadHorizontalAdapter;
|
||||||
import com.cappielloantonio.tempo.util.Constants;
|
import com.cappielloantonio.tempo.util.Constants;
|
||||||
|
import com.cappielloantonio.tempo.util.Preferences;
|
||||||
import com.cappielloantonio.tempo.viewmodel.DownloadViewModel;
|
import com.cappielloantonio.tempo.viewmodel.DownloadViewModel;
|
||||||
import com.google.android.material.appbar.MaterialToolbar;
|
import com.google.android.material.appbar.MaterialToolbar;
|
||||||
import com.google.common.util.concurrent.ListenableFuture;
|
import com.google.common.util.concurrent.ListenableFuture;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
@UnstableApi
|
@UnstableApi
|
||||||
@@ -59,7 +64,7 @@ public class DownloadFragment extends Fragment implements ClickCallback {
|
|||||||
super.onViewCreated(view, savedInstanceState);
|
super.onViewCreated(view, savedInstanceState);
|
||||||
|
|
||||||
initAppBar();
|
initAppBar();
|
||||||
initDownloadedSongView();
|
initDownloadedView();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -90,11 +95,12 @@ public class DownloadFragment extends Fragment implements ClickCallback {
|
|||||||
Objects.requireNonNull(materialToolbar.getOverflowIcon()).setTint(requireContext().getResources().getColor(R.color.titleTextColor, null));
|
Objects.requireNonNull(materialToolbar.getOverflowIcon()).setTint(requireContext().getResources().getColor(R.color.titleTextColor, null));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initDownloadedSongView() {
|
private void initDownloadedView() {
|
||||||
bind.downloadedTracksRecyclerView.setHasFixedSize(true);
|
bind.downloadedRecyclerView.setHasFixedSize(true);
|
||||||
|
|
||||||
downloadHorizontalAdapter = new DownloadHorizontalAdapter(this);
|
downloadHorizontalAdapter = new DownloadHorizontalAdapter(this);
|
||||||
bind.downloadedTracksRecyclerView.setAdapter(downloadHorizontalAdapter);
|
bind.downloadedRecyclerView.setAdapter(downloadHorizontalAdapter);
|
||||||
|
|
||||||
downloadViewModel.getDownloadedTracks(getViewLifecycleOwner()).observe(getViewLifecycleOwner(), songs -> {
|
downloadViewModel.getDownloadedTracks(getViewLifecycleOwner()).observe(getViewLifecycleOwner(), songs -> {
|
||||||
if (songs != null) {
|
if (songs != null) {
|
||||||
if (songs.isEmpty()) {
|
if (songs.isEmpty()) {
|
||||||
@@ -102,26 +108,92 @@ public class DownloadFragment extends Fragment implements ClickCallback {
|
|||||||
bind.emptyDownloadLayout.setVisibility(View.VISIBLE);
|
bind.emptyDownloadLayout.setVisibility(View.VISIBLE);
|
||||||
bind.fragmentDownloadNestedScrollView.setVisibility(View.GONE);
|
bind.fragmentDownloadNestedScrollView.setVisibility(View.GONE);
|
||||||
|
|
||||||
bind.downloadDownloadedTracksPlaceholder.placeholder.setVisibility(View.VISIBLE);
|
bind.downloadDownloadedPlaceholder.placeholder.setVisibility(View.VISIBLE);
|
||||||
bind.downloadDownloadedTracksSector.setVisibility(View.GONE);
|
bind.downloadDownloadedSector.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.downloadDownloadedTracksPlaceholder.placeholder.setVisibility(View.GONE);
|
bind.downloadDownloadedPlaceholder.placeholder.setVisibility(View.GONE);
|
||||||
bind.downloadDownloadedTracksSector.setVisibility(View.VISIBLE);
|
bind.downloadDownloadedSector.setVisibility(View.VISIBLE);
|
||||||
|
|
||||||
bind.downloadedTracksRecyclerView.setLayoutManager(new LinearLayoutManager(requireContext()));
|
bind.downloadedGroupByImageView.setVisibility(View.VISIBLE);
|
||||||
|
|
||||||
downloadHorizontalAdapter.setItems(songs);
|
finishDownloadView(songs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bind != null) bind.loadingProgressBar.setVisibility(View.GONE);
|
if (bind != null) bind.loadingProgressBar.setVisibility(View.GONE);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
bind.downloadedGroupByImageView.setOnClickListener(view -> showPopupMenu(view, R.menu.download_popup_menu));
|
||||||
|
bind.downloadedGoBackImageView.setOnClickListener(view -> downloadViewModel.popViewStack());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void finishDownloadView(List<Child> songs) {
|
||||||
|
downloadViewModel.getViewStack().observe(getViewLifecycleOwner(), stack -> {
|
||||||
|
bind.downloadedRecyclerView.setLayoutManager(new LinearLayoutManager(requireContext()));
|
||||||
|
|
||||||
|
DownloadStack lastLevel = stack.get(stack.size() - 1);
|
||||||
|
|
||||||
|
switch (lastLevel.getId()) {
|
||||||
|
case Constants.DOWNLOAD_TYPE_TRACK:
|
||||||
|
downloadHorizontalAdapter.setItems(Constants.DOWNLOAD_TYPE_TRACK, lastLevel.getId(), lastLevel.getView(), songs);
|
||||||
|
break;
|
||||||
|
case Constants.DOWNLOAD_TYPE_ALBUM:
|
||||||
|
downloadHorizontalAdapter.setItems(Constants.DOWNLOAD_TYPE_TRACK, lastLevel.getId(), lastLevel.getView(), songs);
|
||||||
|
break;
|
||||||
|
case Constants.DOWNLOAD_TYPE_ARTIST:
|
||||||
|
downloadHorizontalAdapter.setItems(Constants.DOWNLOAD_TYPE_ALBUM, lastLevel.getId(), lastLevel.getView(), songs);
|
||||||
|
break;
|
||||||
|
case Constants.DOWNLOAD_TYPE_GENRE:
|
||||||
|
downloadHorizontalAdapter.setItems(Constants.DOWNLOAD_TYPE_TRACK, lastLevel.getId(), lastLevel.getView(), songs);
|
||||||
|
break;
|
||||||
|
case Constants.DOWNLOAD_TYPE_YEAR:
|
||||||
|
downloadHorizontalAdapter.setItems(Constants.DOWNLOAD_TYPE_TRACK, lastLevel.getId(), lastLevel.getView(), songs);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
bind.downloadedGoBackImageView.setVisibility(stack.size() > 1 ? View.VISIBLE : View.GONE);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
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_download_group_by_track) {
|
||||||
|
downloadViewModel.initViewStack(new DownloadStack(Constants.DOWNLOAD_TYPE_TRACK, null));
|
||||||
|
Preferences.setDefaultDownloadViewType(Constants.DOWNLOAD_TYPE_TRACK);
|
||||||
|
return true;
|
||||||
|
} else if (menuItem.getItemId() == R.id.menu_download_group_by_album) {
|
||||||
|
downloadViewModel.initViewStack(new DownloadStack(Constants.DOWNLOAD_TYPE_ALBUM, null));
|
||||||
|
Preferences.setDefaultDownloadViewType(Constants.DOWNLOAD_TYPE_ALBUM);
|
||||||
|
return true;
|
||||||
|
} else if (menuItem.getItemId() == R.id.menu_download_group_by_artist) {
|
||||||
|
downloadViewModel.initViewStack(new DownloadStack(Constants.DOWNLOAD_TYPE_ARTIST, null));
|
||||||
|
Preferences.setDefaultDownloadViewType(Constants.DOWNLOAD_TYPE_ARTIST);
|
||||||
|
return true;
|
||||||
|
} else if (menuItem.getItemId() == R.id.menu_download_group_by_genre) {
|
||||||
|
downloadViewModel.initViewStack(new DownloadStack(Constants.DOWNLOAD_TYPE_GENRE, null));
|
||||||
|
Preferences.setDefaultDownloadViewType(Constants.DOWNLOAD_TYPE_GENRE);
|
||||||
|
return true;
|
||||||
|
} else if (menuItem.getItemId() == R.id.menu_download_group_by_year) {
|
||||||
|
downloadViewModel.initViewStack(new DownloadStack(Constants.DOWNLOAD_TYPE_YEAR, null));
|
||||||
|
Preferences.setDefaultDownloadViewType(Constants.DOWNLOAD_TYPE_YEAR);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
popup.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initializeMediaBrowser() {
|
private void initializeMediaBrowser() {
|
||||||
@@ -132,6 +204,26 @@ public class DownloadFragment extends Fragment implements ClickCallback {
|
|||||||
MediaBrowser.releaseFuture(mediaBrowserListenableFuture);
|
MediaBrowser.releaseFuture(mediaBrowserListenableFuture);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onYearClick(Bundle bundle) {
|
||||||
|
downloadViewModel.pushViewStack(new DownloadStack(Constants.DOWNLOAD_TYPE_YEAR, bundle.getString(Constants.DOWNLOAD_TYPE_YEAR)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onGenreClick(Bundle bundle) {
|
||||||
|
downloadViewModel.pushViewStack(new DownloadStack(Constants.DOWNLOAD_TYPE_GENRE, bundle.getString(Constants.DOWNLOAD_TYPE_GENRE)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onArtistClick(Bundle bundle) {
|
||||||
|
downloadViewModel.pushViewStack(new DownloadStack(Constants.DOWNLOAD_TYPE_ARTIST, bundle.getString(Constants.DOWNLOAD_TYPE_ARTIST)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAlbumClick(Bundle bundle) {
|
||||||
|
downloadViewModel.pushViewStack(new DownloadStack(Constants.DOWNLOAD_TYPE_ALBUM, bundle.getString(Constants.DOWNLOAD_TYPE_ALBUM)));
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onMediaClick(Bundle bundle) {
|
public void onMediaClick(Bundle bundle) {
|
||||||
MediaManager.startQueue(mediaBrowserListenableFuture, bundle.getParcelableArrayList(Constants.TRACKS_OBJECT), bundle.getInt(Constants.ITEM_POSITION));
|
MediaManager.startQueue(mediaBrowserListenableFuture, bundle.getParcelableArrayList(Constants.TRACKS_OBJECT), bundle.getInt(Constants.ITEM_POSITION));
|
||||||
|
|||||||
@@ -96,6 +96,9 @@ public class IndexFragment extends Fragment implements ClickCallback {
|
|||||||
musicIndexAdapter.setItems(IndexUtil.getArtist(indexes));
|
musicIndexAdapter.setItems(IndexUtil.getArtist(indexes));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
bind.fastScrollbar.setRecyclerView(bind.indexRecyclerView);
|
||||||
|
bind.fastScrollbar.setViewsToUse(R.layout.layout_fast_scrollbar, R.id.fastscroller_bubble, R.id.fastscroller_handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -86,7 +86,7 @@ public class PlayerBottomSheetFragment extends Fragment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void customizeBottomSheetBackground() {
|
private void customizeBottomSheetBackground() {
|
||||||
bind.playerHeaderLayout.getRoot().setBackgroundColor(SurfaceColors.getColorForElevation(requireContext(), 2));
|
bind.playerHeaderLayout.getRoot().setBackgroundColor(SurfaceColors.getColorForElevation(requireContext(), 8));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void customizeBottomSheetAction() {
|
private void customizeBottomSheetAction() {
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import androidx.media3.session.SessionToken;
|
|||||||
import androidx.navigation.Navigation;
|
import androidx.navigation.Navigation;
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||||
|
|
||||||
|
import com.bumptech.glide.load.resource.bitmap.GranularRoundedCorners;
|
||||||
import com.cappielloantonio.tempo.R;
|
import com.cappielloantonio.tempo.R;
|
||||||
import com.cappielloantonio.tempo.databinding.FragmentPlaylistPageBinding;
|
import com.cappielloantonio.tempo.databinding.FragmentPlaylistPageBinding;
|
||||||
import com.cappielloantonio.tempo.glide.CustomGlideRequest;
|
import com.cappielloantonio.tempo.glide.CustomGlideRequest;
|
||||||
@@ -146,13 +147,13 @@ public class PlaylistPageFragment extends Fragment implements ClickCallback {
|
|||||||
playlistPageViewModel.getPlaylistSongLiveList().observe(getViewLifecycleOwner(), songs -> {
|
playlistPageViewModel.getPlaylistSongLiveList().observe(getViewLifecycleOwner(), songs -> {
|
||||||
if (bind != null) {
|
if (bind != null) {
|
||||||
bind.playlistPagePlayButton.setOnClickListener(v -> {
|
bind.playlistPagePlayButton.setOnClickListener(v -> {
|
||||||
MediaManager.startQueue(mediaBrowserListenableFuture, songs, 0);
|
MediaManager.startQueue(mediaBrowserListenableFuture, songs.subList(0, Math.min(100, songs.size())), 0);
|
||||||
activity.setBottomSheetInPeek(true);
|
activity.setBottomSheetInPeek(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
bind.playlistPageShuffleButton.setOnClickListener(v -> {
|
bind.playlistPageShuffleButton.setOnClickListener(v -> {
|
||||||
Collections.shuffle(songs);
|
Collections.shuffle(songs);
|
||||||
MediaManager.startQueue(mediaBrowserListenableFuture, songs, 0);
|
MediaManager.startQueue(mediaBrowserListenableFuture, songs.subList(0, Math.min(100, songs.size())), 0);
|
||||||
activity.setBottomSheetInPeek(true);
|
activity.setBottomSheetInPeek(true);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -160,10 +161,39 @@ public class PlaylistPageFragment extends Fragment implements ClickCallback {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void initBackCover() {
|
private void initBackCover() {
|
||||||
CustomGlideRequest.Builder
|
playlistPageViewModel.getPlaylistSongLiveList().observe(requireActivity(), songs -> {
|
||||||
.from(requireContext(), playlistPageViewModel.getPlaylist().getCoverArtId())
|
if (bind != null && songs != null && !songs.isEmpty()) {
|
||||||
.build()
|
Collections.shuffle(songs);
|
||||||
.into(bind.playlistCoverImageView);
|
|
||||||
|
// Pic top-left
|
||||||
|
CustomGlideRequest.Builder
|
||||||
|
.from(requireContext(), songs.size() > 0 ? songs.get(0).getCoverArtId() : playlistPageViewModel.getPlaylist().getCoverArtId())
|
||||||
|
.build()
|
||||||
|
.transform(new GranularRoundedCorners(CustomGlideRequest.CORNER_RADIUS, 0, 0, 0))
|
||||||
|
.into(bind.playlistCoverImageViewTopLeft);
|
||||||
|
|
||||||
|
// Pic top-right
|
||||||
|
CustomGlideRequest.Builder
|
||||||
|
.from(requireContext(), songs.size() > 1 ? songs.get(1).getCoverArtId() : playlistPageViewModel.getPlaylist().getCoverArtId())
|
||||||
|
.build()
|
||||||
|
.transform(new GranularRoundedCorners(0, CustomGlideRequest.CORNER_RADIUS, 0, 0))
|
||||||
|
.into(bind.playlistCoverImageViewTopRight);
|
||||||
|
|
||||||
|
// Pic bottom-left
|
||||||
|
CustomGlideRequest.Builder
|
||||||
|
.from(requireContext(), songs.size() > 2 ? songs.get(2).getCoverArtId() : playlistPageViewModel.getPlaylist().getCoverArtId())
|
||||||
|
.build()
|
||||||
|
.transform(new GranularRoundedCorners(0, 0, 0, CustomGlideRequest.CORNER_RADIUS))
|
||||||
|
.into(bind.playlistCoverImageViewBottomLeft);
|
||||||
|
|
||||||
|
// Pic bottom-right
|
||||||
|
CustomGlideRequest.Builder
|
||||||
|
.from(requireContext(), songs.size() > 3 ? songs.get(3).getCoverArtId() : playlistPageViewModel.getPlaylist().getCoverArtId())
|
||||||
|
.build()
|
||||||
|
.transform(new GranularRoundedCorners(0, 0, CustomGlideRequest.CORNER_RADIUS, 0))
|
||||||
|
.into(bind.playlistCoverImageViewBottomRight);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initSongsView() {
|
private void initSongsView() {
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ 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.PopupMenu;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.fragment.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
@@ -17,7 +18,6 @@ import androidx.recyclerview.widget.LinearLayoutManager;
|
|||||||
|
|
||||||
import com.cappielloantonio.tempo.R;
|
import com.cappielloantonio.tempo.R;
|
||||||
import com.cappielloantonio.tempo.databinding.FragmentPodcastChannelPageBinding;
|
import com.cappielloantonio.tempo.databinding.FragmentPodcastChannelPageBinding;
|
||||||
import com.cappielloantonio.tempo.glide.CustomGlideRequest;
|
|
||||||
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;
|
||||||
@@ -28,11 +28,10 @@ import com.cappielloantonio.tempo.util.Constants;
|
|||||||
import com.cappielloantonio.tempo.util.MusicUtil;
|
import com.cappielloantonio.tempo.util.MusicUtil;
|
||||||
import com.cappielloantonio.tempo.util.UIUtil;
|
import com.cappielloantonio.tempo.util.UIUtil;
|
||||||
import com.cappielloantonio.tempo.viewmodel.PodcastChannelPageViewModel;
|
import com.cappielloantonio.tempo.viewmodel.PodcastChannelPageViewModel;
|
||||||
|
import com.google.android.material.snackbar.Snackbar;
|
||||||
import com.google.common.util.concurrent.ListenableFuture;
|
import com.google.common.util.concurrent.ListenableFuture;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
@UnstableApi
|
@UnstableApi
|
||||||
public class PodcastChannelPageFragment extends Fragment implements ClickCallback {
|
public class PodcastChannelPageFragment extends Fragment implements ClickCallback {
|
||||||
@@ -84,28 +83,26 @@ public class PodcastChannelPageFragment extends Fragment implements ClickCallbac
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void initAppBar() {
|
private void initAppBar() {
|
||||||
activity.setSupportActionBar(bind.animToolbar);
|
activity.setSupportActionBar(bind.toolbar);
|
||||||
if (activity.getSupportActionBar() != null)
|
|
||||||
activity.getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
|
||||||
|
|
||||||
bind.collapsingToolbar.setTitle(MusicUtil.getReadableString(podcastChannelPageViewModel.getPodcastChannel().getTitle()));
|
if (activity.getSupportActionBar() != null) {
|
||||||
bind.animToolbar.setNavigationOnClickListener(v -> activity.navController.navigateUp());
|
activity.getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||||
bind.collapsingToolbar.setExpandedTitleColor(getResources().getColor(R.color.white, null));
|
activity.getSupportActionBar().setDisplayShowHomeEnabled(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
bind.toolbar.setTitle(MusicUtil.getReadableString(podcastChannelPageViewModel.getPodcastChannel().getTitle()));
|
||||||
|
bind.toolbar.setNavigationOnClickListener(v -> activity.navController.navigateUp());
|
||||||
|
bind.toolbar.setTitle(MusicUtil.getReadableString(podcastChannelPageViewModel.getPodcastChannel().getTitle()));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initPodcastChannelInfo() {
|
private void initPodcastChannelInfo() {
|
||||||
String normalizePodcastChannelDescription = MusicUtil.forceReadableString(podcastChannelPageViewModel.getPodcastChannel().getDescription());
|
String normalizePodcastChannelDescription = MusicUtil.forceReadableString(podcastChannelPageViewModel.getPodcastChannel().getDescription());
|
||||||
|
|
||||||
if (bind != null)
|
if (bind != null) {
|
||||||
bind.podcastChannelDescriptionTextView.setVisibility(!normalizePodcastChannelDescription.trim().isEmpty() ? View.VISIBLE : View.GONE);
|
bind.podcastChannelDescriptionTextView.setVisibility(!normalizePodcastChannelDescription.trim().isEmpty() ? View.VISIBLE : View.GONE);
|
||||||
|
|
||||||
if (getContext() != null && bind != null) CustomGlideRequest.Builder
|
|
||||||
.from(requireContext(), podcastChannelPageViewModel.getPodcastChannel().getCoverArtId())
|
|
||||||
.build()
|
|
||||||
.into(bind.podcastChannelBackdropImageView);
|
|
||||||
|
|
||||||
if (bind != null)
|
|
||||||
bind.podcastChannelDescriptionTextView.setText(normalizePodcastChannelDescription);
|
bind.podcastChannelDescriptionTextView.setText(normalizePodcastChannelDescription);
|
||||||
|
bind.podcastEpisodesFilterImageView.setOnClickListener(view -> showPopupMenu(view, R.menu.filter_podcast_episode_popup_menu));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initPodcastChannelEpisodesView() {
|
private void initPodcastChannelEpisodesView() {
|
||||||
@@ -116,22 +113,21 @@ public class PodcastChannelPageFragment extends Fragment implements ClickCallbac
|
|||||||
bind.podcastEpisodesRecyclerView.setAdapter(podcastEpisodeAdapter);
|
bind.podcastEpisodesRecyclerView.setAdapter(podcastEpisodeAdapter);
|
||||||
podcastChannelPageViewModel.getPodcastChannelEpisodes().observe(getViewLifecycleOwner(), channels -> {
|
podcastChannelPageViewModel.getPodcastChannelEpisodes().observe(getViewLifecycleOwner(), channels -> {
|
||||||
if (channels == null) {
|
if (channels == null) {
|
||||||
if (bind != null)
|
if (bind != null) {
|
||||||
bind.podcastChannelPageEpisodesPlaceholder.placeholder.setVisibility(View.VISIBLE);
|
bind.podcastEpisodesRecyclerView.setVisibility(View.GONE);
|
||||||
if (bind != null) bind.podcastChannelPageEpisodesSector.setVisibility(View.GONE);
|
}
|
||||||
} else {
|
} else {
|
||||||
if (bind != null)
|
if (bind != null) {
|
||||||
bind.podcastChannelPageEpisodesPlaceholder.placeholder.setVisibility(View.GONE);
|
bind.podcastEpisodesRecyclerView.setVisibility(View.VISIBLE);
|
||||||
|
}
|
||||||
|
|
||||||
if (!channels.isEmpty() && channels.get(0) != null && channels.get(0).getEpisodes() != null) {
|
if (!channels.isEmpty() && channels.get(0) != null && channels.get(0).getEpisodes() != null) {
|
||||||
List<PodcastEpisode> availableEpisode = channels.get(0).getEpisodes().stream().filter(podcastEpisode -> Objects.equals(podcastEpisode.getStatus(), "completed")).collect(Collectors.toList());
|
List<PodcastEpisode> availableEpisode = channels.get(0).getEpisodes();
|
||||||
|
|
||||||
if (bind != null) {
|
if (bind != null && availableEpisode != null) {
|
||||||
bind.podcastEpisodesRecyclerView.setVisibility(availableEpisode.isEmpty() ? View.GONE : View.VISIBLE);
|
bind.podcastEpisodesRecyclerView.setVisibility(availableEpisode.isEmpty() ? View.GONE : View.VISIBLE);
|
||||||
bind.podcastEpisodesAvailabilityTextView.setVisibility(availableEpisode.isEmpty() ? View.VISIBLE : View.GONE);
|
podcastEpisodeAdapter.setItems(availableEpisode);
|
||||||
}
|
}
|
||||||
|
|
||||||
podcastEpisodeAdapter.setItems(availableEpisode);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -145,6 +141,25 @@ public class PodcastChannelPageFragment extends Fragment implements ClickCallbac
|
|||||||
MediaBrowser.releaseFuture(mediaBrowserListenableFuture);
|
MediaBrowser.releaseFuture(mediaBrowserListenableFuture);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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_podcast_filter_download) {
|
||||||
|
podcastEpisodeAdapter.sort(Constants.PODCAST_FILTER_BY_DOWNLOAD);
|
||||||
|
return true;
|
||||||
|
} else if (menuItem.getItemId() == R.id.menu_podcast_filter_all) {
|
||||||
|
podcastEpisodeAdapter.sort(Constants.PODCAST_FILTER_BY_ALL);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
popup.show();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onPodcastEpisodeClick(Bundle bundle) {
|
public void onPodcastEpisodeClick(Bundle bundle) {
|
||||||
MediaManager.startPodcast(mediaBrowserListenableFuture, bundle.getParcelable(Constants.PODCAST_OBJECT));
|
MediaManager.startPodcast(mediaBrowserListenableFuture, bundle.getParcelable(Constants.PODCAST_OBJECT));
|
||||||
@@ -155,4 +170,14 @@ public class PodcastChannelPageFragment extends Fragment implements ClickCallbac
|
|||||||
public void onPodcastEpisodeLongClick(Bundle bundle) {
|
public void onPodcastEpisodeLongClick(Bundle bundle) {
|
||||||
Navigation.findNavController(requireView()).navigate(R.id.podcastEpisodeBottomSheetDialog, bundle);
|
Navigation.findNavController(requireView()).navigate(R.id.podcastEpisodeBottomSheetDialog, bundle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPodcastEpisodeAltClick(Bundle bundle) {
|
||||||
|
PodcastEpisode episode = bundle.getParcelable(Constants.PODCAST_OBJECT);
|
||||||
|
podcastChannelPageViewModel.requestPodcastEpisodeDownload(episode);
|
||||||
|
|
||||||
|
Snackbar.make(requireView(), R.string.podcast_episode_download_request_snackbar, Snackbar.LENGTH_SHORT)
|
||||||
|
.setAnchorView(activity.bind.bottomNavigation)
|
||||||
|
.show();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -12,6 +12,8 @@ import androidx.activity.result.contract.ActivityResultContracts;
|
|||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.annotation.OptIn;
|
import androidx.annotation.OptIn;
|
||||||
|
import androidx.appcompat.app.AppCompatDelegate;
|
||||||
|
import androidx.core.os.LocaleListCompat;
|
||||||
import androidx.lifecycle.ViewModelProvider;
|
import androidx.lifecycle.ViewModelProvider;
|
||||||
import androidx.media3.common.util.UnstableApi;
|
import androidx.media3.common.util.UnstableApi;
|
||||||
import androidx.preference.ListPreference;
|
import androidx.preference.ListPreference;
|
||||||
@@ -21,12 +23,19 @@ import androidx.preference.PreferenceFragmentCompat;
|
|||||||
import com.cappielloantonio.tempo.BuildConfig;
|
import com.cappielloantonio.tempo.BuildConfig;
|
||||||
import com.cappielloantonio.tempo.R;
|
import com.cappielloantonio.tempo.R;
|
||||||
import com.cappielloantonio.tempo.helper.ThemeHelper;
|
import com.cappielloantonio.tempo.helper.ThemeHelper;
|
||||||
|
import com.cappielloantonio.tempo.interfaces.DialogClickCallback;
|
||||||
import com.cappielloantonio.tempo.interfaces.ScanCallback;
|
import com.cappielloantonio.tempo.interfaces.ScanCallback;
|
||||||
import com.cappielloantonio.tempo.ui.activity.MainActivity;
|
import com.cappielloantonio.tempo.ui.activity.MainActivity;
|
||||||
|
import com.cappielloantonio.tempo.ui.dialog.DeleteDownloadStorageDialog;
|
||||||
|
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.util.Preferences;
|
import com.cappielloantonio.tempo.util.Preferences;
|
||||||
|
import com.cappielloantonio.tempo.util.UIUtil;
|
||||||
import com.cappielloantonio.tempo.viewmodel.SettingViewModel;
|
import com.cappielloantonio.tempo.viewmodel.SettingViewModel;
|
||||||
|
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
@OptIn(markerClass = UnstableApi.class)
|
@OptIn(markerClass = UnstableApi.class)
|
||||||
public class SettingsFragment extends PreferenceFragmentCompat {
|
public class SettingsFragment extends PreferenceFragmentCompat {
|
||||||
private static final String TAG = "SettingsFragment";
|
private static final String TAG = "SettingsFragment";
|
||||||
@@ -72,39 +81,16 @@ public class SettingsFragment extends PreferenceFragmentCompat {
|
|||||||
super.onResume();
|
super.onResume();
|
||||||
|
|
||||||
checkEqualizer();
|
checkEqualizer();
|
||||||
|
checkStorage();
|
||||||
|
|
||||||
findPreference("version").setSummary(BuildConfig.VERSION_NAME);
|
setAppLanguage();
|
||||||
|
setVersion();
|
||||||
|
|
||||||
findPreference("logout").setOnPreferenceClickListener(preference -> {
|
actionLogout();
|
||||||
activity.quit();
|
actionScan();
|
||||||
return true;
|
actionSyncStarredTracks();
|
||||||
});
|
actionChangeDownloadStorage();
|
||||||
|
actionDeleteDownloadStorage();
|
||||||
findPreference("scan_library").setOnPreferenceClickListener(preference -> {
|
|
||||||
settingViewModel.launchScan(new ScanCallback() {
|
|
||||||
@Override
|
|
||||||
public void onError(Exception exception) {
|
|
||||||
findPreference("scan_library").setSummary(exception.getMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onSuccess(boolean isScanning, long count) {
|
|
||||||
getScanStatus();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
|
|
||||||
findPreference("sync_starred_tracks_for_offline_use").setOnPreferenceChangeListener((preference, newValue) -> {
|
|
||||||
if (newValue instanceof Boolean) {
|
|
||||||
if ((Boolean) newValue) {
|
|
||||||
StarredSyncDialog dialog = new StarredSyncDialog();
|
|
||||||
dialog.show(activity.getSupportFragmentManager(), null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -144,6 +130,110 @@ public class SettingsFragment extends PreferenceFragmentCompat {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void checkStorage() {
|
||||||
|
Preference storage = findPreference("download_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 setAppLanguage() {
|
||||||
|
ListPreference localePref = (ListPreference) findPreference("language");
|
||||||
|
|
||||||
|
Map<String, String> locales = UIUtil.getLangPreferenceDropdownEntries(requireContext());
|
||||||
|
|
||||||
|
CharSequence[] entries = locales.keySet().toArray(new CharSequence[locales.size()]);
|
||||||
|
CharSequence[] entryValues = locales.values().toArray(new CharSequence[locales.size()]);
|
||||||
|
|
||||||
|
localePref.setEntries(entries);
|
||||||
|
localePref.setEntryValues(entryValues);
|
||||||
|
|
||||||
|
localePref.setDefaultValue(entryValues[0]);
|
||||||
|
localePref.setSummary(Locale.forLanguageTag(localePref.getValue()).getDisplayLanguage());
|
||||||
|
|
||||||
|
localePref.setOnPreferenceChangeListener((preference, newValue) -> {
|
||||||
|
LocaleListCompat appLocale = LocaleListCompat.forLanguageTags((String) newValue);
|
||||||
|
AppCompatDelegate.setApplicationLocales(appLocale);
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setVersion() {
|
||||||
|
findPreference("version").setSummary(BuildConfig.VERSION_NAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void actionLogout() {
|
||||||
|
findPreference("logout").setOnPreferenceClickListener(preference -> {
|
||||||
|
activity.quit();
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void actionScan() {
|
||||||
|
findPreference("scan_library").setOnPreferenceClickListener(preference -> {
|
||||||
|
settingViewModel.launchScan(new ScanCallback() {
|
||||||
|
@Override
|
||||||
|
public void onError(Exception exception) {
|
||||||
|
findPreference("scan_library").setSummary(exception.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSuccess(boolean isScanning, long count) {
|
||||||
|
getScanStatus();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void actionSyncStarredTracks() {
|
||||||
|
findPreference("sync_starred_tracks_for_offline_use").setOnPreferenceChangeListener((preference, newValue) -> {
|
||||||
|
if (newValue instanceof Boolean) {
|
||||||
|
if ((Boolean) newValue) {
|
||||||
|
StarredSyncDialog dialog = new StarredSyncDialog();
|
||||||
|
dialog.show(activity.getSupportFragmentManager(), null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void actionChangeDownloadStorage() {
|
||||||
|
findPreference("download_storage").setOnPreferenceClickListener(preference -> {
|
||||||
|
DownloadStorageDialog dialog = new DownloadStorageDialog(new DialogClickCallback() {
|
||||||
|
@Override
|
||||||
|
public void onPositiveClick() {
|
||||||
|
findPreference("download_storage").setSummary(R.string.download_storage_external_dialog_positive_button);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onNegativeClick() {
|
||||||
|
findPreference("download_storage").setSummary(R.string.download_storage_internal_dialog_negative_button);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
dialog.show(activity.getSupportFragmentManager(), null);
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void actionDeleteDownloadStorage() {
|
||||||
|
findPreference("delete_download_storage").setOnPreferenceClickListener(preference -> {
|
||||||
|
DeleteDownloadStorageDialog dialog = new DeleteDownloadStorageDialog();
|
||||||
|
dialog.show(activity.getSupportFragmentManager(), null);
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private void getScanStatus() {
|
private void getScanStatus() {
|
||||||
settingViewModel.getScanStatus(new ScanCallback() {
|
settingViewModel.getScanStatus(new ScanCallback() {
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -91,10 +91,9 @@ public class AlbumBottomSheetDialog extends BottomSheetDialogFragment implements
|
|||||||
artistAlbum.setText(MusicUtil.getReadableString(albumBottomSheetViewModel.getAlbum().getArtist()));
|
artistAlbum.setText(MusicUtil.getReadableString(albumBottomSheetViewModel.getAlbum().getArtist()));
|
||||||
|
|
||||||
ToggleButton favoriteToggle = view.findViewById(R.id.button_favorite);
|
ToggleButton favoriteToggle = view.findViewById(R.id.button_favorite);
|
||||||
favoriteToggle.setChecked(Boolean.TRUE.equals(albumBottomSheetViewModel.getAlbum().getStarred()));
|
favoriteToggle.setChecked(albumBottomSheetViewModel.getAlbum().getStarred() != null);
|
||||||
favoriteToggle.setOnClickListener(v -> {
|
favoriteToggle.setOnClickListener(v -> {
|
||||||
albumBottomSheetViewModel.setFavorite();
|
albumBottomSheetViewModel.setFavorite();
|
||||||
dismissBottomSheet();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
TextView playRadio = view.findViewById(R.id.play_radio_text_view);
|
TextView playRadio = view.findViewById(R.id.play_radio_text_view);
|
||||||
|
|||||||
@@ -79,10 +79,9 @@ public class ArtistBottomSheetDialog extends BottomSheetDialogFragment implement
|
|||||||
nameArtist.setSelected(true);
|
nameArtist.setSelected(true);
|
||||||
|
|
||||||
ToggleButton favoriteToggle = view.findViewById(R.id.button_favorite);
|
ToggleButton favoriteToggle = view.findViewById(R.id.button_favorite);
|
||||||
favoriteToggle.setChecked(Boolean.TRUE.equals(artistBottomSheetViewModel.getArtist().getStarred()));
|
favoriteToggle.setChecked(artistBottomSheetViewModel.getArtist().getStarred() != null);
|
||||||
favoriteToggle.setOnClickListener(v -> {
|
favoriteToggle.setOnClickListener(v -> {
|
||||||
artistBottomSheetViewModel.setFavorite();
|
artistBottomSheetViewModel.setFavorite();
|
||||||
dismissBottomSheet();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
TextView playRadio = view.findViewById(R.id.play_radio_text_view);
|
TextView playRadio = view.findViewById(R.id.play_radio_text_view);
|
||||||
|
|||||||
@@ -87,10 +87,9 @@ public class SongBottomSheetDialog extends BottomSheetDialogFragment implements
|
|||||||
artistSong.setText(MusicUtil.getReadableString(songBottomSheetViewModel.getSong().getArtist()));
|
artistSong.setText(MusicUtil.getReadableString(songBottomSheetViewModel.getSong().getArtist()));
|
||||||
|
|
||||||
ToggleButton favoriteToggle = view.findViewById(R.id.button_favorite);
|
ToggleButton favoriteToggle = view.findViewById(R.id.button_favorite);
|
||||||
favoriteToggle.setChecked(Boolean.TRUE.equals(songBottomSheetViewModel.getSong().getStarred()));
|
favoriteToggle.setChecked(songBottomSheetViewModel.getSong().getStarred() != null);
|
||||||
favoriteToggle.setOnClickListener(v -> {
|
favoriteToggle.setOnClickListener(v -> {
|
||||||
songBottomSheetViewModel.setFavorite(requireContext());
|
songBottomSheetViewModel.setFavorite(requireContext());
|
||||||
dismissBottomSheet();
|
|
||||||
});
|
});
|
||||||
favoriteToggle.setOnLongClickListener(v -> {
|
favoriteToggle.setOnLongClickListener(v -> {
|
||||||
Bundle bundle = new Bundle();
|
Bundle bundle = new Bundle();
|
||||||
@@ -216,7 +215,6 @@ public class SongBottomSheetDialog extends BottomSheetDialogFragment implements
|
|||||||
|
|
||||||
private void initDownloadUI(TextView download, TextView remove) {
|
private void initDownloadUI(TextView download, TextView remove) {
|
||||||
if (DownloadUtil.getDownloadTracker(requireContext()).isDownloaded(song.getId())) {
|
if (DownloadUtil.getDownloadTracker(requireContext()).isDownloaded(song.getId())) {
|
||||||
download.setVisibility(View.GONE);
|
|
||||||
remove.setVisibility(View.VISIBLE);
|
remove.setVisibility(View.VISIBLE);
|
||||||
} else {
|
} else {
|
||||||
download.setVisibility(View.VISIBLE);
|
download.setVisibility(View.VISIBLE);
|
||||||
|
|||||||
@@ -44,6 +44,9 @@ object Constants {
|
|||||||
const val PLAYLIST_ORDER_BY_NAME = "ORDER_BY_NAME"
|
const val PLAYLIST_ORDER_BY_NAME = "ORDER_BY_NAME"
|
||||||
const val PLAYLIST_ORDER_BY_RANDOM = "ORDER_BY_RANDOM"
|
const val PLAYLIST_ORDER_BY_RANDOM = "ORDER_BY_RANDOM"
|
||||||
|
|
||||||
|
const val PODCAST_FILTER_BY_DOWNLOAD = "PODCAST_FILTER_BY_DOWNLOAD"
|
||||||
|
const val PODCAST_FILTER_BY_ALL = "PODCAST_FILTER_BY_ALL"
|
||||||
|
|
||||||
const val MEDIA_TYPE_MUSIC = "music"
|
const val MEDIA_TYPE_MUSIC = "music"
|
||||||
const val MEDIA_TYPE_PODCAST = "podcast"
|
const val MEDIA_TYPE_PODCAST = "podcast"
|
||||||
const val MEDIA_TYPE_AUDIOBOOK = "audiobook"
|
const val MEDIA_TYPE_AUDIOBOOK = "audiobook"
|
||||||
@@ -73,6 +76,12 @@ object Constants {
|
|||||||
|
|
||||||
const val DOWNLOAD_URI = "rest/download"
|
const val DOWNLOAD_URI = "rest/download"
|
||||||
|
|
||||||
|
const val DOWNLOAD_TYPE_TRACK = "download_type_track"
|
||||||
|
const val DOWNLOAD_TYPE_ALBUM = "download_type_album"
|
||||||
|
const val DOWNLOAD_TYPE_ARTIST = "download_type_artist"
|
||||||
|
const val DOWNLOAD_TYPE_GENRE = "download_type_genre"
|
||||||
|
const val DOWNLOAD_TYPE_YEAR = "download_type_year"
|
||||||
|
|
||||||
const val PLAYABLE_MEDIA_LIMIT = 100
|
const val PLAYABLE_MEDIA_LIMIT = 100
|
||||||
const val PRE_PLAYABLE_MEDIA = 15
|
const val PRE_PLAYABLE_MEDIA = 15
|
||||||
}
|
}
|
||||||
@@ -2,6 +2,7 @@ package com.cappielloantonio.tempo.util;
|
|||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
|
||||||
|
import androidx.media3.common.util.Log;
|
||||||
import androidx.media3.common.util.UnstableApi;
|
import androidx.media3.common.util.UnstableApi;
|
||||||
import androidx.media3.database.DatabaseProvider;
|
import androidx.media3.database.DatabaseProvider;
|
||||||
import androidx.media3.database.StandaloneDatabaseProvider;
|
import androidx.media3.database.StandaloneDatabaseProvider;
|
||||||
@@ -23,6 +24,8 @@ import java.io.File;
|
|||||||
import java.net.CookieHandler;
|
import java.net.CookieHandler;
|
||||||
import java.net.CookieManager;
|
import java.net.CookieManager;
|
||||||
import java.net.CookiePolicy;
|
import java.net.CookiePolicy;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
import java.util.concurrent.Executors;
|
import java.util.concurrent.Executors;
|
||||||
|
|
||||||
@UnstableApi
|
@UnstableApi
|
||||||
@@ -127,9 +130,19 @@ public final class DownloadUtil {
|
|||||||
|
|
||||||
private static synchronized File getDownloadDirectory(Context context) {
|
private static synchronized File getDownloadDirectory(Context context) {
|
||||||
if (downloadDirectory == null) {
|
if (downloadDirectory == null) {
|
||||||
downloadDirectory = context.getExternalFilesDir(null);
|
if (Preferences.getDownloadStoragePreference() == 0) {
|
||||||
if (downloadDirectory == null) {
|
downloadDirectory = context.getExternalFilesDirs(null)[0];
|
||||||
downloadDirectory = context.getFilesDir();
|
if (downloadDirectory == null) {
|
||||||
|
downloadDirectory = context.getFilesDir();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
downloadDirectory = context.getExternalFilesDirs(null)[1];
|
||||||
|
} catch (Exception exception) {
|
||||||
|
downloadDirectory = context.getExternalFilesDirs(null)[0];
|
||||||
|
Preferences.setDownloadStoragePreference(0);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -143,4 +156,32 @@ public final class DownloadUtil {
|
|||||||
.setCacheWriteDataSinkFactory(null)
|
.setCacheWriteDataSinkFactory(null)
|
||||||
.setFlags(CacheDataSource.FLAG_IGNORE_CACHE_ON_ERROR);
|
.setFlags(CacheDataSource.FLAG_IGNORE_CACHE_ON_ERROR);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static synchronized void eraseDownloadFolder(Context context) {
|
||||||
|
File directory = getDownloadDirectory(context);
|
||||||
|
|
||||||
|
ArrayList<File> files = listFiles(directory, new ArrayList<>());
|
||||||
|
|
||||||
|
for (File file : files) {
|
||||||
|
file.delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static synchronized ArrayList<File> listFiles(File directory, ArrayList<File> files) {
|
||||||
|
if (directory.isDirectory()) {
|
||||||
|
File[] list = directory.listFiles();
|
||||||
|
|
||||||
|
if (list != null) {
|
||||||
|
for (File file : list) {
|
||||||
|
if (file.isFile() && file.getName().toLowerCase().endsWith(".exo")) {
|
||||||
|
files.add(file);
|
||||||
|
} else if (file.isDirectory()) {
|
||||||
|
listFiles(file, files);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return files;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ import androidx.media3.common.MimeTypes;
|
|||||||
import androidx.media3.common.util.UnstableApi;
|
import androidx.media3.common.util.UnstableApi;
|
||||||
|
|
||||||
import com.cappielloantonio.tempo.App;
|
import com.cappielloantonio.tempo.App;
|
||||||
|
import com.cappielloantonio.tempo.model.Download;
|
||||||
|
import com.cappielloantonio.tempo.repository.DownloadRepository;
|
||||||
import com.cappielloantonio.tempo.subsonic.models.Child;
|
import com.cappielloantonio.tempo.subsonic.models.Child;
|
||||||
import com.cappielloantonio.tempo.subsonic.models.InternetRadioStation;
|
import com.cappielloantonio.tempo.subsonic.models.InternetRadioStation;
|
||||||
import com.cappielloantonio.tempo.subsonic.models.PodcastEpisode;
|
import com.cappielloantonio.tempo.subsonic.models.PodcastEpisode;
|
||||||
@@ -54,8 +56,8 @@ public class MappingUtil {
|
|||||||
bundle.putBoolean("isVideo", media.isVideo());
|
bundle.putBoolean("isVideo", media.isVideo());
|
||||||
bundle.putInt("userRating", media.getUserRating() != null ? media.getUserRating() : 0);
|
bundle.putInt("userRating", media.getUserRating() != null ? media.getUserRating() : 0);
|
||||||
bundle.putDouble("averageRating", media.getAverageRating() != null ? media.getAverageRating() : 0);
|
bundle.putDouble("averageRating", media.getAverageRating() != null ? media.getAverageRating() : 0);
|
||||||
bundle.putLong("playCount", media.getPlayCount() != null ? media.getTrack() : 0);
|
bundle.putLong("playCount", media.getPlayCount() != null ? media.getPlayCount() : 0);
|
||||||
bundle.putInt("discNumber", media.getDiscNumber() != null ? media.getTrack() : 0);
|
bundle.putInt("discNumber", media.getDiscNumber() != null ? media.getDiscNumber() : 0);
|
||||||
bundle.putLong("created", media.getCreated() != null ? media.getCreated().getTime() : 0);
|
bundle.putLong("created", media.getCreated() != null ? media.getCreated().getTime() : 0);
|
||||||
bundle.putLong("starred", media.getStarred() != null ? media.getStarred().getTime() : 0);
|
bundle.putLong("starred", media.getStarred() != null ? media.getStarred().getTime() : 0);
|
||||||
bundle.putString("albumId", media.getAlbumId());
|
bundle.putString("albumId", media.getAlbumId());
|
||||||
@@ -71,9 +73,9 @@ public class MappingUtil {
|
|||||||
.setMediaMetadata(
|
.setMediaMetadata(
|
||||||
new MediaMetadata.Builder()
|
new MediaMetadata.Builder()
|
||||||
.setTitle(MusicUtil.getReadableString(media.getTitle()))
|
.setTitle(MusicUtil.getReadableString(media.getTitle()))
|
||||||
.setTrackNumber(media.getTrack())
|
.setTrackNumber(media.getTrack() != null ? media.getTrack() : 0)
|
||||||
.setDiscNumber(media.getDiscNumber())
|
.setDiscNumber(media.getDiscNumber() != null ? media.getDiscNumber() : 0)
|
||||||
.setReleaseYear(media.getYear())
|
.setReleaseYear(media.getYear() != null ? media.getYear() : 0)
|
||||||
.setAlbumTitle(MusicUtil.getReadableString(media.getAlbum()))
|
.setAlbumTitle(MusicUtil.getReadableString(media.getAlbum()))
|
||||||
.setArtist(MusicUtil.getReadableString(media.getArtist()))
|
.setArtist(MusicUtil.getReadableString(media.getArtist()))
|
||||||
.setExtras(bundle)
|
.setExtras(bundle)
|
||||||
@@ -106,20 +108,20 @@ public class MappingUtil {
|
|||||||
.setMediaMetadata(
|
.setMediaMetadata(
|
||||||
new MediaMetadata.Builder()
|
new MediaMetadata.Builder()
|
||||||
.setTitle(MusicUtil.getReadableString(media.getTitle()))
|
.setTitle(MusicUtil.getReadableString(media.getTitle()))
|
||||||
.setTrackNumber(media.getTrack())
|
.setTrackNumber(media.getTrack() != null ? media.getTrack() : 0)
|
||||||
.setDiscNumber(media.getDiscNumber())
|
.setDiscNumber(media.getDiscNumber() != null ? media.getDiscNumber() : 0)
|
||||||
.setReleaseYear(media.getYear())
|
.setReleaseYear(media.getYear() != null ? media.getYear() : 0)
|
||||||
.setAlbumTitle(MusicUtil.getReadableString(media.getAlbum()))
|
.setAlbumTitle(MusicUtil.getReadableString(media.getAlbum()))
|
||||||
.setArtist(MusicUtil.getReadableString(media.getArtist()))
|
.setArtist(MusicUtil.getReadableString(media.getArtist()))
|
||||||
.build()
|
.build()
|
||||||
)
|
)
|
||||||
.setRequestMetadata(
|
.setRequestMetadata(
|
||||||
new MediaItem.RequestMetadata.Builder()
|
new MediaItem.RequestMetadata.Builder()
|
||||||
.setMediaUri(MusicUtil.getDownloadUri(media.getId()))
|
.setMediaUri(Preferences.preferTranscodedDownload() ? MusicUtil.getTranscodedDownloadUri(media.getId()) : MusicUtil.getDownloadUri(media.getId()))
|
||||||
.build()
|
.build()
|
||||||
)
|
)
|
||||||
.setMimeType(MimeTypes.BASE_TYPE_AUDIO)
|
.setMimeType(MimeTypes.BASE_TYPE_AUDIO)
|
||||||
.setUri(MusicUtil.getDownloadUri(media.getId()))
|
.setUri(Preferences.preferTranscodedDownload() ? MusicUtil.getTranscodedDownloadUri(media.getId()) : MusicUtil.getDownloadUri(media.getId()))
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -178,8 +180,8 @@ public class MappingUtil {
|
|||||||
bundle.putBoolean("isVideo", podcastEpisode.isVideo());
|
bundle.putBoolean("isVideo", podcastEpisode.isVideo());
|
||||||
bundle.putInt("userRating", podcastEpisode.getUserRating() != null ? podcastEpisode.getUserRating() : 0);
|
bundle.putInt("userRating", podcastEpisode.getUserRating() != null ? podcastEpisode.getUserRating() : 0);
|
||||||
bundle.putDouble("averageRating", podcastEpisode.getAverageRating() != null ? podcastEpisode.getAverageRating() : 0);
|
bundle.putDouble("averageRating", podcastEpisode.getAverageRating() != null ? podcastEpisode.getAverageRating() : 0);
|
||||||
bundle.putLong("playCount", podcastEpisode.getPlayCount() != null ? podcastEpisode.getTrack() : 0);
|
bundle.putLong("playCount", podcastEpisode.getPlayCount() != null ? podcastEpisode.getPlayCount() : 0);
|
||||||
bundle.putInt("discNumber", podcastEpisode.getDiscNumber() != null ? podcastEpisode.getTrack() : 0);
|
bundle.putInt("discNumber", podcastEpisode.getDiscNumber() != null ? podcastEpisode.getDiscNumber() : 0);
|
||||||
bundle.putLong("created", podcastEpisode.getCreated() != null ? podcastEpisode.getCreated().getTime() : 0);
|
bundle.putLong("created", podcastEpisode.getCreated() != null ? podcastEpisode.getCreated().getTime() : 0);
|
||||||
bundle.putLong("starred", podcastEpisode.getStarred() != null ? podcastEpisode.getStarred().getTime() : 0);
|
bundle.putLong("starred", podcastEpisode.getStarred() != null ? podcastEpisode.getStarred().getTime() : 0);
|
||||||
bundle.putString("albumId", podcastEpisode.getAlbumId());
|
bundle.putString("albumId", podcastEpisode.getAlbumId());
|
||||||
@@ -195,9 +197,9 @@ public class MappingUtil {
|
|||||||
.setMediaMetadata(
|
.setMediaMetadata(
|
||||||
new MediaMetadata.Builder()
|
new MediaMetadata.Builder()
|
||||||
.setTitle(MusicUtil.getReadableString(podcastEpisode.getTitle()))
|
.setTitle(MusicUtil.getReadableString(podcastEpisode.getTitle()))
|
||||||
.setTrackNumber(podcastEpisode.getTrack())
|
.setTrackNumber(podcastEpisode.getTrack() != null ? podcastEpisode.getTrack() : 0)
|
||||||
.setDiscNumber(podcastEpisode.getDiscNumber())
|
.setDiscNumber(podcastEpisode.getDiscNumber() != null ? podcastEpisode.getDiscNumber() : 0)
|
||||||
.setReleaseYear(podcastEpisode.getYear())
|
.setReleaseYear(podcastEpisode.getYear() != null ? podcastEpisode.getYear() : 0)
|
||||||
.setAlbumTitle(MusicUtil.getReadableString(podcastEpisode.getAlbum()))
|
.setAlbumTitle(MusicUtil.getReadableString(podcastEpisode.getAlbum()))
|
||||||
.setArtist(MusicUtil.getReadableString(podcastEpisode.getArtist()))
|
.setArtist(MusicUtil.getReadableString(podcastEpisode.getArtist()))
|
||||||
.setExtras(bundle)
|
.setExtras(bundle)
|
||||||
@@ -216,13 +218,18 @@ public class MappingUtil {
|
|||||||
|
|
||||||
private static Uri getUri(Child media) {
|
private static Uri getUri(Child media) {
|
||||||
return DownloadUtil.getDownloadTracker(App.getContext()).isDownloaded(media.getId())
|
return DownloadUtil.getDownloadTracker(App.getContext()).isDownloaded(media.getId())
|
||||||
? MusicUtil.getDownloadUri(media.getId())
|
? getDownloadUri(media.getId())
|
||||||
: MusicUtil.getStreamUri(media.getId());
|
: MusicUtil.getStreamUri(media.getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Uri getUri(PodcastEpisode podcastEpisode) {
|
private static Uri getUri(PodcastEpisode podcastEpisode) {
|
||||||
return DownloadUtil.getDownloadTracker(App.getContext()).isDownloaded(podcastEpisode.getId())
|
return DownloadUtil.getDownloadTracker(App.getContext()).isDownloaded(podcastEpisode.getStreamId())
|
||||||
? MusicUtil.getDownloadUri(podcastEpisode.getId())
|
? getDownloadUri(podcastEpisode.getStreamId())
|
||||||
: MusicUtil.getStreamUri(podcastEpisode.getId());
|
: MusicUtil.getStreamUri(podcastEpisode.getStreamId());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Uri getDownloadUri(String id) {
|
||||||
|
Download download = new DownloadRepository().getDownload(id);
|
||||||
|
return download != null && !download.getDownloadUri().isEmpty() ? Uri.parse(download.getDownloadUri()) : MusicUtil.getDownloadUri(id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,9 @@ import android.text.Html;
|
|||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import com.cappielloantonio.tempo.App;
|
import com.cappielloantonio.tempo.App;
|
||||||
|
import com.cappielloantonio.tempo.R;
|
||||||
|
import com.cappielloantonio.tempo.model.Download;
|
||||||
|
import com.cappielloantonio.tempo.repository.DownloadRepository;
|
||||||
import com.cappielloantonio.tempo.subsonic.models.Child;
|
import com.cappielloantonio.tempo.subsonic.models.Child;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@@ -29,7 +32,7 @@ public class MusicUtil {
|
|||||||
uri.append("stream");
|
uri.append("stream");
|
||||||
|
|
||||||
if (params.containsKey("u") && params.get("u") != null)
|
if (params.containsKey("u") && params.get("u") != null)
|
||||||
uri.append("?u=").append(params.get("u"));
|
uri.append("?u=").append(Util.encode(params.get("u")));
|
||||||
if (params.containsKey("p") && params.get("p") != null)
|
if (params.containsKey("p") && params.get("p") != null)
|
||||||
uri.append("&p=").append(params.get("p"));
|
uri.append("&p=").append(params.get("p"));
|
||||||
if (params.containsKey("s") && params.get("s") != null)
|
if (params.containsKey("s") && params.get("s") != null)
|
||||||
@@ -54,15 +57,49 @@ public class MusicUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static Uri getDownloadUri(String id) {
|
public static Uri getDownloadUri(String id) {
|
||||||
|
StringBuilder uri = new StringBuilder();
|
||||||
|
|
||||||
|
Download download = new DownloadRepository().getDownload(id);
|
||||||
|
|
||||||
|
if (download == null || download.getDownloadUri().isEmpty()) {
|
||||||
|
Map<String, String> params = App.getSubsonicClientInstance(false).getParams();
|
||||||
|
|
||||||
|
uri.append(App.getSubsonicClientInstance(false).getUrl());
|
||||||
|
uri.append("download");
|
||||||
|
|
||||||
|
if (params.containsKey("u") && params.get("u") != null)
|
||||||
|
uri.append("?u=").append(Util.encode(params.get("u")));
|
||||||
|
if (params.containsKey("p") && params.get("p") != null)
|
||||||
|
uri.append("&p=").append(params.get("p"));
|
||||||
|
if (params.containsKey("s") && params.get("s") != null)
|
||||||
|
uri.append("&s=").append(params.get("s"));
|
||||||
|
if (params.containsKey("t") && params.get("t") != null)
|
||||||
|
uri.append("&t=").append(params.get("t"));
|
||||||
|
if (params.containsKey("v") && params.get("v") != null)
|
||||||
|
uri.append("&v=").append(params.get("v"));
|
||||||
|
if (params.containsKey("c") && params.get("c") != null)
|
||||||
|
uri.append("&c=").append(params.get("c"));
|
||||||
|
|
||||||
|
uri.append("&id=").append(id);
|
||||||
|
} else {
|
||||||
|
uri.append(download.getDownloadUri());
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.d(TAG, "getDownloadUri: " + uri);
|
||||||
|
|
||||||
|
return Uri.parse(uri.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Uri getTranscodedDownloadUri(String id) {
|
||||||
Map<String, String> params = App.getSubsonicClientInstance(false).getParams();
|
Map<String, String> params = App.getSubsonicClientInstance(false).getParams();
|
||||||
|
|
||||||
StringBuilder uri = new StringBuilder();
|
StringBuilder uri = new StringBuilder();
|
||||||
|
|
||||||
uri.append(App.getSubsonicClientInstance(false).getUrl());
|
uri.append(App.getSubsonicClientInstance(false).getUrl());
|
||||||
uri.append("download");
|
uri.append("stream");
|
||||||
|
|
||||||
if (params.containsKey("u") && params.get("u") != null)
|
if (params.containsKey("u") && params.get("u") != null)
|
||||||
uri.append("?u=").append(params.get("u"));
|
uri.append("?u=").append(Util.encode(params.get("u")));
|
||||||
if (params.containsKey("p") && params.get("p") != null)
|
if (params.containsKey("p") && params.get("p") != null)
|
||||||
uri.append("&p=").append(params.get("p"));
|
uri.append("&p=").append(params.get("p"));
|
||||||
if (params.containsKey("s") && params.get("s") != null)
|
if (params.containsKey("s") && params.get("s") != null)
|
||||||
@@ -74,13 +111,19 @@ public class MusicUtil {
|
|||||||
if (params.containsKey("c") && params.get("c") != null)
|
if (params.containsKey("c") && params.get("c") != null)
|
||||||
uri.append("&c=").append(params.get("c"));
|
uri.append("&c=").append(params.get("c"));
|
||||||
|
|
||||||
|
if (!Preferences.isServerPrioritizedInTranscodedDownload())
|
||||||
|
uri.append("&maxBitRate=").append(getBitratePreferenceForDownload());
|
||||||
|
if (!Preferences.isServerPrioritizedInTranscodedDownload())
|
||||||
|
uri.append("&format=").append(getTranscodingFormatPreferenceForDownload());
|
||||||
|
|
||||||
uri.append("&id=").append(id);
|
uri.append("&id=").append(id);
|
||||||
|
|
||||||
Log.d(TAG, "getDownloadUri: " + uri);
|
Log.d(TAG, "getTranscodedDownloadUri: " + uri);
|
||||||
|
|
||||||
return Uri.parse(uri.toString());
|
return Uri.parse(uri.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static String getReadableDurationString(long duration, boolean millis) {
|
public static String getReadableDurationString(long duration, boolean millis) {
|
||||||
long minutes;
|
long minutes;
|
||||||
long seconds;
|
long seconds;
|
||||||
@@ -122,6 +165,14 @@ public class MusicUtil {
|
|||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String getReadableTrackNumber(Context context, Integer trackNumber) {
|
||||||
|
if (trackNumber != null) {
|
||||||
|
return String.valueOf(trackNumber);
|
||||||
|
}
|
||||||
|
|
||||||
|
return context.getString(R.string.label_placeholder);
|
||||||
|
}
|
||||||
|
|
||||||
public static String forceReadableString(String string) {
|
public static String forceReadableString(String string) {
|
||||||
if (string != null) {
|
if (string != null) {
|
||||||
return getReadableString(string)
|
return getReadableString(string)
|
||||||
@@ -196,6 +247,19 @@ public class MusicUtil {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String getBitratePreferenceForDownload() {
|
||||||
|
String audioTranscodeFormat = getTranscodingFormatPreferenceForDownload();
|
||||||
|
|
||||||
|
if (audioTranscodeFormat.equals("raw"))
|
||||||
|
return "0";
|
||||||
|
|
||||||
|
return Preferences.getBitrateTranscodedDownload();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getTranscodingFormatPreferenceForDownload() {
|
||||||
|
return Preferences.getAudioTranscodeFormatTranscodedDownload();
|
||||||
|
}
|
||||||
|
|
||||||
public static List<Child> limitPlayableMedia(List<Child> toLimit, int position) {
|
public static List<Child> limitPlayableMedia(List<Child> toLimit, int position) {
|
||||||
if (!toLimit.isEmpty() && toLimit.size() > Constants.PLAYABLE_MEDIA_LIMIT) {
|
if (!toLimit.isEmpty() && toLimit.size() > Constants.PLAYABLE_MEDIA_LIMIT) {
|
||||||
int from = position < Constants.PRE_PLAYABLE_MEDIA ? 0 : position - Constants.PRE_PLAYABLE_MEDIA;
|
int from = position < Constants.PRE_PLAYABLE_MEDIA ? 0 : position - Constants.PRE_PLAYABLE_MEDIA;
|
||||||
@@ -207,8 +271,12 @@ public class MusicUtil {
|
|||||||
return toLimit;
|
return toLimit;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int getPlayableMediaPosition(int initialPosition) {
|
public static int getPlayableMediaPosition(List<Child> toLimit, int position) {
|
||||||
return initialPosition > Constants.PLAYABLE_MEDIA_LIMIT ? Constants.PRE_PLAYABLE_MEDIA : initialPosition;
|
if (!toLimit.isEmpty() && toLimit.size() > Constants.PLAYABLE_MEDIA_LIMIT) {
|
||||||
|
return Math.min(position, Constants.PRE_PLAYABLE_MEDIA);
|
||||||
|
}
|
||||||
|
|
||||||
|
return position;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ConnectivityManager getConnectivityManager() {
|
private static ConnectivityManager getConnectivityManager() {
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package com.cappielloantonio.tempo.util;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.net.ConnectivityManager;
|
||||||
|
import android.net.NetworkInfo;
|
||||||
|
|
||||||
|
import com.cappielloantonio.tempo.App;
|
||||||
|
|
||||||
|
public class NetworkUtil {
|
||||||
|
public static boolean isOffline() {
|
||||||
|
ConnectivityManager connectivityManager = (ConnectivityManager) App.getContext().getSystemService(Context.CONNECTIVITY_SERVICE);
|
||||||
|
|
||||||
|
if (connectivityManager != null) {
|
||||||
|
NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo();
|
||||||
|
|
||||||
|
return networkInfo == null || !networkInfo.isConnected();
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -33,6 +33,12 @@ object Preferences {
|
|||||||
private const val MUSIC_DIRECTORY_SECTION_VISIBILITY = "music_directory_section_visibility"
|
private const val MUSIC_DIRECTORY_SECTION_VISIBILITY = "music_directory_section_visibility"
|
||||||
private const val REPLAY_GAIN_MODE = "replay_gain_mode"
|
private const val REPLAY_GAIN_MODE = "replay_gain_mode"
|
||||||
private const val AUDIO_TRANSCODE_PRIORITY = "audio_transcode_priority"
|
private const val AUDIO_TRANSCODE_PRIORITY = "audio_transcode_priority"
|
||||||
|
private const val DOWNLOAD_STORAGE = "download_storage"
|
||||||
|
private const val DEFAULT_DOWNLOAD_VIEW_TYPE = "default_download_view_type"
|
||||||
|
private const val AUDIO_TRANSCODE_DOWNLOAD = "audio_transcode_download"
|
||||||
|
private const val AUDIO_TRANSCODE_DOWNLOAD_PRIORITY = "audio_transcode_download_priority"
|
||||||
|
private const val MAX_BITRATE_DOWNLOAD = "max_bitrate_download"
|
||||||
|
private const val AUDIO_TRANSCODE_FORMAT_DOWNLOAD = "audio_transcode_format_download"
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun getServer(): String? {
|
fun getServer(): String? {
|
||||||
@@ -105,7 +111,7 @@ object Preferences {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun askForOptimization(): Boolean? {
|
fun askForOptimization(): Boolean {
|
||||||
return App.getInstance().preferences.getBoolean(BATTERY_OPTIMIZATION, true)
|
return App.getInstance().preferences.getBoolean(BATTERY_OPTIMIZATION, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -258,4 +264,53 @@ object Preferences {
|
|||||||
fun isServerPrioritized(): Boolean {
|
fun isServerPrioritized(): Boolean {
|
||||||
return App.getInstance().preferences.getBoolean(AUDIO_TRANSCODE_PRIORITY, false)
|
return App.getInstance().preferences.getBoolean(AUDIO_TRANSCODE_PRIORITY, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun getDownloadStoragePreference(): Int {
|
||||||
|
return App.getInstance().preferences.getString(DOWNLOAD_STORAGE, "0")!!.toInt()
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun setDownloadStoragePreference(storagePreference: Int) {
|
||||||
|
return App.getInstance().preferences.edit().putString(
|
||||||
|
DOWNLOAD_STORAGE,
|
||||||
|
storagePreference.toString()
|
||||||
|
).apply()
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun getDefaultDownloadViewType(): String {
|
||||||
|
return App.getInstance().preferences.getString(
|
||||||
|
DEFAULT_DOWNLOAD_VIEW_TYPE,
|
||||||
|
Constants.DOWNLOAD_TYPE_TRACK
|
||||||
|
)!!
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun setDefaultDownloadViewType(viewType: String) {
|
||||||
|
return App.getInstance().preferences.edit().putString(
|
||||||
|
DEFAULT_DOWNLOAD_VIEW_TYPE,
|
||||||
|
viewType
|
||||||
|
).apply()
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun preferTranscodedDownload(): Boolean {
|
||||||
|
return App.getInstance().preferences.getBoolean(AUDIO_TRANSCODE_DOWNLOAD, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun isServerPrioritizedInTranscodedDownload(): Boolean {
|
||||||
|
return App.getInstance().preferences.getBoolean(AUDIO_TRANSCODE_DOWNLOAD_PRIORITY, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun getBitrateTranscodedDownload(): String {
|
||||||
|
return App.getInstance().preferences.getString(MAX_BITRATE_DOWNLOAD, "0")!!
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun getAudioTranscodeFormatTranscodedDownload(): String {
|
||||||
|
return App.getInstance().preferences.getString(AUDIO_TRANSCODE_FORMAT_DOWNLOAD, "raw")!!
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,9 @@
|
|||||||
package com.cappielloantonio.tempo.util;
|
package com.cappielloantonio.tempo.util;
|
||||||
|
|
||||||
|
import androidx.annotation.OptIn;
|
||||||
import androidx.media3.common.Metadata;
|
import androidx.media3.common.Metadata;
|
||||||
import androidx.media3.common.Tracks;
|
import androidx.media3.common.Tracks;
|
||||||
|
import androidx.media3.common.util.UnstableApi;
|
||||||
import androidx.media3.exoplayer.ExoPlayer;
|
import androidx.media3.exoplayer.ExoPlayer;
|
||||||
|
|
||||||
import com.cappielloantonio.tempo.model.ReplayGain;
|
import com.cappielloantonio.tempo.model.ReplayGain;
|
||||||
@@ -10,6 +12,7 @@ import java.util.ArrayList;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
|
@OptIn(markerClass = UnstableApi.class)
|
||||||
public class ReplayGainUtil {
|
public class ReplayGainUtil {
|
||||||
private static final String[] tags = {"REPLAYGAIN_TRACK_GAIN", "REPLAYGAIN_ALBUM_GAIN", "R128_TRACK_GAIN", "R128_ALBUM_GAIN"};
|
private static final String[] tags = {"REPLAYGAIN_TRACK_GAIN", "REPLAYGAIN_ALBUM_GAIN", "R128_TRACK_GAIN", "R128_ALBUM_GAIN"};
|
||||||
|
|
||||||
@@ -23,11 +26,15 @@ public class ReplayGainUtil {
|
|||||||
private static List<Metadata> getMetadata(Tracks tracks) {
|
private static List<Metadata> getMetadata(Tracks tracks) {
|
||||||
List<Metadata> metadata = new ArrayList<>();
|
List<Metadata> metadata = new ArrayList<>();
|
||||||
|
|
||||||
for (int i = 0; i < tracks.getGroups().size(); i++) {
|
if (tracks != null && !tracks.getGroups().isEmpty()) {
|
||||||
Tracks.Group group = tracks.getGroups().get(i);
|
for (int i = 0; i < tracks.getGroups().size(); i++) {
|
||||||
|
Tracks.Group group = tracks.getGroups().get(i);
|
||||||
|
|
||||||
for (int j = 0; j < group.getMediaTrackGroup().length; j++) {
|
if (group != null && group.getMediaTrackGroup() != null) {
|
||||||
metadata.add(group.getTrackFormat(j).metadata);
|
for (int j = 0; j < group.getMediaTrackGroup().length; j++) {
|
||||||
|
metadata.add(group.getTrackFormat(j).metadata);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -37,13 +44,19 @@ public class ReplayGainUtil {
|
|||||||
private static List<ReplayGain> getReplayGains(List<Metadata> metadata) {
|
private static List<ReplayGain> getReplayGains(List<Metadata> metadata) {
|
||||||
List<ReplayGain> gains = new ArrayList<>();
|
List<ReplayGain> gains = new ArrayList<>();
|
||||||
|
|
||||||
for (int i = 0; i < metadata.size(); i++) {
|
if (metadata != null) {
|
||||||
for (int j = 0; j < metadata.get(i).length(); j++) {
|
for (int i = 0; i < metadata.size(); i++) {
|
||||||
Metadata.Entry entry = metadata.get(i).get(j);
|
Metadata singleMetadata = metadata.get(i);
|
||||||
|
|
||||||
if (checkReplayGain(entry)) {
|
if (singleMetadata != null) {
|
||||||
ReplayGain replayGain = setReplayGains(entry);
|
for (int j = 0; j < singleMetadata.length(); j++) {
|
||||||
gains.add(replayGain);
|
Metadata.Entry entry = singleMetadata.get(j);
|
||||||
|
|
||||||
|
if (checkReplayGain(entry)) {
|
||||||
|
ReplayGain replayGain = setReplayGains(entry);
|
||||||
|
gains.add(replayGain);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,8 +5,21 @@ import android.content.res.TypedArray;
|
|||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
import android.graphics.drawable.InsetDrawable;
|
import android.graphics.drawable.InsetDrawable;
|
||||||
|
|
||||||
|
import androidx.core.os.LocaleListCompat;
|
||||||
import androidx.recyclerview.widget.DividerItemDecoration;
|
import androidx.recyclerview.widget.DividerItemDecoration;
|
||||||
|
|
||||||
|
import com.cappielloantonio.tempo.R;
|
||||||
|
|
||||||
|
import org.xmlpull.v1.XmlPullParser;
|
||||||
|
import org.xmlpull.v1.XmlPullParserException;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
public class UIUtil {
|
public class UIUtil {
|
||||||
public static int getSpanCount(int itemCount, int maxSpan) {
|
public static int getSpanCount(int itemCount, int maxSpan) {
|
||||||
int itemSize = itemCount == 0 ? 1 : itemCount;
|
int itemSize = itemCount == 0 ? 1 : itemCount;
|
||||||
@@ -31,4 +44,44 @@ public class UIUtil {
|
|||||||
|
|
||||||
return itemDecoration;
|
return itemDecoration;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static LocaleListCompat getLocalesFromResources(Context context) {
|
||||||
|
final List<String> tagsList = new ArrayList<>();
|
||||||
|
|
||||||
|
XmlPullParser xpp = context.getResources().getXml(R.xml.locale_config);
|
||||||
|
|
||||||
|
try {
|
||||||
|
while (xpp.getEventType() != XmlPullParser.END_DOCUMENT) {
|
||||||
|
String tagName = xpp.getName();
|
||||||
|
|
||||||
|
if (xpp.getEventType() == XmlPullParser.START_TAG) {
|
||||||
|
if ("locale".equals(tagName) && xpp.getAttributeCount() > 0 && xpp.getAttributeName(0).equals("name")) {
|
||||||
|
tagsList.add(xpp.getAttributeValue(0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
xpp.next();
|
||||||
|
}
|
||||||
|
} catch (XmlPullParserException | IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
return LocaleListCompat.forLanguageTags(String.join(",", tagsList));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Map<String, String> getLangPreferenceDropdownEntries(Context context) {
|
||||||
|
LocaleListCompat localeList = getLocalesFromResources(context);
|
||||||
|
|
||||||
|
Map<String, String> map = new HashMap<>();
|
||||||
|
|
||||||
|
for (int i = 0; i < localeList.size(); i++) {
|
||||||
|
Locale locale = localeList.get(i);
|
||||||
|
|
||||||
|
if (locale != null) {
|
||||||
|
map.put(Util.toPascalCase(locale.getDisplayName()), locale.toLanguageTag());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return map;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
64
app/src/main/java/com/cappielloantonio/tempo/util/Util.java
Normal file
64
app/src/main/java/com/cappielloantonio/tempo/util/Util.java
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
package com.cappielloantonio.tempo.util;
|
||||||
|
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
|
import java.net.URLEncoder;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
|
public class Util {
|
||||||
|
public static <T> Predicate<T> distinctByKey(Function<? super T, Object> keyExtractor) {
|
||||||
|
try {
|
||||||
|
Map<Object, Boolean> uniqueMap = new ConcurrentHashMap<>();
|
||||||
|
return t -> uniqueMap.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null;
|
||||||
|
} catch (NullPointerException exception) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String toPascalCase(String name) {
|
||||||
|
if (name == null || name.isEmpty()) {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
StringBuilder pascalCase = new StringBuilder();
|
||||||
|
|
||||||
|
char newChar;
|
||||||
|
boolean toUpper = false;
|
||||||
|
char[] charArray = name.toCharArray();
|
||||||
|
|
||||||
|
for (int ctr = 0; ctr <= charArray.length - 1; ctr++) {
|
||||||
|
if (ctr == 0) {
|
||||||
|
newChar = Character.toUpperCase(charArray[ctr]);
|
||||||
|
pascalCase = new StringBuilder(Character.toString(newChar));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (charArray[ctr] == '_') {
|
||||||
|
toUpper = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (toUpper) {
|
||||||
|
newChar = Character.toUpperCase(charArray[ctr]);
|
||||||
|
pascalCase.append(newChar);
|
||||||
|
toUpper = false;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
pascalCase.append(charArray[ctr]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return pascalCase.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String encode(String value) {
|
||||||
|
try {
|
||||||
|
return URLEncoder.encode(value, StandardCharsets.UTF_8.toString());
|
||||||
|
} catch (UnsupportedEncodingException ex) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,11 +7,14 @@ import androidx.lifecycle.AndroidViewModel;
|
|||||||
import androidx.lifecycle.LiveData;
|
import androidx.lifecycle.LiveData;
|
||||||
import androidx.lifecycle.MutableLiveData;
|
import androidx.lifecycle.MutableLiveData;
|
||||||
|
|
||||||
|
import com.cappielloantonio.tempo.interfaces.StarCallback;
|
||||||
import com.cappielloantonio.tempo.repository.AlbumRepository;
|
import com.cappielloantonio.tempo.repository.AlbumRepository;
|
||||||
import com.cappielloantonio.tempo.repository.ArtistRepository;
|
import com.cappielloantonio.tempo.repository.ArtistRepository;
|
||||||
|
import com.cappielloantonio.tempo.repository.FavoriteRepository;
|
||||||
import com.cappielloantonio.tempo.subsonic.models.AlbumID3;
|
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.subsonic.models.Child;
|
import com.cappielloantonio.tempo.subsonic.models.Child;
|
||||||
|
import com.cappielloantonio.tempo.util.NetworkUtil;
|
||||||
|
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -19,6 +22,7 @@ import java.util.List;
|
|||||||
public class AlbumBottomSheetViewModel extends AndroidViewModel {
|
public class AlbumBottomSheetViewModel extends AndroidViewModel {
|
||||||
private final AlbumRepository albumRepository;
|
private final AlbumRepository albumRepository;
|
||||||
private final ArtistRepository artistRepository;
|
private final ArtistRepository artistRepository;
|
||||||
|
private final FavoriteRepository favoriteRepository;
|
||||||
|
|
||||||
private AlbumID3 album;
|
private AlbumID3 album;
|
||||||
|
|
||||||
@@ -27,6 +31,7 @@ public class AlbumBottomSheetViewModel extends AndroidViewModel {
|
|||||||
|
|
||||||
albumRepository = new AlbumRepository();
|
albumRepository = new AlbumRepository();
|
||||||
artistRepository = new ArtistRepository();
|
artistRepository = new ArtistRepository();
|
||||||
|
favoriteRepository = new FavoriteRepository();
|
||||||
}
|
}
|
||||||
|
|
||||||
public AlbumID3 getAlbum() {
|
public AlbumID3 getAlbum() {
|
||||||
@@ -47,11 +52,51 @@ public class AlbumBottomSheetViewModel extends AndroidViewModel {
|
|||||||
|
|
||||||
public void setFavorite() {
|
public void setFavorite() {
|
||||||
if (album.getStarred() != null) {
|
if (album.getStarred() != null) {
|
||||||
artistRepository.unstar(album.getId());
|
if (NetworkUtil.isOffline()) {
|
||||||
album.setStarred(null);
|
removeFavoriteOffline();
|
||||||
|
} else {
|
||||||
|
removeFavoriteOnline();
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
artistRepository.star(album.getId());
|
if (NetworkUtil.isOffline()) {
|
||||||
album.setStarred(new Date());
|
setFavoriteOffline();
|
||||||
|
} else {
|
||||||
|
setFavoriteOnline();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void removeFavoriteOffline() {
|
||||||
|
favoriteRepository.starLater(null, album.getId(), null, false);
|
||||||
|
album.setStarred(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void removeFavoriteOnline() {
|
||||||
|
favoriteRepository.unstar(null, album.getId(), null, new StarCallback() {
|
||||||
|
@Override
|
||||||
|
public void onError() {
|
||||||
|
// album.setStarred(new Date());
|
||||||
|
favoriteRepository.starLater(null, album.getId(), null, false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
album.setStarred(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setFavoriteOffline() {
|
||||||
|
favoriteRepository.starLater(null, album.getId(), null, true);
|
||||||
|
album.setStarred(new Date());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setFavoriteOnline() {
|
||||||
|
favoriteRepository.star(null, album.getId(), null, new StarCallback() {
|
||||||
|
@Override
|
||||||
|
public void onError() {
|
||||||
|
// album.setStarred(null);
|
||||||
|
favoriteRepository.starLater(null, album.getId(), null, true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
album.setStarred(new Date());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,20 +5,25 @@ import android.app.Application;
|
|||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.lifecycle.AndroidViewModel;
|
import androidx.lifecycle.AndroidViewModel;
|
||||||
|
|
||||||
import com.cappielloantonio.tempo.repository.AlbumRepository;
|
import com.cappielloantonio.tempo.interfaces.StarCallback;
|
||||||
|
import com.cappielloantonio.tempo.repository.ArtistRepository;
|
||||||
|
import com.cappielloantonio.tempo.repository.FavoriteRepository;
|
||||||
import com.cappielloantonio.tempo.subsonic.models.ArtistID3;
|
import com.cappielloantonio.tempo.subsonic.models.ArtistID3;
|
||||||
|
import com.cappielloantonio.tempo.util.NetworkUtil;
|
||||||
|
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
|
||||||
public class ArtistBottomSheetViewModel extends AndroidViewModel {
|
public class ArtistBottomSheetViewModel extends AndroidViewModel {
|
||||||
private final AlbumRepository albumRepository;
|
private final ArtistRepository artistRepository;
|
||||||
|
private final FavoriteRepository favoriteRepository;
|
||||||
|
|
||||||
private ArtistID3 artist;
|
private ArtistID3 artist;
|
||||||
|
|
||||||
public ArtistBottomSheetViewModel(@NonNull Application application) {
|
public ArtistBottomSheetViewModel(@NonNull Application application) {
|
||||||
super(application);
|
super(application);
|
||||||
|
|
||||||
albumRepository = new AlbumRepository();
|
artistRepository = new ArtistRepository();
|
||||||
|
favoriteRepository = new FavoriteRepository();
|
||||||
}
|
}
|
||||||
|
|
||||||
public ArtistID3 getArtist() {
|
public ArtistID3 getArtist() {
|
||||||
@@ -31,11 +36,51 @@ public class ArtistBottomSheetViewModel extends AndroidViewModel {
|
|||||||
|
|
||||||
public void setFavorite() {
|
public void setFavorite() {
|
||||||
if (artist.getStarred() != null) {
|
if (artist.getStarred() != null) {
|
||||||
albumRepository.unstar(artist.getId());
|
if (NetworkUtil.isOffline()) {
|
||||||
artist.setStarred(null);
|
removeFavoriteOffline();
|
||||||
|
} else {
|
||||||
|
removeFavoriteOnline();
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
albumRepository.star(artist.getId());
|
if (NetworkUtil.isOffline()) {
|
||||||
artist.setStarred(new Date());
|
setFavoriteOffline();
|
||||||
|
} else {
|
||||||
|
setFavoriteOnline();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void removeFavoriteOffline() {
|
||||||
|
favoriteRepository.starLater(null, null, artist.getId(), false);
|
||||||
|
artist.setStarred(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void removeFavoriteOnline() {
|
||||||
|
favoriteRepository.unstar(null, null, artist.getId(), new StarCallback() {
|
||||||
|
@Override
|
||||||
|
public void onError() {
|
||||||
|
// artist.setStarred(new Date());
|
||||||
|
favoriteRepository.starLater(null, null, artist.getId(), false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
artist.setStarred(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setFavoriteOffline() {
|
||||||
|
favoriteRepository.starLater(null, null, artist.getId(), true);
|
||||||
|
artist.setStarred(new Date());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setFavoriteOnline() {
|
||||||
|
favoriteRepository.star(null, null, artist.getId(), new StarCallback() {
|
||||||
|
@Override
|
||||||
|
public void onError() {
|
||||||
|
// artist.setStarred(null);
|
||||||
|
favoriteRepository.starLater(null, null, artist.getId(), true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
artist.setStarred(new Date());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,27 +8,56 @@ import androidx.lifecycle.LifecycleOwner;
|
|||||||
import androidx.lifecycle.LiveData;
|
import androidx.lifecycle.LiveData;
|
||||||
import androidx.lifecycle.MutableLiveData;
|
import androidx.lifecycle.MutableLiveData;
|
||||||
|
|
||||||
|
import com.cappielloantonio.tempo.model.DownloadStack;
|
||||||
import com.cappielloantonio.tempo.repository.DownloadRepository;
|
import com.cappielloantonio.tempo.repository.DownloadRepository;
|
||||||
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.Preferences;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
public class DownloadViewModel extends AndroidViewModel {
|
public class DownloadViewModel extends AndroidViewModel {
|
||||||
private static final String TAG = "HomeViewModel";
|
private static final String TAG = "DownloadViewModel";
|
||||||
|
|
||||||
private final DownloadRepository downloadRepository;
|
private final DownloadRepository downloadRepository;
|
||||||
|
|
||||||
private final MutableLiveData<List<Child>> downloadedTrackSample = new MutableLiveData<>(null);
|
private final MutableLiveData<List<Child>> downloadedTrackSample = new MutableLiveData<>(null);
|
||||||
|
private final MutableLiveData<ArrayList<DownloadStack>> viewStack = new MutableLiveData<>(null);
|
||||||
|
|
||||||
public DownloadViewModel(@NonNull Application application) {
|
public DownloadViewModel(@NonNull Application application) {
|
||||||
super(application);
|
super(application);
|
||||||
|
|
||||||
downloadRepository = new DownloadRepository();
|
downloadRepository = new DownloadRepository();
|
||||||
|
|
||||||
|
initViewStack(new DownloadStack(Preferences.getDefaultDownloadViewType(), null));
|
||||||
}
|
}
|
||||||
|
|
||||||
public LiveData<List<Child>> getDownloadedTracks(LifecycleOwner owner) {
|
public LiveData<List<Child>> getDownloadedTracks(LifecycleOwner owner) {
|
||||||
downloadRepository.getLiveDownload().observe(owner, downloads -> downloadedTrackSample.postValue(downloads.stream().map(download -> (Child) download).collect(Collectors.toList())));
|
downloadRepository.getLiveDownload().observe(owner, downloads -> downloadedTrackSample.postValue(downloads.stream().map(download -> (Child) download).collect(Collectors.toList())));
|
||||||
return downloadedTrackSample;
|
return downloadedTrackSample;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public LiveData<ArrayList<DownloadStack>> getViewStack() {
|
||||||
|
return viewStack;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void initViewStack(DownloadStack level) {
|
||||||
|
ArrayList<DownloadStack> stack = new ArrayList<>();
|
||||||
|
stack.add(level);
|
||||||
|
viewStack.setValue(stack);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void pushViewStack(DownloadStack level) {
|
||||||
|
ArrayList<DownloadStack> stack = viewStack.getValue();
|
||||||
|
stack.add(level);
|
||||||
|
viewStack.setValue(stack);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void popViewStack() {
|
||||||
|
ArrayList<DownloadStack> stack = viewStack.getValue();
|
||||||
|
stack.remove(stack.size() - 1);
|
||||||
|
viewStack.setValue(stack);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,20 +8,24 @@ import androidx.lifecycle.LifecycleOwner;
|
|||||||
import androidx.lifecycle.LiveData;
|
import androidx.lifecycle.LiveData;
|
||||||
import androidx.lifecycle.MutableLiveData;
|
import androidx.lifecycle.MutableLiveData;
|
||||||
|
|
||||||
|
import com.cappielloantonio.tempo.interfaces.StarCallback;
|
||||||
import com.cappielloantonio.tempo.model.Chronology;
|
import com.cappielloantonio.tempo.model.Chronology;
|
||||||
|
import com.cappielloantonio.tempo.model.Favorite;
|
||||||
import com.cappielloantonio.tempo.repository.AlbumRepository;
|
import com.cappielloantonio.tempo.repository.AlbumRepository;
|
||||||
import com.cappielloantonio.tempo.repository.ArtistRepository;
|
import com.cappielloantonio.tempo.repository.ArtistRepository;
|
||||||
import com.cappielloantonio.tempo.repository.ChronologyRepository;
|
import com.cappielloantonio.tempo.repository.ChronologyRepository;
|
||||||
import com.cappielloantonio.tempo.repository.PodcastRepository;
|
import com.cappielloantonio.tempo.repository.FavoriteRepository;
|
||||||
import com.cappielloantonio.tempo.repository.SongRepository;
|
import com.cappielloantonio.tempo.repository.SongRepository;
|
||||||
import com.cappielloantonio.tempo.subsonic.models.AlbumID3;
|
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.subsonic.models.Child;
|
import com.cappielloantonio.tempo.subsonic.models.Child;
|
||||||
import com.cappielloantonio.tempo.util.Preferences;
|
import com.cappielloantonio.tempo.util.Preferences;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Calendar;
|
import java.util.Calendar;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class HomeViewModel extends AndroidViewModel {
|
public class HomeViewModel extends AndroidViewModel {
|
||||||
@@ -31,6 +35,7 @@ public class HomeViewModel extends AndroidViewModel {
|
|||||||
private final AlbumRepository albumRepository;
|
private final AlbumRepository albumRepository;
|
||||||
private final ArtistRepository artistRepository;
|
private final ArtistRepository artistRepository;
|
||||||
private final ChronologyRepository chronologyRepository;
|
private final ChronologyRepository chronologyRepository;
|
||||||
|
private final FavoriteRepository favoriteRepository;
|
||||||
|
|
||||||
private final MutableLiveData<List<Child>> dicoverSongSample = new MutableLiveData<>(null);
|
private final MutableLiveData<List<Child>> dicoverSongSample = new MutableLiveData<>(null);
|
||||||
private final MutableLiveData<List<AlbumID3>> newReleasedAlbum = new MutableLiveData<>(null);
|
private final MutableLiveData<List<AlbumID3>> newReleasedAlbum = new MutableLiveData<>(null);
|
||||||
@@ -57,6 +62,9 @@ public class HomeViewModel extends AndroidViewModel {
|
|||||||
albumRepository = new AlbumRepository();
|
albumRepository = new AlbumRepository();
|
||||||
artistRepository = new ArtistRepository();
|
artistRepository = new ArtistRepository();
|
||||||
chronologyRepository = new ChronologyRepository();
|
chronologyRepository = new ChronologyRepository();
|
||||||
|
favoriteRepository = new FavoriteRepository();
|
||||||
|
|
||||||
|
setOfflineFavorite();
|
||||||
}
|
}
|
||||||
|
|
||||||
public LiveData<List<Child>> getDiscoverSongSample(LifecycleOwner owner) {
|
public LiveData<List<Child>> getDiscoverSongSample(LifecycleOwner owner) {
|
||||||
@@ -239,4 +247,109 @@ public class HomeViewModel extends AndroidViewModel {
|
|||||||
public void refreshRecentlyPlayedAlbumList(LifecycleOwner owner) {
|
public void refreshRecentlyPlayedAlbumList(LifecycleOwner owner) {
|
||||||
albumRepository.getAlbums("recent", 20, null, null).observe(owner, recentlyPlayedAlbumSample::postValue);
|
albumRepository.getAlbums("recent", 20, null, null).observe(owner, recentlyPlayedAlbumSample::postValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setOfflineFavorite() {
|
||||||
|
ArrayList<Favorite> favorites = getFavorites();
|
||||||
|
ArrayList<Favorite> favoritesToSave = getFavoritesToSave(favorites);
|
||||||
|
ArrayList<Favorite> favoritesToDelete = getFavoritesToDelete(favorites, favoritesToSave);
|
||||||
|
|
||||||
|
manageFavoriteToSave(favoritesToSave);
|
||||||
|
manageFavoriteToDelete(favoritesToDelete);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ArrayList<Favorite> getFavorites() {
|
||||||
|
return new ArrayList<>(favoriteRepository.getFavorites());
|
||||||
|
}
|
||||||
|
|
||||||
|
private ArrayList<Favorite> getFavoritesToSave(ArrayList<Favorite> favorites) {
|
||||||
|
HashMap<String, Favorite> filteredMap = new HashMap<>();
|
||||||
|
|
||||||
|
for (Favorite favorite : favorites) {
|
||||||
|
String key = favorite.toString();
|
||||||
|
|
||||||
|
if (!filteredMap.containsKey(key) || favorite.getTimestamp() > filteredMap.get(key).getTimestamp()) {
|
||||||
|
filteredMap.put(key, favorite);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ArrayList<>(filteredMap.values());
|
||||||
|
}
|
||||||
|
|
||||||
|
private ArrayList<Favorite> getFavoritesToDelete(ArrayList<Favorite> favorites, ArrayList<Favorite> favoritesToSave) {
|
||||||
|
ArrayList<Favorite> favoritesToDelete = new ArrayList<>();
|
||||||
|
|
||||||
|
for (Favorite favorite : favorites) {
|
||||||
|
if (!favoritesToSave.contains(favorite)) {
|
||||||
|
favoritesToDelete.add(favorite);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return favoritesToDelete;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void manageFavoriteToSave(ArrayList<Favorite> favoritesToSave) {
|
||||||
|
for (Favorite favorite : favoritesToSave) {
|
||||||
|
if (favorite.getToStar()) {
|
||||||
|
favoriteToStar(favorite);
|
||||||
|
} else {
|
||||||
|
favoriteToUnstar(favorite);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void manageFavoriteToDelete(ArrayList<Favorite> favoritesToDelete) {
|
||||||
|
for (Favorite favorite : favoritesToDelete) {
|
||||||
|
favoriteRepository.delete(favorite);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void favoriteToStar(Favorite favorite) {
|
||||||
|
if (favorite.getSongId() != null) {
|
||||||
|
favoriteRepository.star(favorite.getSongId(), null, null, new StarCallback() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess() {
|
||||||
|
favoriteRepository.delete(favorite);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else if (favorite.getAlbumId() != null) {
|
||||||
|
favoriteRepository.star(null, favorite.getAlbumId(), null, new StarCallback() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess() {
|
||||||
|
favoriteRepository.delete(favorite);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else if (favorite.getArtistId() != null) {
|
||||||
|
favoriteRepository.star(null, null, favorite.getArtistId(), new StarCallback() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess() {
|
||||||
|
favoriteRepository.delete(favorite);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void favoriteToUnstar(Favorite favorite) {
|
||||||
|
if (favorite.getSongId() != null) {
|
||||||
|
favoriteRepository.unstar(favorite.getSongId(), null, null, new StarCallback() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess() {
|
||||||
|
favoriteRepository.delete(favorite);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else if (favorite.getAlbumId() != null) {
|
||||||
|
favoriteRepository.unstar(null, favorite.getAlbumId(), null, new StarCallback() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess() {
|
||||||
|
favoriteRepository.delete(favorite);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else if (favorite.getArtistId() != null) {
|
||||||
|
favoriteRepository.unstar(null, null, favorite.getArtistId(), new StarCallback() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess() {
|
||||||
|
favoriteRepository.delete(favorite);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,9 +11,11 @@ import androidx.lifecycle.LiveData;
|
|||||||
import androidx.lifecycle.MutableLiveData;
|
import androidx.lifecycle.MutableLiveData;
|
||||||
import androidx.media3.common.util.UnstableApi;
|
import androidx.media3.common.util.UnstableApi;
|
||||||
|
|
||||||
|
import com.cappielloantonio.tempo.interfaces.StarCallback;
|
||||||
import com.cappielloantonio.tempo.model.Download;
|
import com.cappielloantonio.tempo.model.Download;
|
||||||
import com.cappielloantonio.tempo.model.Queue;
|
import com.cappielloantonio.tempo.model.Queue;
|
||||||
import com.cappielloantonio.tempo.repository.ArtistRepository;
|
import com.cappielloantonio.tempo.repository.ArtistRepository;
|
||||||
|
import com.cappielloantonio.tempo.repository.FavoriteRepository;
|
||||||
import com.cappielloantonio.tempo.repository.QueueRepository;
|
import com.cappielloantonio.tempo.repository.QueueRepository;
|
||||||
import com.cappielloantonio.tempo.repository.SongRepository;
|
import com.cappielloantonio.tempo.repository.SongRepository;
|
||||||
import com.cappielloantonio.tempo.subsonic.models.ArtistID3;
|
import com.cappielloantonio.tempo.subsonic.models.ArtistID3;
|
||||||
@@ -22,6 +24,7 @@ import com.cappielloantonio.tempo.subsonic.models.PlayQueue;
|
|||||||
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;
|
||||||
|
import com.cappielloantonio.tempo.util.NetworkUtil;
|
||||||
import com.cappielloantonio.tempo.util.Preferences;
|
import com.cappielloantonio.tempo.util.Preferences;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
@@ -36,6 +39,7 @@ public class PlayerBottomSheetViewModel extends AndroidViewModel {
|
|||||||
private final SongRepository songRepository;
|
private final SongRepository songRepository;
|
||||||
private final ArtistRepository artistRepository;
|
private final ArtistRepository artistRepository;
|
||||||
private final QueueRepository queueRepository;
|
private final QueueRepository queueRepository;
|
||||||
|
private final FavoriteRepository favoriteRepository;
|
||||||
|
|
||||||
private final MutableLiveData<String> lyricsLiveData = new MutableLiveData<>(null);
|
private final MutableLiveData<String> lyricsLiveData = new MutableLiveData<>(null);
|
||||||
|
|
||||||
@@ -50,6 +54,7 @@ public class PlayerBottomSheetViewModel extends AndroidViewModel {
|
|||||||
songRepository = new SongRepository();
|
songRepository = new SongRepository();
|
||||||
artistRepository = new ArtistRepository();
|
artistRepository = new ArtistRepository();
|
||||||
queueRepository = new QueueRepository();
|
queueRepository = new QueueRepository();
|
||||||
|
favoriteRepository = new FavoriteRepository();
|
||||||
}
|
}
|
||||||
|
|
||||||
public LiveData<List<Queue>> getQueueSong() {
|
public LiveData<List<Queue>> getQueueSong() {
|
||||||
@@ -59,22 +64,62 @@ public class PlayerBottomSheetViewModel extends AndroidViewModel {
|
|||||||
public void setFavorite(Context context, Child media) {
|
public void setFavorite(Context context, Child media) {
|
||||||
if (media != null) {
|
if (media != null) {
|
||||||
if (media.getStarred() != null) {
|
if (media.getStarred() != null) {
|
||||||
songRepository.unstar(media.getId());
|
if (NetworkUtil.isOffline()) {
|
||||||
media.setStarred(null);
|
removeFavoriteOffline(media);
|
||||||
|
} else {
|
||||||
|
removeFavoriteOnline(media);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
songRepository.star(media.getId());
|
if (NetworkUtil.isOffline()) {
|
||||||
media.setStarred(new Date());
|
setFavoriteOffline(media);
|
||||||
|
} else {
|
||||||
if (Preferences.isStarredSyncEnabled()) {
|
setFavoriteOnline(context, media);
|
||||||
DownloadUtil.getDownloadTracker(context).download(
|
|
||||||
MappingUtil.mapDownload(media),
|
|
||||||
new Download(media)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void removeFavoriteOffline(Child media) {
|
||||||
|
favoriteRepository.starLater(media.getId(), null, null, false);
|
||||||
|
media.setStarred(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void removeFavoriteOnline(Child media) {
|
||||||
|
favoriteRepository.unstar(media.getId(), null, null, new StarCallback() {
|
||||||
|
@Override
|
||||||
|
public void onError() {
|
||||||
|
// media.setStarred(new Date());
|
||||||
|
favoriteRepository.starLater(media.getId(), null, null, false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
media.setStarred(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setFavoriteOffline(Child media) {
|
||||||
|
favoriteRepository.starLater(media.getId(), null, null, true);
|
||||||
|
media.setStarred(new Date());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setFavoriteOnline(Context context, Child media) {
|
||||||
|
favoriteRepository.star(media.getId(), null, null, new StarCallback() {
|
||||||
|
@Override
|
||||||
|
public void onError() {
|
||||||
|
// media.setStarred(null);
|
||||||
|
favoriteRepository.starLater(media.getId(), null, null, true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
media.setStarred(new Date());
|
||||||
|
|
||||||
|
if (Preferences.isStarredSyncEnabled()) {
|
||||||
|
DownloadUtil.getDownloadTracker(context).download(
|
||||||
|
MappingUtil.mapDownload(media),
|
||||||
|
new Download(media)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public LiveData<String> getLiveLyrics() {
|
public LiveData<String> getLiveLyrics() {
|
||||||
return lyricsLiveData;
|
return lyricsLiveData;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import androidx.lifecycle.LiveData;
|
|||||||
|
|
||||||
import com.cappielloantonio.tempo.repository.PodcastRepository;
|
import com.cappielloantonio.tempo.repository.PodcastRepository;
|
||||||
import com.cappielloantonio.tempo.subsonic.models.PodcastChannel;
|
import com.cappielloantonio.tempo.subsonic.models.PodcastChannel;
|
||||||
|
import com.cappielloantonio.tempo.subsonic.models.PodcastEpisode;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@@ -33,4 +34,8 @@ public class PodcastChannelPageViewModel extends AndroidViewModel {
|
|||||||
public void setPodcastChannel(PodcastChannel podcastChannel) {
|
public void setPodcastChannel(PodcastChannel podcastChannel) {
|
||||||
this.podcastChannel = podcastChannel;
|
this.podcastChannel = podcastChannel;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void requestPodcastEpisodeDownload(PodcastEpisode podcastEpisode) {
|
||||||
|
podcastRepository.downloadPodcastEpisode(podcastEpisode.getId());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,15 +10,18 @@ import androidx.lifecycle.LiveData;
|
|||||||
import androidx.lifecycle.MutableLiveData;
|
import androidx.lifecycle.MutableLiveData;
|
||||||
import androidx.media3.common.util.UnstableApi;
|
import androidx.media3.common.util.UnstableApi;
|
||||||
|
|
||||||
|
import com.cappielloantonio.tempo.interfaces.StarCallback;
|
||||||
import com.cappielloantonio.tempo.model.Download;
|
import com.cappielloantonio.tempo.model.Download;
|
||||||
import com.cappielloantonio.tempo.repository.AlbumRepository;
|
import com.cappielloantonio.tempo.repository.AlbumRepository;
|
||||||
import com.cappielloantonio.tempo.repository.ArtistRepository;
|
import com.cappielloantonio.tempo.repository.ArtistRepository;
|
||||||
|
import com.cappielloantonio.tempo.repository.FavoriteRepository;
|
||||||
import com.cappielloantonio.tempo.repository.SongRepository;
|
import com.cappielloantonio.tempo.repository.SongRepository;
|
||||||
import com.cappielloantonio.tempo.subsonic.models.AlbumID3;
|
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.subsonic.models.Child;
|
import com.cappielloantonio.tempo.subsonic.models.Child;
|
||||||
import com.cappielloantonio.tempo.util.DownloadUtil;
|
import com.cappielloantonio.tempo.util.DownloadUtil;
|
||||||
import com.cappielloantonio.tempo.util.MappingUtil;
|
import com.cappielloantonio.tempo.util.MappingUtil;
|
||||||
|
import com.cappielloantonio.tempo.util.NetworkUtil;
|
||||||
import com.cappielloantonio.tempo.util.Preferences;
|
import com.cappielloantonio.tempo.util.Preferences;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
@@ -30,6 +33,7 @@ public class SongBottomSheetViewModel extends AndroidViewModel {
|
|||||||
private final SongRepository songRepository;
|
private final SongRepository songRepository;
|
||||||
private final AlbumRepository albumRepository;
|
private final AlbumRepository albumRepository;
|
||||||
private final ArtistRepository artistRepository;
|
private final ArtistRepository artistRepository;
|
||||||
|
private final FavoriteRepository favoriteRepository;
|
||||||
|
|
||||||
private Child song;
|
private Child song;
|
||||||
|
|
||||||
@@ -41,6 +45,7 @@ public class SongBottomSheetViewModel extends AndroidViewModel {
|
|||||||
songRepository = new SongRepository();
|
songRepository = new SongRepository();
|
||||||
albumRepository = new AlbumRepository();
|
albumRepository = new AlbumRepository();
|
||||||
artistRepository = new ArtistRepository();
|
artistRepository = new ArtistRepository();
|
||||||
|
favoriteRepository = new FavoriteRepository();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Child getSong() {
|
public Child getSong() {
|
||||||
@@ -53,18 +58,58 @@ public class SongBottomSheetViewModel extends AndroidViewModel {
|
|||||||
|
|
||||||
public void setFavorite(Context context) {
|
public void setFavorite(Context context) {
|
||||||
if (song.getStarred() != null) {
|
if (song.getStarred() != null) {
|
||||||
songRepository.unstar(song.getId());
|
if (NetworkUtil.isOffline()) {
|
||||||
song.setStarred(null);
|
removeFavoriteOffline(song);
|
||||||
} else {
|
} else {
|
||||||
songRepository.star(song.getId());
|
removeFavoriteOnline(song);
|
||||||
song.setStarred(new Date());
|
|
||||||
|
|
||||||
if (Preferences.isStarredSyncEnabled()) {
|
|
||||||
DownloadUtil.getDownloadTracker(context).download(
|
|
||||||
MappingUtil.mapDownload(song),
|
|
||||||
new Download(song)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
if (NetworkUtil.isOffline()) {
|
||||||
|
setFavoriteOffline(song);
|
||||||
|
} else {
|
||||||
|
setFavoriteOnline(context, song);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void removeFavoriteOffline(Child media) {
|
||||||
|
favoriteRepository.starLater(media.getId(), null, null, false);
|
||||||
|
media.setStarred(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void removeFavoriteOnline(Child media) {
|
||||||
|
favoriteRepository.unstar(media.getId(), null, null, new StarCallback() {
|
||||||
|
@Override
|
||||||
|
public void onError() {
|
||||||
|
// media.setStarred(new Date());
|
||||||
|
favoriteRepository.starLater(media.getId(), null, null, false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
media.setStarred(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setFavoriteOffline(Child media) {
|
||||||
|
favoriteRepository.starLater(media.getId(), null, null, true);
|
||||||
|
media.setStarred(new Date());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setFavoriteOnline(Context context, Child media) {
|
||||||
|
favoriteRepository.star(media.getId(), null, null, new StarCallback() {
|
||||||
|
@Override
|
||||||
|
public void onError() {
|
||||||
|
// media.setStarred(null);
|
||||||
|
favoriteRepository.starLater(media.getId(), null, null, true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
media.setStarred(new Date());
|
||||||
|
|
||||||
|
if (Preferences.isStarredSyncEnabled()) {
|
||||||
|
DownloadUtil.getDownloadTracker(context).download(
|
||||||
|
MappingUtil.mapDownload(media),
|
||||||
|
new Download(media)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -73,9 +73,11 @@ public class SongListPageViewModel extends AndroidViewModel {
|
|||||||
case Constants.MEDIA_BY_GENRE:
|
case Constants.MEDIA_BY_GENRE:
|
||||||
int page = (songList.getValue() != null ? songList.getValue().size() : 0) / 100;
|
int page = (songList.getValue() != null ? songList.getValue().size() : 0) / 100;
|
||||||
songRepository.getSongsByGenre(genre.getGenre(), page).observe(owner, children -> {
|
songRepository.getSongsByGenre(genre.getGenre(), page).observe(owner, children -> {
|
||||||
List<Child> currentMedia = songList.getValue();
|
if (children != null && !children.isEmpty()) {
|
||||||
currentMedia.addAll(children);
|
List<Child> currentMedia = songList.getValue();
|
||||||
songList.setValue(currentMedia);
|
currentMedia.addAll(children);
|
||||||
|
songList.setValue(currentMedia);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
case Constants.MEDIA_BY_ARTIST:
|
case Constants.MEDIA_BY_ARTIST:
|
||||||
|
|||||||
15
app/src/main/res/drawable/fast_scrollbar_bubble.xml
Normal file
15
app/src/main/res/drawable/fast_scrollbar_bubble.xml
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:shape="rectangle">
|
||||||
|
|
||||||
|
<corners
|
||||||
|
android:bottomLeftRadius="44dp"
|
||||||
|
android:bottomRightRadius="0px"
|
||||||
|
android:topLeftRadius="44dp"
|
||||||
|
android:topRightRadius="44dp" />
|
||||||
|
|
||||||
|
<solid android:color="?attr/colorPrimary" />
|
||||||
|
<size
|
||||||
|
android:width="88dp"
|
||||||
|
android:height="88dp" />
|
||||||
|
</shape>
|
||||||
17
app/src/main/res/drawable/fast_scrollbar_handle.xml
Normal file
17
app/src/main/res/drawable/fast_scrollbar_handle.xml
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item android:state_selected="true">
|
||||||
|
<shape android:shape="rectangle">
|
||||||
|
<corners android:radius="2dp" />
|
||||||
|
<solid android:color="?attr/colorPrimary" />
|
||||||
|
<size android:width="4dp" android:height="32dp" />
|
||||||
|
</shape>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<shape android:shape="rectangle">
|
||||||
|
<corners android:radius="2dp" />
|
||||||
|
<solid android:color="?attr/colorPrimaryContainer" />
|
||||||
|
<size android:width="4dp" android:height="32dp" />
|
||||||
|
</shape>
|
||||||
|
</item>
|
||||||
|
</selector>
|
||||||
9
app/src/main/res/drawable/ic_podcast_download.xml
Normal file
9
app/src/main/res/drawable/ic_podcast_download.xml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="960"
|
||||||
|
android:viewportHeight="960">
|
||||||
|
<path
|
||||||
|
android:fillColor="@color/titleTextColor"
|
||||||
|
android:pathData="M439,878Q363,870 297.5,835.5Q232,801 184,747.5Q136,694 108.5,625Q81,556 81,479Q81,324 183.5,210.5Q286,97 440,80L440,160Q319,177 240,267.5Q161,358 161,479Q161,600 240,690.5Q319,781 439,798L439,878ZM479,680L278,478L335,421L439,525L439,280L519,280L519,525L622,422L679,480L479,680ZM519,878L519,798Q562,792 601.5,775Q641,758 675,732L733,790Q686,827 632,849.5Q578,872 519,878ZM677,226Q642,200 602.5,183Q563,166 520,160L520,80Q579,86 633,108.5Q687,131 733,168L677,226ZM789,732L733,675Q759,641 775,601.5Q791,562 797,519L879,519Q871,578 849,632.5Q827,687 789,732ZM797,439Q791,396 775,356.5Q759,317 733,283L789,226Q827,271 850,325.5Q873,380 879,439L797,439Z"/>
|
||||||
|
</vector>
|
||||||
15
app/src/main/res/layout/dialog_delete_download_storage.xml
Normal file
15
app/src/main/res/layout/dialog_delete_download_storage.xml
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="24dp"
|
||||||
|
android:layout_marginTop="12dp"
|
||||||
|
android:layout_marginEnd="24dp"
|
||||||
|
android:layout_marginBottom="4dp"
|
||||||
|
android:text="@string/delete_download_storage_dialog_summary" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
23
app/src/main/res/layout/dialog_download_storage.xml
Normal file
23
app/src/main/res/layout/dialog_download_storage.xml
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="24dp"
|
||||||
|
android:layout_marginTop="12dp"
|
||||||
|
android:layout_marginEnd="24dp"
|
||||||
|
android:layout_marginBottom="4dp"
|
||||||
|
android:text="@string/download_storage_dialog_summary" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="24dp"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:layout_marginEnd="24dp"
|
||||||
|
android:layout_marginBottom="4dp"
|
||||||
|
android:text="@string/download_storage_dialog_sub_summary" />
|
||||||
|
</LinearLayout>
|
||||||
@@ -9,13 +9,13 @@
|
|||||||
android:id="@+id/toolbar"
|
android:id="@+id/toolbar"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="?attr/actionBarSize"
|
android:layout_height="?attr/actionBarSize"
|
||||||
|
android:background="?attr/colorSurface"
|
||||||
app:layout_collapseMode="pin"
|
app:layout_collapseMode="pin"
|
||||||
app:navigationIcon="@drawable/ic_arrow_back" />
|
app:navigationIcon="@drawable/ic_arrow_back" />
|
||||||
|
|
||||||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent">
|
||||||
android:paddingTop="8dp">
|
|
||||||
|
|
||||||
<com.google.android.material.appbar.AppBarLayout
|
<com.google.android.material.appbar.AppBarLayout
|
||||||
android:id="@+id/app_bar_layout"
|
android:id="@+id/app_bar_layout"
|
||||||
@@ -39,8 +39,7 @@
|
|||||||
android:paddingBottom="24dp"
|
android:paddingBottom="24dp"
|
||||||
android:text="@string/album_catalogue_title_expanded"
|
android:text="@string/album_catalogue_title_expanded"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent" />
|
||||||
app:layout_constraintStart_toEndOf="parent" />
|
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
android:id="@+id/album_list_sort_image_view"
|
android:id="@+id/album_list_sort_image_view"
|
||||||
@@ -67,6 +66,7 @@
|
|||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:clipToPadding="false"
|
android:clipToPadding="false"
|
||||||
|
android:paddingTop="16dp"
|
||||||
android:paddingStart="16dp"
|
android:paddingStart="16dp"
|
||||||
android:paddingEnd="16dp"
|
android:paddingEnd="16dp"
|
||||||
android:paddingBottom="@dimen/global_padding_bottom"
|
android:paddingBottom="@dimen/global_padding_bottom"
|
||||||
|
|||||||
@@ -9,13 +9,13 @@
|
|||||||
android:id="@+id/toolbar"
|
android:id="@+id/toolbar"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="?attr/actionBarSize"
|
android:layout_height="?attr/actionBarSize"
|
||||||
|
android:background="?attr/colorSurface"
|
||||||
app:layout_collapseMode="pin"
|
app:layout_collapseMode="pin"
|
||||||
app:navigationIcon="@drawable/ic_arrow_back" />
|
app:navigationIcon="@drawable/ic_arrow_back" />
|
||||||
|
|
||||||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent">
|
||||||
android:paddingTop="8dp">
|
|
||||||
|
|
||||||
<com.google.android.material.appbar.AppBarLayout
|
<com.google.android.material.appbar.AppBarLayout
|
||||||
android:id="@+id/app_bar_layout"
|
android:id="@+id/app_bar_layout"
|
||||||
|
|||||||
@@ -9,13 +9,13 @@
|
|||||||
android:id="@+id/toolbar"
|
android:id="@+id/toolbar"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="?attr/actionBarSize"
|
android:layout_height="?attr/actionBarSize"
|
||||||
|
android:background="?attr/colorSurface"
|
||||||
app:layout_collapseMode="pin"
|
app:layout_collapseMode="pin"
|
||||||
app:navigationIcon="@drawable/ic_arrow_back" />
|
app:navigationIcon="@drawable/ic_arrow_back" />
|
||||||
|
|
||||||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent">
|
||||||
android:paddingTop="8dp">
|
|
||||||
|
|
||||||
<com.google.android.material.appbar.AppBarLayout
|
<com.google.android.material.appbar.AppBarLayout
|
||||||
android:id="@+id/app_bar_layout"
|
android:id="@+id/app_bar_layout"
|
||||||
@@ -67,6 +67,7 @@
|
|||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:clipToPadding="false"
|
android:clipToPadding="false"
|
||||||
|
android:paddingTop="16dp"
|
||||||
android:paddingStart="16dp"
|
android:paddingStart="16dp"
|
||||||
android:paddingEnd="16dp"
|
android:paddingEnd="16dp"
|
||||||
android:paddingBottom="@dimen/global_padding_bottom"
|
android:paddingBottom="@dimen/global_padding_bottom"
|
||||||
|
|||||||
@@ -9,13 +9,13 @@
|
|||||||
android:id="@+id/toolbar"
|
android:id="@+id/toolbar"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="?attr/actionBarSize"
|
android:layout_height="?attr/actionBarSize"
|
||||||
|
android:background="?attr/colorSurface"
|
||||||
app:layout_collapseMode="pin"
|
app:layout_collapseMode="pin"
|
||||||
app:navigationIcon="@drawable/ic_arrow_back" />
|
app:navigationIcon="@drawable/ic_arrow_back" />
|
||||||
|
|
||||||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent">
|
||||||
android:paddingTop="8dp">
|
|
||||||
|
|
||||||
<com.google.android.material.appbar.AppBarLayout
|
<com.google.android.material.appbar.AppBarLayout
|
||||||
android:id="@+id/app_bar_layout"
|
android:id="@+id/app_bar_layout"
|
||||||
|
|||||||
@@ -9,13 +9,13 @@
|
|||||||
android:id="@+id/toolbar"
|
android:id="@+id/toolbar"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="?attr/actionBarSize"
|
android:layout_height="?attr/actionBarSize"
|
||||||
|
android:background="?attr/colorSurface"
|
||||||
app:layout_collapseMode="pin"
|
app:layout_collapseMode="pin"
|
||||||
app:navigationIcon="@drawable/ic_arrow_back" />
|
app:navigationIcon="@drawable/ic_arrow_back" />
|
||||||
|
|
||||||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent">
|
||||||
android:paddingTop="8dp">
|
|
||||||
|
|
||||||
<com.google.android.material.appbar.AppBarLayout
|
<com.google.android.material.appbar.AppBarLayout
|
||||||
android:id="@+id/app_bar_layout"
|
android:id="@+id/app_bar_layout"
|
||||||
@@ -38,10 +38,11 @@
|
|||||||
android:paddingStart="16dp"
|
android:paddingStart="16dp"
|
||||||
android:paddingTop="16dp"
|
android:paddingTop="16dp"
|
||||||
android:paddingEnd="16dp"
|
android:paddingEnd="16dp"
|
||||||
android:paddingBottom="24dp"
|
android:paddingBottom="16dp"
|
||||||
|
android:layout_marginBottom="8dp"
|
||||||
android:text="@string/label_placeholder"
|
android:text="@string/label_placeholder"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toStartOf="@+id/directory_back_image_view"
|
||||||
app:layout_constraintStart_toStartOf="parent" />
|
app:layout_constraintStart_toStartOf="parent" />
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
@@ -50,16 +51,15 @@
|
|||||||
android:layout_width="52dp"
|
android:layout_width="52dp"
|
||||||
android:layout_height="52dp"
|
android:layout_height="52dp"
|
||||||
android:layout_marginStart="16dp"
|
android:layout_marginStart="16dp"
|
||||||
android:layout_marginTop="16dp"
|
|
||||||
android:layout_marginEnd="16dp"
|
android:layout_marginEnd="16dp"
|
||||||
android:layout_marginBottom="12dp"
|
|
||||||
android:insetLeft="0dp"
|
android:insetLeft="0dp"
|
||||||
android:insetTop="0dp"
|
android:insetTop="0dp"
|
||||||
android:insetRight="0dp"
|
android:insetRight="0dp"
|
||||||
android:insetBottom="0dp"
|
android:insetBottom="0dp"
|
||||||
app:cornerRadius="30dp"
|
app:cornerRadius="30dp"
|
||||||
app:icon="@drawable/ic_arrow_back"
|
app:icon="@drawable/ic_arrow_back"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintTop_toTopOf="@+id/directory_title_label"
|
||||||
|
app:layout_constraintBottom_toBottomOf="@+id/directory_title_label"
|
||||||
app:layout_constraintEnd_toEndOf="parent" />
|
app:layout_constraintEnd_toEndOf="parent" />
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
</com.google.android.material.appbar.AppBarLayout>
|
</com.google.android.material.appbar.AppBarLayout>
|
||||||
@@ -69,6 +69,7 @@
|
|||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:clipToPadding="false"
|
android:clipToPadding="false"
|
||||||
|
android:paddingTop="12dp"
|
||||||
android:paddingBottom="@dimen/global_padding_bottom"
|
android:paddingBottom="@dimen/global_padding_bottom"
|
||||||
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
|
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
|
||||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||||
|
|||||||
@@ -62,45 +62,68 @@
|
|||||||
android:visibility="gone"
|
android:visibility="gone"
|
||||||
app:layout_behavior="@string/appbar_scrolling_view_behavior">
|
app:layout_behavior="@string/appbar_scrolling_view_behavior">
|
||||||
|
|
||||||
<LinearLayout
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
android:id="@+id/download_linear_layout_container"
|
android:id="@+id/download_downloaded_sector"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:orientation="vertical"
|
android:paddingHorizontal="16dp"
|
||||||
|
android:paddingTop="16dp"
|
||||||
android:paddingBottom="@dimen/global_padding_bottom">
|
android:paddingBottom="@dimen/global_padding_bottom">
|
||||||
|
|
||||||
<!-- Downloaded tracks -->
|
<TextView
|
||||||
<LinearLayout
|
android:id="@+id/downloaded_text_view_refreshable"
|
||||||
android:id="@+id/download_downloaded_tracks_sector"
|
style="@style/TitleLarge"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/download_title_section"
|
||||||
|
android:layout_marginBottom="16dp"
|
||||||
|
app:layout_constraintEnd_toStartOf="@+id/downloaded_go_back_image_view"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/downloaded_go_back_image_view"
|
||||||
|
android:layout_width="24dp"
|
||||||
|
android:layout_height="24dp"
|
||||||
|
android:layout_marginHorizontal="12dp"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:background="@drawable/ic_arrow_back"
|
||||||
|
app:layout_constraintBottom_toBottomOf="@+id/downloaded_text_view_refreshable"
|
||||||
|
app:layout_constraintEnd_toStartOf="@id/downloaded_group_by_image_view"
|
||||||
|
app:layout_constraintTop_toTopOf="@+id/downloaded_text_view_refreshable" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/downloaded_group_by_image_view"
|
||||||
|
android:layout_width="24dp"
|
||||||
|
android:layout_height="24dp"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:background="@drawable/ic_filter_list"
|
||||||
|
app:layout_constraintBottom_toBottomOf="@+id/downloaded_text_view_refreshable"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="@+id/downloaded_text_view_refreshable" />
|
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/downloaded_recycler_view"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:orientation="vertical">
|
android:clipToPadding="false"
|
||||||
|
android:nestedScrollingEnabled="false"
|
||||||
<TextView
|
android:paddingTop="8dp"
|
||||||
android:id="@+id/downloaded_tracks_text_view_refreshable"
|
android:paddingBottom="8dp"
|
||||||
style="@style/TitleLarge"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
android:layout_width="match_parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
android:layout_height="wrap_content"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
android:layout_marginBottom="8dp"
|
app:layout_constraintTop_toBottomOf="@id/downloaded_text_view_refreshable" />
|
||||||
android:paddingStart="16dp"
|
|
||||||
android:paddingTop="16dp"
|
|
||||||
android:paddingEnd="16dp"
|
|
||||||
android:text="@string/download_title_section" />
|
|
||||||
|
|
||||||
<androidx.recyclerview.widget.RecyclerView
|
|
||||||
android:id="@+id/downloaded_tracks_recycler_view"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:clipToPadding="false"
|
|
||||||
android:nestedScrollingEnabled="false"
|
|
||||||
android:paddingBottom="8dp" />
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
<include
|
<include
|
||||||
android:id="@+id/download_downloaded_tracks_placeholder"
|
android:id="@+id/download_downloaded_placeholder"
|
||||||
layout="@layout/item_placeholder_horizontal"
|
layout="@layout/item_placeholder_horizontal"
|
||||||
android:visibility="gone" />
|
android:visibility="gone"
|
||||||
</LinearLayout>
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/downloaded_text_view_refreshable" />
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
</androidx.core.widget.NestedScrollView>
|
</androidx.core.widget.NestedScrollView>
|
||||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||||
|
|
||||||
|
|||||||
@@ -9,13 +9,13 @@
|
|||||||
android:id="@+id/toolbar"
|
android:id="@+id/toolbar"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="?attr/actionBarSize"
|
android:layout_height="?attr/actionBarSize"
|
||||||
|
android:background="?attr/colorSurface"
|
||||||
app:layout_collapseMode="pin"
|
app:layout_collapseMode="pin"
|
||||||
app:navigationIcon="@drawable/ic_arrow_back" />
|
app:navigationIcon="@drawable/ic_arrow_back" />
|
||||||
|
|
||||||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent">
|
||||||
android:paddingTop="8dp">
|
|
||||||
|
|
||||||
<com.google.android.material.appbar.AppBarLayout
|
<com.google.android.material.appbar.AppBarLayout
|
||||||
android:id="@+id/app_bar_layout"
|
android:id="@+id/app_bar_layout"
|
||||||
|
|||||||
@@ -9,13 +9,13 @@
|
|||||||
android:id="@+id/toolbar"
|
android:id="@+id/toolbar"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="?attr/actionBarSize"
|
android:layout_height="?attr/actionBarSize"
|
||||||
|
android:background="?attr/colorSurface"
|
||||||
app:layout_collapseMode="pin"
|
app:layout_collapseMode="pin"
|
||||||
app:navigationIcon="@drawable/ic_arrow_back" />
|
app:navigationIcon="@drawable/ic_arrow_back" />
|
||||||
|
|
||||||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent">
|
||||||
android:paddingTop="8dp">
|
|
||||||
|
|
||||||
<com.google.android.material.appbar.AppBarLayout
|
<com.google.android.material.appbar.AppBarLayout
|
||||||
android:id="@+id/app_bar_layout"
|
android:id="@+id/app_bar_layout"
|
||||||
@@ -85,6 +85,7 @@
|
|||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:clipToPadding="false"
|
android:clipToPadding="false"
|
||||||
|
android:paddingTop="16dp"
|
||||||
android:paddingStart="16dp"
|
android:paddingStart="16dp"
|
||||||
android:paddingEnd="16dp"
|
android:paddingEnd="16dp"
|
||||||
android:paddingBottom="@dimen/global_padding_bottom"
|
android:paddingBottom="@dimen/global_padding_bottom"
|
||||||
|
|||||||
@@ -306,7 +306,7 @@
|
|||||||
android:paddingStart="16dp"
|
android:paddingStart="16dp"
|
||||||
android:paddingTop="12dp"
|
android:paddingTop="12dp"
|
||||||
android:paddingEnd="16dp"
|
android:paddingEnd="16dp"
|
||||||
android:text="Last week"
|
android:text="@string/home_title_last_week"
|
||||||
android:textAllCaps="true" />
|
android:textAllCaps="true" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
@@ -316,7 +316,7 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:paddingStart="16dp"
|
android:paddingStart="16dp"
|
||||||
android:paddingEnd="16dp"
|
android:paddingEnd="16dp"
|
||||||
android:text="Your top songs" />
|
android:text="@string/home_title_top_songs" />
|
||||||
|
|
||||||
<androidx.recyclerview.widget.RecyclerView
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
android:id="@+id/grid_tracks_recycler_view"
|
android:id="@+id/grid_tracks_recycler_view"
|
||||||
|
|||||||
@@ -9,13 +9,13 @@
|
|||||||
android:id="@+id/toolbar"
|
android:id="@+id/toolbar"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="?attr/actionBarSize"
|
android:layout_height="?attr/actionBarSize"
|
||||||
|
android:background="?attr/colorSurface"
|
||||||
app:layout_collapseMode="pin"
|
app:layout_collapseMode="pin"
|
||||||
app:navigationIcon="@drawable/ic_arrow_back" />
|
app:navigationIcon="@drawable/ic_arrow_back" />
|
||||||
|
|
||||||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent">
|
||||||
android:paddingTop="8dp">
|
|
||||||
|
|
||||||
<com.google.android.material.appbar.AppBarLayout
|
<com.google.android.material.appbar.AppBarLayout
|
||||||
android:id="@+id/app_bar_layout"
|
android:id="@+id/app_bar_layout"
|
||||||
@@ -47,13 +47,31 @@
|
|||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
</com.google.android.material.appbar.AppBarLayout>
|
</com.google.android.material.appbar.AppBarLayout>
|
||||||
|
|
||||||
<androidx.recyclerview.widget.RecyclerView
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
android:id="@+id/index_recycler_view"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:clipToPadding="false"
|
app:layout_behavior="@string/appbar_scrolling_view_behavior">
|
||||||
android:paddingBottom="@dimen/global_padding_bottom"
|
|
||||||
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/index_recycler_view"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:clipToPadding="false"
|
||||||
|
android:paddingTop="12dp"
|
||||||
|
android:paddingBottom="@dimen/global_padding_bottom"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
<com.cappielloantonio.tempo.helper.recyclerview.FastScrollbar
|
||||||
|
android:id="@+id/fast_scrollbar"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_marginBottom="@dimen/global_padding_bottom"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
|||||||
@@ -9,12 +9,12 @@
|
|||||||
android:id="@+id/toolbar"
|
android:id="@+id/toolbar"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="?attr/actionBarSize"
|
android:layout_height="?attr/actionBarSize"
|
||||||
|
android:background="?attr/colorSurface"
|
||||||
app:layout_collapseMode="pin" />
|
app:layout_collapseMode="pin" />
|
||||||
|
|
||||||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent">
|
||||||
android:paddingTop="8dp">
|
|
||||||
|
|
||||||
<com.google.android.material.appbar.AppBarLayout
|
<com.google.android.material.appbar.AppBarLayout
|
||||||
android:id="@+id/app_bar_layout"
|
android:id="@+id/app_bar_layout"
|
||||||
|
|||||||
@@ -9,13 +9,13 @@
|
|||||||
android:id="@+id/toolbar"
|
android:id="@+id/toolbar"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="?attr/actionBarSize"
|
android:layout_height="?attr/actionBarSize"
|
||||||
|
android:background="?attr/colorSurface"
|
||||||
app:layout_collapseMode="pin"
|
app:layout_collapseMode="pin"
|
||||||
app:navigationIcon="@drawable/ic_arrow_back" />
|
app:navigationIcon="@drawable/ic_arrow_back" />
|
||||||
|
|
||||||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent">
|
||||||
android:paddingTop="8dp">
|
|
||||||
|
|
||||||
<com.google.android.material.appbar.AppBarLayout
|
<com.google.android.material.appbar.AppBarLayout
|
||||||
android:id="@+id/app_bar_layout"
|
android:id="@+id/app_bar_layout"
|
||||||
@@ -67,6 +67,7 @@
|
|||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:clipToPadding="false"
|
android:clipToPadding="false"
|
||||||
|
android:paddingTop="12dp"
|
||||||
android:paddingStart="2dp"
|
android:paddingStart="2dp"
|
||||||
android:paddingEnd="2dp"
|
android:paddingEnd="2dp"
|
||||||
android:paddingBottom="@dimen/global_padding_bottom"
|
android:paddingBottom="@dimen/global_padding_bottom"
|
||||||
|
|||||||
@@ -9,38 +9,71 @@
|
|||||||
android:id="@+id/anim_toolbar"
|
android:id="@+id/anim_toolbar"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="?attr/actionBarSize"
|
android:layout_height="?attr/actionBarSize"
|
||||||
|
android:background="?attr/colorSurface"
|
||||||
app:layout_collapseMode="pin"
|
app:layout_collapseMode="pin"
|
||||||
app:navigationIcon="@drawable/ic_arrow_back" />
|
app:navigationIcon="@drawable/ic_arrow_back" />
|
||||||
|
|
||||||
<androidx.core.widget.NestedScrollView
|
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||||
android:id="@+id/fragment_playlist_page_nested_scroll_view"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content">
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
<LinearLayout
|
<com.google.android.material.appbar.AppBarLayout
|
||||||
|
android:id="@+id/app_bar_layout"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content">
|
||||||
android:orientation="vertical">
|
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
android:id="@+id/playlist_info_sector"
|
android:id="@+id/playlist_info_sector"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:clipChildren="false"
|
android:background="?attr/colorSurface"
|
||||||
android:paddingTop="8dp">
|
app:layout_scrollFlags="scroll|exitUntilCollapsed|snap">
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/playlist_cover_image_view"
|
android:id="@+id/playlist_cover_image_view_top_left"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="0dp"
|
android:layout_height="0dp"
|
||||||
android:layout_marginStart="64dp"
|
android:layout_marginStart="64dp"
|
||||||
android:layout_marginTop="8dp"
|
android:layout_marginTop="8dp"
|
||||||
|
app:layout_constraintDimensionRatio="H,1:1"
|
||||||
|
app:layout_constraintEnd_toStartOf="@id/playlist_cover_image_view_top_right"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/playlist_cover_image_view_top_right"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
android:layout_marginEnd="64dp"
|
android:layout_marginEnd="64dp"
|
||||||
app:layout_constraintDimensionRatio="H,1:1"
|
app:layout_constraintDimensionRatio="H,1:1"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toEndOf="@id/playlist_cover_image_view_top_left"
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/playlist_cover_image_view_bottom_left"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:layout_marginStart="64dp"
|
||||||
|
android:layout_marginBottom="8dp"
|
||||||
|
app:layout_constraintDimensionRatio="H,1:1"
|
||||||
|
app:layout_constraintEnd_toStartOf="@id/playlist_cover_image_view_bottom_right"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/playlist_cover_image_view_top_left" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/playlist_cover_image_view_bottom_right"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:layout_marginEnd="64dp"
|
||||||
|
android:layout_marginBottom="8dp"
|
||||||
|
app:layout_constraintDimensionRatio="H,1:1"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toEndOf="@id/playlist_cover_image_view_bottom_left"
|
||||||
|
app:layout_constraintTop_toTopOf="@id/playlist_cover_image_view_bottom_left" />
|
||||||
|
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/playlist_name_label"
|
android:id="@+id/playlist_name_label"
|
||||||
style="@style/LabelExtraLarge"
|
style="@style/LabelExtraLarge"
|
||||||
@@ -55,7 +88,7 @@
|
|||||||
android:textAlignment="center"
|
android:textAlignment="center"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toBottomOf="@+id/playlist_cover_image_view" />
|
app:layout_constraintTop_toBottomOf="@+id/playlist_cover_image_view_bottom_left" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/playlist_song_count_label"
|
android:id="@+id/playlist_song_count_label"
|
||||||
@@ -159,15 +192,16 @@
|
|||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toBottomOf="@+id/playlist_page_button_layout" />
|
app:layout_constraintTop_toBottomOf="@+id/playlist_page_button_layout" />
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
</com.google.android.material.appbar.AppBarLayout>
|
||||||
|
|
||||||
<androidx.recyclerview.widget.RecyclerView
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
android:id="@+id/song_recycler_view"
|
android:id="@+id/song_recycler_view"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="match_parent"
|
||||||
android:clipToPadding="false"
|
android:clipToPadding="false"
|
||||||
android:nestedScrollingEnabled="false"
|
android:paddingTop="8dp"
|
||||||
android:paddingTop="8dp"
|
android:paddingBottom="@dimen/global_padding_bottom"
|
||||||
android:paddingBottom="@dimen/global_padding_bottom" />
|
app:layout_behavior="@string/appbar_scrolling_view_behavior"/>
|
||||||
</LinearLayout>
|
|
||||||
</androidx.core.widget.NestedScrollView>
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
@@ -9,13 +9,13 @@
|
|||||||
android:id="@+id/toolbar"
|
android:id="@+id/toolbar"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="?attr/actionBarSize"
|
android:layout_height="?attr/actionBarSize"
|
||||||
|
android:background="?attr/colorSurface"
|
||||||
app:layout_collapseMode="pin"
|
app:layout_collapseMode="pin"
|
||||||
app:navigationIcon="@drawable/ic_arrow_back" />
|
app:navigationIcon="@drawable/ic_arrow_back" />
|
||||||
|
|
||||||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent">
|
||||||
android:paddingTop="8dp">
|
|
||||||
|
|
||||||
<com.google.android.material.appbar.AppBarLayout
|
<com.google.android.material.appbar.AppBarLayout
|
||||||
android:id="@+id/app_bar_layout"
|
android:id="@+id/app_bar_layout"
|
||||||
@@ -50,6 +50,7 @@
|
|||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:clipToPadding="false"
|
android:clipToPadding="false"
|
||||||
|
android:paddingTop="16dp"
|
||||||
android:paddingStart="16dp"
|
android:paddingStart="16dp"
|
||||||
android:paddingEnd="16dp"
|
android:paddingEnd="16dp"
|
||||||
android:paddingBottom="@dimen/global_padding_bottom"
|
android:paddingBottom="@dimen/global_padding_bottom"
|
||||||
|
|||||||
@@ -1,74 +1,46 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
android:id="@+id/layout"
|
android:id="@+id/layout"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent">
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
<com.google.android.material.appbar.AppBarLayout
|
<com.google.android.material.appbar.MaterialToolbar
|
||||||
android:id="@+id/appbar"
|
android:id="@+id/toolbar"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="@dimen/appbar_header_height">
|
android:layout_height="?attr/actionBarSize"
|
||||||
|
android:background="?attr/colorSurface"
|
||||||
|
app:layout_collapseMode="pin"
|
||||||
|
app:navigationIcon="@drawable/ic_arrow_back" />
|
||||||
|
|
||||||
<com.google.android.material.appbar.CollapsingToolbarLayout
|
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||||
android:id="@+id/collapsing_toolbar"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
app:contentScrim="?attr/colorSurface"
|
|
||||||
app:expandedTitleMarginStart="@dimen/activity_margin_content"
|
|
||||||
app:layout_scrollFlags="scroll|exitUntilCollapsed|snap">
|
|
||||||
|
|
||||||
<ImageView
|
|
||||||
android:id="@+id/podcast_channel_backdrop_image_view"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:fitsSystemWindows="true"
|
|
||||||
android:scaleType="centerCrop"
|
|
||||||
app:layout_collapseMode="parallax" />
|
|
||||||
|
|
||||||
<View
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="@dimen/appbar_header_height"
|
|
||||||
android:layout_gravity="top"
|
|
||||||
android:background="@drawable/gradient_backdrop_background_image" />
|
|
||||||
|
|
||||||
<com.google.android.material.appbar.MaterialToolbar
|
|
||||||
android:id="@+id/anim_toolbar"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="?attr/actionBarSize"
|
|
||||||
app:layout_collapseMode="pin" />
|
|
||||||
|
|
||||||
</com.google.android.material.appbar.CollapsingToolbarLayout>
|
|
||||||
</com.google.android.material.appbar.AppBarLayout>
|
|
||||||
|
|
||||||
<androidx.core.widget.NestedScrollView
|
|
||||||
android:id="@+id/fragment_artist_page_nested_scroll_view"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="match_parent">
|
||||||
app:layout_behavior="@string/appbar_scrolling_view_behavior">
|
|
||||||
|
|
||||||
<LinearLayout
|
<com.google.android.material.appbar.AppBarLayout
|
||||||
|
android:id="@+id/app_bar_layout"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content">
|
||||||
android:clipToPadding="false"
|
|
||||||
android:orientation="vertical"
|
|
||||||
android:paddingTop="18dp"
|
|
||||||
android:paddingBottom="@dimen/global_padding_bottom">
|
|
||||||
|
|
||||||
<LinearLayout
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
android:id="@+id/podcast_channel_page_bio_sector"
|
android:id="@+id/podcast_channel_page_info_sector"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:orientation="vertical"
|
android:background="?attr/colorSurface"
|
||||||
android:paddingBottom="22dp">
|
app:layout_scrollFlags="scroll|exitUntilCollapsed|snap">
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
|
android:id="@+id/podcast_channel_description_label"
|
||||||
style="@style/TitleLarge"
|
style="@style/TitleLarge"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:paddingStart="16dp"
|
android:paddingStart="16dp"
|
||||||
android:paddingEnd="8dp"
|
android:paddingEnd="8dp"
|
||||||
android:text="@string/podcast_channel_page_title_description_section" />
|
android:text="@string/podcast_channel_page_title_description_section"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/podcast_channel_description_text_view"
|
android:id="@+id/podcast_channel_description_text_view"
|
||||||
@@ -77,63 +49,62 @@
|
|||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:paddingStart="16dp"
|
android:paddingStart="16dp"
|
||||||
android:paddingTop="8dp"
|
android:paddingTop="8dp"
|
||||||
android:paddingEnd="16dp" />
|
android:paddingEnd="16dp"
|
||||||
</LinearLayout>
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/podcast_channel_description_label" />
|
||||||
|
|
||||||
<include
|
<View
|
||||||
android:id="@+id/podcast_channel_page_description_placeholder"
|
android:id="@+id/upper_button_divider"
|
||||||
layout="@layout/item_placehoder_biography"
|
style="@style/Divider"
|
||||||
android:visibility="gone" />
|
android:layout_marginHorizontal="16dp"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
<View
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
android:id="@+id/upper_button_divider"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
style="@style/Divider"
|
app:layout_constraintTop_toBottomOf="@+id/podcast_channel_description_text_view" />
|
||||||
android:layout_marginHorizontal="16dp" />
|
|
||||||
|
|
||||||
<!-- Label and button -->
|
|
||||||
<LinearLayout
|
|
||||||
android:id="@+id/podcast_channel_page_episodes_sector"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="vertical"
|
|
||||||
android:paddingBottom="22dp">
|
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
|
android:id="@+id/podcast_episodes_section_label"
|
||||||
style="@style/TitleLarge"
|
style="@style/TitleLarge"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:paddingStart="16dp"
|
|
||||||
android:paddingTop="16dp"
|
|
||||||
android:paddingEnd="8dp"
|
|
||||||
android:text="@string/podcast_channel_page_title_episode_section" />
|
|
||||||
|
|
||||||
<androidx.recyclerview.widget.RecyclerView
|
|
||||||
android:id="@+id/podcast_episodes_recycler_view"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:layout_marginTop="8dp"
|
android:layout_marginTop="8dp"
|
||||||
android:layout_marginBottom="8dp"
|
|
||||||
android:clipToPadding="false"
|
|
||||||
android:nestedScrollingEnabled="false"
|
|
||||||
android:paddingTop="8dp" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/podcast_episodes_availability_text_view"
|
|
||||||
style="@style/TitleMedium"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:paddingTop="8dp"
|
|
||||||
android:paddingStart="16dp"
|
android:paddingStart="16dp"
|
||||||
android:paddingEnd="8dp"
|
android:paddingEnd="8dp"
|
||||||
android:text="@string/podcast_channel_page_title_no_episode_available"
|
android:paddingBottom="12dp"
|
||||||
android:visibility="gone" />
|
android:text="@string/podcast_channel_page_title_episode_section"
|
||||||
</LinearLayout>
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/upper_button_divider" />
|
||||||
|
|
||||||
<include
|
<Button
|
||||||
android:id="@+id/podcast_channel_page_episodes_placeholder"
|
android:id="@+id/podcast_episodes_filter_image_view"
|
||||||
layout="@layout/item_placeholder_horizontal"
|
style="@style/Widget.Material3.Button.TonalButton.Icon"
|
||||||
android:visibility="gone" />
|
android:layout_width="52dp"
|
||||||
|
android:layout_height="52dp"
|
||||||
|
android:layout_marginStart="16dp"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
|
android:insetLeft="0dp"
|
||||||
|
android:insetTop="0dp"
|
||||||
|
android:insetRight="0dp"
|
||||||
|
android:insetBottom="0dp"
|
||||||
|
app:cornerRadius="30dp"
|
||||||
|
app:icon="@drawable/ic_filter_list"
|
||||||
|
app:layout_constraintBottom_toBottomOf="@+id/podcast_episodes_section_label"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="@+id/podcast_episodes_section_label" />
|
||||||
|
|
||||||
</LinearLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
</androidx.core.widget.NestedScrollView>
|
</com.google.android.material.appbar.AppBarLayout>
|
||||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/podcast_episodes_recycler_view"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:clipToPadding="false"
|
||||||
|
android:paddingTop="16dp"
|
||||||
|
android:paddingBottom="@dimen/global_padding_bottom"
|
||||||
|
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
|
||||||
|
|
||||||
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||||
|
</LinearLayout>
|
||||||
@@ -9,13 +9,13 @@
|
|||||||
android:id="@+id/toolbar"
|
android:id="@+id/toolbar"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="?attr/actionBarSize"
|
android:layout_height="?attr/actionBarSize"
|
||||||
|
android:background="?attr/colorSurface"
|
||||||
app:layout_collapseMode="pin"
|
app:layout_collapseMode="pin"
|
||||||
app:navigationIcon="@drawable/ic_arrow_back" />
|
app:navigationIcon="@drawable/ic_arrow_back" />
|
||||||
|
|
||||||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent">
|
||||||
android:paddingTop="8dp">
|
|
||||||
|
|
||||||
<com.google.android.material.appbar.AppBarLayout
|
<com.google.android.material.appbar.AppBarLayout
|
||||||
android:id="@+id/app_bar_layout"
|
android:id="@+id/app_bar_layout"
|
||||||
@@ -69,6 +69,7 @@
|
|||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:clipToPadding="false"
|
android:clipToPadding="false"
|
||||||
|
android:paddingTop="12dp"
|
||||||
android:paddingBottom="@dimen/global_padding_bottom"
|
android:paddingBottom="@dimen/global_padding_bottom"
|
||||||
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
|
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
|
||||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||||
|
|||||||
@@ -20,9 +20,9 @@
|
|||||||
style="@style/LabelMedium"
|
style="@style/LabelMedium"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="12dp"
|
||||||
android:ellipsize="end"
|
android:ellipsize="end"
|
||||||
android:maxLines="5"
|
android:maxLines="5"
|
||||||
android:layout_marginStart="12dp"
|
|
||||||
android:text="@string/label_placeholder"
|
android:text="@string/label_placeholder"
|
||||||
app:layout_constraintBottom_toTopOf="@+id/podcast_subtitle_label"
|
app:layout_constraintBottom_toTopOf="@+id/podcast_subtitle_label"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
@@ -36,9 +36,9 @@
|
|||||||
style="@style/LabelSmall"
|
style="@style/LabelSmall"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="12dp"
|
||||||
android:ellipsize="end"
|
android:ellipsize="end"
|
||||||
android:maxLines="2"
|
android:maxLines="2"
|
||||||
android:layout_marginStart="12dp"
|
|
||||||
android:text="@string/label_placeholder"
|
android:text="@string/label_placeholder"
|
||||||
app:layout_constraintBottom_toTopOf="@id/podcast_upper_divider"
|
app:layout_constraintBottom_toTopOf="@id/podcast_upper_divider"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
@@ -100,9 +100,21 @@
|
|||||||
style="@style/Widget.Material3.Button.IconButton"
|
style="@style/Widget.Material3.Button.IconButton"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:visibility="gone"
|
||||||
app:icon="@drawable/ic_more_vert"
|
app:icon="@drawable/ic_more_vert"
|
||||||
app:layout_constraintBottom_toBottomOf="@+id/podcast_play_button"
|
app:layout_constraintBottom_toBottomOf="@+id/podcast_play_button"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="@+id/podcast_play_button" />
|
app:layout_constraintTop_toTopOf="@+id/podcast_play_button" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/podcast_download_request_button"
|
||||||
|
style="@style/Widget.Material3.Button.IconButton"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:icon="@drawable/ic_podcast_download"
|
||||||
|
app:layout_constraintBottom_toBottomOf="@+id/podcast_play_button"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="@+id/podcast_play_button" />
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|||||||
@@ -3,8 +3,7 @@
|
|||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:clipChildren="false"
|
android:clipChildren="false"
|
||||||
android:orientation="horizontal"
|
android:orientation="horizontal">
|
||||||
android:paddingStart="16dp">
|
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:id="@+id/divider"
|
android:id="@+id/divider"
|
||||||
@@ -17,7 +16,7 @@
|
|||||||
app:layout_constraintTop_toTopOf="parent">
|
app:layout_constraintTop_toTopOf="parent">
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/downloaded_song_album_text_view"
|
android:id="@+id/downloaded_item_pre_text_view"
|
||||||
style="@style/LabelExtraSmall"
|
style="@style/LabelExtraSmall"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
@@ -28,8 +27,20 @@
|
|||||||
android:layout_gravity="center_vertical" />
|
android:layout_gravity="center_vertical" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/item_cover_image_view"
|
||||||
|
android:layout_width="52dp"
|
||||||
|
android:layout_height="52dp"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:layout_marginEnd="12dp"
|
||||||
|
app:layout_constraintEnd_toStartOf="@id/downloaded_item_title_text_view"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/divider" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/downloaded_song_title_text_view"
|
android:id="@+id/downloaded_item_title_text_view"
|
||||||
style="@style/LabelMedium"
|
style="@style/LabelMedium"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
@@ -37,12 +48,14 @@
|
|||||||
android:paddingEnd="12dp"
|
android:paddingEnd="12dp"
|
||||||
android:singleLine="true"
|
android:singleLine="true"
|
||||||
android:text="@string/label_placeholder"
|
android:text="@string/label_placeholder"
|
||||||
app:layout_constraintEnd_toStartOf="@+id/downloaded_song_more_button"
|
app:layout_constraintVertical_chainStyle="packed"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintEnd_toStartOf="@+id/downloaded_item_more_button"
|
||||||
app:layout_constraintTop_toBottomOf="@+id/divider" />
|
app:layout_constraintStart_toEndOf="@+id/item_cover_image_view"
|
||||||
|
app:layout_constraintTop_toTopOf="@+id/item_cover_image_view"
|
||||||
|
app:layout_constraintBottom_toTopOf="@id/downloaded_item_subtitle_text_view"/>
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/downloaded_song_artist_text_view"
|
android:id="@+id/downloaded_item_subtitle_text_view"
|
||||||
style="@style/LabelSmall"
|
style="@style/LabelSmall"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
@@ -51,16 +64,15 @@
|
|||||||
android:paddingBottom="6dp"
|
android:paddingBottom="6dp"
|
||||||
android:singleLine="true"
|
android:singleLine="true"
|
||||||
android:text="@string/label_placeholder"
|
android:text="@string/label_placeholder"
|
||||||
app:layout_constraintEnd_toStartOf="@+id/downloaded_song_more_button"
|
app:layout_constraintTop_toBottomOf="@id/downloaded_item_title_text_view"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintEnd_toStartOf="@+id/downloaded_item_more_button"
|
||||||
app:layout_constraintTop_toBottomOf="@+id/downloaded_song_title_text_view" />
|
app:layout_constraintStart_toStartOf="@+id/downloaded_item_title_text_view"
|
||||||
|
app:layout_constraintBottom_toBottomOf="@+id/item_cover_image_view" />
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/downloaded_song_more_button"
|
android:id="@+id/downloaded_item_more_button"
|
||||||
android:layout_width="18dp"
|
android:layout_width="18dp"
|
||||||
android:layout_height="18dp"
|
android:layout_height="18dp"
|
||||||
android:layout_marginHorizontal="12dp"
|
|
||||||
android:layout_marginTop="10dp"
|
|
||||||
android:background="@drawable/ic_more_vert"
|
android:background="@drawable/ic_more_vert"
|
||||||
android:foreground="?android:attr/selectableItemBackgroundBorderless"
|
android:foreground="?android:attr/selectableItemBackgroundBorderless"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
|||||||
@@ -52,5 +52,19 @@
|
|||||||
android:foreground="?android:attr/selectableItemBackgroundBorderless"
|
android:foreground="?android:attr/selectableItemBackgroundBorderless"
|
||||||
app:layout_constraintBottom_toBottomOf="@id/music_directory_title_text_view"
|
app:layout_constraintBottom_toBottomOf="@id/music_directory_title_text_view"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="@+id/music_directory_title_text_view" />
|
app:layout_constraintTop_toTopOf="@+id/music_directory_title_text_view"
|
||||||
|
android:visibility="gone"/>
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/music_directory_play_button"
|
||||||
|
android:layout_width="22dp"
|
||||||
|
android:layout_height="22dp"
|
||||||
|
android:layout_marginStart="12dp"
|
||||||
|
android:layout_marginEnd="12dp"
|
||||||
|
android:background="@drawable/ic_play"
|
||||||
|
android:foreground="?android:attr/selectableItemBackgroundBorderless"
|
||||||
|
app:layout_constraintBottom_toBottomOf="@id/music_directory_title_text_view"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="@+id/music_directory_title_text_view"
|
||||||
|
android:visibility="gone"/>
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
@@ -15,6 +15,7 @@
|
|||||||
android:layout_height="52dp"
|
android:layout_height="52dp"
|
||||||
android:layout_gravity="center"
|
android:layout_gravity="center"
|
||||||
android:layout_margin="2dp"
|
android:layout_margin="2dp"
|
||||||
|
android:background="?attr/colorSurfaceVariant"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|||||||
26
app/src/main/res/layout/layout_fast_scrollbar.xml
Normal file
26
app/src/main/res/layout/layout_fast_scrollbar.xml
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<merge xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/fastscroller_bubble"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="end"
|
||||||
|
android:background="@drawable/fast_scrollbar_bubble"
|
||||||
|
android:gravity="center"
|
||||||
|
android:textColor="?attr/colorOnPrimary"
|
||||||
|
android:textSize="48sp"
|
||||||
|
android:visibility="visible"
|
||||||
|
tools:text="A" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/fastscroller_handle"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="8dp"
|
||||||
|
android:layout_marginEnd="8dp"
|
||||||
|
android:src="@drawable/fast_scrollbar_handle" />
|
||||||
|
</merge>
|
||||||
9
app/src/main/res/menu/directory_page_menu.xml
Normal file
9
app/src/main/res/menu/directory_page_menu.xml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
<item
|
||||||
|
android:id="@+id/action_download_directory"
|
||||||
|
android:icon="@drawable/ic_file_download"
|
||||||
|
android:title="@string/menu_download_all_button"
|
||||||
|
app:showAsAction="never" />
|
||||||
|
</menu>
|
||||||
19
app/src/main/res/menu/download_popup_menu.xml
Normal file
19
app/src/main/res/menu/download_popup_menu.xml
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item
|
||||||
|
android:id="@+id/menu_download_group_by_track"
|
||||||
|
android:title="@string/menu_group_by_track" />
|
||||||
|
<item
|
||||||
|
android:id="@+id/menu_download_group_by_album"
|
||||||
|
android:title="@string/menu_group_by_album" />
|
||||||
|
<item
|
||||||
|
android:id="@+id/menu_download_group_by_artist"
|
||||||
|
android:title="@string/menu_group_by_artist" />
|
||||||
|
<item
|
||||||
|
android:id="@+id/menu_download_group_by_genre"
|
||||||
|
android:title="@string/menu_group_by_genre" />
|
||||||
|
|
||||||
|
<item
|
||||||
|
android:id="@+id/menu_download_group_by_year"
|
||||||
|
android:title="@string/menu_group_by_year" />
|
||||||
|
</menu>
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item
|
||||||
|
android:id="@+id/menu_podcast_filter_download"
|
||||||
|
android:title="@string/menu_filter_download" />
|
||||||
|
<item
|
||||||
|
android:id="@+id/menu_podcast_filter_all"
|
||||||
|
android:title="@string/menu_filter_all" />
|
||||||
|
</menu>
|
||||||
155
app/src/main/res/values-de/arrays.xml
Normal file
155
app/src/main/res/values-de/arrays.xml
Normal file
@@ -0,0 +1,155 @@
|
|||||||
|
<resources>
|
||||||
|
<string-array name="theme_list_titles">
|
||||||
|
<item>Hell</item>
|
||||||
|
<item>Dunkel</item>
|
||||||
|
<item>System Vorgabe</item>
|
||||||
|
</string-array>
|
||||||
|
<string-array name="theme_list_values">
|
||||||
|
<item>light</item>
|
||||||
|
<item>dark</item>
|
||||||
|
<item>default</item>
|
||||||
|
</string-array>
|
||||||
|
|
||||||
|
<string-array name="pref_cache_size_titles">
|
||||||
|
<item>Hoch</item>
|
||||||
|
<item>Mittel</item>
|
||||||
|
<item>Niedrig</item>
|
||||||
|
</string-array>
|
||||||
|
<string-array name="pref_cache_size_values">
|
||||||
|
<item>500</item>
|
||||||
|
<item>250</item>
|
||||||
|
<item>125</item>
|
||||||
|
</string-array>
|
||||||
|
|
||||||
|
<string-array name="pref_image_size_titles">
|
||||||
|
<item>Hoch</item>
|
||||||
|
<item>Mittel</item>
|
||||||
|
<item>Niedrig</item>
|
||||||
|
</string-array>
|
||||||
|
<string-array name="pref_image_size_values">
|
||||||
|
<item>-1</item>
|
||||||
|
<item>500</item>
|
||||||
|
<item>300</item>
|
||||||
|
</string-array>
|
||||||
|
|
||||||
|
<string-array name="max_bitrate_wifi_list_titles">
|
||||||
|
<item>Original</item>
|
||||||
|
<item>32 kbps</item>
|
||||||
|
<item>48 kbps</item>
|
||||||
|
<item>64 kbps</item>
|
||||||
|
<item>80 kbps</item>
|
||||||
|
<item>96 kbps</item>
|
||||||
|
<item>112 kbps</item>
|
||||||
|
<item>128 kbps</item>
|
||||||
|
<item>160 kbps</item>
|
||||||
|
<item>192 kbps</item>
|
||||||
|
<item>256 kbps</item>
|
||||||
|
<item>320 kbps</item>
|
||||||
|
</string-array>
|
||||||
|
<string-array name="max_bitrate_wifi_list_values">
|
||||||
|
<item>0</item>
|
||||||
|
<item>32</item>
|
||||||
|
<item>48</item>
|
||||||
|
<item>64</item>
|
||||||
|
<item>80</item>
|
||||||
|
<item>96</item>
|
||||||
|
<item>112</item>
|
||||||
|
<item>128</item>
|
||||||
|
<item>160</item>
|
||||||
|
<item>192</item>
|
||||||
|
<item>256</item>
|
||||||
|
<item>320</item>
|
||||||
|
</string-array>
|
||||||
|
|
||||||
|
<string-array name="max_bitrate_mobile_list_titles">
|
||||||
|
<item>Original</item>
|
||||||
|
<item>32 kbps</item>
|
||||||
|
<item>48 kbps</item>
|
||||||
|
<item>64 kbps</item>
|
||||||
|
<item>80 kbps</item>
|
||||||
|
<item>96 kbps</item>
|
||||||
|
<item>112 kbps</item>
|
||||||
|
<item>128 kbps</item>
|
||||||
|
<item>160 kbps</item>
|
||||||
|
<item>192 kbps</item>
|
||||||
|
<item>256 kbps</item>
|
||||||
|
<item>320 kbps</item>
|
||||||
|
</string-array>
|
||||||
|
<string-array name="max_bitrate_mobile_list_values">
|
||||||
|
<item>0</item>
|
||||||
|
<item>32</item>
|
||||||
|
<item>48</item>
|
||||||
|
<item>64</item>
|
||||||
|
<item>80</item>
|
||||||
|
<item>96</item>
|
||||||
|
<item>112</item>
|
||||||
|
<item>128</item>
|
||||||
|
<item>160</item>
|
||||||
|
<item>192</item>
|
||||||
|
<item>256</item>
|
||||||
|
<item>320</item>
|
||||||
|
</string-array>
|
||||||
|
|
||||||
|
<string-array name="audio_transcode_format_wifi_list_titles">
|
||||||
|
<item>Direktes Abspielen</item>
|
||||||
|
<item>Opus</item>
|
||||||
|
<item>AAC</item>
|
||||||
|
<item>Mp3</item>
|
||||||
|
<item>Flac</item>
|
||||||
|
</string-array>
|
||||||
|
<string-array name="audio_transcode_format_wifi_list_values">
|
||||||
|
<item>raw</item>
|
||||||
|
<item>opus</item>
|
||||||
|
<item>aac</item>
|
||||||
|
<item>mp3</item>
|
||||||
|
<item>flac</item>
|
||||||
|
</string-array>
|
||||||
|
|
||||||
|
<string-array name="audio_transcode_format_mobile_list_titles">
|
||||||
|
<item>Direct play</item>
|
||||||
|
<item>Opus</item>
|
||||||
|
<item>AAC</item>
|
||||||
|
<item>Mp3</item>
|
||||||
|
<item>Flac</item>
|
||||||
|
</string-array>
|
||||||
|
<string-array name="audio_transcode_format_mobile_list_values">
|
||||||
|
<item>raw</item>
|
||||||
|
<item>opus</item>
|
||||||
|
<item>aac</item>
|
||||||
|
<item>mp3</item>
|
||||||
|
<item>flac</item>
|
||||||
|
</string-array>
|
||||||
|
|
||||||
|
<string-array name="queue_syncing_countdown_titles">
|
||||||
|
<item>Zehn Sekunden</item>
|
||||||
|
<item>Fünf Sekunden</item>
|
||||||
|
<item>Zwei Sekunden</item>
|
||||||
|
</string-array>
|
||||||
|
<string-array name="queue_syncing_countdown_values">
|
||||||
|
<item>10</item>
|
||||||
|
<item>5</item>
|
||||||
|
<item>2</item>
|
||||||
|
</string-array>
|
||||||
|
|
||||||
|
<string-array name="rounded_corner_size_titles">
|
||||||
|
<item>Hoch</item>
|
||||||
|
<item>Mittel</item>
|
||||||
|
<item>Niedrig</item>
|
||||||
|
</string-array>
|
||||||
|
<string-array name="rounded_corner_size_values">
|
||||||
|
<item>18</item>
|
||||||
|
<item>12</item>
|
||||||
|
<item>6</item>
|
||||||
|
</string-array>
|
||||||
|
|
||||||
|
<string-array name="replay_gain_titles">
|
||||||
|
<item>Deaktiviert</item>
|
||||||
|
<item>Track bevorzugt</item>
|
||||||
|
<item>Album bevorzugt</item>
|
||||||
|
</string-array>
|
||||||
|
<string-array name="replay_gain_values">
|
||||||
|
<item>disabled</item>
|
||||||
|
<item>track</item>
|
||||||
|
<item>album</item>
|
||||||
|
</string-array>
|
||||||
|
</resources>
|
||||||
300
app/src/main/res/values-de/strings.xml
Normal file
300
app/src/main/res/values-de/strings.xml
Normal file
@@ -0,0 +1,300 @@
|
|||||||
|
<resources>
|
||||||
|
<string name="activity_battery_optimizations_summary">Bitte deaktiviere die Batterieoptimierung, damit die Medienwiedergabe bei ausgeschaltetem Bildschirm richtig funktioniert.</string>
|
||||||
|
<string name="activity_battery_optimizations_conclusion">Bei Problemen besuche https://dontkillmyapp.com. Dort findest Du detaillierte Anweisungen wie Du Energiesparfunktionen, welche die App-Performance beeinflussen können, deaktivieren kannst.</string>
|
||||||
|
<string name="activity_battery_optimizations_title">Batterie Optimierung</string>
|
||||||
|
<string name="activity_info_offline_mode">Offlinebetrieb</string>
|
||||||
|
<string name="battery_optimization_negative_button">Ignorieren</string>
|
||||||
|
<string name="battery_optimization_positive_button">Ausschalten</string>
|
||||||
|
<string name="battery_optimization_neutral_button">Nicht wieder fragen</string>
|
||||||
|
<string name="album_bottom_sheet_add_to_queue">Zur Warteschlange hinzufügen</string>
|
||||||
|
<string name="album_bottom_sheet_download_all">Alle herunterladen</string>
|
||||||
|
<string name="album_bottom_sheet_go_to_artist">Gehe zu Künstler</string>
|
||||||
|
<string name="album_bottom_sheet_instant_mix">Sofort-Mix</string>
|
||||||
|
<string name="album_bottom_sheet_play_next">Nächsten Titel spielen</string>
|
||||||
|
<string name="album_bottom_sheet_remove_all">Alle entfernen</string>
|
||||||
|
<string name="album_bottom_sheet_shuffle">Mischen</string>
|
||||||
|
<string name="album_catalogue_title">Alben</string>
|
||||||
|
<string name="album_catalogue_title_expanded">Alben durchsuchen</string>
|
||||||
|
<string name="album_error_retrieving_artist">Error retrieving artist</string>
|
||||||
|
<string name="album_list_page_downloaded">Heruntergeladene Alben</string>
|
||||||
|
<string name="album_list_page_most_played">Oft gehörte Alben</string>
|
||||||
|
<string name="album_list_page_new_releases">Neue Releases</string>
|
||||||
|
<string name="album_list_page_recently_added">Kürzlich hinzugefügte Alben</string>
|
||||||
|
<string name="album_list_page_recently_played">Kürzlich gespielte Alben</string>
|
||||||
|
<string name="album_list_page_starred">Lieblingsalben</string>
|
||||||
|
<string name="album_list_page_title">Alben</string>
|
||||||
|
<string name="album_page_extra_info_button">Ähnliches</string>
|
||||||
|
<string name="album_page_play_button">Wiedergabe</string>
|
||||||
|
<string name="album_page_shuffle_button">Zufällige Wiedergabe</string>
|
||||||
|
<string name="app_name">Tempo</string>
|
||||||
|
<string name="artist_bottom_sheet_instant_mix">Instant mix</string>
|
||||||
|
<string name="artist_bottom_sheet_shuffle">Mischen</string>
|
||||||
|
<string name="artist_catalogue_title">Künstler</string>
|
||||||
|
<string name="artist_catalogue_title_expanded">Künstler durchsuchen</string>
|
||||||
|
<string name="artist_error_retrieving_radio">Fehler beim Abruf des Künstlerradios</string>
|
||||||
|
<string name="artist_error_retrieving_tracks">Fehler beim Abruf der Tracks des Künstlers</string>
|
||||||
|
<string name="artist_list_page_downloaded">Heruntergeladene Künstler</string>
|
||||||
|
<string name="artist_list_page_starred">Lieblingskünstler</string>
|
||||||
|
<string name="artist_list_page_title">Künstler</string>
|
||||||
|
<string name="artist_page_radio_button">Radio</string>
|
||||||
|
<string name="artist_page_shuffle_button">Mischen</string>
|
||||||
|
<string name="artist_page_title_album_more_like_this_button">Ähnliches</string>
|
||||||
|
<string name="artist_page_title_album_section">Alben</string>
|
||||||
|
<string name="artist_page_title_biography_more_button">Mehr</string>
|
||||||
|
<string name="artist_page_title_biography_section">Biographie</string>
|
||||||
|
<string name="artist_page_title_most_streamed_song_section">Oft gestreamte Tracks</string>
|
||||||
|
<string name="artist_page_title_most_streamed_song_see_all_button">Alles</string>
|
||||||
|
<string name="connection_alert_dialog_negative_button">Abbrechen</string>
|
||||||
|
<string name="connection_alert_dialog_neutral_button">Enable data saver</string>
|
||||||
|
<string name="connection_alert_dialog_positive_button">OK</string>
|
||||||
|
<string name="connection_alert_dialog_title">Wi-Fi ist nicht verbuden</string>
|
||||||
|
<string name="connection_alert_dialog_summary">Der Zugriff auf den Subsonic server ohne Wi-Fi Verbindung ist deaktiviert. Du kannst das in den App-Einstellungen ändern.</string>
|
||||||
|
<string name="delete_download_storage_dialog_negative_button">Abbrechen</string>
|
||||||
|
<string name="delete_download_storage_dialog_positive_button">Weiter</string>
|
||||||
|
<string name="delete_download_storage_dialog_summary">Wenn Du weitermachst werden alle zuvor heruntergeladenen Inhalte gelöscht.</string>
|
||||||
|
<string name="delete_download_storage_dialog_title">Heruntergeladene Inhalte löschen</string>
|
||||||
|
<string name="download_info_empty_subtitle">Wenn Du einen Track heruntergeladen hast findest Du ihn hier</string>
|
||||||
|
<string name="download_info_empty_title">Bisher keine Downloads!</string>
|
||||||
|
<string name="download_item_multiple_subtitle_formatter">%1$s • %2$s items</string>
|
||||||
|
<string name="download_item_single_subtitle_formatter">%1$s items</string>
|
||||||
|
<string name="download_title_section">Downloads</string>
|
||||||
|
<string name="download_storage_internal_dialog_negative_button">Intern</string>
|
||||||
|
<string name="download_storage_external_dialog_positive_button">Extern</string>
|
||||||
|
<string name="download_storage_dialog_summary">Das Ändern des Speicherorts löscht alle Inhalte im zuvor gewählten Speicherort.</string>
|
||||||
|
<string name="download_storage_dialog_sub_summary">Neustart der Anwendung ist nötig.</string>
|
||||||
|
<string name="download_storage_dialog_title">Wähle den Speicherort aus</string>
|
||||||
|
<string name="empty_string" />
|
||||||
|
<string name="error_required">Benötigt</string>
|
||||||
|
<string name="error_server_prefix">http or https prefix benötigt</string>
|
||||||
|
<string name="exo_download_notification_channel_name">Downloads</string>
|
||||||
|
<string name="filter_info_selection">Wähle mindestens zwei Filter aus</string>
|
||||||
|
<string name="filter_title">Filter</string>
|
||||||
|
<string name="filter_title_expanded">Genres filtern</string>
|
||||||
|
<string name="genre_catalogue_title">Genre Übersicht</string>
|
||||||
|
<string name="genre_catalogue_title_expanded">Genres durchsuchen</string>
|
||||||
|
<string name="home_subtitle_new_internet_radio_station">Radio hinzufügen</string>
|
||||||
|
<string name="home_subtitle_new_podcast_channel">Podcast Kanal hinzufügen</string>
|
||||||
|
<string name="home_subtitle_made_for_you">Ein Mix von einem deiner Lieblingslieder erstellen</string>
|
||||||
|
<string name="home_sync_starred_title">Einige Lieblingslieder müssen synchronisiert werden</string>
|
||||||
|
<string name="home_sync_starred_subtitle">Das Herunterladen dieser Tracks kann erheblichen Datenverbrauch verursachen</string>
|
||||||
|
<string name="home_sync_starred_cancel">Abbrechen</string>
|
||||||
|
<string name="home_sync_starred_download">Download</string>
|
||||||
|
<string name="home_title_flashback">Flashback</string>
|
||||||
|
<string name="home_title_last_played">Zuletzt gespielt</string>
|
||||||
|
<string name="home_title_last_played_see_all_button">Alle zeigen</string>
|
||||||
|
<string name="home_title_made_for_you">Wie für Dich gemacht</string>
|
||||||
|
<string name="home_title_most_played">Oft gespielt</string>
|
||||||
|
<string name="home_title_most_played_see_all_button">Alle zeigen</string>
|
||||||
|
<string name="home_title_discovery">Entdeckungsreise</string>
|
||||||
|
<string name="home_title_recently_added">Kürzlich hinzugefügt</string>
|
||||||
|
<string name="home_title_recently_added_see_all_button">Alle zeigen</string>
|
||||||
|
<string name="home_title_discovery_shuffle_all_button">Alle mischen</string>
|
||||||
|
<string name="home_title_starred_albums">★ Lieblingsalben</string>
|
||||||
|
<string name="home_title_starred_albums_see_all_button">Alle zeigen</string>
|
||||||
|
<string name="home_title_starred_artists">★ Lieblingskünstler</string>
|
||||||
|
<string name="home_title_starred_artists_see_all_button">Alle zeigen</string>
|
||||||
|
<string name="home_title_starred_tracks">★ Lieblingslieder</string>
|
||||||
|
<string name="home_title_starred_tracks_see_all_button">Alle zeigen</string>
|
||||||
|
<string name="home_title_internet_radio_station">Internet Radios</string>
|
||||||
|
<string name="home_title_podcast_channels_see_all_button">Alle zeigen</string>
|
||||||
|
<string name="library_title_music_folder">Sammlung</string>
|
||||||
|
<string name="library_title_album">Alben</string>
|
||||||
|
<string name="library_title_album_see_all_button">Alle zeigen</string>
|
||||||
|
<string name="library_title_artist">Künstler</string>
|
||||||
|
<string name="library_title_artist_see_all_button">Alle zeigen</string>
|
||||||
|
<string name="library_title_genre">Genres</string>
|
||||||
|
<string name="library_title_genre_see_all_button">Alle zeigen</string>
|
||||||
|
<string name="library_title_playlist">Playlisten</string>
|
||||||
|
<string name="library_title_playlist_see_all_button">Alle zeigen</string>
|
||||||
|
<string name="login_empty">Kein Server hinzugefügt</string>
|
||||||
|
<string name="login_title">Subsonic Server</string>
|
||||||
|
<string name="login_title_expanded">Subsonic Server</string>
|
||||||
|
<string name="media_route_menu_title">Cast</string>
|
||||||
|
<string name="menu_add_button">Hinzufügen</string>
|
||||||
|
<string name="menu_download_all_button">Alle Herunterladen</string>
|
||||||
|
<string name="menu_download_label">Downloads</string>
|
||||||
|
<string name="menu_home_label">Start</string>
|
||||||
|
<string name="menu_library_label">Sammlung</string>
|
||||||
|
<string name="menu_search_button">Suche</string>
|
||||||
|
<string name="menu_settings_button">Einstellungen</string>
|
||||||
|
<string name="player_playback_speed">%1$.2fx</string>
|
||||||
|
<string name="player_server_priority">Server Priorität</string>
|
||||||
|
<string name="playlist_catalogue_title">Playlisten</string>
|
||||||
|
<string name="playlist_catalogue_title_expanded">Playlisten durchsuchen</string>
|
||||||
|
<string name="playlist_chooser_dialog_empty">Keine Playlisten erstellt</string>
|
||||||
|
<string name="playlist_chooser_dialog_negative_button">Abbrechen</string>
|
||||||
|
<string name="playlist_chooser_dialog_neutral_button">Erstellen</string>
|
||||||
|
<string name="playlist_chooser_dialog_title">Zu einer Playliste hinzufügen</string>
|
||||||
|
<string name="playlist_counted_tracks">%1$d Tracks • %2$s</string>
|
||||||
|
<string name="playlist_duration">Länge • %1$s</string>
|
||||||
|
<string name="playlist_editor_dialog_hint_name">Name der Playliste</string>
|
||||||
|
<string name="playlist_editor_dialog_negative_button">Abbrechen</string>
|
||||||
|
<string name="playlist_editor_dialog_neutral_button">Löschen</string>
|
||||||
|
<string name="playlist_editor_dialog_positive_button">Speichern</string>
|
||||||
|
<string name="playlist_editor_dialog_title">Playliste erstellen</string>
|
||||||
|
<string name="playlist_page_play_button">Wiedergabe</string>
|
||||||
|
<string name="playlist_page_shuffle_button">Shuffle</string>
|
||||||
|
<string name="playlist_song_count">Playliste • %1$d Tracks</string>
|
||||||
|
<string name="podcast_channel_catalogue_title_expanded">Kanäle durchsuchen</string>
|
||||||
|
<string name="podcast_channel_catalogue_title">Kanäle</string>
|
||||||
|
<string name="podcast_channel_page_title_description_section">Beschreibung</string>
|
||||||
|
<string name="podcast_channel_page_title_episode_section">Episoden</string>
|
||||||
|
<string name="podcast_channel_page_title_no_episode_available">Keine Episoden verfügbar</string>
|
||||||
|
<string name="podcast_channel_editor_dialog_hint_rss_url">RSS Url</string>
|
||||||
|
<string name="podcast_channel_editor_dialog_title">Podcast Kanal</string>
|
||||||
|
<string name="podcast_release_date_duration_formatter">%1$s • %2$s</string>
|
||||||
|
<string name="podcast_episode_download_request_snackbar">Der Request wurde an den Server geschickt.</string>
|
||||||
|
<string name="podcast_info_empty_subtitle">Wenn Du einen Kanal hinzufügst findest Du ihn hier</string>
|
||||||
|
<string name="podcast_info_empty_title">Keine Podcasts gefunden.</string>
|
||||||
|
<string name="podcast_info_empty_button">Hier klicken, um den Bereich auszublenden\nAnwendungsneustart ist notwendig</string>
|
||||||
|
<string name="radio_editor_dialog_hint_name">Radio Name</string>
|
||||||
|
<string name="radio_editor_dialog_hint_stream_url">Radio Stream URL</string>
|
||||||
|
<string name="radio_editor_dialog_hint_homepage_url">Radio Homepage URL</string>
|
||||||
|
<string name="radio_editor_dialog_negative_button">Abbrechen</string>
|
||||||
|
<string name="radio_editor_dialog_neutral_button">Löschen</string>
|
||||||
|
<string name="radio_editor_dialog_positive_button">Speichern</string>
|
||||||
|
<string name="radio_editor_dialog_title">Internet Radio Station</string>
|
||||||
|
<string name="radio_station_info_empty_subtitle">Wenn Du eine Radio Station hinzugefügt hast findest Du sie hier</string>
|
||||||
|
<string name="radio_station_info_empty_title">Keine Radio Stationen gefunden.</string>
|
||||||
|
<string name="radio_station_info_empty_button">Hier klicken, um den Bereich auszublenden\nAnwendungsneustart ist notwendig</string>
|
||||||
|
<string name="rating_dialog_negative_button">Abbrechen</string>
|
||||||
|
<string name="rating_dialog_positive_button">Speichern</string>
|
||||||
|
<string name="rating_dialog_title">Bewerten</string>
|
||||||
|
<string name="search_hint">Titel, Künstler oder Alben durchsuchen</string>
|
||||||
|
<string name="search_info_minimum_characters">Gib mindestens drei Zeichen ein</string>
|
||||||
|
<string name="search_title_album">Alben</string>
|
||||||
|
<string name="search_title_artist">Künstler</string>
|
||||||
|
<string name="search_title_song">Tracks</string>
|
||||||
|
<string name="server_signup_dialog_action_low_security">Niedrige Sicherheit</string>
|
||||||
|
<string name="server_signup_dialog_hint_name">Server Name</string>
|
||||||
|
<string name="server_signup_dialog_hint_password">Passwort</string>
|
||||||
|
<string name="server_signup_dialog_hint_url">Server URL</string>
|
||||||
|
<string name="server_signup_dialog_hint_username">Benutzername</string>
|
||||||
|
<string name="server_signup_dialog_negative_button">Abbrechen</string>
|
||||||
|
<string name="server_signup_dialog_neutral_button">Löschen</string>
|
||||||
|
<string name="server_signup_dialog_positive_button">Speichern</string>
|
||||||
|
<string name="server_signup_dialog_title">Server hinzufügen</string>
|
||||||
|
<string name="server_unreachable_dialog_negative_button">Abbrechen</string>
|
||||||
|
<string name="server_unreachable_dialog_neutral_button">Gehe zum Login</string>
|
||||||
|
<string name="server_unreachable_dialog_positive_button">Trotzdem weitermachen</string>
|
||||||
|
<string name="server_unreachable_dialog_title">Server nicht erreichbar</string>
|
||||||
|
<string name="server_unreachable_dialog_summary">Der angefragte Server ist nicht erreichbar. Wenn Du trotzdem weitermachst, wird dieser Dialog für eine Stunden nicht wieder erscheinen.</string>
|
||||||
|
<string name="settings_about_summary">Tempo ist ein nativ für Android entwickelter, leichtgewichtiger Open-Source Client für Subsonic.</string>
|
||||||
|
<string name="settings_about_title">Über</string>
|
||||||
|
<string name="settings_audio_transcode_download_priority_summary">Diese Option deaktiviert die Transkodierungssettings für Downloads.</string>
|
||||||
|
<string name="settings_audio_transcode_download_priority_title">Transkodierungseinstellungen des Servers für Downloads bevorzugen.</string>
|
||||||
|
<string name="settings_audio_transcode_download_summary">Diese Option aktiviert das Transkodieren für heruntergeladene Tracks.</string>
|
||||||
|
<string name="settings_audio_transcode_download_title">Transkodierte Tracks herunterladen</string>
|
||||||
|
<string name="settings_audio_transcode_download_format">Transkodierungs-Format</string>
|
||||||
|
<string name="settings_audio_transcode_priority_summary">Diese Option deaktiviert die weiter unten folgenden Transkodierungseinstellungen.</string>
|
||||||
|
<string name="settings_audio_transcode_priority_title">Transkodierungseinstellungen des Servers bevorzugen</string>
|
||||||
|
<string name="settings_audio_transcode_priority_toast">Servereinstellungen zur Transkodierung des Tracks werden bevorzugt</string>
|
||||||
|
<string name="settings_audio_transcode_format_download">Transkodierungs-Format für Downloads</string>
|
||||||
|
<string name="settings_audio_transcode_format_mobile">Transkodierungsformat im mobilen Netz</string>
|
||||||
|
<string name="settings_audio_transcode_format_wifi">Transkodierungsformat im Wi-Fi</string>
|
||||||
|
<string name="settings_covers_cache">Größe des Artwork Caches</string>
|
||||||
|
<string name="settings_data_saving_mode_summary">Um das Datenvolumen zu begrenzen werden keine Cover heruntergeladen.</string>
|
||||||
|
<string name="settings_data_saving_mode_title">Mobile Datennutzung begrenzen</string>
|
||||||
|
<string name="settings_delete_download_storage_title">Gespeicherte Inhalte löschen</string>
|
||||||
|
<string name="settings_delete_download_storage_summary">Wenn Du weitermachst werden alle gespeicherten Inhalte unwiderruflich gelöscht.</string>
|
||||||
|
<string name="settings_download_storage_title">Download storage</string>
|
||||||
|
<string name="settings_equalizer_summary">Audio Einstellungen anpassen</string>
|
||||||
|
<string name="settings_equalizer_title">Equalizer</string>
|
||||||
|
<string name="settings_github_link">https://github.com/CappielloAntonio/tempo</string>
|
||||||
|
<string name="settings_github_summary">Verfolge die Entwicklung</string>
|
||||||
|
<string name="settings_github_title">Github</string>
|
||||||
|
<string name="settings_image_size">Bilder Auflösung anpassen</string>
|
||||||
|
<string name="settings_language">Sprache</string>
|
||||||
|
<string name="settings_logout_title">Abmelden</string>
|
||||||
|
<string name="settings_max_bitrate_download">Bitrate für Downloads</string>
|
||||||
|
<string name="settings_max_bitrate_wifi">Bitrate bei Wi-Fi Nutzung</string>
|
||||||
|
<string name="settings_max_bitrate_mobile">Bitrate bei mobiler Nutzung</string>
|
||||||
|
<string name="settings_media_cache">Größe des Medienfile Caches</string>
|
||||||
|
<string name="settings_music_directory">Zeige Musikverzeichnisse</string>
|
||||||
|
<string name="settings_music_directory_summary">Zeige den Bereich für Musikverzeichnisse. Der Server muss das Feature unterstützen.</string>
|
||||||
|
<string name="settings_queue_syncing_title">Warteschlange für diesen User synchronisieren</string>
|
||||||
|
<string name="settings_queue_syncing_countdown">Timer synchronisieren</string>
|
||||||
|
<string name="settings_queue_syncing_summary">Der Benutzer kann seine Warteschlange speichern und beim Neustart der Anwendung wiederherstellen.</string>
|
||||||
|
<string name="settings_podcast">Podcasts anzeigen</string>
|
||||||
|
<string name="settings_podcast_summary">Zeige den Bereich für Podcasts.</string>
|
||||||
|
<string name="settings_radio">Radios anzeigen</string>
|
||||||
|
<string name="settings_radio_summary">Zeige den Bereich für Radios.</string>
|
||||||
|
<string name="settings_replay_gain">Set replay gain mode</string>
|
||||||
|
<string name="settings_rounded_corner">Abgerundete Ecken</string>
|
||||||
|
<string name="settings_rounded_corner_summary">Abgerundete Ecken für alle gerenderten Cover. Anwendungsneustart ist notwendig.</string>
|
||||||
|
<string name="settings_rounded_corner_size">Eckenradius</string>
|
||||||
|
<string name="settings_rounded_corner_size_summary">Definiert den Eckenradius.</string>
|
||||||
|
<string name="settings_scan_title">Sammlung scannen</string>
|
||||||
|
<string name="settings_summary_replay_gain">Replay-Gain ist ein Feature, das die Lautstärke von Tracks für ein konsistentes Hörerlebnis anpasst. Diese Einstellung funktioniert nur, wenn Tracks die entsprechenden Metadaten haben.</string>
|
||||||
|
<string name="settings_summary_syncing">Den Zustand der Warteschlange synchronisieren. Das beinhaltet die Tracks in der Warteschlange, den aktuell gespielten Track und die Position innerhalb dieses Tracks. Der Server muss dieses Feature unterstützen.</string>
|
||||||
|
<string name="settings_summary_transcoding">Priorität des Transkodierungsmodus. \"Direktes Abspielen\" ändert die Bitrate der Dateien nicht.</string>
|
||||||
|
<string name="settings_summary_transcoding_download">Transkodierte Medien herunterladen. Diese Option deaktiviert den Download-Endpoint und benutzt stattdessen die folgenden Einstellungen. \n\n If \"Transkodierungs-Format\" ist auf \"Direktes Abspielen\" gesetzt, Die Bitrate des Tracks wird nicht geändert.</string>
|
||||||
|
<string name="settings_sync_starred_tracks_for_offline_use_summary">Lieblingslieder werden automatisch heruntergeladen.</string>
|
||||||
|
<string name="settings_sync_starred_tracks_for_offline_use_title">Lieblingslieder für Offline-Modus sychronisieren</string>
|
||||||
|
<string name="settings_theme">Design</string>
|
||||||
|
<string name="settings_title_data">Daten</string>
|
||||||
|
<string name="settings_title_general">Allgemein</string>
|
||||||
|
<string name="settings_title_replay_gain">Replay Gain</string>
|
||||||
|
<string name="settings_title_syncing">Sychronisierung</string>
|
||||||
|
<string name="settings_title_transcoding">Transkodierung</string>
|
||||||
|
<string name="settings_title_transcoding_download">Transkodierung Download</string>
|
||||||
|
<string name="settings_title_ui">Benutzeroberfläche</string>
|
||||||
|
<string name="settings_transcoded_download">Transcoded download</string>
|
||||||
|
<string name="settings_version_title">Version</string>
|
||||||
|
<string name="settings_wifi_only_title">Warnung bei Streamen ohne Wi-Fi</string>
|
||||||
|
<string name="settings_wifi_only_summary">Um Erlaubnis fragen bevor über das mobile Netzwerk gestreamed wird.</string>
|
||||||
|
<string name="song_bottom_sheet_add_to_playlist">Zu Playliste hinzufügen</string>
|
||||||
|
<string name="song_bottom_sheet_add_to_queue">Zur Warteschlange hinzufügen</string>
|
||||||
|
<string name="song_bottom_sheet_download">Download</string>
|
||||||
|
<string name="song_bottom_sheet_error_retrieving_album">Fehler beim Abruf des Albums</string>
|
||||||
|
<string name="song_bottom_sheet_error_retrieving_artist">Fehler beim Abruf des Künstlers</string>
|
||||||
|
<string name="song_bottom_sheet_go_to_album">Zum Album gehen</string>
|
||||||
|
<string name="song_bottom_sheet_go_to_artist">Zum Künstler gehen</string>
|
||||||
|
<string name="song_bottom_sheet_instant_mix">Sofort-Mix</string>
|
||||||
|
<string name="song_bottom_sheet_play_next">Nächsten Titel spielen</string>
|
||||||
|
<string name="song_bottom_sheet_rate">Bewerten</string>
|
||||||
|
<string name="song_bottom_sheet_remove">Entfernen</string>
|
||||||
|
<string name="song_list_page_downloaded">Heruntergeladen</string>
|
||||||
|
<string name="song_list_page_most_played">Oft gespielte Tracks</string>
|
||||||
|
<string name="song_list_page_recently_added">Zuletzt hinzugefügte Tracks</string>
|
||||||
|
<string name="song_list_page_recently_played">Zuletzt gespielte Tracks</string>
|
||||||
|
<string name="song_list_page_starred">Lieblingslieder</string>
|
||||||
|
<string name="song_list_page_top">%1$s\'s Top Tracks</string>
|
||||||
|
<string name="song_list_page_year">Jahr %1$d</string>
|
||||||
|
<string name="song_subtitle_formatter">%1$s • %2$s</string>
|
||||||
|
<string name="starred_sync_dialog_negative_button">Abbrechen</string>
|
||||||
|
<string name="starred_sync_dialog_neutral_button">Weiter</string>
|
||||||
|
<string name="starred_sync_dialog_positive_button">Weiter und Herunterladen</string>
|
||||||
|
<string name="starred_sync_dialog_summary">Das Herunterladen deiner Lieblingslieder kann viel Datenvolumen verbrauchen.</string>
|
||||||
|
<string name="starred_sync_dialog_title">Lieblingslieder synchronisieren</string>
|
||||||
|
<string name="undraw_url">https://undraw.co/</string>
|
||||||
|
<string name="undraw_page">unDraw</string>
|
||||||
|
<string name="undraw_thanks">Besonders möchten wir uns bei unDraw bedanken, durch deren Illustrationen wir diese App so schön machen konnten.</string>
|
||||||
|
<string name="home_title_radio_station">Radio Stationen</string>
|
||||||
|
<string name="home_title_last_week">Letzte Woche</string>
|
||||||
|
<string name="home_title_top_songs">Deine Top Songs</string>
|
||||||
|
<string name="home_title_new_releases">Neue Releases</string>
|
||||||
|
<string name="home_title_best_of">Best Of</string>
|
||||||
|
<string name="home_subtitle_best_of">Top Tracks Deiner Lieblingskünstler</string>
|
||||||
|
<string name="home_title_newest_podcasts">Neueste Podcasts</string>
|
||||||
|
<string name="home_title_podcast_channels">Kanäle</string>
|
||||||
|
<string name="artist_adapter_radio_station_starting">Suche…</string>
|
||||||
|
<string name="podcast_bottom_sheet_go_to_channel">Zum Kanal gehen</string>
|
||||||
|
<string name="podcast_bottom_sheet_delete">Löschen</string>
|
||||||
|
<string name="podcast_bottom_sheet_remove">Entfernen</string>
|
||||||
|
<string name="podcast_bottom_sheet_download">Download</string>
|
||||||
|
<string name="podcast_bottom_sheet_add_to_queue">Zur Warteschlange hinzufügen</string>
|
||||||
|
<string name="podcast_bottom_sheet_play_next">Nächsten Titel spielen</string>
|
||||||
|
<string name="menu_sort_year">Jahr</string>
|
||||||
|
<string name="menu_sort_artist">Künstler</string>
|
||||||
|
<string name="menu_sort_name">Name</string>
|
||||||
|
<string name="menu_sort_random">Zufall</string>
|
||||||
|
<string name="description_empty_title">Keine Beschreibung verfügbar</string>
|
||||||
|
<string name="menu_filter_download">Heruntergeladen</string>
|
||||||
|
<string name="menu_filter_all">Alle</string>
|
||||||
|
<string name="menu_group_by_track">Track</string>
|
||||||
|
<string name="menu_group_by_album">Album</string>
|
||||||
|
<string name="menu_group_by_artist">Künstler</string>
|
||||||
|
<string name="menu_group_by_genre">Genre</string>
|
||||||
|
<string name="menu_group_by_year">Jahr</string>
|
||||||
|
</resources>
|
||||||
@@ -90,6 +90,35 @@
|
|||||||
<item>320</item>
|
<item>320</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
|
|
||||||
|
<string-array name="max_bitrate_download_list_titles">
|
||||||
|
<item>Original</item>
|
||||||
|
<item>32 kbps</item>
|
||||||
|
<item>48 kbps</item>
|
||||||
|
<item>64 kbps</item>
|
||||||
|
<item>80 kbps</item>
|
||||||
|
<item>96 kbps</item>
|
||||||
|
<item>112 kbps</item>
|
||||||
|
<item>128 kbps</item>
|
||||||
|
<item>160 kbps</item>
|
||||||
|
<item>192 kbps</item>
|
||||||
|
<item>256 kbps</item>
|
||||||
|
<item>320 kbps</item>
|
||||||
|
</string-array>
|
||||||
|
<string-array name="max_bitrate_download_list_values">
|
||||||
|
<item>0</item>
|
||||||
|
<item>32</item>
|
||||||
|
<item>48</item>
|
||||||
|
<item>64</item>
|
||||||
|
<item>80</item>
|
||||||
|
<item>96</item>
|
||||||
|
<item>112</item>
|
||||||
|
<item>128</item>
|
||||||
|
<item>160</item>
|
||||||
|
<item>192</item>
|
||||||
|
<item>256</item>
|
||||||
|
<item>320</item>
|
||||||
|
</string-array>
|
||||||
|
|
||||||
<string-array name="audio_transcode_format_wifi_list_titles">
|
<string-array name="audio_transcode_format_wifi_list_titles">
|
||||||
<item>Direct play</item>
|
<item>Direct play</item>
|
||||||
<item>Opus</item>
|
<item>Opus</item>
|
||||||
@@ -120,6 +149,21 @@
|
|||||||
<item>flac</item>
|
<item>flac</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
|
|
||||||
|
<string-array name="audio_transcode_format_download_list_titles">
|
||||||
|
<item>Direct download</item>
|
||||||
|
<item>Opus</item>
|
||||||
|
<item>AAC</item>
|
||||||
|
<item>Mp3</item>
|
||||||
|
<item>Flac</item>
|
||||||
|
</string-array>
|
||||||
|
<string-array name="audio_transcode_format_download_list_values">
|
||||||
|
<item>raw</item>
|
||||||
|
<item>opus</item>
|
||||||
|
<item>aac</item>
|
||||||
|
<item>mp3</item>
|
||||||
|
<item>flac</item>
|
||||||
|
</string-array>
|
||||||
|
|
||||||
<string-array name="queue_syncing_countdown_titles">
|
<string-array name="queue_syncing_countdown_titles">
|
||||||
<item>Ten seconds</item>
|
<item>Ten seconds</item>
|
||||||
<item>Five seconds</item>
|
<item>Five seconds</item>
|
||||||
@@ -152,4 +196,17 @@
|
|||||||
<item>track</item>
|
<item>track</item>
|
||||||
<item>album</item>
|
<item>album</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
|
|
||||||
|
<string-array name="transcoded_download_option_list_titles">
|
||||||
|
<item>Do not transcode</item>
|
||||||
|
<item>Server settings</item>
|
||||||
|
<item>Wi-Fi Transcode format</item>
|
||||||
|
<item>Mobile Transcode format</item>
|
||||||
|
</string-array>
|
||||||
|
<string-array name="transcoded_download_option_list_values">
|
||||||
|
<item>0</item>
|
||||||
|
<item>1</item>
|
||||||
|
<item>2</item>
|
||||||
|
<item>3</item>
|
||||||
|
</string-array>
|
||||||
</resources>
|
</resources>
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user