mirror of
https://github.com/CappielloAntonio/tempo.git
synced 2026-01-30 14:22:05 +00:00
Compare commits
97 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
73b368d202 | ||
|
|
f293a0116b | ||
|
|
ff5bec30c0 | ||
|
|
5c66bed477 | ||
|
|
d2068106e4 | ||
|
|
b7b02854d5 | ||
|
|
0edafc2d8e | ||
|
|
279302737d | ||
|
|
634de67d74 | ||
|
|
cd44368d66 | ||
|
|
ae8aa56602 | ||
|
|
34d6692dae | ||
|
|
8d2f0edbab | ||
|
|
d4d7aaba2b | ||
|
|
d690df86d8 | ||
|
|
e8f3cdbb48 | ||
|
|
33aa38e885 | ||
|
|
2faba71df0 | ||
|
|
1d3a32be5d | ||
|
|
5b8e7d1404 | ||
|
|
85a5d01e72 | ||
|
|
c7b17f2214 | ||
|
|
d8c8a783de | ||
|
|
293b0f71c8 | ||
|
|
e6e0e399e0 | ||
|
|
3223de5d03 | ||
|
|
c6d08d6a3f | ||
|
|
375501f282 | ||
|
|
0a62f0d81e | ||
|
|
112e468c7d | ||
|
|
0ccc601f1b | ||
|
|
ae82bcd7bf | ||
|
|
68512b7e12 | ||
|
|
d6cc4fc028 | ||
|
|
e8c7c065e2 | ||
|
|
427394a105 | ||
|
|
75fc2cbafa | ||
|
|
b9a3393b39 | ||
|
|
ed60677608 | ||
|
|
1a407341ec | ||
|
|
567350771c | ||
|
|
47bef77723 | ||
|
|
12f09b2201 | ||
|
|
77d0b4182e | ||
|
|
caf64e0fa4 | ||
|
|
ac90b89099 | ||
|
|
4523bb8e49 | ||
|
|
06f4898892 | ||
|
|
5bbab10485 | ||
|
|
8fe66d058e | ||
|
|
6a893ac424 | ||
|
|
f184ace301 | ||
|
|
121c2b33da | ||
|
|
215f1021d6 | ||
|
|
ce6159ad93 | ||
|
|
746ea93dbe | ||
|
|
612c05fabc | ||
|
|
dfc246d079 | ||
|
|
0a5983e32b | ||
|
|
9fde629233 | ||
|
|
e089c5e466 | ||
|
|
94b0572958 | ||
|
|
133b5e4794 | ||
|
|
6b0ba573de | ||
|
|
6ae7fc2172 | ||
|
|
9cabfb0e2c | ||
|
|
17f05cb3d8 | ||
|
|
f2839f4ff9 | ||
|
|
9aadcb91fb | ||
|
|
5d4dfe1ac7 | ||
|
|
d3d76dd435 | ||
|
|
0c3086d68b | ||
|
|
9b3b9771e6 | ||
|
|
a92403bda4 | ||
|
|
67905affd3 | ||
|
|
c20cadff94 | ||
|
|
0cc45b2b12 | ||
|
|
b6063f6b20 | ||
|
|
678c61e569 | ||
|
|
e88d6d7844 | ||
|
|
bae43cbfe9 | ||
|
|
68c77cefff | ||
|
|
75980ee18b | ||
|
|
ed30198c8d | ||
|
|
185a671f82 | ||
|
|
42905819a7 | ||
|
|
93d6faa392 | ||
|
|
64337775d6 | ||
|
|
d690cf37fb | ||
|
|
32c8ef3b25 | ||
|
|
607c68967d | ||
|
|
7fb9f63d1f | ||
|
|
a04a2072f7 | ||
|
|
72bd71dea2 | ||
|
|
f87328f874 | ||
|
|
4d9b71d48e | ||
|
|
06f8bc771a |
22
.github/ISSUE_TEMPLATE/bug_report.md
vendored
22
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -7,27 +7,29 @@ assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
## Describe the bug
|
||||
<!-- A clear and concise description of what the bug is. -->
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
## Expected behavior
|
||||
<!-- A clear and concise description of what you expected to happen. -->
|
||||
|
||||
**To Reproduce**
|
||||
## To Reproduce
|
||||
<!--
|
||||
Outline the steps required to reproduce the bug, including any specific actions, inputs, or conditions:
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
3. Scroll down to '....'
|
||||
4. See error
|
||||
-->
|
||||
|
||||
**Environment**
|
||||
## Environment
|
||||
- Android device: [Device Model]
|
||||
- Android OS version: [Android Version]
|
||||
- App version: [App Version]
|
||||
- Other relevant details: [e.g., specific network conditions, external dependencies]
|
||||
|
||||
**Logs or Screenshots**
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
## Logs or Screenshots
|
||||
<!-- If applicable, add screenshots to help explain your problem. -->
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
||||
## Additional context
|
||||
<!-- Add any other context about the problem here. -->
|
||||
|
||||
30
.github/ISSUE_TEMPLATE/crash-report.md
vendored
30
.github/ISSUE_TEMPLATE/crash-report.md
vendored
@@ -7,33 +7,35 @@ assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Description**
|
||||
Provide a clear and concise description of the crash you encountered.
|
||||
## Description
|
||||
<!-- Provide a clear and concise description of the crash you encountered. -->
|
||||
|
||||
**Steps to Reproduce**
|
||||
## Steps to Reproduce
|
||||
<!--
|
||||
Please provide the steps to reproduce the crash:
|
||||
1. Go to '...'
|
||||
2. Click on '...'
|
||||
3. Scroll down to '...'
|
||||
4. See the crash
|
||||
-->
|
||||
|
||||
**Environment**
|
||||
## Environment
|
||||
- Android device: [Device Model]
|
||||
- Android OS version: [Android Version]
|
||||
- App version: [App Version]
|
||||
- Other relevant details: [e.g., specific network conditions, external dependencies]
|
||||
|
||||
**Crash Logs/Stack Trace**
|
||||
If available, please provide the crash log or stack trace related to the crash. Include it inside a code block (surround with triple backticks ```).
|
||||
## Crash Logs/Stack trace
|
||||
<!-- If available, please provide the crash log or stack trace related to the crash. Include it inside a code block (surround with triple backticks ```). Please use the unsigned apk (app-tempo-debug.apk), as the logs would be illegible and therefore useless for this purpose. -->
|
||||
|
||||
**Screenshots**
|
||||
If applicable, add screenshots to help explain the problem.
|
||||
## Screenshots
|
||||
<!-- If applicable, add screenshots to help explain the problem. -->
|
||||
|
||||
**Additional Context**
|
||||
Add any other context about the problem here.
|
||||
## Additional Context
|
||||
<!-- Add any other context about the problem here. -->
|
||||
|
||||
**Reproducibility**
|
||||
Mention the frequency of the crash occurrence (e.g., always, sometimes, occasionally).
|
||||
## Reproducibility
|
||||
<!-- Mention the frequency of the crash occurrence (e.g., always, sometimes, occasionally). -->
|
||||
|
||||
**Additional Notes**
|
||||
Include any other notes or details that could be helpful for troubleshooting the crash.
|
||||
## Additional Notes
|
||||
<!-- Include any other notes or details that could be helpful for troubleshooting the crash. -->
|
||||
|
||||
16
.github/ISSUE_TEMPLATE/feature_request.md
vendored
16
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@@ -7,14 +7,14 @@ assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Summary**
|
||||
Provide a concise summary of the feature you are requesting.
|
||||
## Summary
|
||||
<!-- Provide a concise summary of the feature you are requesting. -->
|
||||
|
||||
**Description**
|
||||
Please describe in detail the feature you would like to see implemented.
|
||||
## Description
|
||||
<!-- Please describe in detail the feature you would like to see implemented. -->
|
||||
|
||||
**Use Case**
|
||||
Explain why this feature is important and how it would improve the user experience.
|
||||
## Use Case
|
||||
<!-- Explain why this feature is important and how it would improve the user experience. -->
|
||||
|
||||
**Additional context**
|
||||
Include any additional information, screenshots, or examples that could be helpful in understanding or implementing the feature.
|
||||
## Additional context
|
||||
<!-- Include any additional information, screenshots, or examples that could be helpful in understanding or implementing the feature. -->
|
||||
|
||||
50
.github/workflows/github_release.yml
vendored
50
.github/workflows/github_release.yml
vendored
@@ -10,7 +10,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Setup JDK 17
|
||||
uses: actions/setup-java@v2
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: 'zulu'
|
||||
java-version: '17'
|
||||
@@ -28,6 +28,13 @@ jobs:
|
||||
- name: Make gradlew executable
|
||||
run: chmod +x ./gradlew
|
||||
|
||||
- name: Setup build tool version variable
|
||||
shell: bash
|
||||
run: |
|
||||
BUILD_TOOL_VERSION=$(ls /usr/local/lib/android/sdk/build-tools/ | tail -n 1)
|
||||
echo "BUILD_TOOL_VERSION=$BUILD_TOOL_VERSION" >> $GITHUB_ENV
|
||||
echo Last build tool version is: $BUILD_TOOL_VERSION
|
||||
|
||||
- name: Build APK
|
||||
id: build
|
||||
run: bash ./gradlew assembleTempoRelease
|
||||
@@ -41,6 +48,8 @@ jobs:
|
||||
alias: ${{ secrets.KEY_ALIAS_GITHUB }}
|
||||
keyStorePassword: ${{ secrets.KEYSTORE_PASSWORD }}
|
||||
keyPassword: ${{ secrets.KEY_PASSWORD_GITHUB }}
|
||||
env:
|
||||
BUILD_TOOLS_VERSION: ${{ env.BUILD_TOOL_VERSION }}
|
||||
|
||||
- name: Make artifact
|
||||
uses: actions/upload-artifact@v2
|
||||
@@ -48,34 +57,6 @@ jobs:
|
||||
name: app-release-signed
|
||||
path: ${{steps.sign_apk.outputs.signedReleaseFile}}
|
||||
|
||||
# - name: Build AAB
|
||||
# run: bash ./gradlew bundleRelease
|
||||
|
||||
# - name: Sign AAB
|
||||
# id: sign_aab
|
||||
# uses: r0adkll/sign-android-release@v1
|
||||
# with:
|
||||
# releaseDirectory: app/build/outputs/bundle/release
|
||||
# signingKeyBase64: ${{ secrets.KEYSTORE_BASE64 }}
|
||||
# alias: ${{ secrets.KEY_ALIAS_GITHUB }}
|
||||
# keyStorePassword: ${{ secrets.KEYSTORE_PASSWORD }}
|
||||
# keyPassword: ${{ secrets.KEY_PASSWORD_GITHUB }}
|
||||
|
||||
# - name: Make artifact
|
||||
# uses: actions/upload-artifact@v2
|
||||
# with:
|
||||
# name: app-release-signed
|
||||
# path: ${{steps.sign_aab.outputs.signedReleaseFile}}
|
||||
|
||||
# - name: Build Changelog
|
||||
# id: changelog
|
||||
# uses: ardalanamini/auto-changelog@v3
|
||||
# with:
|
||||
# mention-authors: false
|
||||
# mention-new-contributors: false
|
||||
# include-compare: false
|
||||
# semver: false
|
||||
|
||||
- name: Create Release
|
||||
id: create_release
|
||||
uses: actions/create-release@v1
|
||||
@@ -83,7 +64,6 @@ jobs:
|
||||
tag_name: ${{ github.ref }}
|
||||
release_name: Release v${{ github.ref }}
|
||||
body: '> Changelog coming soon'
|
||||
# body: ${{ steps.changelog.outputs.changelog }}
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ github.token }}
|
||||
|
||||
@@ -96,13 +76,3 @@ jobs:
|
||||
asset_path: ${{steps.sign_apk.outputs.signedReleaseFile}}
|
||||
asset_name: app-tempo-release.apk
|
||||
asset_content_type: application/zip
|
||||
|
||||
# - name: Upload AAB
|
||||
# uses: actions/upload-release-asset@v1
|
||||
# env:
|
||||
# GITHUB_TOKEN: ${{ github.token }}
|
||||
# with:
|
||||
# upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
# asset_path: ${{steps.sign_aab.outputs.signedReleaseFile}}
|
||||
# asset_name: app-release.aab
|
||||
# asset_content_type: application/zip
|
||||
|
||||
5
.idea/gradle.xml
generated
5
.idea/gradle.xml
generated
@@ -4,16 +4,15 @@
|
||||
<component name="GradleSettings">
|
||||
<option name="linkedExternalProjectsSettings">
|
||||
<GradleProjectSettings>
|
||||
<option name="testRunner" value="GRADLE" />
|
||||
<option name="distributionType" value="DEFAULT_WRAPPED" />
|
||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||
<option name="gradleJvm" value="17" />
|
||||
<option name="gradleJvm" value="#GRADLE_LOCAL_JAVA_HOME" />
|
||||
<option name="modules">
|
||||
<set>
|
||||
<option value="$PROJECT_DIR$" />
|
||||
<option value="$PROJECT_DIR$/app" />
|
||||
</set>
|
||||
</option>
|
||||
<option name="resolveExternalAnnotations" value="false" />
|
||||
</GradleProjectSettings>
|
||||
</option>
|
||||
</component>
|
||||
|
||||
@@ -3,13 +3,16 @@ apply plugin: 'kotlin-android'
|
||||
apply plugin: 'kotlin-parcelize'
|
||||
|
||||
android {
|
||||
compileSdk 34
|
||||
buildToolsVersion "34.0.0"
|
||||
compileSdk = 34
|
||||
buildToolsVersion = "34.0.0"
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion 24
|
||||
targetSdkVersion 34
|
||||
|
||||
versionCode 24
|
||||
versionName '3.7.0'
|
||||
|
||||
testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
|
||||
|
||||
javaCompileOptions {
|
||||
@@ -22,21 +25,17 @@ android {
|
||||
}
|
||||
}
|
||||
|
||||
flavorDimensions "default"
|
||||
flavorDimensions += "default"
|
||||
|
||||
productFlavors {
|
||||
tempo {
|
||||
dimension "default"
|
||||
dimension = "default"
|
||||
applicationId 'com.cappielloantonio.tempo'
|
||||
versionCode 22
|
||||
versionName '3.5.7'
|
||||
}
|
||||
|
||||
notquitemy {
|
||||
dimension "default"
|
||||
dimension = "default"
|
||||
applicationId "com.cappielloantonio.notquitemy.tempo"
|
||||
versionCode 1
|
||||
versionName "1.0.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,38 +65,36 @@ android {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||
|
||||
// AndroidX
|
||||
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
|
||||
implementation 'androidx.coordinatorlayout:coordinatorlayout:1.2.0'
|
||||
implementation 'androidx.preference:preference-ktx:1.2.1'
|
||||
implementation 'androidx.navigation:navigation-fragment-ktx:2.7.2'
|
||||
implementation 'androidx.navigation:navigation-ui-ktx:2.7.2'
|
||||
implementation 'androidx.recyclerview:recyclerview:1.3.1'
|
||||
implementation 'androidx.room:room-runtime:2.5.2'
|
||||
implementation 'androidx.navigation:navigation-fragment-ktx:2.7.6'
|
||||
implementation 'androidx.navigation:navigation-ui-ktx:2.7.6'
|
||||
implementation 'androidx.recyclerview:recyclerview:1.3.2'
|
||||
implementation 'androidx.room:room-runtime:2.6.1'
|
||||
implementation 'androidx.core:core-splashscreen:1.0.1'
|
||||
implementation "androidx.appcompat:appcompat:1.6.1"
|
||||
|
||||
// Android Material
|
||||
implementation 'com.google.android.material:material:1.9.0'
|
||||
implementation 'com.google.android.material:material:1.10.0'
|
||||
|
||||
// Glide
|
||||
implementation 'com.github.bumptech.glide:glide:4.16.0'
|
||||
implementation 'com.github.bumptech.glide:annotations:4.16.0'
|
||||
|
||||
// Media3
|
||||
implementation 'androidx.media3:media3-session:1.1.1'
|
||||
implementation 'androidx.media3:media3-common:1.1.1'
|
||||
implementation 'androidx.media3:media3-exoplayer:1.1.1'
|
||||
implementation 'androidx.media3:media3-ui:1.1.1'
|
||||
tempoImplementation 'androidx.media3:media3-cast:1.1.1'
|
||||
implementation 'androidx.media3:media3-session:1.2.1'
|
||||
implementation 'androidx.media3:media3-common:1.2.1'
|
||||
implementation 'androidx.media3:media3-exoplayer:1.2.1'
|
||||
implementation 'androidx.media3:media3-ui:1.2.1'
|
||||
tempoImplementation 'androidx.media3:media3-cast:1.2.1'
|
||||
|
||||
annotationProcessor 'com.github.bumptech.glide:compiler:4.16.0'
|
||||
annotationProcessor 'androidx.room:room-compiler:2.5.2'
|
||||
annotationProcessor 'androidx.room:room-compiler:2.6.1'
|
||||
|
||||
// Retrofit
|
||||
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
|
||||
implementation 'com.squareup.okhttp3:logging-interceptor:5.0.0-alpha.11'
|
||||
implementation 'com.squareup.okhttp3:logging-interceptor:5.0.0-alpha.12'
|
||||
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
|
||||
}
|
||||
@@ -0,0 +1,997 @@
|
||||
{
|
||||
"formatVersion": 1,
|
||||
"database": {
|
||||
"version": 4,
|
||||
"identityHash": "528d037bee0f0575f8e0670ae1b04e00",
|
||||
"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": []
|
||||
},
|
||||
{
|
||||
"tableName": "session_media_item",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `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": "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, '528d037bee0f0575f8e0670ae1b04e00')"
|
||||
]
|
||||
}
|
||||
}
|
||||
1004
app/schemas/com.cappielloantonio.tempo.database.AppDatabase/5.json
Normal file
1004
app/schemas/com.cappielloantonio.tempo.database.AppDatabase/5.json
Normal file
File diff suppressed because it is too large
Load Diff
1016
app/schemas/com.cappielloantonio.tempo.database.AppDatabase/6.json
Normal file
1016
app/schemas/com.cappielloantonio.tempo.database.AppDatabase/6.json
Normal file
File diff suppressed because it is too large
Load Diff
1021
app/schemas/com.cappielloantonio.tempo.database.AppDatabase/7.json
Normal file
1021
app/schemas/com.cappielloantonio.tempo.database.AppDatabase/7.json
Normal file
File diff suppressed because it is too large
Load Diff
1021
app/schemas/com.cappielloantonio.tempo.database.AppDatabase/8.json
Normal file
1021
app/schemas/com.cappielloantonio.tempo.database.AppDatabase/8.json
Normal file
File diff suppressed because it is too large
Load Diff
@@ -16,11 +16,17 @@
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:localeConfig="@xml/locale_config"
|
||||
android:networkSecurityConfig="@xml/network_security_config"
|
||||
android:roundIcon="@mipmap/ic_launcher"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/AppTheme.SplashScreen"
|
||||
android:usesCleartextTraffic="true">
|
||||
|
||||
<!-- Declare that this session demo supports Android Auto. -->
|
||||
<meta-data
|
||||
android:name="com.google.android.gms.car.application"
|
||||
android:resource="@xml/auto_app_desc" />
|
||||
|
||||
<meta-data
|
||||
android:name="com.google.android.gms.cast.framework.OPTIONS_PROVIDER_CLASS_NAME"
|
||||
android:value="androidx.media3.cast.DefaultCastOptionsProvider" />
|
||||
@@ -45,7 +51,8 @@
|
||||
android:foregroundServiceType="mediaPlayback">
|
||||
<intent-filter>
|
||||
<action android:name="androidx.media3.session.MediaLibraryService" />
|
||||
<action android:name="androidx.media3.session.MediaBrowserService" />
|
||||
<action android:name="android.media.browse.MediaBrowserService"/>
|
||||
<action android:name="android.media.action.MEDIA_PLAY_FROM_SEARCH"/>
|
||||
</intent-filter>
|
||||
</service>
|
||||
|
||||
|
||||
@@ -14,17 +14,19 @@ import com.cappielloantonio.tempo.database.dao.FavoriteDao;
|
||||
import com.cappielloantonio.tempo.database.dao.QueueDao;
|
||||
import com.cappielloantonio.tempo.database.dao.RecentSearchDao;
|
||||
import com.cappielloantonio.tempo.database.dao.ServerDao;
|
||||
import com.cappielloantonio.tempo.database.dao.SessionMediaItemDao;
|
||||
import com.cappielloantonio.tempo.model.Chronology;
|
||||
import com.cappielloantonio.tempo.model.Download;
|
||||
import com.cappielloantonio.tempo.model.Favorite;
|
||||
import com.cappielloantonio.tempo.model.Queue;
|
||||
import com.cappielloantonio.tempo.model.RecentSearch;
|
||||
import com.cappielloantonio.tempo.model.Server;
|
||||
import com.cappielloantonio.tempo.model.SessionMediaItem;
|
||||
|
||||
@Database(
|
||||
version = 3,
|
||||
entities = {Queue.class, Server.class, RecentSearch.class, Download.class, Chronology.class, Favorite.class},
|
||||
autoMigrations = {@AutoMigration(from = 2, to = 3)}
|
||||
version = 8,
|
||||
entities = {Queue.class, Server.class, RecentSearch.class, Download.class, Chronology.class, Favorite.class, SessionMediaItem.class},
|
||||
autoMigrations = {@AutoMigration(from = 7, to = 8)}
|
||||
)
|
||||
@TypeConverters({DateConverters.class})
|
||||
public abstract class AppDatabase extends RoomDatabase {
|
||||
@@ -52,4 +54,6 @@ public abstract class AppDatabase extends RoomDatabase {
|
||||
public abstract ChronologyDao chronologyDao();
|
||||
|
||||
public abstract FavoriteDao favoriteDao();
|
||||
|
||||
public abstract SessionMediaItemDao sessionMediaItemDao();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.cappielloantonio.tempo.database.dao;
|
||||
|
||||
import androidx.room.Dao;
|
||||
import androidx.room.Insert;
|
||||
import androidx.room.OnConflictStrategy;
|
||||
import androidx.room.Query;
|
||||
|
||||
import com.cappielloantonio.tempo.model.Queue;
|
||||
import com.cappielloantonio.tempo.model.SessionMediaItem;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Dao
|
||||
public interface SessionMediaItemDao {
|
||||
@Query("SELECT * FROM session_media_item WHERE id = :id")
|
||||
SessionMediaItem get(String id);
|
||||
|
||||
@Query("SELECT * FROM session_media_item WHERE timestamp = :timestamp")
|
||||
List<SessionMediaItem> get(long timestamp);
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.IGNORE)
|
||||
void insert(SessionMediaItem sessionMediaItem);
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.IGNORE)
|
||||
void insertAll(List<SessionMediaItem> sessionMediaItems);
|
||||
|
||||
@Query("DELETE FROM session_media_item")
|
||||
void deleteAll();
|
||||
}
|
||||
@@ -0,0 +1,281 @@
|
||||
package com.cappielloantonio.tempo.model
|
||||
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import androidx.annotation.Keep
|
||||
import androidx.media3.common.MediaItem
|
||||
import androidx.media3.common.MediaItem.RequestMetadata
|
||||
import androidx.media3.common.MediaMetadata
|
||||
import androidx.media3.common.MimeTypes
|
||||
import androidx.media3.common.util.UnstableApi
|
||||
import androidx.room.ColumnInfo
|
||||
import androidx.room.Entity
|
||||
import androidx.room.PrimaryKey
|
||||
import com.cappielloantonio.tempo.glide.CustomGlideRequest
|
||||
import com.cappielloantonio.tempo.subsonic.models.Child
|
||||
import com.cappielloantonio.tempo.subsonic.models.InternetRadioStation
|
||||
import com.cappielloantonio.tempo.subsonic.models.PodcastEpisode
|
||||
import com.cappielloantonio.tempo.util.Constants
|
||||
import com.cappielloantonio.tempo.util.MusicUtil
|
||||
import com.cappielloantonio.tempo.util.Preferences.getImageSize
|
||||
import java.util.Date
|
||||
|
||||
@UnstableApi
|
||||
@Keep
|
||||
@Entity(tableName = "session_media_item")
|
||||
class SessionMediaItem() {
|
||||
@PrimaryKey(autoGenerate = true)
|
||||
@ColumnInfo(name = "index")
|
||||
var index: Int = 0
|
||||
|
||||
@ColumnInfo(name = "id")
|
||||
var id: String? = null
|
||||
|
||||
@ColumnInfo(name = "parent_id")
|
||||
var parentId: String? = null
|
||||
|
||||
@ColumnInfo(name = "is_dir")
|
||||
var isDir: Boolean = false
|
||||
|
||||
@ColumnInfo
|
||||
var title: String? = null
|
||||
|
||||
@ColumnInfo
|
||||
var album: String? = null
|
||||
|
||||
@ColumnInfo
|
||||
var artist: String? = null
|
||||
|
||||
@ColumnInfo
|
||||
var track: Int? = null
|
||||
|
||||
@ColumnInfo
|
||||
var year: Int? = null
|
||||
|
||||
@ColumnInfo
|
||||
var genre: String? = null
|
||||
|
||||
@ColumnInfo(name = "cover_art_id")
|
||||
var coverArtId: String? = null
|
||||
|
||||
@ColumnInfo
|
||||
var size: Long? = null
|
||||
|
||||
@ColumnInfo(name = "content_type")
|
||||
var contentType: String? = null
|
||||
|
||||
@ColumnInfo
|
||||
var suffix: String? = null
|
||||
|
||||
@ColumnInfo("transcoding_content_type")
|
||||
var transcodedContentType: String? = null
|
||||
|
||||
@ColumnInfo(name = "transcoded_suffix")
|
||||
var transcodedSuffix: String? = null
|
||||
|
||||
@ColumnInfo
|
||||
var duration: Int? = null
|
||||
|
||||
@ColumnInfo("bitrate")
|
||||
var bitrate: Int? = null
|
||||
|
||||
@ColumnInfo
|
||||
var path: String? = null
|
||||
|
||||
@ColumnInfo(name = "is_video")
|
||||
var isVideo: Boolean = false
|
||||
|
||||
@ColumnInfo(name = "user_rating")
|
||||
var userRating: Int? = null
|
||||
|
||||
@ColumnInfo(name = "average_rating")
|
||||
var averageRating: Double? = null
|
||||
|
||||
@ColumnInfo(name = "play_count")
|
||||
var playCount: Long? = null
|
||||
|
||||
@ColumnInfo(name = "disc_number")
|
||||
var discNumber: Int? = null
|
||||
|
||||
@ColumnInfo
|
||||
var created: Date? = null
|
||||
|
||||
@ColumnInfo
|
||||
var starred: Date? = null
|
||||
|
||||
@ColumnInfo(name = "album_id")
|
||||
var albumId: String? = null
|
||||
|
||||
@ColumnInfo(name = "artist_id")
|
||||
var artistId: String? = null
|
||||
|
||||
@ColumnInfo
|
||||
var type: String? = null
|
||||
|
||||
@ColumnInfo(name = "bookmark_position")
|
||||
var bookmarkPosition: Long? = null
|
||||
|
||||
@ColumnInfo(name = "original_width")
|
||||
var originalWidth: Int? = null
|
||||
|
||||
@ColumnInfo(name = "original_height")
|
||||
var originalHeight: Int? = null
|
||||
|
||||
@ColumnInfo(name = "stream_id")
|
||||
var streamId: String? = null
|
||||
|
||||
@ColumnInfo(name = "stream_url")
|
||||
var streamUrl: String? = null
|
||||
|
||||
@ColumnInfo(name = "timestamp")
|
||||
var timestamp: Long? = null
|
||||
|
||||
constructor(child: Child) : this() {
|
||||
id = child.id
|
||||
parentId = child.parentId
|
||||
isDir = child.isDir
|
||||
title = child.title
|
||||
album = child.album
|
||||
artist = child.artist
|
||||
track = child.track
|
||||
year = child.year
|
||||
genre = child.genre
|
||||
coverArtId = child.coverArtId
|
||||
size = child.size
|
||||
contentType = child.contentType
|
||||
suffix = child.suffix
|
||||
transcodedContentType = child.transcodedContentType
|
||||
transcodedSuffix = child.transcodedSuffix
|
||||
duration = child.duration
|
||||
bitrate = child.bitrate
|
||||
path = child.path
|
||||
isVideo = child.isVideo
|
||||
userRating = child.userRating
|
||||
averageRating = child.averageRating
|
||||
playCount = child.playCount
|
||||
discNumber = child.discNumber
|
||||
created = child.created
|
||||
starred = child.starred
|
||||
albumId = child.albumId
|
||||
artistId = child.artistId
|
||||
type = Constants.MEDIA_TYPE_MUSIC
|
||||
bookmarkPosition = child.bookmarkPosition
|
||||
originalWidth = child.originalWidth
|
||||
originalHeight = child.originalHeight
|
||||
}
|
||||
|
||||
constructor(podcastEpisode: PodcastEpisode) : this() {
|
||||
id = podcastEpisode.id
|
||||
parentId = podcastEpisode.parentId
|
||||
isDir = podcastEpisode.isDir
|
||||
title = podcastEpisode.title
|
||||
album = podcastEpisode.album
|
||||
artist = podcastEpisode.artist
|
||||
year = podcastEpisode.year
|
||||
genre = podcastEpisode.genre
|
||||
coverArtId = podcastEpisode.coverArtId
|
||||
size = podcastEpisode.size
|
||||
contentType = podcastEpisode.contentType
|
||||
suffix = podcastEpisode.suffix
|
||||
duration = podcastEpisode.duration
|
||||
bitrate = podcastEpisode.bitrate
|
||||
path = podcastEpisode.path
|
||||
isVideo = podcastEpisode.isVideo
|
||||
created = podcastEpisode.created
|
||||
artistId = podcastEpisode.artistId
|
||||
streamId = podcastEpisode.streamId
|
||||
type = Constants.MEDIA_TYPE_PODCAST
|
||||
}
|
||||
|
||||
constructor(internetRadioStation: InternetRadioStation) : this() {
|
||||
id = internetRadioStation.id
|
||||
title = internetRadioStation.name
|
||||
streamUrl = internetRadioStation.streamUrl
|
||||
type = Constants.MEDIA_TYPE_RADIO
|
||||
}
|
||||
|
||||
fun getMediaItem(): MediaItem {
|
||||
val uri: Uri = getStreamUri()
|
||||
val artworkUri = Uri.parse(CustomGlideRequest.createUrl(coverArtId, getImageSize()))
|
||||
|
||||
val bundle = Bundle()
|
||||
bundle.putString("id", id)
|
||||
bundle.putString("parentId", parentId)
|
||||
bundle.putBoolean("isDir", isDir)
|
||||
bundle.putString("title", title)
|
||||
bundle.putString("album", album)
|
||||
bundle.putString("artist", artist)
|
||||
bundle.putInt("track", track ?: 0)
|
||||
bundle.putInt("year", year ?: 0)
|
||||
bundle.putString("genre", genre)
|
||||
bundle.putString("coverArtId", coverArtId)
|
||||
bundle.putLong("size", size ?: 0)
|
||||
bundle.putString("contentType", contentType)
|
||||
bundle.putString("suffix", suffix)
|
||||
bundle.putString("transcodedContentType", transcodedContentType)
|
||||
bundle.putString("transcodedSuffix", transcodedSuffix)
|
||||
bundle.putInt("duration", duration ?: 0)
|
||||
bundle.putInt("bitrate", bitrate ?: 0)
|
||||
bundle.putString("path", path)
|
||||
bundle.putBoolean("isVideo", isVideo)
|
||||
bundle.putInt("userRating", userRating ?: 0)
|
||||
bundle.putDouble("averageRating", averageRating ?: .0)
|
||||
bundle.putLong("playCount", playCount ?: 0)
|
||||
bundle.putInt("discNumber", discNumber ?: 0)
|
||||
bundle.putLong("created", created?.time ?: 0)
|
||||
bundle.putLong("starred", starred?.time ?: 0)
|
||||
bundle.putString("albumId", albumId)
|
||||
bundle.putString("artistId", artistId)
|
||||
bundle.putString("type", Constants.MEDIA_TYPE_MUSIC)
|
||||
bundle.putLong("bookmarkPosition", bookmarkPosition ?: 0)
|
||||
bundle.putInt("originalWidth", originalWidth ?: 0)
|
||||
bundle.putInt("originalHeight", originalHeight ?: 0)
|
||||
bundle.putString("uri", uri.toString())
|
||||
|
||||
return MediaItem.Builder()
|
||||
.setMediaId(id!!)
|
||||
.setMediaMetadata(
|
||||
MediaMetadata.Builder()
|
||||
.setTitle(MusicUtil.getReadableString(title))
|
||||
.setTrackNumber(track ?: 0)
|
||||
.setDiscNumber(discNumber ?: 0)
|
||||
.setReleaseYear(year ?: 0)
|
||||
.setAlbumTitle(MusicUtil.getReadableString(album))
|
||||
.setArtist(MusicUtil.getReadableString(artist))
|
||||
.setArtworkUri(artworkUri)
|
||||
.setExtras(bundle)
|
||||
.setIsBrowsable(false)
|
||||
.setIsPlayable(true)
|
||||
.build()
|
||||
)
|
||||
.setRequestMetadata(
|
||||
RequestMetadata.Builder()
|
||||
.setMediaUri(uri)
|
||||
.setExtras(bundle)
|
||||
.build()
|
||||
)
|
||||
.setMimeType(MimeTypes.BASE_TYPE_AUDIO)
|
||||
.setUri(uri)
|
||||
.build()
|
||||
}
|
||||
|
||||
private fun getStreamUri(): Uri {
|
||||
return when (type) {
|
||||
Constants.MEDIA_TYPE_MUSIC -> {
|
||||
MusicUtil.getStreamUri(id)
|
||||
}
|
||||
|
||||
Constants.MEDIA_TYPE_PODCAST -> {
|
||||
MusicUtil.getStreamUri(streamId)
|
||||
}
|
||||
|
||||
Constants.MEDIA_TYPE_RADIO -> {
|
||||
Uri.parse(streamUrl)
|
||||
}
|
||||
|
||||
else -> {
|
||||
MusicUtil.getStreamUri(id)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,957 @@
|
||||
package com.cappielloantonio.tempo.repository;
|
||||
|
||||
|
||||
import android.net.Uri;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.OptIn;
|
||||
import androidx.media3.common.MediaItem;
|
||||
import androidx.media3.common.MediaMetadata;
|
||||
import androidx.media3.common.util.UnstableApi;
|
||||
import androidx.media3.session.LibraryResult;
|
||||
|
||||
import com.cappielloantonio.tempo.App;
|
||||
import com.cappielloantonio.tempo.database.AppDatabase;
|
||||
import com.cappielloantonio.tempo.database.dao.SessionMediaItemDao;
|
||||
import com.cappielloantonio.tempo.glide.CustomGlideRequest;
|
||||
import com.cappielloantonio.tempo.model.SessionMediaItem;
|
||||
import com.cappielloantonio.tempo.subsonic.base.ApiResponse;
|
||||
import com.cappielloantonio.tempo.subsonic.models.AlbumID3;
|
||||
import com.cappielloantonio.tempo.subsonic.models.Artist;
|
||||
import com.cappielloantonio.tempo.subsonic.models.ArtistID3;
|
||||
import com.cappielloantonio.tempo.subsonic.models.Child;
|
||||
import com.cappielloantonio.tempo.subsonic.models.Directory;
|
||||
import com.cappielloantonio.tempo.subsonic.models.Index;
|
||||
import com.cappielloantonio.tempo.subsonic.models.InternetRadioStation;
|
||||
import com.cappielloantonio.tempo.subsonic.models.MusicFolder;
|
||||
import com.cappielloantonio.tempo.subsonic.models.Playlist;
|
||||
import com.cappielloantonio.tempo.subsonic.models.PodcastEpisode;
|
||||
import com.cappielloantonio.tempo.util.MappingUtil;
|
||||
import com.cappielloantonio.tempo.util.MusicUtil;
|
||||
import com.cappielloantonio.tempo.util.Preferences;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
import com.google.common.util.concurrent.SettableFuture;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import retrofit2.Call;
|
||||
import retrofit2.Callback;
|
||||
import retrofit2.Response;
|
||||
|
||||
public class AutomotiveRepository {
|
||||
private final SessionMediaItemDao sessionMediaItemDao = AppDatabase.getInstance().sessionMediaItemDao();
|
||||
|
||||
public ListenableFuture<LibraryResult<ImmutableList<MediaItem>>> getAlbums(String prefix, String type, int size) {
|
||||
final SettableFuture<LibraryResult<ImmutableList<MediaItem>>> listenableFuture = SettableFuture.create();
|
||||
|
||||
App.getSubsonicClientInstance(false)
|
||||
.getAlbumSongListClient()
|
||||
.getAlbumList2(type, size, 0, null, null)
|
||||
.enqueue(new Callback<ApiResponse>() {
|
||||
@Override
|
||||
public void onResponse(@NonNull Call<ApiResponse> call, @NonNull Response<ApiResponse> response) {
|
||||
if (response.isSuccessful() && response.body() != null && response.body().getSubsonicResponse().getAlbumList2() != null && response.body().getSubsonicResponse().getAlbumList2().getAlbums() != null) {
|
||||
List<AlbumID3> albums = response.body().getSubsonicResponse().getAlbumList2().getAlbums();
|
||||
|
||||
List<MediaItem> mediaItems = new ArrayList<>();
|
||||
|
||||
for (AlbumID3 album : albums) {
|
||||
Uri artworkUri = Uri.parse(CustomGlideRequest.createUrl(album.getCoverArtId(), Preferences.getImageSize()));
|
||||
|
||||
MediaMetadata mediaMetadata = new MediaMetadata.Builder()
|
||||
.setTitle(album.getName())
|
||||
.setAlbumTitle(album.getName())
|
||||
.setArtist(album.getArtist())
|
||||
.setGenre(album.getGenre())
|
||||
.setIsBrowsable(true)
|
||||
.setIsPlayable(false)
|
||||
.setMediaType(MediaMetadata.MEDIA_TYPE_ALBUM)
|
||||
.setArtworkUri(artworkUri)
|
||||
.build();
|
||||
|
||||
MediaItem mediaItem = new MediaItem.Builder()
|
||||
.setMediaId(prefix + album.getId())
|
||||
.setMediaMetadata(mediaMetadata)
|
||||
.setUri("")
|
||||
.build();
|
||||
|
||||
mediaItems.add(mediaItem);
|
||||
}
|
||||
|
||||
LibraryResult<ImmutableList<MediaItem>> libraryResult = LibraryResult.ofItemList(ImmutableList.copyOf(mediaItems), null);
|
||||
|
||||
listenableFuture.set(libraryResult);
|
||||
} else {
|
||||
listenableFuture.set(LibraryResult.ofError(LibraryResult.RESULT_ERROR_BAD_VALUE));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NonNull Call<ApiResponse> call, @NonNull Throwable t) {
|
||||
listenableFuture.setException(t);
|
||||
}
|
||||
});
|
||||
|
||||
return listenableFuture;
|
||||
}
|
||||
|
||||
public ListenableFuture<LibraryResult<ImmutableList<MediaItem>>> getStarredSongs() {
|
||||
final SettableFuture<LibraryResult<ImmutableList<MediaItem>>> listenableFuture = SettableFuture.create();
|
||||
|
||||
App.getSubsonicClientInstance(false)
|
||||
.getAlbumSongListClient()
|
||||
.getStarred2()
|
||||
.enqueue(new Callback<ApiResponse>() {
|
||||
@Override
|
||||
public void onResponse(@NonNull Call<ApiResponse> call, @NonNull Response<ApiResponse> response) {
|
||||
if (response.isSuccessful() && response.body() != null && response.body().getSubsonicResponse().getStarred2() != null && response.body().getSubsonicResponse().getStarred2().getSongs() != null) {
|
||||
List<Child> songs = response.body().getSubsonicResponse().getStarred2().getSongs();
|
||||
|
||||
setChildrenMetadata(songs);
|
||||
|
||||
List<MediaItem> mediaItems = MappingUtil.mapMediaItems(songs);
|
||||
|
||||
LibraryResult<ImmutableList<MediaItem>> libraryResult = LibraryResult.ofItemList(ImmutableList.copyOf(mediaItems), null);
|
||||
|
||||
listenableFuture.set(libraryResult);
|
||||
} else {
|
||||
listenableFuture.set(LibraryResult.ofError(LibraryResult.RESULT_ERROR_BAD_VALUE));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NonNull Call<ApiResponse> call, @NonNull Throwable t) {
|
||||
listenableFuture.setException(t);
|
||||
}
|
||||
});
|
||||
|
||||
return listenableFuture;
|
||||
}
|
||||
|
||||
public ListenableFuture<LibraryResult<ImmutableList<MediaItem>>> getStarredAlbums(String prefix) {
|
||||
final SettableFuture<LibraryResult<ImmutableList<MediaItem>>> listenableFuture = SettableFuture.create();
|
||||
|
||||
App.getSubsonicClientInstance(false)
|
||||
.getAlbumSongListClient()
|
||||
.getStarred2()
|
||||
.enqueue(new Callback<ApiResponse>() {
|
||||
@Override
|
||||
public void onResponse(@NonNull Call<ApiResponse> call, @NonNull Response<ApiResponse> response) {
|
||||
if (response.isSuccessful() && response.body() != null && response.body().getSubsonicResponse().getStarred2() != null && response.body().getSubsonicResponse().getStarred2().getAlbums() != null) {
|
||||
List<AlbumID3> albums = response.body().getSubsonicResponse().getStarred2().getAlbums();
|
||||
|
||||
List<MediaItem> mediaItems = new ArrayList<>();
|
||||
|
||||
for (AlbumID3 album : albums) {
|
||||
Uri artworkUri = Uri.parse(CustomGlideRequest.createUrl(album.getCoverArtId(), Preferences.getImageSize()));
|
||||
|
||||
MediaMetadata mediaMetadata = new MediaMetadata.Builder()
|
||||
.setTitle(album.getName())
|
||||
.setArtist(album.getArtist())
|
||||
.setGenre(album.getGenre())
|
||||
.setIsBrowsable(true)
|
||||
.setIsPlayable(false)
|
||||
.setMediaType(MediaMetadata.MEDIA_TYPE_ALBUM)
|
||||
.setArtworkUri(artworkUri)
|
||||
.build();
|
||||
|
||||
MediaItem mediaItem = new MediaItem.Builder()
|
||||
.setMediaId(prefix + album.getId())
|
||||
.setMediaMetadata(mediaMetadata)
|
||||
.setUri("")
|
||||
.build();
|
||||
|
||||
mediaItems.add(mediaItem);
|
||||
}
|
||||
|
||||
LibraryResult<ImmutableList<MediaItem>> libraryResult = LibraryResult.ofItemList(ImmutableList.copyOf(mediaItems), null);
|
||||
|
||||
listenableFuture.set(libraryResult);
|
||||
} else {
|
||||
listenableFuture.set(LibraryResult.ofError(LibraryResult.RESULT_ERROR_BAD_VALUE));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NonNull Call<ApiResponse> call, @NonNull Throwable t) {
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
return listenableFuture;
|
||||
}
|
||||
|
||||
public ListenableFuture<LibraryResult<ImmutableList<MediaItem>>> getStarredArtists(String prefix) {
|
||||
final SettableFuture<LibraryResult<ImmutableList<MediaItem>>> listenableFuture = SettableFuture.create();
|
||||
|
||||
App.getSubsonicClientInstance(false)
|
||||
.getAlbumSongListClient()
|
||||
.getStarred2()
|
||||
.enqueue(new Callback<ApiResponse>() {
|
||||
@Override
|
||||
public void onResponse(@NonNull Call<ApiResponse> call, @NonNull Response<ApiResponse> response) {
|
||||
if (response.isSuccessful() && response.body() != null && response.body().getSubsonicResponse().getStarred2() != null && response.body().getSubsonicResponse().getStarred2().getArtists() != null) {
|
||||
List<ArtistID3> artists = response.body().getSubsonicResponse().getStarred2().getArtists();
|
||||
|
||||
Collections.shuffle(artists);
|
||||
|
||||
List<MediaItem> mediaItems = new ArrayList<>();
|
||||
|
||||
for (ArtistID3 artist : artists) {
|
||||
Uri artworkUri = Uri.parse(CustomGlideRequest.createUrl(artist.getCoverArtId(), Preferences.getImageSize()));
|
||||
|
||||
MediaMetadata mediaMetadata = new MediaMetadata.Builder()
|
||||
.setTitle(artist.getName())
|
||||
.setIsBrowsable(true)
|
||||
.setIsPlayable(false)
|
||||
.setMediaType(MediaMetadata.MEDIA_TYPE_PLAYLIST)
|
||||
.setArtworkUri(artworkUri)
|
||||
.build();
|
||||
|
||||
MediaItem mediaItem = new MediaItem.Builder()
|
||||
.setMediaId(prefix + artist.getId())
|
||||
.setMediaMetadata(mediaMetadata)
|
||||
.setUri("")
|
||||
.build();
|
||||
|
||||
mediaItems.add(mediaItem);
|
||||
}
|
||||
|
||||
LibraryResult<ImmutableList<MediaItem>> libraryResult = LibraryResult.ofItemList(ImmutableList.copyOf(mediaItems), null);
|
||||
|
||||
listenableFuture.set(libraryResult);
|
||||
} else {
|
||||
listenableFuture.set(LibraryResult.ofError(LibraryResult.RESULT_ERROR_BAD_VALUE));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NonNull Call<ApiResponse> call, @NonNull Throwable t) {
|
||||
listenableFuture.setException(t);
|
||||
}
|
||||
});
|
||||
|
||||
return listenableFuture;
|
||||
}
|
||||
|
||||
public ListenableFuture<LibraryResult<ImmutableList<MediaItem>>> getMusicFolders(String prefix) {
|
||||
final SettableFuture<LibraryResult<ImmutableList<MediaItem>>> listenableFuture = SettableFuture.create();
|
||||
|
||||
App.getSubsonicClientInstance(false)
|
||||
.getBrowsingClient()
|
||||
.getMusicFolders()
|
||||
.enqueue(new Callback<ApiResponse>() {
|
||||
@Override
|
||||
public void onResponse(@NonNull Call<ApiResponse> call, @NonNull Response<ApiResponse> response) {
|
||||
if (response.isSuccessful() && response.body() != null && response.body().getSubsonicResponse().getMusicFolders() != null && response.body().getSubsonicResponse().getMusicFolders().getMusicFolders() != null) {
|
||||
List<MusicFolder> musicFolders = response.body().getSubsonicResponse().getMusicFolders().getMusicFolders();
|
||||
|
||||
List<MediaItem> mediaItems = new ArrayList<>();
|
||||
|
||||
for (MusicFolder musicFolder : musicFolders) {
|
||||
MediaMetadata mediaMetadata = new MediaMetadata.Builder()
|
||||
.setTitle(musicFolder.getName())
|
||||
.setIsBrowsable(true)
|
||||
.setIsPlayable(false)
|
||||
.setMediaType(MediaMetadata.MEDIA_TYPE_FOLDER_MIXED)
|
||||
.build();
|
||||
|
||||
MediaItem mediaItem = new MediaItem.Builder()
|
||||
.setMediaId(prefix + musicFolder.getId())
|
||||
.setMediaMetadata(mediaMetadata)
|
||||
.setUri("")
|
||||
.build();
|
||||
|
||||
mediaItems.add(mediaItem);
|
||||
}
|
||||
|
||||
LibraryResult<ImmutableList<MediaItem>> libraryResult = LibraryResult.ofItemList(ImmutableList.copyOf(mediaItems), null);
|
||||
|
||||
listenableFuture.set(libraryResult);
|
||||
} else {
|
||||
listenableFuture.set(LibraryResult.ofError(LibraryResult.RESULT_ERROR_BAD_VALUE));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NonNull Call<ApiResponse> call, @NonNull Throwable t) {
|
||||
listenableFuture.setException(t);
|
||||
}
|
||||
});
|
||||
|
||||
return listenableFuture;
|
||||
}
|
||||
|
||||
public ListenableFuture<LibraryResult<ImmutableList<MediaItem>>> getIndexes(String prefix, String id) {
|
||||
final SettableFuture<LibraryResult<ImmutableList<MediaItem>>> listenableFuture = SettableFuture.create();
|
||||
|
||||
App.getSubsonicClientInstance(false)
|
||||
.getBrowsingClient()
|
||||
.getIndexes(id, null)
|
||||
.enqueue(new Callback<ApiResponse>() {
|
||||
@Override
|
||||
public void onResponse(@NonNull Call<ApiResponse> call, @NonNull Response<ApiResponse> response) {
|
||||
if (response.isSuccessful() && response.body() != null && response.body().getSubsonicResponse().getIndexes() != null) {
|
||||
List<MediaItem> mediaItems = new ArrayList<>();
|
||||
|
||||
if (response.body().getSubsonicResponse().getIndexes().getIndices() != null) {
|
||||
List<Index> indices = response.body().getSubsonicResponse().getIndexes().getIndices();
|
||||
|
||||
for (Index index : indices) {
|
||||
if (index.getArtists() != null) {
|
||||
for (Artist artist : index.getArtists()) {
|
||||
MediaMetadata mediaMetadata = new MediaMetadata.Builder()
|
||||
.setTitle(artist.getName())
|
||||
.setIsBrowsable(true)
|
||||
.setIsPlayable(false)
|
||||
.setMediaType(MediaMetadata.MEDIA_TYPE_ARTIST)
|
||||
.build();
|
||||
|
||||
MediaItem mediaItem = new MediaItem.Builder()
|
||||
.setMediaId(prefix + artist.getId())
|
||||
.setMediaMetadata(mediaMetadata)
|
||||
.setUri("")
|
||||
.build();
|
||||
|
||||
mediaItems.add(mediaItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (response.body().getSubsonicResponse().getIndexes().getChildren() != null) {
|
||||
List<Child> children = response.body().getSubsonicResponse().getIndexes().getChildren();
|
||||
|
||||
for (Child song : children) {
|
||||
Uri artworkUri = Uri.parse(CustomGlideRequest.createUrl(song.getCoverArtId(), Preferences.getImageSize()));
|
||||
|
||||
MediaMetadata mediaMetadata = new MediaMetadata.Builder()
|
||||
.setTitle(song.getTitle())
|
||||
.setAlbumTitle(song.getAlbum())
|
||||
.setArtist(song.getArtist())
|
||||
.setIsBrowsable(false)
|
||||
.setIsPlayable(true)
|
||||
.setMediaType(MediaMetadata.MEDIA_TYPE_MUSIC)
|
||||
.setArtworkUri(artworkUri)
|
||||
.build();
|
||||
|
||||
MediaItem mediaItem = new MediaItem.Builder()
|
||||
.setMediaId(prefix + song.getId())
|
||||
.setMediaMetadata(mediaMetadata)
|
||||
.setUri(MusicUtil.getStreamUri(song.getId()))
|
||||
.build();
|
||||
|
||||
mediaItems.add(mediaItem);
|
||||
}
|
||||
|
||||
setChildrenMetadata(children);
|
||||
}
|
||||
|
||||
LibraryResult<ImmutableList<MediaItem>> libraryResult = LibraryResult.ofItemList(ImmutableList.copyOf(mediaItems), null);
|
||||
|
||||
listenableFuture.set(libraryResult);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NonNull Call<ApiResponse> call, @NonNull Throwable t) {
|
||||
listenableFuture.setException(t);
|
||||
}
|
||||
});
|
||||
|
||||
return listenableFuture;
|
||||
}
|
||||
|
||||
public ListenableFuture<LibraryResult<ImmutableList<MediaItem>>> getDirectories(String prefix, String id) {
|
||||
final SettableFuture<LibraryResult<ImmutableList<MediaItem>>> listenableFuture = SettableFuture.create();
|
||||
|
||||
App.getSubsonicClientInstance(false)
|
||||
.getBrowsingClient()
|
||||
.getMusicDirectory(id)
|
||||
.enqueue(new Callback<ApiResponse>() {
|
||||
@Override
|
||||
public void onResponse(@NonNull Call<ApiResponse> call, @NonNull Response<ApiResponse> response) {
|
||||
if (response.isSuccessful() && response.body() != null && response.body().getSubsonicResponse().getDirectory() != null && response.body().getSubsonicResponse().getDirectory().getChildren() != null) {
|
||||
Directory directory = response.body().getSubsonicResponse().getDirectory();
|
||||
|
||||
List<MediaItem> mediaItems = new ArrayList<>();
|
||||
|
||||
for (Child child : directory.getChildren()) {
|
||||
Uri artworkUri = Uri.parse(CustomGlideRequest.createUrl(child.getCoverArtId(), Preferences.getImageSize()));
|
||||
|
||||
MediaMetadata mediaMetadata = new MediaMetadata.Builder()
|
||||
.setTitle(child.getTitle())
|
||||
.setIsBrowsable(child.isDir())
|
||||
.setIsPlayable(!child.isDir())
|
||||
.setMediaType(MediaMetadata.MEDIA_TYPE_FOLDER_MIXED)
|
||||
.setArtworkUri(artworkUri)
|
||||
.build();
|
||||
|
||||
MediaItem mediaItem = new MediaItem.Builder()
|
||||
.setMediaId(child.isDir() ? prefix + child.getId() : child.getId())
|
||||
.setMediaMetadata(mediaMetadata)
|
||||
.setUri(!child.isDir() ? MusicUtil.getStreamUri(child.getId()) : Uri.parse(""))
|
||||
.build();
|
||||
|
||||
mediaItems.add(mediaItem);
|
||||
}
|
||||
|
||||
setChildrenMetadata(directory.getChildren().stream().filter(child -> !child.isDir()).collect(Collectors.toList()));
|
||||
|
||||
LibraryResult<ImmutableList<MediaItem>> libraryResult = LibraryResult.ofItemList(ImmutableList.copyOf(mediaItems), null);
|
||||
|
||||
listenableFuture.set(libraryResult);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NonNull Call<ApiResponse> call, @NonNull Throwable t) {
|
||||
listenableFuture.setException(t);
|
||||
}
|
||||
});
|
||||
|
||||
return listenableFuture;
|
||||
}
|
||||
|
||||
public ListenableFuture<LibraryResult<ImmutableList<MediaItem>>> getPlaylists(String prefix) {
|
||||
final SettableFuture<LibraryResult<ImmutableList<MediaItem>>> listenableFuture = SettableFuture.create();
|
||||
|
||||
App.getSubsonicClientInstance(false)
|
||||
.getPlaylistClient()
|
||||
.getPlaylists()
|
||||
.enqueue(new Callback<ApiResponse>() {
|
||||
@Override
|
||||
public void onResponse(@NonNull Call<ApiResponse> call, @NonNull Response<ApiResponse> response) {
|
||||
if (response.isSuccessful() && response.body() != null && response.body().getSubsonicResponse().getPlaylists() != null && response.body().getSubsonicResponse().getPlaylists().getPlaylists() != null) {
|
||||
List<Playlist> playlists = response.body().getSubsonicResponse().getPlaylists().getPlaylists();
|
||||
|
||||
List<MediaItem> mediaItems = new ArrayList<>();
|
||||
|
||||
for (Playlist playlist : playlists) {
|
||||
MediaMetadata mediaMetadata = new MediaMetadata.Builder()
|
||||
.setTitle(playlist.getName())
|
||||
.setIsBrowsable(true)
|
||||
.setIsPlayable(false)
|
||||
.setMediaType(MediaMetadata.MEDIA_TYPE_PLAYLIST)
|
||||
.build();
|
||||
|
||||
MediaItem mediaItem = new MediaItem.Builder()
|
||||
.setMediaId(prefix + playlist.getId())
|
||||
.setMediaMetadata(mediaMetadata)
|
||||
.setUri("")
|
||||
.build();
|
||||
|
||||
mediaItems.add(mediaItem);
|
||||
}
|
||||
|
||||
LibraryResult<ImmutableList<MediaItem>> libraryResult = LibraryResult.ofItemList(ImmutableList.copyOf(mediaItems), null);
|
||||
|
||||
listenableFuture.set(libraryResult);
|
||||
} else {
|
||||
listenableFuture.set(LibraryResult.ofError(LibraryResult.RESULT_ERROR_BAD_VALUE));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NonNull Call<ApiResponse> call, @NonNull Throwable t) {
|
||||
listenableFuture.setException(t);
|
||||
}
|
||||
});
|
||||
|
||||
return listenableFuture;
|
||||
}
|
||||
|
||||
public ListenableFuture<LibraryResult<ImmutableList<MediaItem>>> getNewestPodcastEpisodes(int count) {
|
||||
final SettableFuture<LibraryResult<ImmutableList<MediaItem>>> listenableFuture = SettableFuture.create();
|
||||
|
||||
App.getSubsonicClientInstance(false)
|
||||
.getPodcastClient()
|
||||
.getNewestPodcasts(count)
|
||||
.enqueue(new Callback<ApiResponse>() {
|
||||
@Override
|
||||
public void onResponse(@NonNull Call<ApiResponse> call, @NonNull Response<ApiResponse> response) {
|
||||
if (response.isSuccessful() && response.body() != null && response.body().getSubsonicResponse().getNewestPodcasts() != null && response.body().getSubsonicResponse().getNewestPodcasts().getEpisodes() != null) {
|
||||
List<PodcastEpisode> episodes = response.body().getSubsonicResponse().getNewestPodcasts().getEpisodes();
|
||||
|
||||
List<MediaItem> mediaItems = new ArrayList<>();
|
||||
|
||||
for (PodcastEpisode episode : episodes) {
|
||||
Uri artworkUri = Uri.parse(CustomGlideRequest.createUrl(episode.getCoverArtId(), Preferences.getImageSize()));
|
||||
|
||||
MediaMetadata mediaMetadata = new MediaMetadata.Builder()
|
||||
.setTitle(episode.getTitle())
|
||||
.setIsBrowsable(false)
|
||||
.setIsPlayable(true)
|
||||
.setMediaType(MediaMetadata.MEDIA_TYPE_PODCAST_EPISODE)
|
||||
.setArtworkUri(artworkUri)
|
||||
.build();
|
||||
|
||||
MediaItem mediaItem = new MediaItem.Builder()
|
||||
.setMediaId(episode.getId())
|
||||
.setMediaMetadata(mediaMetadata)
|
||||
.setUri(MusicUtil.getStreamUri(episode.getStreamId()))
|
||||
.build();
|
||||
|
||||
mediaItems.add(mediaItem);
|
||||
}
|
||||
|
||||
setPodcastEpisodesMetadata(episodes);
|
||||
|
||||
LibraryResult<ImmutableList<MediaItem>> libraryResult = LibraryResult.ofItemList(ImmutableList.copyOf(mediaItems), null);
|
||||
|
||||
listenableFuture.set(libraryResult);
|
||||
} else {
|
||||
listenableFuture.set(LibraryResult.ofError(LibraryResult.RESULT_ERROR_BAD_VALUE));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NonNull Call<ApiResponse> call, @NonNull Throwable t) {
|
||||
listenableFuture.setException(t);
|
||||
}
|
||||
});
|
||||
|
||||
return listenableFuture;
|
||||
}
|
||||
|
||||
public ListenableFuture<LibraryResult<ImmutableList<MediaItem>>> getInternetRadioStations() {
|
||||
final SettableFuture<LibraryResult<ImmutableList<MediaItem>>> listenableFuture = SettableFuture.create();
|
||||
|
||||
App.getSubsonicClientInstance(false)
|
||||
.getInternetRadioClient()
|
||||
.getInternetRadioStations()
|
||||
.enqueue(new Callback<ApiResponse>() {
|
||||
@Override
|
||||
public void onResponse(@NonNull Call<ApiResponse> call, @NonNull Response<ApiResponse> response) {
|
||||
if (response.isSuccessful() && response.body() != null && response.body().getSubsonicResponse().getInternetRadioStations() != null && response.body().getSubsonicResponse().getInternetRadioStations().getInternetRadioStations() != null) {
|
||||
|
||||
List<InternetRadioStation> radioStations = response.body().getSubsonicResponse().getInternetRadioStations().getInternetRadioStations();
|
||||
|
||||
List<MediaItem> mediaItems = new ArrayList<>();
|
||||
|
||||
for (InternetRadioStation radioStation : radioStations) {
|
||||
MediaMetadata mediaMetadata = new MediaMetadata.Builder()
|
||||
.setTitle(radioStation.getName())
|
||||
.setIsBrowsable(false)
|
||||
.setIsPlayable(true)
|
||||
.setMediaType(MediaMetadata.MEDIA_TYPE_RADIO_STATION)
|
||||
.build();
|
||||
|
||||
MediaItem mediaItem = new MediaItem.Builder()
|
||||
.setMediaId(radioStation.getId())
|
||||
.setMediaMetadata(mediaMetadata)
|
||||
.setUri(radioStation.getStreamUrl())
|
||||
.build();
|
||||
|
||||
mediaItems.add(mediaItem);
|
||||
}
|
||||
|
||||
setInternetRadioStationsMetadata(radioStations);
|
||||
|
||||
LibraryResult<ImmutableList<MediaItem>> libraryResult = LibraryResult.ofItemList(ImmutableList.copyOf(mediaItems), null);
|
||||
|
||||
listenableFuture.set(libraryResult);
|
||||
} else {
|
||||
listenableFuture.set(LibraryResult.ofError(LibraryResult.RESULT_ERROR_BAD_VALUE));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NonNull Call<ApiResponse> call, @NonNull Throwable t) {
|
||||
listenableFuture.setException(t);
|
||||
}
|
||||
});
|
||||
|
||||
return listenableFuture;
|
||||
}
|
||||
|
||||
public ListenableFuture<LibraryResult<ImmutableList<MediaItem>>> getAlbumTracks(String id) {
|
||||
final SettableFuture<LibraryResult<ImmutableList<MediaItem>>> listenableFuture = SettableFuture.create();
|
||||
|
||||
App.getSubsonicClientInstance(false)
|
||||
.getBrowsingClient()
|
||||
.getAlbum(id)
|
||||
.enqueue(new Callback<ApiResponse>() {
|
||||
@Override
|
||||
public void onResponse(@NonNull Call<ApiResponse> call, @NonNull Response<ApiResponse> response) {
|
||||
if (response.isSuccessful() && response.body() != null && response.body().getSubsonicResponse().getAlbum() != null && response.body().getSubsonicResponse().getAlbum().getSongs() != null) {
|
||||
List<Child> tracks = response.body().getSubsonicResponse().getAlbum().getSongs();
|
||||
|
||||
setChildrenMetadata(tracks);
|
||||
|
||||
List<MediaItem> mediaItems = MappingUtil.mapMediaItems(tracks);
|
||||
|
||||
LibraryResult<ImmutableList<MediaItem>> libraryResult = LibraryResult.ofItemList(ImmutableList.copyOf(mediaItems), null);
|
||||
|
||||
listenableFuture.set(libraryResult);
|
||||
} else {
|
||||
listenableFuture.set(LibraryResult.ofError(LibraryResult.RESULT_ERROR_BAD_VALUE));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NonNull Call<ApiResponse> call, @NonNull Throwable t) {
|
||||
listenableFuture.setException(t);
|
||||
}
|
||||
});
|
||||
|
||||
return listenableFuture;
|
||||
}
|
||||
|
||||
public ListenableFuture<LibraryResult<ImmutableList<MediaItem>>> getArtistAlbum(String prefix, String id) {
|
||||
final SettableFuture<LibraryResult<ImmutableList<MediaItem>>> listenableFuture = SettableFuture.create();
|
||||
|
||||
App.getSubsonicClientInstance(false)
|
||||
.getBrowsingClient()
|
||||
.getArtist(id)
|
||||
.enqueue(new Callback<ApiResponse>() {
|
||||
@Override
|
||||
public void onResponse(@NonNull Call<ApiResponse> call, @NonNull Response<ApiResponse> response) {
|
||||
if (response.isSuccessful() && response.body() != null && response.body().getSubsonicResponse().getArtist() != null && response.body().getSubsonicResponse().getArtist().getAlbums() != null) {
|
||||
List<AlbumID3> albums = response.body().getSubsonicResponse().getArtist().getAlbums();
|
||||
|
||||
List<MediaItem> mediaItems = new ArrayList<>();
|
||||
|
||||
for (AlbumID3 album : albums) {
|
||||
Uri artworkUri = Uri.parse(CustomGlideRequest.createUrl(album.getCoverArtId(), Preferences.getImageSize()));
|
||||
|
||||
MediaMetadata mediaMetadata = new MediaMetadata.Builder()
|
||||
.setTitle(album.getName())
|
||||
.setAlbumTitle(album.getName())
|
||||
.setArtist(album.getArtist())
|
||||
.setGenre(album.getGenre())
|
||||
.setIsBrowsable(true)
|
||||
.setIsPlayable(false)
|
||||
.setMediaType(MediaMetadata.MEDIA_TYPE_ALBUM)
|
||||
.setArtworkUri(artworkUri)
|
||||
.build();
|
||||
|
||||
MediaItem mediaItem = new MediaItem.Builder()
|
||||
.setMediaId(prefix + album.getId())
|
||||
.setMediaMetadata(mediaMetadata)
|
||||
.setUri("")
|
||||
.build();
|
||||
|
||||
mediaItems.add(mediaItem);
|
||||
}
|
||||
|
||||
LibraryResult<ImmutableList<MediaItem>> libraryResult = LibraryResult.ofItemList(ImmutableList.copyOf(mediaItems), null);
|
||||
|
||||
listenableFuture.set(libraryResult);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NonNull Call<ApiResponse> call, @NonNull Throwable t) {
|
||||
listenableFuture.setException(t);
|
||||
}
|
||||
});
|
||||
|
||||
return listenableFuture;
|
||||
}
|
||||
|
||||
public ListenableFuture<LibraryResult<ImmutableList<MediaItem>>> getPlaylistSongs(String id) {
|
||||
final SettableFuture<LibraryResult<ImmutableList<MediaItem>>> listenableFuture = SettableFuture.create();
|
||||
|
||||
App.getSubsonicClientInstance(false)
|
||||
.getPlaylistClient()
|
||||
.getPlaylist(id)
|
||||
.enqueue(new Callback<ApiResponse>() {
|
||||
@Override
|
||||
public void onResponse(@NonNull Call<ApiResponse> call, @NonNull Response<ApiResponse> response) {
|
||||
if (response.isSuccessful() && response.body() != null && response.body().getSubsonicResponse().getPlaylist() != null && response.body().getSubsonicResponse().getPlaylist().getEntries() != null) {
|
||||
List<Child> tracks = response.body().getSubsonicResponse().getPlaylist().getEntries();
|
||||
|
||||
setChildrenMetadata(tracks);
|
||||
|
||||
List<MediaItem> mediaItems = MappingUtil.mapMediaItems(tracks);
|
||||
|
||||
LibraryResult<ImmutableList<MediaItem>> libraryResult = LibraryResult.ofItemList(ImmutableList.copyOf(mediaItems), null);
|
||||
|
||||
listenableFuture.set(libraryResult);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NonNull Call<ApiResponse> call, @NonNull Throwable t) {
|
||||
listenableFuture.setException(t);
|
||||
}
|
||||
});
|
||||
|
||||
return listenableFuture;
|
||||
}
|
||||
|
||||
public ListenableFuture<LibraryResult<ImmutableList<MediaItem>>> getMadeForYou(String id, int count) {
|
||||
final SettableFuture<LibraryResult<ImmutableList<MediaItem>>> listenableFuture = SettableFuture.create();
|
||||
|
||||
App.getSubsonicClientInstance(false)
|
||||
.getBrowsingClient()
|
||||
.getSimilarSongs2(id, count)
|
||||
.enqueue(new Callback<ApiResponse>() {
|
||||
@Override
|
||||
public void onResponse(@NonNull Call<ApiResponse> call, @NonNull Response<ApiResponse> response) {
|
||||
if (response.isSuccessful() && response.body() != null && response.body().getSubsonicResponse().getSimilarSongs2() != null && response.body().getSubsonicResponse().getSimilarSongs2().getSongs() != null) {
|
||||
List<Child> tracks = response.body().getSubsonicResponse().getSimilarSongs2().getSongs();
|
||||
|
||||
setChildrenMetadata(tracks);
|
||||
|
||||
List<MediaItem> mediaItems = MappingUtil.mapMediaItems(tracks);
|
||||
|
||||
LibraryResult<ImmutableList<MediaItem>> libraryResult = LibraryResult.ofItemList(ImmutableList.copyOf(mediaItems), null);
|
||||
|
||||
listenableFuture.set(libraryResult);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NonNull Call<ApiResponse> call, @NonNull Throwable t) {
|
||||
listenableFuture.setException(t);
|
||||
}
|
||||
});
|
||||
|
||||
return listenableFuture;
|
||||
}
|
||||
|
||||
public ListenableFuture<LibraryResult<ImmutableList<MediaItem>>> search(String query, String albumPrefix, String artistPrefix) {
|
||||
final SettableFuture<LibraryResult<ImmutableList<MediaItem>>> listenableFuture = SettableFuture.create();
|
||||
|
||||
App.getSubsonicClientInstance(false)
|
||||
.getSearchingClient()
|
||||
.search3(query, 20, 20, 20)
|
||||
.enqueue(new Callback<ApiResponse>() {
|
||||
@Override
|
||||
public void onResponse(@NonNull Call<ApiResponse> call, @NonNull Response<ApiResponse> response) {
|
||||
if (response.isSuccessful() && response.body() != null && response.body().getSubsonicResponse().getSearchResult3() != null) {
|
||||
List<MediaItem> mediaItems = new ArrayList<>();
|
||||
|
||||
if (response.body().getSubsonicResponse().getSearchResult3().getArtists() != null) {
|
||||
for (ArtistID3 artist : response.body().getSubsonicResponse().getSearchResult3().getArtists()) {
|
||||
Uri artworkUri = Uri.parse(CustomGlideRequest.createUrl(artist.getCoverArtId(), Preferences.getImageSize()));
|
||||
|
||||
MediaMetadata mediaMetadata = new MediaMetadata.Builder()
|
||||
.setTitle(artist.getName())
|
||||
.setIsBrowsable(true)
|
||||
.setIsPlayable(false)
|
||||
.setMediaType(MediaMetadata.MEDIA_TYPE_PLAYLIST)
|
||||
.setArtworkUri(artworkUri)
|
||||
.build();
|
||||
|
||||
MediaItem mediaItem = new MediaItem.Builder()
|
||||
.setMediaId(artistPrefix + artist.getId())
|
||||
.setMediaMetadata(mediaMetadata)
|
||||
.setUri("")
|
||||
.build();
|
||||
|
||||
mediaItems.add(mediaItem);
|
||||
}
|
||||
}
|
||||
|
||||
if (response.body().getSubsonicResponse().getSearchResult3().getAlbums() != null) {
|
||||
for (AlbumID3 album : response.body().getSubsonicResponse().getSearchResult3().getAlbums()) {
|
||||
Uri artworkUri = Uri.parse(CustomGlideRequest.createUrl(album.getCoverArtId(), Preferences.getImageSize()));
|
||||
|
||||
MediaMetadata mediaMetadata = new MediaMetadata.Builder()
|
||||
.setTitle(album.getName())
|
||||
.setAlbumTitle(album.getName())
|
||||
.setArtist(album.getArtist())
|
||||
.setGenre(album.getGenre())
|
||||
.setIsBrowsable(true)
|
||||
.setIsPlayable(false)
|
||||
.setMediaType(MediaMetadata.MEDIA_TYPE_ALBUM)
|
||||
.setArtworkUri(artworkUri)
|
||||
.build();
|
||||
|
||||
MediaItem mediaItem = new MediaItem.Builder()
|
||||
.setMediaId(albumPrefix + album.getId())
|
||||
.setMediaMetadata(mediaMetadata)
|
||||
.setUri("")
|
||||
.build();
|
||||
|
||||
mediaItems.add(mediaItem);
|
||||
}
|
||||
}
|
||||
|
||||
if (response.body().getSubsonicResponse().getSearchResult3().getSongs() != null) {
|
||||
List<Child> tracks = response.body().getSubsonicResponse().getSearchResult3().getSongs();
|
||||
setChildrenMetadata(tracks);
|
||||
mediaItems.addAll(MappingUtil.mapMediaItems(tracks));
|
||||
}
|
||||
|
||||
LibraryResult<ImmutableList<MediaItem>> libraryResult = LibraryResult.ofItemList(ImmutableList.copyOf(mediaItems), null);
|
||||
|
||||
listenableFuture.set(libraryResult);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NonNull Call<ApiResponse> call, @NonNull Throwable t) {
|
||||
listenableFuture.setException(t);
|
||||
}
|
||||
});
|
||||
|
||||
return listenableFuture;
|
||||
}
|
||||
|
||||
@OptIn(markerClass = UnstableApi.class)
|
||||
public void setChildrenMetadata(List<Child> children) {
|
||||
long timestamp = System.currentTimeMillis();
|
||||
ArrayList<SessionMediaItem> sessionMediaItems = new ArrayList<>();
|
||||
|
||||
for (Child child : children) {
|
||||
SessionMediaItem sessionMediaItem = new SessionMediaItem(child);
|
||||
sessionMediaItem.setTimestamp(timestamp);
|
||||
sessionMediaItems.add(sessionMediaItem);
|
||||
}
|
||||
|
||||
InsertAllThreadSafe insertAll = new InsertAllThreadSafe(sessionMediaItemDao, sessionMediaItems);
|
||||
Thread thread = new Thread(insertAll);
|
||||
thread.start();
|
||||
}
|
||||
|
||||
@OptIn(markerClass = UnstableApi.class)
|
||||
public void setPodcastEpisodesMetadata(List<PodcastEpisode> podcastEpisodes) {
|
||||
long timestamp = System.currentTimeMillis();
|
||||
ArrayList<SessionMediaItem> sessionMediaItems = new ArrayList<>();
|
||||
|
||||
for (PodcastEpisode podcastEpisode : podcastEpisodes) {
|
||||
SessionMediaItem sessionMediaItem = new SessionMediaItem(podcastEpisode);
|
||||
sessionMediaItem.setTimestamp(timestamp);
|
||||
sessionMediaItems.add(sessionMediaItem);
|
||||
}
|
||||
|
||||
InsertAllThreadSafe insertAll = new InsertAllThreadSafe(sessionMediaItemDao, sessionMediaItems);
|
||||
Thread thread = new Thread(insertAll);
|
||||
thread.start();
|
||||
}
|
||||
|
||||
@OptIn(markerClass = UnstableApi.class)
|
||||
public void setInternetRadioStationsMetadata(List<InternetRadioStation> internetRadioStations) {
|
||||
long timestamp = System.currentTimeMillis();
|
||||
ArrayList<SessionMediaItem> sessionMediaItems = new ArrayList<>();
|
||||
|
||||
for (InternetRadioStation internetRadioStation : internetRadioStations) {
|
||||
SessionMediaItem sessionMediaItem = new SessionMediaItem(internetRadioStation);
|
||||
sessionMediaItem.setTimestamp(timestamp);
|
||||
sessionMediaItems.add(sessionMediaItem);
|
||||
}
|
||||
|
||||
InsertAllThreadSafe insertAll = new InsertAllThreadSafe(sessionMediaItemDao, sessionMediaItems);
|
||||
Thread thread = new Thread(insertAll);
|
||||
thread.start();
|
||||
}
|
||||
|
||||
public SessionMediaItem getSessionMediaItem(String id) {
|
||||
SessionMediaItem sessionMediaItem = null;
|
||||
|
||||
GetMediaItemThreadSafe getMediaItemThreadSafe = new GetMediaItemThreadSafe(sessionMediaItemDao, id);
|
||||
Thread thread = new Thread(getMediaItemThreadSafe);
|
||||
thread.start();
|
||||
|
||||
try {
|
||||
thread.join();
|
||||
sessionMediaItem = getMediaItemThreadSafe.getSessionMediaItem();
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return sessionMediaItem;
|
||||
}
|
||||
|
||||
public List<MediaItem> getMetadatas(long timestamp) {
|
||||
List<MediaItem> mediaItems = Collections.emptyList();
|
||||
|
||||
GetMediaItemsThreadSafe getMediaItemsThreadSafe = new GetMediaItemsThreadSafe(sessionMediaItemDao, timestamp);
|
||||
Thread thread = new Thread(getMediaItemsThreadSafe);
|
||||
thread.start();
|
||||
|
||||
try {
|
||||
thread.join();
|
||||
mediaItems = getMediaItemsThreadSafe.getMediaItems();
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return mediaItems;
|
||||
}
|
||||
|
||||
public void deleteMetadata() {
|
||||
DeleteAllThreadSafe delete = new DeleteAllThreadSafe(sessionMediaItemDao);
|
||||
Thread thread = new Thread(delete);
|
||||
thread.start();
|
||||
}
|
||||
|
||||
private static class GetMediaItemThreadSafe implements Runnable {
|
||||
private final SessionMediaItemDao sessionMediaItemDao;
|
||||
private final String id;
|
||||
|
||||
private SessionMediaItem sessionMediaItem;
|
||||
|
||||
public GetMediaItemThreadSafe(SessionMediaItemDao sessionMediaItemDao, String id) {
|
||||
this.sessionMediaItemDao = sessionMediaItemDao;
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
sessionMediaItem = sessionMediaItemDao.get(id);
|
||||
}
|
||||
|
||||
public SessionMediaItem getSessionMediaItem() {
|
||||
return sessionMediaItem;
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(markerClass = UnstableApi.class)
|
||||
private static class GetMediaItemsThreadSafe implements Runnable {
|
||||
private final SessionMediaItemDao sessionMediaItemDao;
|
||||
private final Long timestamp;
|
||||
private final List<MediaItem> mediaItems = new ArrayList<>();
|
||||
|
||||
public GetMediaItemsThreadSafe(SessionMediaItemDao sessionMediaItemDao, Long timestamp) {
|
||||
this.sessionMediaItemDao = sessionMediaItemDao;
|
||||
this.timestamp = timestamp;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
List<SessionMediaItem> sessionMediaItems = sessionMediaItemDao.get(timestamp);
|
||||
sessionMediaItems.forEach(sessionMediaItem -> mediaItems.add(sessionMediaItem.getMediaItem()));
|
||||
}
|
||||
|
||||
public List<MediaItem> getMediaItems() {
|
||||
return mediaItems;
|
||||
}
|
||||
}
|
||||
|
||||
private static class InsertAllThreadSafe implements Runnable {
|
||||
private final SessionMediaItemDao sessionMediaItemDao;
|
||||
private final List<SessionMediaItem> sessionMediaItems;
|
||||
|
||||
public InsertAllThreadSafe(SessionMediaItemDao sessionMediaItemDao, List<SessionMediaItem> sessionMediaItems) {
|
||||
this.sessionMediaItemDao = sessionMediaItemDao;
|
||||
this.sessionMediaItems = sessionMediaItems;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
sessionMediaItemDao.insertAll(sessionMediaItems);
|
||||
}
|
||||
}
|
||||
|
||||
private static class DeleteAllThreadSafe implements Runnable {
|
||||
private final SessionMediaItemDao sessionMediaItemDao;
|
||||
|
||||
public DeleteAllThreadSafe(SessionMediaItemDao sessionMediaItemDao) {
|
||||
this.sessionMediaItemDao = sessionMediaItemDao;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
sessionMediaItemDao.deleteAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,9 @@ import com.cappielloantonio.tempo.subsonic.base.ApiResponse;
|
||||
import com.cappielloantonio.tempo.subsonic.models.Genre;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import retrofit2.Call;
|
||||
import retrofit2.Callback;
|
||||
@@ -39,7 +41,7 @@ public class GenreRepository {
|
||||
if (size != -1) {
|
||||
genres.setValue(genreList.subList(0, Math.min(size, genreList.size())));
|
||||
} else {
|
||||
genres.setValue(genreList);
|
||||
genres.setValue(genreList.stream().sorted(Comparator.comparing(Genre::getGenre)).collect(Collectors.toList()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
package com.cappielloantonio.tempo.repository;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.lifecycle.MutableLiveData;
|
||||
|
||||
|
||||
@@ -49,12 +49,14 @@ public class SharingRepository {
|
||||
public void onResponse(@NonNull Call<ApiResponse> call, @NonNull Response<ApiResponse> response) {
|
||||
if (response.isSuccessful() && response.body() != null && response.body().getSubsonicResponse().getShares() != null && response.body().getSubsonicResponse().getShares().getShares() != null && response.body().getSubsonicResponse().getShares().getShares().get(0) != null) {
|
||||
share.setValue(response.body().getSubsonicResponse().getShares().getShares().get(0));
|
||||
} else {
|
||||
share.setValue(null);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NonNull Call<ApiResponse> call, @NonNull Throwable t) {
|
||||
|
||||
share.setValue(null);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -104,10 +104,10 @@ public class SongRepository {
|
||||
return randomSongsSample;
|
||||
}
|
||||
|
||||
public void scrobble(String id) {
|
||||
public void scrobble(String id, boolean submission) {
|
||||
App.getSubsonicClientInstance(false)
|
||||
.getMediaAnnotationClient()
|
||||
.scrobble(id)
|
||||
.scrobble(id, submission)
|
||||
.enqueue(new Callback<ApiResponse>() {
|
||||
@Override
|
||||
public void onResponse(@NonNull Call<ApiResponse> call, @NonNull Response<ApiResponse> response) {
|
||||
|
||||
@@ -31,9 +31,10 @@ public class DownloaderManager {
|
||||
|
||||
private final Context context;
|
||||
private final DataSource.Factory dataSourceFactory;
|
||||
private final HashMap<String, Download> downloads;
|
||||
private final DownloadIndex downloadIndex;
|
||||
|
||||
private static HashMap<String, Download> downloads;
|
||||
|
||||
public DownloaderManager(Context context, DataSource.Factory dataSourceFactory, DownloadManager downloadManager) {
|
||||
this.context = context.getApplicationContext();
|
||||
this.dataSourceFactory = dataSourceFactory;
|
||||
@@ -61,19 +62,11 @@ public class DownloaderManager {
|
||||
}
|
||||
|
||||
public boolean isDownloaded(MediaItem mediaItem) {
|
||||
@Nullable Download download = downloads.get(mediaItem.mediaId);
|
||||
return download != null && download.state != Download.STATE_FAILED;
|
||||
return isDownloaded(mediaItem.mediaId);
|
||||
}
|
||||
|
||||
public boolean areDownloaded(List<MediaItem> mediaItems) {
|
||||
for (MediaItem mediaItem : mediaItems) {
|
||||
@Nullable Download download = downloads.get(mediaItem.mediaId);
|
||||
if (download != null && download.state != Download.STATE_FAILED) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
return mediaItems.stream().anyMatch(this::isDownloaded);
|
||||
}
|
||||
|
||||
public void download(MediaItem mediaItem, com.cappielloantonio.tempo.model.Download download) {
|
||||
@@ -92,6 +85,7 @@ public class DownloaderManager {
|
||||
public void remove(MediaItem mediaItem, com.cappielloantonio.tempo.model.Download download) {
|
||||
DownloadService.sendRemoveDownload(context, DownloaderService.class, buildDownloadRequest(mediaItem).id, false);
|
||||
deleteDatabase(download.getId());
|
||||
downloads.remove(download.getId());
|
||||
}
|
||||
|
||||
public void remove(List<MediaItem> mediaItems, List<com.cappielloantonio.tempo.model.Download> downloads) {
|
||||
@@ -122,23 +116,33 @@ public class DownloaderManager {
|
||||
return download != null ? download.getTitle() : null;
|
||||
}
|
||||
|
||||
public static void updateRequestDownload(Download download) {
|
||||
updateDatabase(download.request.id);
|
||||
downloads.put(download.request.id, download);
|
||||
}
|
||||
|
||||
public static void removeRequestDownload(Download download) {
|
||||
deleteDatabase(download.request.id);
|
||||
downloads.remove(download.request.id);
|
||||
}
|
||||
|
||||
private static DownloadRepository getDownloadRepository() {
|
||||
return new DownloadRepository();
|
||||
}
|
||||
|
||||
public static void insertDatabase(com.cappielloantonio.tempo.model.Download download) {
|
||||
private static void insertDatabase(com.cappielloantonio.tempo.model.Download download) {
|
||||
getDownloadRepository().insert(download);
|
||||
}
|
||||
|
||||
public static void deleteDatabase(String id) {
|
||||
private static void deleteDatabase(String id) {
|
||||
getDownloadRepository().delete(id);
|
||||
}
|
||||
|
||||
public static void deleteAllDatabase() {
|
||||
private static void deleteAllDatabase() {
|
||||
getDownloadRepository().deleteAll();
|
||||
}
|
||||
|
||||
public static void updateDatabase(String id) {
|
||||
private static void updateDatabase(String id) {
|
||||
getDownloadRepository().update(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -95,7 +95,7 @@ public class DownloaderService extends androidx.media3.exoplayer.offline.Downloa
|
||||
notification = notificationHelper.buildDownloadCompletedNotification(context, R.drawable.ic_check_circle, null, DownloaderManager.getDownloadNotificationMessage(download.request.id));
|
||||
notification = Notification.Builder.recoverBuilder(context, notification).setGroup(DownloadUtil.DOWNLOAD_NOTIFICATION_SUCCESSFUL_GROUP).build();
|
||||
NotificationUtil.setNotification(this.context, successfulDownloadGroupNotificationId, successfulDownloadGroupNotification);
|
||||
DownloaderManager.updateDatabase(download.request.id);
|
||||
DownloaderManager.updateRequestDownload(download);
|
||||
} else if (download.state == Download.STATE_FAILED) {
|
||||
notification = notificationHelper.buildDownloadFailedNotification(context, R.drawable.ic_error, null, DownloaderManager.getDownloadNotificationMessage(download.request.id));
|
||||
notification = Notification.Builder.recoverBuilder(context, notification).setGroup(DownloadUtil.DOWNLOAD_NOTIFICATION_FAILED_GROUP).build();
|
||||
@@ -109,7 +109,7 @@ public class DownloaderService extends androidx.media3.exoplayer.offline.Downloa
|
||||
|
||||
@Override
|
||||
public void onDownloadRemoved(@NonNull DownloadManager downloadManager, Download download) {
|
||||
DownloaderManager.deleteDatabase(download.request.id);
|
||||
DownloaderManager.removeRequestDownload(download);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import com.cappielloantonio.tempo.subsonic.models.Child;
|
||||
import com.cappielloantonio.tempo.subsonic.models.InternetRadioStation;
|
||||
import com.cappielloantonio.tempo.subsonic.models.PodcastEpisode;
|
||||
import com.cappielloantonio.tempo.util.MappingUtil;
|
||||
import com.cappielloantonio.tempo.util.Preferences;
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
import com.google.common.util.concurrent.MoreExecutors;
|
||||
|
||||
@@ -254,6 +255,21 @@ public class MediaManager {
|
||||
}
|
||||
}
|
||||
|
||||
public static void removeRange(ListenableFuture<MediaBrowser> mediaBrowserListenableFuture, List<Child> media, int fromItem, int toItem) {
|
||||
if (mediaBrowserListenableFuture != null) {
|
||||
mediaBrowserListenableFuture.addListener(() -> {
|
||||
try {
|
||||
if (mediaBrowserListenableFuture.isDone()) {
|
||||
mediaBrowserListenableFuture.get().removeMediaItems(fromItem, toItem);
|
||||
removeRangeDatabase(media, fromItem, toItem);
|
||||
}
|
||||
} catch (ExecutionException | InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}, MoreExecutors.directExecutor());
|
||||
}
|
||||
}
|
||||
|
||||
public static void getCurrentIndex(ListenableFuture<MediaBrowser> mediaBrowserListenableFuture, MediaIndexCallback callback) {
|
||||
if (mediaBrowserListenableFuture != null) {
|
||||
mediaBrowserListenableFuture.addListener(() -> {
|
||||
@@ -277,9 +293,9 @@ public class MediaManager {
|
||||
getQueueRepository().setPlayingPausedTimestamp(mediaItem.mediaId, ms);
|
||||
}
|
||||
|
||||
public static void scrobble(MediaItem mediaItem) {
|
||||
if (mediaItem != null) {
|
||||
getSongRepository().scrobble(mediaItem.mediaMetadata.extras.getString("id"));
|
||||
public static void scrobble(MediaItem mediaItem, boolean submission) {
|
||||
if (mediaItem != null && Preferences.isScrobblingEnabled()) {
|
||||
getSongRepository().scrobble(mediaItem.mediaMetadata.extras.getString("id"), submission);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -320,6 +336,14 @@ public class MediaManager {
|
||||
}
|
||||
}
|
||||
|
||||
private static void removeRangeDatabase(List<Child> media, int fromItem, int toItem) {
|
||||
List<Child> toRemove = media.subList(fromItem, toItem);
|
||||
|
||||
media.removeAll(toRemove);
|
||||
|
||||
getQueueRepository().insertAll(media, true, 0);
|
||||
}
|
||||
|
||||
public static void clearDatabase() {
|
||||
getQueueRepository().deleteAll();
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ import com.cappielloantonio.tempo.subsonic.base.ApiResponse;
|
||||
import retrofit2.Call;
|
||||
|
||||
public class MediaAnnotationClient {
|
||||
private static final String TAG = "BrowsingClient";
|
||||
private static final String TAG = "MediaAnnotationClient";
|
||||
|
||||
private final Subsonic subsonic;
|
||||
private final MediaAnnotationService mediaAnnotationService;
|
||||
@@ -34,8 +34,8 @@ public class MediaAnnotationClient {
|
||||
return mediaAnnotationService.setRating(subsonic.getParams(), id, rating);
|
||||
}
|
||||
|
||||
public Call<ApiResponse> scrobble(String id) {
|
||||
public Call<ApiResponse> scrobble(String id, boolean submission) {
|
||||
Log.d(TAG, "scrobble()");
|
||||
return mediaAnnotationService.scrobble(subsonic.getParams(), id);
|
||||
return mediaAnnotationService.scrobble(subsonic.getParams(), id, submission);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,5 +20,5 @@ public interface MediaAnnotationService {
|
||||
Call<ApiResponse> setRating(@QueryMap Map<String, String> params, @Query("id") String id, @Query("rating") int rating);
|
||||
|
||||
@GET("scrobble")
|
||||
Call<ApiResponse> scrobble(@QueryMap Map<String, String> params, @Query("id") String id);
|
||||
Call<ApiResponse> scrobble(@QueryMap Map<String, String> params, @Query("id") String id, @Query("submission") Boolean submission);
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ import com.cappielloantonio.tempo.subsonic.base.ApiResponse;
|
||||
import retrofit2.Call;
|
||||
|
||||
public class MediaLibraryScanningClient {
|
||||
private static final String TAG = "SystemClient";
|
||||
private static final String TAG = "MediaLibraryScanningClient";
|
||||
|
||||
private final Subsonic subsonic;
|
||||
private final MediaLibraryScanningService mediaLibraryScanningService;
|
||||
|
||||
@@ -9,7 +9,7 @@ import com.cappielloantonio.tempo.subsonic.base.ApiResponse;
|
||||
import retrofit2.Call;
|
||||
|
||||
public class PodcastClient {
|
||||
private static final String TAG = "SystemClient";
|
||||
private static final String TAG = "PodcastClient";
|
||||
|
||||
private final Subsonic subsonic;
|
||||
private final PodcastService podcastService;
|
||||
|
||||
@@ -7,5 +7,5 @@ import com.google.gson.annotations.SerializedName
|
||||
@Keep
|
||||
class ApiResponse {
|
||||
@SerializedName("subsonic-response")
|
||||
lateinit var subsonicResponse: SubsonicResponse;
|
||||
lateinit var subsonicResponse: SubsonicResponse
|
||||
}
|
||||
@@ -14,6 +14,7 @@ class PodcastChannel : Parcelable {
|
||||
var url: String? = null
|
||||
var title: String? = null
|
||||
var description: String? = null
|
||||
@SerializedName("coverArt")
|
||||
var coverArtId: String? = null
|
||||
var originalImageUrl: String? = null
|
||||
var status: String? = null
|
||||
|
||||
@@ -12,12 +12,10 @@ class PodcastEpisode : Parcelable {
|
||||
var id: String? = null
|
||||
@SerializedName("parent")
|
||||
var parentId: String? = null
|
||||
@SerializedName("isDir")
|
||||
var isDir = false
|
||||
var title: String? = null
|
||||
var album: String? = null
|
||||
var artist: String? = null
|
||||
var track: Int? = null
|
||||
var year: Int? = null
|
||||
var genre: String? = null
|
||||
@SerializedName("coverArt")
|
||||
@@ -25,26 +23,14 @@ class PodcastEpisode : Parcelable {
|
||||
var size: Long? = null
|
||||
var contentType: String? = null
|
||||
var suffix: String? = null
|
||||
var transcodedContentType: String? = null
|
||||
var transcodedSuffix: String? = null
|
||||
var duration: Int? = null
|
||||
@SerializedName("bitRate")
|
||||
var bitrate: Int? = null
|
||||
var path: String? = null
|
||||
@SerializedName("isVideo")
|
||||
var isVideo: Boolean = false
|
||||
var userRating: Int? = null
|
||||
var averageRating: Double? = null
|
||||
var playCount: Long? = null
|
||||
var discNumber: Int? = null
|
||||
var created: Date? = null
|
||||
var starred: Date? = null
|
||||
var albumId: String? = null
|
||||
var artistId: String? = null
|
||||
var type: String? = null
|
||||
var bookmarkPosition: Long? = null
|
||||
var originalWidth: Int? = null
|
||||
var originalHeight: Int? = null
|
||||
var streamId: String? = null
|
||||
var channelId: String? = null
|
||||
var description: String? = null
|
||||
|
||||
@@ -94,7 +94,7 @@ public class MainActivity extends BaseActivity {
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
if (bottomSheetBehavior.getState() == BottomSheetBehavior.STATE_EXPANDED)
|
||||
collapseBottomSheet();
|
||||
collapseBottomSheetDelayed();
|
||||
else
|
||||
super.onBackPressed();
|
||||
}
|
||||
@@ -118,9 +118,7 @@ public class MainActivity extends BaseActivity {
|
||||
bottomSheetBehavior.addBottomSheetCallback(bottomSheetCallback);
|
||||
fragmentManager.beginTransaction().replace(R.id.player_bottom_sheet, new PlayerBottomSheetFragment(), "PlayerBottomSheet").commit();
|
||||
|
||||
setBottomSheetInPeek(mainViewModel.isQueueLoaded());
|
||||
|
||||
collapseBottomSheet();
|
||||
checkBottomSheetAfterStateChanged();
|
||||
}
|
||||
|
||||
public void setBottomSheetInPeek(Boolean isVisible) {
|
||||
@@ -139,7 +137,13 @@ public class MainActivity extends BaseActivity {
|
||||
}
|
||||
}
|
||||
|
||||
public void collapseBottomSheet() {
|
||||
private void checkBottomSheetAfterStateChanged() {
|
||||
final Handler handler = new Handler();
|
||||
final Runnable runnable = () -> setBottomSheetInPeek(mainViewModel.isQueueLoaded());
|
||||
handler.postDelayed(runnable, 100);
|
||||
}
|
||||
|
||||
public void collapseBottomSheetDelayed() {
|
||||
final Handler handler = new Handler();
|
||||
final Runnable runnable = () -> bottomSheetBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED);
|
||||
handler.postDelayed(runnable, 100);
|
||||
@@ -163,7 +167,7 @@ public class MainActivity extends BaseActivity {
|
||||
|
||||
switch (state) {
|
||||
case BottomSheetBehavior.STATE_HIDDEN:
|
||||
hideMusicSession();
|
||||
resetMusicSession();
|
||||
break;
|
||||
case BottomSheetBehavior.STATE_COLLAPSED:
|
||||
if (playerBottomSheetFragment != null)
|
||||
|
||||
@@ -6,6 +6,7 @@ import android.content.pm.PackageManager;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.PowerManager;
|
||||
import android.view.WindowManager;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
@@ -37,6 +38,7 @@ public class BaseActivity extends AppCompatActivity {
|
||||
initializeDownloader();
|
||||
checkBatteryOptimization();
|
||||
checkPermission();
|
||||
checkAlwaysOnDisplay();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -66,6 +68,12 @@ public class BaseActivity extends AppCompatActivity {
|
||||
}
|
||||
}
|
||||
|
||||
private void checkAlwaysOnDisplay() {
|
||||
if (Preferences.isDisplayAlwaysOn()) {
|
||||
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean detectBatteryOptimization() {
|
||||
String packageName = getPackageName();
|
||||
PowerManager powerManager = (PowerManager) getSystemService(POWER_SERVICE);
|
||||
|
||||
@@ -23,6 +23,8 @@ import java.util.List;
|
||||
|
||||
public class AlbumCatalogueAdapter extends RecyclerView.Adapter<AlbumCatalogueAdapter.ViewHolder> implements Filterable {
|
||||
private final ClickCallback click;
|
||||
private String currentFilter;
|
||||
|
||||
private final Filter filtering = new Filter() {
|
||||
@Override
|
||||
protected FilterResults performFiltering(CharSequence constraint) {
|
||||
@@ -32,6 +34,7 @@ public class AlbumCatalogueAdapter extends RecyclerView.Adapter<AlbumCatalogueAd
|
||||
filteredList.addAll(albumsFull);
|
||||
} else {
|
||||
String filterPattern = constraint.toString().toLowerCase().trim();
|
||||
currentFilter = filterPattern;
|
||||
|
||||
for (AlbumID3 item : albumsFull) {
|
||||
if (item.getName().toLowerCase().contains(filterPattern)) {
|
||||
@@ -48,8 +51,7 @@ public class AlbumCatalogueAdapter extends RecyclerView.Adapter<AlbumCatalogueAd
|
||||
|
||||
@Override
|
||||
protected void publishResults(CharSequence constraint, FilterResults results) {
|
||||
albums.clear();
|
||||
albums.addAll((List) results.values);
|
||||
albums = (List<AlbumID3>) results.values;
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
};
|
||||
@@ -60,6 +62,8 @@ public class AlbumCatalogueAdapter extends RecyclerView.Adapter<AlbumCatalogueAd
|
||||
public AlbumCatalogueAdapter(ClickCallback click) {
|
||||
this.click = click;
|
||||
this.albums = Collections.emptyList();
|
||||
this.albumsFull = Collections.emptyList();
|
||||
this.currentFilter = "";
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@@ -92,9 +96,8 @@ public class AlbumCatalogueAdapter extends RecyclerView.Adapter<AlbumCatalogueAd
|
||||
}
|
||||
|
||||
public void setItems(List<AlbumID3> albums) {
|
||||
this.albums = albums;
|
||||
this.albumsFull = new ArrayList<>(albums);
|
||||
notifyDataSetChanged();
|
||||
filtering.filter(currentFilter);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -162,4 +165,4 @@ public class AlbumCatalogueAdapter extends RecyclerView.Adapter<AlbumCatalogueAd
|
||||
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -33,6 +33,7 @@ public class DownloadHorizontalAdapter extends RecyclerView.Adapter<DownloadHori
|
||||
private String filterValue;
|
||||
|
||||
private List<Child> songs;
|
||||
private List<Child> shuffling;
|
||||
private List<Child> grouped;
|
||||
|
||||
public DownloadHorizontalAdapter(ClickCallback click) {
|
||||
@@ -82,6 +83,7 @@ public class DownloadHorizontalAdapter extends RecyclerView.Adapter<DownloadHori
|
||||
|
||||
this.songs = songs;
|
||||
this.grouped = groupSong(songs);
|
||||
this.shuffling = shufflingSong(new ArrayList<>(songs));
|
||||
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
@@ -90,6 +92,10 @@ public class DownloadHorizontalAdapter extends RecyclerView.Adapter<DownloadHori
|
||||
return grouped.get(id);
|
||||
}
|
||||
|
||||
public List<Child> getShuffling() {
|
||||
return shuffling;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemViewType(int position) {
|
||||
return position;
|
||||
@@ -136,6 +142,27 @@ public class DownloadHorizontalAdapter extends RecyclerView.Adapter<DownloadHori
|
||||
return songs;
|
||||
}
|
||||
|
||||
private List<Child> shufflingSong(List<Child> songs) {
|
||||
if (filterValue == null) {
|
||||
return songs;
|
||||
}
|
||||
|
||||
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());
|
||||
default:
|
||||
return songs;
|
||||
}
|
||||
}
|
||||
|
||||
private String countSong(String filterKey, String filterValue, List<Child> songs) {
|
||||
if (filterValue != null) {
|
||||
switch (filterKey) {
|
||||
|
||||
@@ -77,6 +77,8 @@ public class MusicDirectoryAdapter extends RecyclerView.Adapter<MusicDirectoryAd
|
||||
item.musicDirectoryTitleTextView.setSelected(true);
|
||||
|
||||
itemView.setOnClickListener(v -> onClick());
|
||||
itemView.setOnLongClickListener(v -> onLongClick());
|
||||
|
||||
item.musicDirectoryMoreButton.setOnClickListener(v -> onClick());
|
||||
}
|
||||
|
||||
@@ -92,5 +94,18 @@ public class MusicDirectoryAdapter extends RecyclerView.Adapter<MusicDirectoryAd
|
||||
click.onMediaClick(bundle);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean onLongClick() {
|
||||
if (!children.get(getBindingAdapterPosition()).isDir()) {
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putParcelable(Constants.TRACK_OBJECT, children.get(getBindingAdapterPosition()));
|
||||
|
||||
click.onMediaLongClick(bundle);
|
||||
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ public class PodcastChannelHorizontalAdapter extends RecyclerView.Adapter<Podcas
|
||||
holder.item.podcastChannelDescriptionTextView.setText(MusicUtil.getReadableString(podcastChannel.getDescription()));
|
||||
|
||||
CustomGlideRequest.Builder
|
||||
.from(holder.itemView.getContext(), podcastChannel.getOriginalImageUrl(), CustomGlideRequest.ResourceType.Podcast)
|
||||
.from(holder.itemView.getContext(), podcastChannel.getCoverArtId(), CustomGlideRequest.ResourceType.Podcast)
|
||||
.build()
|
||||
.into(holder.item.podcastChannelCoverImageView);
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ public class SongHorizontalAdapter extends RecyclerView.Adapter<SongHorizontalAd
|
||||
Child song = songs.get(position);
|
||||
|
||||
holder.item.searchResultSongTitleTextView.setText(MusicUtil.getReadableString(song.getTitle()));
|
||||
holder.item.searchResultSongSubtitleTextView.setText(holder.itemView.getContext().getString(R.string.song_subtitle_formatter, MusicUtil.getReadableString(this.showAlbum ? song.getAlbum() : song.getArtist()), MusicUtil.getReadableDurationString(song.getDuration() != null ? song.getDuration() : 0, false)));
|
||||
holder.item.searchResultSongSubtitleTextView.setText(holder.itemView.getContext().getString(R.string.song_subtitle_formatter, MusicUtil.getReadableString(this.showAlbum ? song.getAlbum() : song.getArtist()), MusicUtil.getReadableDurationString(song.getDuration(), false)));
|
||||
holder.item.trackNumberTextView.setText(MusicUtil.getReadableTrackNumber(holder.itemView.getContext(), song.getTrack()));
|
||||
|
||||
if (DownloadUtil.getDownloadTracker(holder.itemView.getContext()).isDownloaded(song.getId())) {
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package com.cappielloantonio.tempo.ui.dialog;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Dialog;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
@@ -14,38 +13,23 @@ import androidx.media3.common.util.UnstableApi;
|
||||
import com.cappielloantonio.tempo.R;
|
||||
import com.cappielloantonio.tempo.databinding.DialogBatteryOptimizationBinding;
|
||||
import com.cappielloantonio.tempo.util.Preferences;
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||
|
||||
@OptIn(markerClass = UnstableApi.class)
|
||||
public class BatteryOptimizationDialog extends DialogFragment {
|
||||
private static final String TAG = "BatteryOptimizationDialog";
|
||||
|
||||
private DialogBatteryOptimizationBinding bind;
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
bind = DialogBatteryOptimizationBinding.inflate(getLayoutInflater());
|
||||
DialogBatteryOptimizationBinding bind = DialogBatteryOptimizationBinding.inflate(getLayoutInflater());
|
||||
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
|
||||
|
||||
builder.setView(bind.getRoot())
|
||||
return new MaterialAlertDialogBuilder(requireContext())
|
||||
.setView(bind.getRoot())
|
||||
.setTitle(R.string.activity_battery_optimizations_title)
|
||||
.setNeutralButton(R.string.battery_optimization_neutral_button, (dialog, id) -> Preferences.dontAskForOptimization())
|
||||
.setPositiveButton(R.string.battery_optimization_positive_button, (dialog, listener) -> openPowerSettings())
|
||||
.setNeutralButton(R.string.battery_optimization_neutral_button, (dialog, listener) -> Preferences.dontAskForOptimization())
|
||||
.setNegativeButton(R.string.battery_optimization_negative_button, null)
|
||||
.setPositiveButton(R.string.battery_optimization_positive_button, (dialog, id) -> openPowerSettings());
|
||||
|
||||
AlertDialog popup = builder.create();
|
||||
|
||||
popup.setCancelable(false);
|
||||
popup.setCanceledOnTouchOutside(false);
|
||||
|
||||
return popup;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
bind = null;
|
||||
.create();
|
||||
}
|
||||
|
||||
private void openPowerSettings() {
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package com.cappielloantonio.tempo.ui.dialog;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Dialog;
|
||||
import android.os.Bundle;
|
||||
|
||||
@@ -10,22 +9,18 @@ import androidx.fragment.app.DialogFragment;
|
||||
import com.cappielloantonio.tempo.R;
|
||||
import com.cappielloantonio.tempo.databinding.DialogConnectionAlertBinding;
|
||||
import com.cappielloantonio.tempo.util.Preferences;
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public class ConnectionAlertDialog extends DialogFragment {
|
||||
private static final String TAG = "ServerUnreachableDialog";
|
||||
|
||||
private DialogConnectionAlertBinding bind;
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
bind = DialogConnectionAlertBinding.inflate(getLayoutInflater());
|
||||
DialogConnectionAlertBinding bind = DialogConnectionAlertBinding.inflate(getLayoutInflater());
|
||||
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
|
||||
|
||||
builder.setView(bind.getRoot())
|
||||
MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(getActivity())
|
||||
.setView(bind.getRoot())
|
||||
.setTitle(R.string.connection_alert_dialog_title)
|
||||
.setPositiveButton(R.string.connection_alert_dialog_positive_button, (dialog, id) -> dialog.cancel())
|
||||
.setNegativeButton(R.string.connection_alert_dialog_negative_button, (dialog, id) -> dialog.cancel());
|
||||
@@ -45,16 +40,12 @@ public class ConnectionAlertDialog extends DialogFragment {
|
||||
setButtonAction();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
bind = null;
|
||||
}
|
||||
|
||||
private void setButtonAction() {
|
||||
((AlertDialog) Objects.requireNonNull(getDialog())).getButton(AlertDialog.BUTTON_NEUTRAL).setOnClickListener(v -> {
|
||||
androidx.appcompat.app.AlertDialog alertDialog = (androidx.appcompat.app.AlertDialog) Objects.requireNonNull(getDialog());
|
||||
|
||||
alertDialog.getButton(androidx.appcompat.app.AlertDialog.BUTTON_NEUTRAL).setOnClickListener(v -> {
|
||||
Preferences.setDataSavingMode(true);
|
||||
Objects.requireNonNull(getDialog()).dismiss();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
package com.cappielloantonio.tempo.ui.dialog;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Dialog;
|
||||
import android.os.Bundle;
|
||||
import android.widget.Button;
|
||||
@@ -12,26 +11,23 @@ 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;
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||
|
||||
@OptIn(markerClass = UnstableApi.class)
|
||||
public class DeleteDownloadStorageDialog extends DialogFragment {
|
||||
private DialogDeleteDownloadStorageBinding bind;
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
bind = DialogDeleteDownloadStorageBinding.inflate(getLayoutInflater());
|
||||
DialogDeleteDownloadStorageBinding bind = DialogDeleteDownloadStorageBinding.inflate(getLayoutInflater());
|
||||
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
|
||||
|
||||
builder.setView(bind.getRoot())
|
||||
return new MaterialAlertDialogBuilder(requireContext())
|
||||
.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();
|
||||
.setNegativeButton(R.string.delete_download_storage_dialog_negative_button, null)
|
||||
.create();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -40,14 +36,8 @@ public class DeleteDownloadStorageDialog extends DialogFragment {
|
||||
setButtonAction();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
bind = null;
|
||||
}
|
||||
|
||||
private void setButtonAction() {
|
||||
AlertDialog dialog = ((AlertDialog) getDialog());
|
||||
androidx.appcompat.app.AlertDialog dialog = (androidx.appcompat.app.AlertDialog) getDialog();
|
||||
|
||||
if (dialog != null) {
|
||||
Button positiveButton = dialog.getButton(Dialog.BUTTON_POSITIVE);
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
package com.cappielloantonio.tempo.ui.dialog;
|
||||
|
||||
import android.app.Dialog;
|
||||
import android.os.Bundle;
|
||||
import android.widget.Button;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.OptIn;
|
||||
import androidx.fragment.app.DialogFragment;
|
||||
import androidx.media3.common.util.UnstableApi;
|
||||
|
||||
import com.cappielloantonio.tempo.R;
|
||||
import com.cappielloantonio.tempo.databinding.DialogDownloadDirectoryBinding;
|
||||
import com.cappielloantonio.tempo.interfaces.DialogClickCallback;
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||
|
||||
@OptIn(markerClass = UnstableApi.class)
|
||||
public class DownloadDirectoryDialog extends DialogFragment {
|
||||
private final DialogClickCallback dialogClickCallback;
|
||||
|
||||
public DownloadDirectoryDialog(DialogClickCallback dialogClickCallback) {
|
||||
this.dialogClickCallback = dialogClickCallback;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
DialogDownloadDirectoryBinding bind = DialogDownloadDirectoryBinding.inflate(getLayoutInflater());
|
||||
|
||||
return new MaterialAlertDialogBuilder(requireContext())
|
||||
.setView(bind.getRoot())
|
||||
.setTitle(R.string.download_directory_dialog_title)
|
||||
.setPositiveButton(R.string.download_directory_dialog_positive_button, null)
|
||||
.setNegativeButton(R.string.download_directory_dialog_negative_button, null)
|
||||
.create();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
setButtonAction();
|
||||
}
|
||||
|
||||
private void setButtonAction() {
|
||||
androidx.appcompat.app.AlertDialog dialog = (androidx.appcompat.app.AlertDialog) getDialog();
|
||||
|
||||
if (dialog != null) {
|
||||
Button positiveButton = dialog.getButton(Dialog.BUTTON_POSITIVE);
|
||||
positiveButton.setOnClickListener(v -> {
|
||||
dialogClickCallback.onPositiveClick();
|
||||
dialog.dismiss();
|
||||
});
|
||||
|
||||
Button negativeButton = dialog.getButton(Dialog.BUTTON_NEGATIVE);
|
||||
negativeButton.setOnClickListener(v -> {
|
||||
dialogClickCallback.onNegativeClick();
|
||||
dialog.dismiss();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
package com.cappielloantonio.tempo.ui.dialog;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Dialog;
|
||||
import android.os.Bundle;
|
||||
import android.widget.Button;
|
||||
@@ -15,11 +14,10 @@ import com.cappielloantonio.tempo.databinding.DialogDownloadStorageBinding;
|
||||
import com.cappielloantonio.tempo.interfaces.DialogClickCallback;
|
||||
import com.cappielloantonio.tempo.util.DownloadUtil;
|
||||
import com.cappielloantonio.tempo.util.Preferences;
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||
|
||||
@OptIn(markerClass = UnstableApi.class)
|
||||
public class DownloadStorageDialog extends DialogFragment {
|
||||
private DialogDownloadStorageBinding bind;
|
||||
|
||||
private final DialogClickCallback dialogClickCallback;
|
||||
|
||||
public DownloadStorageDialog(DialogClickCallback dialogClickCallback) {
|
||||
@@ -29,16 +27,14 @@ public class DownloadStorageDialog extends DialogFragment {
|
||||
@NonNull
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
bind = DialogDownloadStorageBinding.inflate(getLayoutInflater());
|
||||
DialogDownloadStorageBinding bind = DialogDownloadStorageBinding.inflate(getLayoutInflater());
|
||||
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
|
||||
|
||||
builder.setView(bind.getRoot())
|
||||
return new MaterialAlertDialogBuilder(getActivity())
|
||||
.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();
|
||||
.setNegativeButton(R.string.download_storage_internal_dialog_negative_button, null)
|
||||
.create();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -47,14 +43,8 @@ public class DownloadStorageDialog extends DialogFragment {
|
||||
setButtonAction();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
bind = null;
|
||||
}
|
||||
|
||||
private void setButtonAction() {
|
||||
AlertDialog dialog = ((AlertDialog) getDialog());
|
||||
androidx.appcompat.app.AlertDialog dialog = (androidx.appcompat.app.AlertDialog) getDialog();
|
||||
|
||||
if (dialog != null) {
|
||||
Button positiveButton = dialog.getButton(Dialog.BUTTON_POSITIVE);
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package com.cappielloantonio.tempo.ui.dialog;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Dialog;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
@@ -17,6 +16,7 @@ import com.cappielloantonio.tempo.subsonic.models.Playlist;
|
||||
import com.cappielloantonio.tempo.ui.adapter.PlaylistDialogHorizontalAdapter;
|
||||
import com.cappielloantonio.tempo.util.Constants;
|
||||
import com.cappielloantonio.tempo.viewmodel.PlaylistChooserViewModel;
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
@@ -30,17 +30,15 @@ public class PlaylistChooserDialog extends DialogFragment implements ClickCallba
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
bind = DialogPlaylistChooserBinding.inflate(getLayoutInflater());
|
||||
|
||||
playlistChooserViewModel = new ViewModelProvider(requireActivity()).get(PlaylistChooserViewModel.class);
|
||||
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
|
||||
|
||||
builder.setView(bind.getRoot())
|
||||
return new MaterialAlertDialogBuilder(getActivity())
|
||||
.setView(bind.getRoot())
|
||||
.setTitle(R.string.playlist_chooser_dialog_title)
|
||||
.setNeutralButton(R.string.playlist_chooser_dialog_neutral_button, (dialog, id) -> {
|
||||
})
|
||||
.setNegativeButton(R.string.playlist_chooser_dialog_negative_button, (dialog, id) -> dialog.cancel());
|
||||
|
||||
return builder.create();
|
||||
.setNeutralButton(R.string.playlist_chooser_dialog_neutral_button, (dialog, id) -> { })
|
||||
.setNegativeButton(R.string.playlist_chooser_dialog_negative_button, (dialog, id) -> dialog.cancel())
|
||||
.create();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -63,7 +61,8 @@ public class PlaylistChooserDialog extends DialogFragment implements ClickCallba
|
||||
}
|
||||
|
||||
private void setButtonAction() {
|
||||
((AlertDialog) Objects.requireNonNull(getDialog())).getButton(AlertDialog.BUTTON_NEUTRAL).setOnClickListener(v -> {
|
||||
androidx.appcompat.app.AlertDialog alertDialog = (androidx.appcompat.app.AlertDialog) Objects.requireNonNull(getDialog());
|
||||
alertDialog.getButton(androidx.appcompat.app.AlertDialog.BUTTON_NEUTRAL).setOnClickListener(v -> {
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putParcelable(Constants.TRACK_OBJECT, playlistChooserViewModel.getSongToAdd());
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package com.cappielloantonio.tempo.ui.dialog;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Dialog;
|
||||
import android.content.ClipData;
|
||||
import android.content.ClipboardManager;
|
||||
@@ -24,6 +23,7 @@ import com.cappielloantonio.tempo.util.Constants;
|
||||
import com.cappielloantonio.tempo.util.MusicUtil;
|
||||
import com.cappielloantonio.tempo.util.Preferences;
|
||||
import com.cappielloantonio.tempo.viewmodel.PlaylistEditorViewModel;
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Objects;
|
||||
@@ -31,7 +31,8 @@ import java.util.Objects;
|
||||
public class PlaylistEditorDialog extends DialogFragment {
|
||||
private DialogPlaylistEditorBinding bind;
|
||||
private PlaylistEditorViewModel playlistEditorViewModel;
|
||||
private PlaylistCallback playlistCallback;
|
||||
|
||||
private final PlaylistCallback playlistCallback;
|
||||
|
||||
private String playlistName;
|
||||
private PlaylistDialogSongHorizontalAdapter playlistDialogSongHorizontalAdapter;
|
||||
@@ -44,18 +45,16 @@ public class PlaylistEditorDialog extends DialogFragment {
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
bind = DialogPlaylistEditorBinding.inflate(getLayoutInflater());
|
||||
|
||||
playlistEditorViewModel = new ViewModelProvider(requireActivity()).get(PlaylistEditorViewModel.class);
|
||||
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
|
||||
|
||||
builder.setView(bind.getRoot())
|
||||
return new MaterialAlertDialogBuilder(getActivity())
|
||||
.setView(bind.getRoot())
|
||||
.setTitle(R.string.playlist_editor_dialog_title)
|
||||
.setPositiveButton(R.string.playlist_editor_dialog_positive_button, (dialog, id) -> {
|
||||
})
|
||||
.setPositiveButton(R.string.playlist_editor_dialog_positive_button, (dialog, id) -> { })
|
||||
.setNeutralButton(R.string.playlist_editor_dialog_neutral_button, (dialog, id) -> dialog.cancel())
|
||||
.setNegativeButton(R.string.playlist_editor_dialog_negative_button, (dialog, id) -> dialog.cancel());
|
||||
|
||||
return builder.create();
|
||||
.setNegativeButton(R.string.playlist_editor_dialog_negative_button, (dialog, id) -> dialog.cancel())
|
||||
.create();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -88,7 +87,9 @@ public class PlaylistEditorDialog extends DialogFragment {
|
||||
}
|
||||
|
||||
private void setButtonAction() {
|
||||
((AlertDialog) Objects.requireNonNull(getDialog())).getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener(v -> {
|
||||
androidx.appcompat.app.AlertDialog alertDialog = (androidx.appcompat.app.AlertDialog) Objects.requireNonNull(getDialog());
|
||||
|
||||
alertDialog.getButton(androidx.appcompat.app.AlertDialog.BUTTON_POSITIVE).setOnClickListener(v -> {
|
||||
if (validateInput()) {
|
||||
if (playlistEditorViewModel.getSongToAdd() != null) {
|
||||
playlistEditorViewModel.createPlaylist(playlistName);
|
||||
@@ -100,7 +101,7 @@ public class PlaylistEditorDialog extends DialogFragment {
|
||||
}
|
||||
});
|
||||
|
||||
((AlertDialog) Objects.requireNonNull(getDialog())).getButton(AlertDialog.BUTTON_NEUTRAL).setOnClickListener(v -> {
|
||||
alertDialog.getButton(androidx.appcompat.app.AlertDialog.BUTTON_NEUTRAL).setOnClickListener(v -> {
|
||||
playlistEditorViewModel.deletePlaylist();
|
||||
dialogDismiss();
|
||||
});
|
||||
@@ -183,6 +184,8 @@ public class PlaylistEditorDialog extends DialogFragment {
|
||||
|
||||
private void dialogDismiss() {
|
||||
Objects.requireNonNull(getDialog()).dismiss();
|
||||
playlistCallback.onDismiss();
|
||||
if (playlistCallback != null) {
|
||||
playlistCallback.onDismiss();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package com.cappielloantonio.tempo.ui.dialog;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Dialog;
|
||||
import android.os.Bundle;
|
||||
import android.text.TextUtils;
|
||||
import android.widget.Button;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.fragment.app.DialogFragment;
|
||||
@@ -13,13 +13,15 @@ import com.cappielloantonio.tempo.R;
|
||||
import com.cappielloantonio.tempo.databinding.DialogPodcastChannelEditorBinding;
|
||||
import com.cappielloantonio.tempo.interfaces.PodcastCallback;
|
||||
import com.cappielloantonio.tempo.viewmodel.PodcastChannelEditorViewModel;
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public class PodcastChannelEditorDialog extends DialogFragment {
|
||||
private DialogPodcastChannelEditorBinding bind;
|
||||
private PodcastChannelEditorViewModel podcastChannelEditorViewModel;
|
||||
private PodcastCallback podcastCallback;
|
||||
|
||||
private final PodcastCallback podcastCallback;
|
||||
|
||||
private String channelUrl;
|
||||
|
||||
@@ -31,17 +33,15 @@ public class PodcastChannelEditorDialog extends DialogFragment {
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
bind = DialogPodcastChannelEditorBinding.inflate(getLayoutInflater());
|
||||
|
||||
podcastChannelEditorViewModel = new ViewModelProvider(requireActivity()).get(PodcastChannelEditorViewModel.class);
|
||||
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
|
||||
|
||||
builder.setView(bind.getRoot())
|
||||
return new MaterialAlertDialogBuilder(getActivity())
|
||||
.setView(bind.getRoot())
|
||||
.setTitle(R.string.podcast_channel_editor_dialog_title)
|
||||
.setPositiveButton(R.string.radio_editor_dialog_positive_button, (dialog, id) -> {
|
||||
})
|
||||
.setNegativeButton(R.string.radio_editor_dialog_negative_button, (dialog, id) -> dialog.cancel());
|
||||
|
||||
return builder.create();
|
||||
.setPositiveButton(R.string.radio_editor_dialog_positive_button, (dialog, id) -> { })
|
||||
.setNegativeButton(R.string.radio_editor_dialog_negative_button, (dialog, id) -> dialog.cancel())
|
||||
.create();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -58,14 +58,19 @@ public class PodcastChannelEditorDialog extends DialogFragment {
|
||||
}
|
||||
|
||||
private void setButtonAction() {
|
||||
((AlertDialog) Objects.requireNonNull(getDialog())).getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener(v -> {
|
||||
if (validateInput()) {
|
||||
podcastChannelEditorViewModel.createChannel(channelUrl);
|
||||
dismissDialog();
|
||||
}
|
||||
});
|
||||
androidx.appcompat.app.AlertDialog dialog = (androidx.appcompat.app.AlertDialog) getDialog();
|
||||
if (dialog != null) {
|
||||
Button positiveButton = dialog.getButton(Dialog.BUTTON_POSITIVE);
|
||||
positiveButton.setOnClickListener(v -> {
|
||||
if (validateInput()) {
|
||||
podcastChannelEditorViewModel.createChannel(channelUrl);
|
||||
dismissDialog();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private boolean validateInput() {
|
||||
channelUrl = Objects.requireNonNull(bind.podcastChannelRssUrlNameTextView.getText()).toString().trim();
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package com.cappielloantonio.tempo.ui.dialog;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Dialog;
|
||||
import android.os.Bundle;
|
||||
import android.text.TextUtils;
|
||||
@@ -15,13 +14,15 @@ import com.cappielloantonio.tempo.interfaces.RadioCallback;
|
||||
import com.cappielloantonio.tempo.subsonic.models.InternetRadioStation;
|
||||
import com.cappielloantonio.tempo.util.Constants;
|
||||
import com.cappielloantonio.tempo.viewmodel.RadioEditorViewModel;
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public class RadioEditorDialog extends DialogFragment {
|
||||
private DialogRadioEditorBinding bind;
|
||||
private RadioEditorViewModel radioEditorViewModel;
|
||||
private RadioCallback radioCallback;
|
||||
|
||||
private final RadioCallback radioCallback;
|
||||
|
||||
private String radioName;
|
||||
private String radioStreamURL;
|
||||
@@ -35,26 +36,36 @@ public class RadioEditorDialog extends DialogFragment {
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
bind = DialogRadioEditorBinding.inflate(getLayoutInflater());
|
||||
|
||||
radioEditorViewModel = new ViewModelProvider(requireActivity()).get(RadioEditorViewModel.class);
|
||||
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
|
||||
|
||||
builder.setView(bind.getRoot())
|
||||
return new MaterialAlertDialogBuilder(requireContext())
|
||||
.setView(bind.getRoot())
|
||||
.setTitle(R.string.radio_editor_dialog_title)
|
||||
.setPositiveButton(R.string.radio_editor_dialog_positive_button, (dialog, id) -> {
|
||||
if (validateInput()) {
|
||||
if (radioEditorViewModel.getRadioToEdit() == null) {
|
||||
radioEditorViewModel.createRadio(radioName, radioStreamURL, radioHomepageURL.isEmpty() ? null : radioHomepageURL);
|
||||
} else {
|
||||
radioEditorViewModel.updateRadio(radioName, radioStreamURL, radioHomepageURL.isEmpty() ? null : radioHomepageURL);
|
||||
}
|
||||
dismissDialog();
|
||||
}
|
||||
})
|
||||
.setNeutralButton(R.string.radio_editor_dialog_neutral_button, (dialog, id) -> dialog.cancel())
|
||||
.setNegativeButton(R.string.radio_editor_dialog_negative_button, (dialog, id) -> dialog.cancel());
|
||||
|
||||
return builder.create();
|
||||
.setNeutralButton(R.string.radio_editor_dialog_neutral_button, (dialog, id) -> {
|
||||
radioEditorViewModel.deleteRadio();
|
||||
dismissDialog();
|
||||
})
|
||||
.setNegativeButton(R.string.radio_editor_dialog_negative_button, (dialog, id) -> {
|
||||
dialog.cancel();
|
||||
})
|
||||
.create();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
|
||||
setParameterInfo();
|
||||
setButtonAction();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -75,25 +86,6 @@ public class RadioEditorDialog extends DialogFragment {
|
||||
}
|
||||
}
|
||||
|
||||
private void setButtonAction() {
|
||||
((AlertDialog) Objects.requireNonNull(getDialog())).getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener(v -> {
|
||||
if (validateInput()) {
|
||||
if (radioEditorViewModel.getRadioToEdit() == null) {
|
||||
radioEditorViewModel.createRadio(radioName, radioStreamURL, radioHomepageURL.isEmpty() ? null : radioHomepageURL);
|
||||
} else {
|
||||
radioEditorViewModel.updateRadio(radioName, radioStreamURL, radioHomepageURL.isEmpty() ? null : radioHomepageURL);
|
||||
}
|
||||
|
||||
dismissDialog();
|
||||
}
|
||||
});
|
||||
|
||||
((AlertDialog) Objects.requireNonNull(getDialog())).getButton(AlertDialog.BUTTON_NEUTRAL).setOnClickListener(v -> {
|
||||
radioEditorViewModel.deleteRadio();
|
||||
dismissDialog();
|
||||
});
|
||||
}
|
||||
|
||||
private boolean validateInput() {
|
||||
radioName = Objects.requireNonNull(bind.internetRadioStationNameTextView.getText()).toString().trim();
|
||||
radioStreamURL = Objects.requireNonNull(bind.internetRadioStationStreamUrlTextView.getText()).toString().trim();
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package com.cappielloantonio.tempo.ui.dialog;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Dialog;
|
||||
import android.os.Bundle;
|
||||
|
||||
@@ -12,6 +11,7 @@ import com.cappielloantonio.tempo.R;
|
||||
import com.cappielloantonio.tempo.databinding.DialogRatingBinding;
|
||||
import com.cappielloantonio.tempo.util.Constants;
|
||||
import com.cappielloantonio.tempo.viewmodel.RatingViewModel;
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||
|
||||
public class RatingDialog extends DialogFragment {
|
||||
private static final String TAG = "ServerSignupDialog";
|
||||
@@ -25,14 +25,12 @@ public class RatingDialog extends DialogFragment {
|
||||
bind = DialogRatingBinding.inflate(getLayoutInflater());
|
||||
ratingViewModel = new ViewModelProvider(requireActivity()).get(RatingViewModel.class);
|
||||
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
|
||||
|
||||
builder.setView(bind.getRoot())
|
||||
return new MaterialAlertDialogBuilder(getActivity())
|
||||
.setView(bind.getRoot())
|
||||
.setTitle(R.string.rating_dialog_title)
|
||||
.setNegativeButton(R.string.rating_dialog_negative_button, (dialog, id) -> dialog.cancel())
|
||||
.setPositiveButton(R.string.rating_dialog_positive_button, (dialog, id) -> ratingViewModel.rate((int) bind.ratingBar.getRating()));
|
||||
|
||||
return builder.create();
|
||||
.setPositiveButton(R.string.rating_dialog_positive_button, (dialog, id) -> ratingViewModel.rate((int) bind.ratingBar.getRating()))
|
||||
.create();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package com.cappielloantonio.tempo.ui.dialog;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Dialog;
|
||||
import android.os.Bundle;
|
||||
import android.text.TextUtils;
|
||||
@@ -14,6 +13,7 @@ import com.cappielloantonio.tempo.databinding.DialogServerSignupBinding;
|
||||
import com.cappielloantonio.tempo.model.Server;
|
||||
import com.cappielloantonio.tempo.util.MusicUtil;
|
||||
import com.cappielloantonio.tempo.viewmodel.LoginViewModel;
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
@@ -33,21 +33,17 @@ public class ServerSignupDialog extends DialogFragment {
|
||||
@NonNull
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
loginViewModel = new ViewModelProvider(requireActivity()).get(LoginViewModel.class);
|
||||
|
||||
bind = DialogServerSignupBinding.inflate(getLayoutInflater());
|
||||
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
|
||||
loginViewModel = new ViewModelProvider(requireActivity()).get(LoginViewModel.class);
|
||||
|
||||
builder.setView(bind.getRoot())
|
||||
return new MaterialAlertDialogBuilder(getActivity())
|
||||
.setView(bind.getRoot())
|
||||
.setTitle(R.string.server_signup_dialog_title)
|
||||
.setNeutralButton(R.string.server_signup_dialog_neutral_button, (dialog, id) -> {
|
||||
})
|
||||
.setPositiveButton(R.string.server_signup_dialog_positive_button, (dialog, id) -> {
|
||||
})
|
||||
.setNegativeButton(R.string.server_signup_dialog_negative_button, (dialog, id) -> dialog.cancel());
|
||||
|
||||
return builder.create();
|
||||
.setNeutralButton(R.string.server_signup_dialog_neutral_button, (dialog, id) -> { })
|
||||
.setPositiveButton(R.string.server_signup_dialog_positive_button, (dialog, id) -> { })
|
||||
.setNegativeButton(R.string.server_signup_dialog_negative_button, (dialog, id) -> dialog.cancel())
|
||||
.create();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -81,14 +77,16 @@ public class ServerSignupDialog extends DialogFragment {
|
||||
}
|
||||
|
||||
private void setButtonAction() {
|
||||
((AlertDialog) Objects.requireNonNull(getDialog())).getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener(v -> {
|
||||
androidx.appcompat.app.AlertDialog alertDialog = (androidx.appcompat.app.AlertDialog) Objects.requireNonNull(getDialog());
|
||||
|
||||
alertDialog.getButton(androidx.appcompat.app.AlertDialog.BUTTON_POSITIVE).setOnClickListener(v -> {
|
||||
if (validateInput()) {
|
||||
saveServerPreference();
|
||||
Objects.requireNonNull(getDialog()).dismiss();
|
||||
}
|
||||
});
|
||||
|
||||
((AlertDialog) Objects.requireNonNull(getDialog())).getButton(AlertDialog.BUTTON_NEUTRAL).setOnClickListener(v -> {
|
||||
alertDialog.getButton(androidx.appcompat.app.AlertDialog.BUTTON_NEUTRAL).setOnClickListener(v -> {
|
||||
loginViewModel.deleteServer(null);
|
||||
Objects.requireNonNull(getDialog()).dismiss();
|
||||
});
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
package com.cappielloantonio.tempo.ui.dialog;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Dialog;
|
||||
import android.os.Bundle;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.OptIn;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.fragment.app.DialogFragment;
|
||||
import androidx.media3.common.util.UnstableApi;
|
||||
|
||||
@@ -13,34 +13,32 @@ import com.cappielloantonio.tempo.R;
|
||||
import com.cappielloantonio.tempo.databinding.DialogServerUnreachableBinding;
|
||||
import com.cappielloantonio.tempo.ui.activity.MainActivity;
|
||||
import com.cappielloantonio.tempo.util.Preferences;
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
@OptIn(markerClass = UnstableApi.class)
|
||||
public class ServerUnreachableDialog extends DialogFragment {
|
||||
private static final String TAG = "ServerUnreachableDialog";
|
||||
|
||||
private DialogServerUnreachableBinding bind;
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
bind = DialogServerUnreachableBinding.inflate(getLayoutInflater());
|
||||
DialogServerUnreachableBinding bind = DialogServerUnreachableBinding.inflate(getLayoutInflater());
|
||||
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
|
||||
|
||||
builder.setView(bind.getRoot())
|
||||
AlertDialog popup = new MaterialAlertDialogBuilder(getActivity()).setView(bind.getRoot())
|
||||
.setTitle(R.string.server_unreachable_dialog_title)
|
||||
.setPositiveButton(R.string.server_unreachable_dialog_positive_button, null)
|
||||
.setNeutralButton(R.string.server_unreachable_dialog_neutral_button, null)
|
||||
.setNegativeButton(R.string.server_unreachable_dialog_negative_button, (dialog, id) -> dialog.cancel());
|
||||
.setNegativeButton(R.string.server_unreachable_dialog_negative_button, (dialog, id) -> dialog.cancel())
|
||||
.create();
|
||||
|
||||
AlertDialog popup = builder.create();
|
||||
|
||||
popup.setCancelable(false);
|
||||
popup.setCanceledOnTouchOutside(false);
|
||||
popup.setCancelable(false);
|
||||
|
||||
return popup;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
@@ -48,26 +46,18 @@ public class ServerUnreachableDialog extends DialogFragment {
|
||||
setButtonAction();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
bind = null;
|
||||
}
|
||||
|
||||
private void setButtonAction() {
|
||||
AlertDialog dialog = (AlertDialog) getDialog();
|
||||
androidx.appcompat.app.AlertDialog alertDialog = (androidx.appcompat.app.AlertDialog) Objects.requireNonNull(getDialog());
|
||||
|
||||
if(dialog != null) {
|
||||
(dialog).getButton(AlertDialog.BUTTON_NEUTRAL).setOnClickListener(v -> {
|
||||
MainActivity activity = (MainActivity) getActivity();
|
||||
if (activity != null) activity.quit();
|
||||
dialog.dismiss();
|
||||
});
|
||||
alertDialog.getButton(androidx.appcompat.app.AlertDialog.BUTTON_NEUTRAL).setOnClickListener(v -> {
|
||||
MainActivity activity = (MainActivity) getActivity();
|
||||
if (activity != null) activity.quit();
|
||||
alertDialog.dismiss();
|
||||
});
|
||||
|
||||
(dialog).getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener(v -> {
|
||||
Preferences.setServerUnreachableDatetime(System.currentTimeMillis());
|
||||
dialog.dismiss();
|
||||
});
|
||||
}
|
||||
alertDialog.getButton(androidx.appcompat.app.AlertDialog.BUTTON_POSITIVE).setOnClickListener(v -> {
|
||||
Preferences.setServerUnreachableDatetime(System.currentTimeMillis());
|
||||
alertDialog.dismiss();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,15 +17,12 @@ import com.cappielloantonio.tempo.viewmodel.ShareBottomSheetViewModel;
|
||||
import com.google.android.material.datepicker.CalendarConstraints;
|
||||
import com.google.android.material.datepicker.DateValidatorPointForward;
|
||||
import com.google.android.material.datepicker.MaterialDatePicker;
|
||||
import com.google.android.material.datepicker.MaterialPickerOnPositiveButtonClickListener;
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.Objects;
|
||||
|
||||
public class ShareUpdateDialog extends DialogFragment {
|
||||
private static final String TAG = "ShareUpdateDialog";
|
||||
|
||||
private DialogShareUpdateBinding bind;
|
||||
private HomeViewModel homeViewModel;
|
||||
private ShareBottomSheetViewModel shareBottomSheetViewModel;
|
||||
@@ -40,19 +37,18 @@ public class ShareUpdateDialog extends DialogFragment {
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
homeViewModel = new ViewModelProvider(requireActivity()).get(HomeViewModel.class);
|
||||
|
||||
shareBottomSheetViewModel = new ViewModelProvider(requireActivity()).get(ShareBottomSheetViewModel.class);
|
||||
|
||||
bind = DialogShareUpdateBinding.inflate(getLayoutInflater());
|
||||
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
|
||||
|
||||
builder.setView(bind.getRoot())
|
||||
return new MaterialAlertDialogBuilder(requireContext())
|
||||
.setView(bind.getRoot())
|
||||
.setTitle(R.string.share_update_dialog_title)
|
||||
.setPositiveButton(R.string.share_update_dialog_positive_button, (dialog, id) -> {
|
||||
})
|
||||
.setNegativeButton(R.string.share_update_dialog_negative_button, (dialog, id) -> dialog.cancel());
|
||||
|
||||
return builder.create();
|
||||
.setNegativeButton(R.string.share_update_dialog_negative_button, (dialog, id) -> dialog.cancel())
|
||||
.create();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package com.cappielloantonio.tempo.ui.dialog;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Dialog;
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
@@ -19,31 +18,28 @@ import com.cappielloantonio.tempo.util.DownloadUtil;
|
||||
import com.cappielloantonio.tempo.util.MappingUtil;
|
||||
import com.cappielloantonio.tempo.util.Preferences;
|
||||
import com.cappielloantonio.tempo.viewmodel.StarredSyncViewModel;
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@OptIn(markerClass = UnstableApi.class)
|
||||
public class StarredSyncDialog extends DialogFragment {
|
||||
private static final String TAG = "ServerUnreachableDialog";
|
||||
|
||||
private DialogStarredSyncBinding bind;
|
||||
private StarredSyncViewModel starredSyncViewModel;
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
bind = DialogStarredSyncBinding.inflate(getLayoutInflater());
|
||||
DialogStarredSyncBinding bind = DialogStarredSyncBinding.inflate(getLayoutInflater());
|
||||
|
||||
starredSyncViewModel = new ViewModelProvider(requireActivity()).get(StarredSyncViewModel.class);
|
||||
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
|
||||
|
||||
builder.setView(bind.getRoot())
|
||||
return new MaterialAlertDialogBuilder(getActivity())
|
||||
.setView(bind.getRoot())
|
||||
.setTitle(R.string.starred_sync_dialog_title)
|
||||
.setPositiveButton(R.string.starred_sync_dialog_positive_button, null)
|
||||
.setNeutralButton(R.string.starred_sync_dialog_neutral_button, null)
|
||||
.setNegativeButton(R.string.starred_sync_dialog_negative_button, null);
|
||||
|
||||
return builder.create();
|
||||
.setNegativeButton(R.string.starred_sync_dialog_negative_button, null)
|
||||
.create();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -52,14 +48,8 @@ public class StarredSyncDialog extends DialogFragment {
|
||||
setButtonAction(requireContext());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
bind = null;
|
||||
}
|
||||
|
||||
private void setButtonAction(Context context) {
|
||||
AlertDialog dialog = ((AlertDialog) getDialog());
|
||||
androidx.appcompat.app.AlertDialog dialog = (androidx.appcompat.app.AlertDialog) getDialog();
|
||||
|
||||
if (dialog != null) {
|
||||
Button positiveButton = dialog.getButton(Dialog.BUTTON_POSITIVE);
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package com.cappielloantonio.tempo.ui.dialog;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Dialog;
|
||||
import android.os.Bundle;
|
||||
|
||||
@@ -14,12 +13,12 @@ import com.cappielloantonio.tempo.glide.CustomGlideRequest;
|
||||
import com.cappielloantonio.tempo.util.Constants;
|
||||
import com.cappielloantonio.tempo.util.MusicUtil;
|
||||
import com.cappielloantonio.tempo.util.Preferences;
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||
|
||||
public class TrackInfoDialog extends DialogFragment {
|
||||
private static final String TAG = "TrackInfoDialog";
|
||||
|
||||
private DialogTrackInfoBinding bind;
|
||||
private MediaMetadata mediaMetadata;
|
||||
|
||||
private final MediaMetadata mediaMetadata;
|
||||
|
||||
public TrackInfoDialog(MediaMetadata mediaMetadata) {
|
||||
this.mediaMetadata = mediaMetadata;
|
||||
@@ -30,12 +29,10 @@ public class TrackInfoDialog extends DialogFragment {
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
bind = DialogTrackInfoBinding.inflate(getLayoutInflater());
|
||||
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
|
||||
|
||||
builder.setView(bind.getRoot())
|
||||
.setPositiveButton(R.string.track_info_dialog_positive_button, (dialog, id) -> dialog.cancel());
|
||||
|
||||
return builder.create();
|
||||
return new MaterialAlertDialogBuilder(requireActivity())
|
||||
.setView(bind.getRoot())
|
||||
.setPositiveButton(R.string.track_info_dialog_positive_button, (dialog, id) -> dialog.cancel())
|
||||
.create();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -52,6 +52,12 @@ public class AlbumCatalogueFragment extends Fragment implements ClickCallback {
|
||||
initData();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
albumCatalogueViewModel.stopLoading();
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
activity = (MainActivity) getActivity();
|
||||
@@ -73,7 +79,7 @@ public class AlbumCatalogueFragment extends Fragment implements ClickCallback {
|
||||
|
||||
private void initData() {
|
||||
albumCatalogueViewModel = new ViewModelProvider(requireActivity()).get(AlbumCatalogueViewModel.class);
|
||||
albumCatalogueViewModel.loadAlbums(500);
|
||||
albumCatalogueViewModel.loadAlbums();
|
||||
}
|
||||
|
||||
private void initAppBar() {
|
||||
|
||||
@@ -16,21 +16,25 @@ import androidx.media3.common.util.UnstableApi;
|
||||
import androidx.media3.session.MediaBrowser;
|
||||
import androidx.media3.session.SessionToken;
|
||||
import androidx.navigation.Navigation;
|
||||
import androidx.recyclerview.widget.GridLayoutManager;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
|
||||
import com.cappielloantonio.tempo.R;
|
||||
import com.cappielloantonio.tempo.databinding.FragmentArtistPageBinding;
|
||||
import com.cappielloantonio.tempo.glide.CustomGlideRequest;
|
||||
import com.cappielloantonio.tempo.helper.recyclerview.CustomLinearSnapHelper;
|
||||
import com.cappielloantonio.tempo.helper.recyclerview.GridItemDecoration;
|
||||
import com.cappielloantonio.tempo.interfaces.ClickCallback;
|
||||
import com.cappielloantonio.tempo.service.MediaManager;
|
||||
import com.cappielloantonio.tempo.service.MediaService;
|
||||
import com.cappielloantonio.tempo.ui.activity.MainActivity;
|
||||
import com.cappielloantonio.tempo.ui.adapter.AlbumArtistPageOrSimilarAdapter;
|
||||
import com.cappielloantonio.tempo.ui.adapter.AlbumCatalogueAdapter;
|
||||
import com.cappielloantonio.tempo.ui.adapter.ArtistSimilarAdapter;
|
||||
import com.cappielloantonio.tempo.ui.adapter.SongHorizontalAdapter;
|
||||
import com.cappielloantonio.tempo.util.Constants;
|
||||
import com.cappielloantonio.tempo.util.MusicUtil;
|
||||
import com.cappielloantonio.tempo.util.Preferences;
|
||||
import com.cappielloantonio.tempo.viewmodel.ArtistPageViewModel;
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
|
||||
@@ -42,6 +46,7 @@ public class ArtistPageFragment extends Fragment implements ClickCallback {
|
||||
|
||||
private SongHorizontalAdapter songHorizontalAdapter;
|
||||
private AlbumArtistPageOrSimilarAdapter albumArtistPageOrSimilarAdapter;
|
||||
private AlbumCatalogueAdapter albumCatalogueAdapter;
|
||||
private ArtistSimilarAdapter artistSimilarAdapter;
|
||||
|
||||
private ListenableFuture<MediaBrowser> mediaBrowserListenableFuture;
|
||||
@@ -59,7 +64,8 @@ public class ArtistPageFragment extends Fragment implements ClickCallback {
|
||||
initArtistInfo();
|
||||
initPlayButtons();
|
||||
initTopSongsView();
|
||||
initAlbumsView();
|
||||
initHorizontalAlbumsView();
|
||||
initVerticalAlbumsView();
|
||||
initSimilarArtistsView();
|
||||
|
||||
return view;
|
||||
@@ -93,6 +99,18 @@ public class ArtistPageFragment extends Fragment implements ClickCallback {
|
||||
bundle.putParcelable(Constants.ARTIST_OBJECT, artistPageViewModel.getArtist());
|
||||
activity.navController.navigate(R.id.action_artistPageFragment_to_songListPageFragment, bundle);
|
||||
});
|
||||
|
||||
bind.artistPageAlbumsSwitchLayoutTextViewClickable.setOnClickListener(view -> {
|
||||
boolean isHorizontalRecyclerViewVisible = bind.albumsHorizontalRecyclerView.getVisibility() == View.VISIBLE;
|
||||
|
||||
bind.albumsHorizontalRecyclerView.setVisibility(isHorizontalRecyclerViewVisible ? View.GONE : View.VISIBLE);
|
||||
bind.albumsVerticalRecyclerView.setVisibility(isHorizontalRecyclerViewVisible ? View.VISIBLE : View.GONE);
|
||||
|
||||
Preferences.setArtistAlbumLayout(!isHorizontalRecyclerViewVisible);
|
||||
});
|
||||
|
||||
bind.albumsHorizontalRecyclerView.setVisibility(Preferences.isArtistAlbumLayoutHorizontal() ? View.VISIBLE : View.GONE);
|
||||
bind.albumsVerticalRecyclerView.setVisibility(Preferences.isArtistAlbumLayoutHorizontal() ? View.GONE : View.VISIBLE);
|
||||
}
|
||||
|
||||
private void initAppBar() {
|
||||
@@ -107,8 +125,9 @@ public class ArtistPageFragment extends Fragment implements ClickCallback {
|
||||
|
||||
private void initArtistInfo() {
|
||||
artistPageViewModel.getArtistInfo(artistPageViewModel.getArtist().getId()).observe(getViewLifecycleOwner(), artistInfo -> {
|
||||
if(artistInfo == null) {
|
||||
if (bind != null) bind.artistPageBioPlaceholder.placeholder.setVisibility(View.VISIBLE);
|
||||
if (artistInfo == null) {
|
||||
if (bind != null)
|
||||
bind.artistPageBioPlaceholder.placeholder.setVisibility(View.VISIBLE);
|
||||
if (bind != null) bind.artistPageBioSector.setVisibility(View.GONE);
|
||||
} else {
|
||||
String normalizedBio = MusicUtil.forceReadableString(artistInfo.getBiography());
|
||||
@@ -131,7 +150,8 @@ public class ArtistPageFragment extends Fragment implements ClickCallback {
|
||||
startActivity(intent);
|
||||
});
|
||||
|
||||
if (bind != null) bind.artistPageBioPlaceholder.placeholder.setVisibility(View.GONE);
|
||||
if (bind != null)
|
||||
bind.artistPageBioPlaceholder.placeholder.setVisibility(View.GONE);
|
||||
if (bind != null) bind.artistPageBioSector.setVisibility(View.VISIBLE);
|
||||
}
|
||||
});
|
||||
@@ -168,34 +188,63 @@ public class ArtistPageFragment extends Fragment implements ClickCallback {
|
||||
bind.mostStreamedSongRecyclerView.setAdapter(songHorizontalAdapter);
|
||||
artistPageViewModel.getArtistTopSongList().observe(getViewLifecycleOwner(), songs -> {
|
||||
if (songs == null) {
|
||||
if (bind != null) bind.artistPageTopTracksPlaceholder.placeholder.setVisibility(View.VISIBLE);
|
||||
if (bind != null)
|
||||
bind.artistPageTopTracksPlaceholder.placeholder.setVisibility(View.VISIBLE);
|
||||
if (bind != null) bind.artistPageTopSongsSector.setVisibility(View.GONE);
|
||||
} else {
|
||||
if (bind != null) bind.artistPageTopTracksPlaceholder.placeholder.setVisibility(View.GONE);
|
||||
if (bind != null) bind.artistPageTopSongsSector.setVisibility(!songs.isEmpty() ? View.VISIBLE : View.GONE);
|
||||
if (bind != null)
|
||||
bind.artistPageTopTracksPlaceholder.placeholder.setVisibility(View.GONE);
|
||||
if (bind != null)
|
||||
bind.artistPageTopSongsSector.setVisibility(!songs.isEmpty() ? View.VISIBLE : View.GONE);
|
||||
songHorizontalAdapter.setItems(songs);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void initAlbumsView() {
|
||||
bind.albumsRecyclerView.setLayoutManager(new LinearLayoutManager(requireContext(), LinearLayoutManager.HORIZONTAL, false));
|
||||
private void initHorizontalAlbumsView() {
|
||||
bind.albumsHorizontalRecyclerView.setLayoutManager(new LinearLayoutManager(requireContext(), LinearLayoutManager.HORIZONTAL, false));
|
||||
|
||||
albumArtistPageOrSimilarAdapter = new AlbumArtistPageOrSimilarAdapter(this);
|
||||
bind.albumsRecyclerView.setAdapter(albumArtistPageOrSimilarAdapter);
|
||||
bind.albumsHorizontalRecyclerView.setAdapter(albumArtistPageOrSimilarAdapter);
|
||||
artistPageViewModel.getAlbumList().observe(getViewLifecycleOwner(), albums -> {
|
||||
if (albums == null) {
|
||||
if (bind != null) bind.artistPageAlbumPlaceholder.placeholder.setVisibility(View.VISIBLE);
|
||||
if (bind != null)
|
||||
bind.artistPageAlbumPlaceholder.placeholder.setVisibility(View.VISIBLE);
|
||||
if (bind != null) bind.artistPageAlbumsSector.setVisibility(View.GONE);
|
||||
} else {
|
||||
if (bind != null) bind.artistPageAlbumPlaceholder.placeholder.setVisibility(View.GONE);
|
||||
if (bind != null) bind.artistPageAlbumsSector.setVisibility(!albums.isEmpty() ? View.VISIBLE : View.GONE);
|
||||
if (bind != null)
|
||||
bind.artistPageAlbumPlaceholder.placeholder.setVisibility(View.GONE);
|
||||
if (bind != null)
|
||||
bind.artistPageAlbumsSector.setVisibility(!albums.isEmpty() ? View.VISIBLE : View.GONE);
|
||||
albumArtistPageOrSimilarAdapter.setItems(albums);
|
||||
}
|
||||
});
|
||||
|
||||
CustomLinearSnapHelper albumSnapHelper = new CustomLinearSnapHelper();
|
||||
albumSnapHelper.attachToRecyclerView(bind.albumsRecyclerView);
|
||||
albumSnapHelper.attachToRecyclerView(bind.albumsHorizontalRecyclerView);
|
||||
}
|
||||
|
||||
private void initVerticalAlbumsView() {
|
||||
bind.albumsVerticalRecyclerView.setLayoutManager(new GridLayoutManager(requireContext(), 2));
|
||||
bind.albumsVerticalRecyclerView.addItemDecoration(new GridItemDecoration(2, 20, false));
|
||||
bind.albumsVerticalRecyclerView.setHasFixedSize(true);
|
||||
|
||||
albumCatalogueAdapter = new AlbumCatalogueAdapter(this);
|
||||
bind.albumsVerticalRecyclerView.setAdapter(albumCatalogueAdapter);
|
||||
|
||||
artistPageViewModel.getAlbumList().observe(getViewLifecycleOwner(), albums -> {
|
||||
if (albums == null) {
|
||||
if (bind != null)
|
||||
bind.artistPageAlbumPlaceholder.placeholder.setVisibility(View.VISIBLE);
|
||||
if (bind != null) bind.artistPageAlbumsSector.setVisibility(View.GONE);
|
||||
} else {
|
||||
if (bind != null)
|
||||
bind.artistPageAlbumPlaceholder.placeholder.setVisibility(View.GONE);
|
||||
if (bind != null)
|
||||
bind.artistPageAlbumsSector.setVisibility(!albums.isEmpty() ? View.VISIBLE : View.GONE);
|
||||
albumCatalogueAdapter.setItems(albums);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void initSimilarArtistsView() {
|
||||
@@ -206,11 +255,14 @@ public class ArtistPageFragment extends Fragment implements ClickCallback {
|
||||
bind.similarArtistsRecyclerView.setAdapter(artistSimilarAdapter);
|
||||
artistPageViewModel.getArtistInfo(artistPageViewModel.getArtist().getId()).observe(getViewLifecycleOwner(), artist -> {
|
||||
if (artist == null) {
|
||||
if (bind != null) bind.artistPageSimilarArtistPlaceholder.placeholder.setVisibility(View.VISIBLE);
|
||||
if (bind != null)
|
||||
bind.artistPageSimilarArtistPlaceholder.placeholder.setVisibility(View.VISIBLE);
|
||||
if (bind != null) bind.similarArtistSector.setVisibility(View.GONE);
|
||||
} else {
|
||||
if (bind != null) bind.artistPageSimilarArtistPlaceholder.placeholder.setVisibility(View.GONE);
|
||||
if (bind != null) bind.similarArtistSector.setVisibility(!artist.getSimilarArtists().isEmpty() ? View.VISIBLE : View.GONE);
|
||||
if (bind != null)
|
||||
bind.artistPageSimilarArtistPlaceholder.placeholder.setVisibility(View.GONE);
|
||||
if (bind != null)
|
||||
bind.similarArtistSector.setVisibility(!artist.getSimilarArtists().isEmpty() ? View.VISIBLE : View.GONE);
|
||||
artistSimilarAdapter.setItems(artist.getSimilarArtists());
|
||||
}
|
||||
});
|
||||
|
||||
@@ -23,12 +23,14 @@ import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import com.cappielloantonio.tempo.R;
|
||||
import com.cappielloantonio.tempo.databinding.FragmentDirectoryBinding;
|
||||
import com.cappielloantonio.tempo.interfaces.ClickCallback;
|
||||
import com.cappielloantonio.tempo.interfaces.DialogClickCallback;
|
||||
import com.cappielloantonio.tempo.model.Download;
|
||||
import com.cappielloantonio.tempo.service.MediaManager;
|
||||
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.adapter.MusicDirectoryAdapter;
|
||||
import com.cappielloantonio.tempo.ui.dialog.DownloadDirectoryDialog;
|
||||
import com.cappielloantonio.tempo.util.Constants;
|
||||
import com.cappielloantonio.tempo.util.DownloadUtil;
|
||||
import com.cappielloantonio.tempo.util.MappingUtil;
|
||||
@@ -50,6 +52,8 @@ public class DirectoryFragment extends Fragment implements ClickCallback {
|
||||
|
||||
private ListenableFuture<MediaBrowser> mediaBrowserListenableFuture;
|
||||
|
||||
private MenuItem menuItem;
|
||||
|
||||
@Override
|
||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
@@ -60,6 +64,8 @@ public class DirectoryFragment extends Fragment implements ClickCallback {
|
||||
public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) {
|
||||
super.onCreateOptionsMenu(menu, inflater);
|
||||
inflater.inflate(R.menu.directory_page_menu, menu);
|
||||
|
||||
menuItem = menu.getItem(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -97,15 +103,23 @@ public class DirectoryFragment extends Fragment implements ClickCallback {
|
||||
@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())
|
||||
);
|
||||
DownloadDirectoryDialog dialog = new DownloadDirectoryDialog(new DialogClickCallback() {
|
||||
@Override
|
||||
public void onPositiveClick() {
|
||||
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())
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
dialog.show(activity.getSupportFragmentManager(), null);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -144,6 +158,14 @@ public class DirectoryFragment extends Fragment implements ClickCallback {
|
||||
bind.directoryTitleLabel.setText(directory.getName());
|
||||
|
||||
musicDirectoryAdapter.setItems(directory.getChildren());
|
||||
|
||||
menuItem.setVisible(
|
||||
directory.getChildren() != null && directory.getChildren()
|
||||
.stream()
|
||||
.filter(child -> !child.isDir())
|
||||
.findFirst()
|
||||
.orElse(null) != null
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -160,6 +182,11 @@ public class DirectoryFragment extends Fragment implements ClickCallback {
|
||||
MediaManager.startQueue(mediaBrowserListenableFuture, bundle.getParcelableArrayList(Constants.TRACKS_OBJECT), bundle.getInt(Constants.ITEM_POSITION));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMediaLongClick(Bundle bundle) {
|
||||
Navigation.findNavController(requireView()).navigate(R.id.songBottomSheetDialog, bundle);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMusicDirectoryClick(Bundle bundle) {
|
||||
Navigation.findNavController(requireView()).navigate(R.id.directoryFragment, bundle);
|
||||
|
||||
@@ -33,6 +33,7 @@ import com.cappielloantonio.tempo.viewmodel.DownloadViewModel;
|
||||
import com.google.android.material.appbar.MaterialToolbar;
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
@@ -165,6 +166,19 @@ public class DownloadFragment extends Fragment implements ClickCallback {
|
||||
bind.downloadedGoBackImageView.setVisibility(stack.size() > 1 ? View.VISIBLE : View.GONE);
|
||||
|
||||
setupBackPressing(stack.size());
|
||||
setupShuffleButton();
|
||||
});
|
||||
}
|
||||
|
||||
private void setupShuffleButton() {
|
||||
bind.shuffleDownloadedTextViewClickable.setOnClickListener(view -> {
|
||||
List<Child> songs = downloadHorizontalAdapter.getShuffling();
|
||||
|
||||
if (songs != null && !songs.isEmpty()) {
|
||||
Collections.shuffle(songs);
|
||||
MediaManager.startQueue(mediaBrowserListenableFuture, songs, 0);
|
||||
activity.setBottomSheetInPeek(true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -51,6 +51,7 @@ import com.cappielloantonio.tempo.ui.adapter.YearAdapter;
|
||||
import com.cappielloantonio.tempo.util.Constants;
|
||||
import com.cappielloantonio.tempo.util.DownloadUtil;
|
||||
import com.cappielloantonio.tempo.util.MappingUtil;
|
||||
import com.cappielloantonio.tempo.util.MusicUtil;
|
||||
import com.cappielloantonio.tempo.util.Preferences;
|
||||
import com.cappielloantonio.tempo.util.UIUtil;
|
||||
import com.cappielloantonio.tempo.viewmodel.HomeViewModel;
|
||||
@@ -153,6 +154,8 @@ public class HomeTabMusicFragment extends Fragment implements ClickCallback {
|
||||
|
||||
bind.discoveryTextViewClickable.setOnClickListener(v -> {
|
||||
homeViewModel.getRandomShuffleSample().observe(getViewLifecycleOwner(), songs -> {
|
||||
MusicUtil.ratingFilter(songs);
|
||||
|
||||
if (songs.size() > 0) {
|
||||
MediaManager.startQueue(mediaBrowserListenableFuture, songs, 0);
|
||||
activity.setBottomSheetInPeek(true);
|
||||
@@ -306,6 +309,8 @@ public class HomeTabMusicFragment extends Fragment implements ClickCallback {
|
||||
bind.discoverSongViewPager.setAdapter(discoverSongAdapter);
|
||||
bind.discoverSongViewPager.setOffscreenPageLimit(1);
|
||||
homeViewModel.getDiscoverSongSample(getViewLifecycleOwner()).observe(getViewLifecycleOwner(), songs -> {
|
||||
MusicUtil.ratingFilter(songs);
|
||||
|
||||
if (songs == null) {
|
||||
if (bind != null)
|
||||
bind.homeDiscoveryPlaceholder.placeholder.setVisibility(View.VISIBLE);
|
||||
@@ -330,6 +335,8 @@ public class HomeTabMusicFragment extends Fragment implements ClickCallback {
|
||||
similarMusicAdapter = new SimilarTrackAdapter(this);
|
||||
bind.similarTracksRecyclerView.setAdapter(similarMusicAdapter);
|
||||
homeViewModel.getStarredTracksSample(getViewLifecycleOwner()).observe(getViewLifecycleOwner(), songs -> {
|
||||
MusicUtil.ratingFilter(songs);
|
||||
|
||||
if (songs == null) {
|
||||
if (bind != null)
|
||||
bind.homeSimilarTracksPlaceholder.placeholder.setVisibility(View.VISIBLE);
|
||||
@@ -706,7 +713,9 @@ public class HomeTabMusicFragment extends Fragment implements ClickCallback {
|
||||
private void refreshSharesView() {
|
||||
final Handler handler = new Handler();
|
||||
final Runnable runnable = () -> {
|
||||
if (Preferences.isSharingEnabled()) homeViewModel.refreshShares(getViewLifecycleOwner());
|
||||
if (getView() != null && bind != null && Preferences.isSharingEnabled()) {
|
||||
homeViewModel.refreshShares(getViewLifecycleOwner());
|
||||
}
|
||||
};
|
||||
handler.postDelayed(runnable, 100);
|
||||
}
|
||||
@@ -742,6 +751,8 @@ public class HomeTabMusicFragment extends Fragment implements ClickCallback {
|
||||
|
||||
if (mediaBrowserListenableFuture != null) {
|
||||
homeViewModel.getMediaInstantMix(getViewLifecycleOwner(), bundle.getParcelable(Constants.TRACK_OBJECT)).observe(getViewLifecycleOwner(), songs -> {
|
||||
MusicUtil.ratingFilter(songs);
|
||||
|
||||
if (songs != null && songs.size() > 0) {
|
||||
MediaManager.enqueue(mediaBrowserListenableFuture, songs, true);
|
||||
}
|
||||
@@ -781,6 +792,8 @@ public class HomeTabMusicFragment extends Fragment implements ClickCallback {
|
||||
|
||||
if (mediaBrowserListenableFuture != null) {
|
||||
homeViewModel.getArtistInstantMix(getViewLifecycleOwner(), bundle.getParcelable(Constants.ARTIST_OBJECT)).observe(getViewLifecycleOwner(), songs -> {
|
||||
MusicUtil.ratingFilter(songs);
|
||||
|
||||
if (songs.size() > 0) {
|
||||
MediaManager.startQueue(mediaBrowserListenableFuture, songs, 0);
|
||||
activity.setBottomSheetInPeek(true);
|
||||
@@ -790,6 +803,8 @@ public class HomeTabMusicFragment extends Fragment implements ClickCallback {
|
||||
} else if (bundle.containsKey(Constants.MEDIA_BEST_OF) && bundle.getBoolean(Constants.MEDIA_BEST_OF)) {
|
||||
if (mediaBrowserListenableFuture != null) {
|
||||
homeViewModel.getArtistBestOf(getViewLifecycleOwner(), bundle.getParcelable(Constants.ARTIST_OBJECT)).observe(getViewLifecycleOwner(), songs -> {
|
||||
MusicUtil.ratingFilter(songs);
|
||||
|
||||
if (songs.size() > 0) {
|
||||
MediaManager.startQueue(mediaBrowserListenableFuture, songs, 0);
|
||||
activity.setBottomSheetInPeek(true);
|
||||
|
||||
@@ -254,7 +254,12 @@ public class LibraryFragment extends Fragment implements ClickCallback {
|
||||
|
||||
private void refreshPlaylistView() {
|
||||
final Handler handler = new Handler();
|
||||
final Runnable runnable = () -> libraryViewModel.refreshPlaylistSample(getViewLifecycleOwner());
|
||||
|
||||
final Runnable runnable = () -> {
|
||||
if (getView() != null && bind != null && libraryViewModel != null)
|
||||
libraryViewModel.refreshPlaylistSample(getViewLifecycleOwner());
|
||||
};
|
||||
|
||||
handler.postDelayed(runnable, 100);
|
||||
}
|
||||
|
||||
|
||||
@@ -37,6 +37,7 @@ import com.google.android.material.elevation.SurfaceColors;
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
import com.google.common.util.concurrent.MoreExecutors;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
@OptIn(markerClass = UnstableApi.class)
|
||||
@@ -156,6 +157,7 @@ public class PlayerBottomSheetFragment extends Fragment {
|
||||
if (mediaMetadata.extras != null) {
|
||||
playerBottomSheetViewModel.setLiveMedia(getViewLifecycleOwner(), mediaMetadata.extras.getString("type"), mediaMetadata.extras.getString("id"));
|
||||
playerBottomSheetViewModel.setLiveArtist(getViewLifecycleOwner(), mediaMetadata.extras.getString("type"), mediaMetadata.extras.getString("artistId"));
|
||||
playerBottomSheetViewModel.setLiveDescription(mediaMetadata.extras.getString("description", null));
|
||||
|
||||
bind.playerHeaderLayout.playerHeaderMediaTitleLabel.setText(MusicUtil.getReadableString(mediaMetadata.extras.getString("title")));
|
||||
bind.playerHeaderLayout.playerHeaderMediaArtistLabel.setText(MusicUtil.getReadableString(mediaMetadata.extras.getString("artist")));
|
||||
@@ -164,6 +166,9 @@ public class PlayerBottomSheetFragment extends Fragment {
|
||||
.from(requireContext(), mediaMetadata.extras.getString("coverArtId"), CustomGlideRequest.ResourceType.Song)
|
||||
.build()
|
||||
.into(bind.playerHeaderLayout.playerHeaderMediaCoverImage);
|
||||
|
||||
bind.playerHeaderLayout.playerHeaderMediaTitleLabel.setVisibility(mediaMetadata.extras.getString("title") != null && !Objects.equals(mediaMetadata.extras.getString("title"), "") ? View.VISIBLE : View.GONE);
|
||||
bind.playerHeaderLayout.playerHeaderMediaArtistLabel.setVisibility(mediaMetadata.extras.getString("artist") != null && !Objects.equals(mediaMetadata.extras.getString("artist"), "") ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -40,6 +40,8 @@ import com.google.android.material.elevation.SurfaceColors;
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
import com.google.common.util.concurrent.MoreExecutors;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
@UnstableApi
|
||||
public class PlayerControllerFragment extends Fragment {
|
||||
private static final String TAG = "PlayerCoverFragment";
|
||||
@@ -54,6 +56,7 @@ public class PlayerControllerFragment extends Fragment {
|
||||
private Chip playerMediaExtension;
|
||||
private TextView playerMediaBitrate;
|
||||
private ConstraintLayout playerQuickActionView;
|
||||
private ImageButton playerOpenQueueButton;
|
||||
private ImageButton playerTrackInfo;
|
||||
|
||||
private MainActivity activity;
|
||||
@@ -107,13 +110,14 @@ public class PlayerControllerFragment extends Fragment {
|
||||
playerMediaExtension = bind.getRoot().findViewById(R.id.player_media_extension);
|
||||
playerMediaBitrate = bind.getRoot().findViewById(R.id.player_media_bitrate);
|
||||
playerQuickActionView = bind.getRoot().findViewById(R.id.player_quick_action_view);
|
||||
playerOpenQueueButton = bind.getRoot().findViewById(R.id.player_open_queue_button);
|
||||
playerTrackInfo = bind.getRoot().findViewById(R.id.player_info_track);
|
||||
}
|
||||
|
||||
private void initQuickActionView() {
|
||||
playerQuickActionView.setBackgroundColor(SurfaceColors.getColorForElevation(requireContext(), 8));
|
||||
|
||||
playerQuickActionView.setOnClickListener(view -> {
|
||||
playerOpenQueueButton.setOnClickListener(view -> {
|
||||
PlayerBottomSheetFragment playerBottomSheetFragment = (PlayerBottomSheetFragment) requireActivity().getSupportFragmentManager().findFragmentByTag("PlayerBottomSheet");
|
||||
if (playerBottomSheetFragment != null) {
|
||||
playerBottomSheetFragment.goToQueuePage();
|
||||
@@ -164,6 +168,9 @@ public class PlayerControllerFragment extends Fragment {
|
||||
|
||||
playerMediaTitleLabel.setSelected(true);
|
||||
playerArtistNameLabel.setSelected(true);
|
||||
|
||||
playerMediaTitleLabel.setVisibility(mediaMetadata.title != null && !Objects.equals(mediaMetadata.title, "") ? View.VISIBLE : View.GONE);
|
||||
playerArtistNameLabel.setVisibility(mediaMetadata.artist != null && !Objects.equals(mediaMetadata.artist, "") ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
|
||||
private void setMediaInfo(MediaMetadata mediaMetadata) {
|
||||
@@ -181,6 +188,14 @@ public class PlayerControllerFragment extends Fragment {
|
||||
}
|
||||
}
|
||||
|
||||
boolean isTranscodingExtension = !MusicUtil.getTranscodingFormatPreference().equals("raw");
|
||||
boolean isTranscodingBitrate = !MusicUtil.getBitratePreference().equals("0");
|
||||
|
||||
if (isTranscodingExtension || isTranscodingBitrate) {
|
||||
playerMediaExtension.setText("Transcoding");
|
||||
playerMediaBitrate.setText("requested");
|
||||
}
|
||||
|
||||
playerTrackInfo.setOnClickListener(view -> {
|
||||
TrackInfoDialog dialog = new TrackInfoDialog(mediaMetadata);
|
||||
dialog.show(activity.getSupportFragmentManager(), null);
|
||||
@@ -201,6 +216,7 @@ public class PlayerControllerFragment extends Fragment {
|
||||
bind.getRoot().setRepeatToggleModes(RepeatModeUtil.REPEAT_TOGGLE_MODE_NONE);
|
||||
bind.getRoot().findViewById(R.id.player_playback_speed_button).setVisibility(View.VISIBLE);
|
||||
bind.getRoot().findViewById(R.id.player_skip_silence_toggle_button).setVisibility(View.VISIBLE);
|
||||
bind.getRoot().findViewById(R.id.button_favorite).setVisibility(View.GONE);
|
||||
setPlaybackParameters(mediaBrowser);
|
||||
break;
|
||||
case Constants.MEDIA_TYPE_RADIO:
|
||||
@@ -212,6 +228,7 @@ public class PlayerControllerFragment extends Fragment {
|
||||
bind.getRoot().setRepeatToggleModes(RepeatModeUtil.REPEAT_TOGGLE_MODE_NONE);
|
||||
bind.getRoot().findViewById(R.id.player_playback_speed_button).setVisibility(View.GONE);
|
||||
bind.getRoot().findViewById(R.id.player_skip_silence_toggle_button).setVisibility(View.GONE);
|
||||
bind.getRoot().findViewById(R.id.button_favorite).setVisibility(View.GONE);
|
||||
setPlaybackParameters(mediaBrowser);
|
||||
break;
|
||||
case Constants.MEDIA_TYPE_MUSIC:
|
||||
@@ -224,6 +241,7 @@ public class PlayerControllerFragment extends Fragment {
|
||||
bind.getRoot().setRepeatToggleModes(RepeatModeUtil.REPEAT_TOGGLE_MODE_ALL | RepeatModeUtil.REPEAT_TOGGLE_MODE_ONE);
|
||||
bind.getRoot().findViewById(R.id.player_playback_speed_button).setVisibility(View.GONE);
|
||||
bind.getRoot().findViewById(R.id.player_skip_silence_toggle_button).setVisibility(View.GONE);
|
||||
bind.getRoot().findViewById(R.id.button_favorite).setVisibility(View.VISIBLE);
|
||||
resetPlaybackParameters(mediaBrowser);
|
||||
break;
|
||||
}
|
||||
@@ -278,7 +296,7 @@ public class PlayerControllerFragment extends Fragment {
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putParcelable(Constants.ARTIST_OBJECT, artist);
|
||||
NavHostFragment.findNavController(this).navigate(R.id.artistPageFragment, bundle);
|
||||
activity.collapseBottomSheet();
|
||||
activity.collapseBottomSheetDelayed();
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
@@ -80,7 +80,10 @@ public class PlayerCoverFragment extends Fragment {
|
||||
|
||||
handler.removeCallbacksAndMessages(null);
|
||||
|
||||
final Runnable runnable = () -> bind.nowPlayingTapButton.setVisibility(View.GONE);
|
||||
final Runnable runnable = () -> {
|
||||
if (bind != null) bind.nowPlayingTapButton.setVisibility(View.GONE);
|
||||
};
|
||||
|
||||
handler.postDelayed(runnable, 10000);
|
||||
}
|
||||
|
||||
|
||||
@@ -44,18 +44,25 @@ public class PlayerLyricsFragment extends Fragment {
|
||||
|
||||
private void initLyrics() {
|
||||
playerBottomSheetViewModel.getLiveLyrics().observe(getViewLifecycleOwner(), lyrics -> {
|
||||
if (bind != null) {
|
||||
if (lyrics == null || lyrics.trim().equals("")) {
|
||||
bind.nowPlayingSongLyricsTextView.setVisibility(View.GONE);
|
||||
bind.emptyDescriptionImageView.setVisibility(View.VISIBLE);
|
||||
bind.titleEmptyDescriptionLabel.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
bind.nowPlayingSongLyricsTextView.setText(MusicUtil.getReadableLyrics(lyrics));
|
||||
bind.nowPlayingSongLyricsTextView.setVisibility(View.VISIBLE);
|
||||
bind.emptyDescriptionImageView.setVisibility(View.GONE);
|
||||
bind.titleEmptyDescriptionLabel.setVisibility(View.GONE);
|
||||
playerBottomSheetViewModel.getLiveDescription().observe(getViewLifecycleOwner(), description -> {
|
||||
if (bind != null) {
|
||||
if (lyrics != null && !lyrics.trim().equals("")) {
|
||||
bind.nowPlayingSongLyricsTextView.setText(MusicUtil.getReadableLyrics(lyrics));
|
||||
bind.nowPlayingSongLyricsTextView.setVisibility(View.VISIBLE);
|
||||
bind.emptyDescriptionImageView.setVisibility(View.GONE);
|
||||
bind.titleEmptyDescriptionLabel.setVisibility(View.GONE);
|
||||
} else if (description != null && !description.trim().equals("")) {
|
||||
bind.nowPlayingSongLyricsTextView.setText(MusicUtil.getReadableLyrics(description));
|
||||
bind.nowPlayingSongLyricsTextView.setVisibility(View.VISIBLE);
|
||||
bind.emptyDescriptionImageView.setVisibility(View.GONE);
|
||||
bind.titleEmptyDescriptionLabel.setVisibility(View.GONE);
|
||||
} else {
|
||||
bind.nowPlayingSongLyricsTextView.setVisibility(View.GONE);
|
||||
bind.emptyDescriptionImageView.setVisibility(View.VISIBLE);
|
||||
bind.titleEmptyDescriptionLabel.setVisibility(View.VISIBLE);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -93,6 +93,7 @@ public class PlayerQueueFragment extends Fragment implements ClickCallback {
|
||||
try {
|
||||
MediaBrowser mediaBrowser = mediaBrowserListenableFuture.get();
|
||||
initShuffleButton(mediaBrowser);
|
||||
initCleanButton(mediaBrowser);
|
||||
} catch (Exception exception) {
|
||||
exception.printStackTrace();
|
||||
}
|
||||
@@ -197,6 +198,16 @@ public class PlayerQueueFragment extends Fragment implements ClickCallback {
|
||||
});
|
||||
}
|
||||
|
||||
private void initCleanButton(MediaBrowser mediaBrowser) {
|
||||
bind.playerCleanQueueButton.setOnClickListener(view -> {
|
||||
int startPosition = mediaBrowser.getCurrentMediaItemIndex() + 1;
|
||||
int endPosition = playerSongQueueAdapter.getItems().size();
|
||||
|
||||
MediaManager.removeRange(mediaBrowserListenableFuture, playerSongQueueAdapter.getItems(), startPosition, endPosition);
|
||||
bind.playerQueueRecyclerView.getAdapter().notifyItemRangeRemoved(startPosition, endPosition);
|
||||
});
|
||||
}
|
||||
|
||||
private void updateNowPlayingItem() {
|
||||
playerSongQueueAdapter.notifyDataSetChanged();
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.WindowManager;
|
||||
|
||||
import androidx.activity.result.ActivityResultLauncher;
|
||||
import androidx.activity.result.contract.ActivityResultContracts;
|
||||
@@ -91,6 +92,7 @@ public class SettingsFragment extends PreferenceFragmentCompat {
|
||||
actionSyncStarredTracks();
|
||||
actionChangeDownloadStorage();
|
||||
actionDeleteDownloadStorage();
|
||||
actionKeepScreenOn();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -248,4 +250,17 @@ public class SettingsFragment extends PreferenceFragmentCompat {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void actionKeepScreenOn() {
|
||||
findPreference("always_on_display").setOnPreferenceChangeListener((preference, newValue) -> {
|
||||
if (newValue instanceof Boolean) {
|
||||
if ((Boolean) newValue) {
|
||||
activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
||||
} else {
|
||||
activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,6 +39,7 @@ import com.cappielloantonio.tempo.util.Preferences;
|
||||
import com.cappielloantonio.tempo.viewmodel.AlbumBottomSheetViewModel;
|
||||
import com.cappielloantonio.tempo.viewmodel.HomeViewModel;
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialogFragment;
|
||||
import com.google.android.material.snackbar.Snackbar;
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -114,6 +115,8 @@ public class AlbumBottomSheetDialog extends BottomSheetDialogFragment implements
|
||||
|
||||
@Override
|
||||
public void onLoadMedia(List<?> media) {
|
||||
MusicUtil.ratingFilter((ArrayList<Child>) media);
|
||||
|
||||
if (media.size() > 0) {
|
||||
MediaManager.startQueue(mediaBrowserListenableFuture, (ArrayList<Child>) media, 0);
|
||||
((MainActivity) requireActivity()).setBottomSheetInPeek(true);
|
||||
@@ -198,6 +201,9 @@ public class AlbumBottomSheetDialog extends BottomSheetDialogFragment implements
|
||||
clipboardManager.setPrimaryClip(clipData);
|
||||
refreshShares();
|
||||
dismissBottomSheet();
|
||||
} else {
|
||||
Toast.makeText(requireContext(), getString(R.string.share_unsupported_error), Toast.LENGTH_SHORT).show();
|
||||
dismissBottomSheet();
|
||||
}
|
||||
}));
|
||||
|
||||
|
||||
@@ -89,6 +89,8 @@ public class ArtistBottomSheetDialog extends BottomSheetDialogFragment implement
|
||||
ArtistRepository artistRepository = new ArtistRepository();
|
||||
|
||||
artistRepository.getInstantMix(artist, 20).observe(getViewLifecycleOwner(), songs -> {
|
||||
MusicUtil.ratingFilter(songs);
|
||||
|
||||
if (songs.size() > 0) {
|
||||
MediaManager.startQueue(mediaBrowserListenableFuture, songs, 0);
|
||||
((MainActivity) requireActivity()).setBottomSheetInPeek(true);
|
||||
@@ -102,6 +104,8 @@ public class ArtistBottomSheetDialog extends BottomSheetDialogFragment implement
|
||||
playRandom.setOnClickListener(v -> {
|
||||
ArtistRepository artistRepository = new ArtistRepository();
|
||||
artistRepository.getRandomSong(artist, 50).observe(getViewLifecycleOwner(), songs -> {
|
||||
MusicUtil.ratingFilter(songs);
|
||||
|
||||
if (songs.size() > 0) {
|
||||
MediaManager.startQueue(mediaBrowserListenableFuture, songs, 0);
|
||||
((MainActivity) requireActivity()).setBottomSheetInPeek(true);
|
||||
|
||||
@@ -114,6 +114,8 @@ public class SongBottomSheetDialog extends BottomSheetDialogFragment implements
|
||||
((MainActivity) requireActivity()).setBottomSheetInPeek(true);
|
||||
|
||||
songBottomSheetViewModel.getInstantMix(getViewLifecycleOwner(), song).observe(getViewLifecycleOwner(), songs -> {
|
||||
MusicUtil.ratingFilter(songs);
|
||||
|
||||
if (songs == null) {
|
||||
dismissBottomSheet();
|
||||
return;
|
||||
@@ -196,6 +198,8 @@ public class SongBottomSheetDialog extends BottomSheetDialogFragment implements
|
||||
dismissBottomSheet();
|
||||
}));
|
||||
|
||||
goToAlbum.setVisibility(songBottomSheetViewModel.getSong().getAlbumId() != null ? View.VISIBLE : View.GONE);
|
||||
|
||||
TextView goToArtist = view.findViewById(R.id.go_to_artist_text_view);
|
||||
goToArtist.setOnClickListener(v -> songBottomSheetViewModel.getArtist().observe(getViewLifecycleOwner(), artist -> {
|
||||
if (artist != null) {
|
||||
@@ -208,6 +212,8 @@ public class SongBottomSheetDialog extends BottomSheetDialogFragment implements
|
||||
dismissBottomSheet();
|
||||
}));
|
||||
|
||||
goToArtist.setVisibility(songBottomSheetViewModel.getSong().getArtistId() != null ? View.VISIBLE : View.GONE);
|
||||
|
||||
TextView share = view.findViewById(R.id.share_text_view);
|
||||
share.setOnClickListener(v -> songBottomSheetViewModel.shareTrack().observe(getViewLifecycleOwner(), sharedTrack -> {
|
||||
if (sharedTrack != null) {
|
||||
@@ -216,6 +222,9 @@ public class SongBottomSheetDialog extends BottomSheetDialogFragment implements
|
||||
clipboardManager.setPrimaryClip(clipData);
|
||||
refreshShares();
|
||||
dismissBottomSheet();
|
||||
} else {
|
||||
Toast.makeText(requireContext(), getString(R.string.share_unsupported_error), Toast.LENGTH_SHORT).show();
|
||||
dismissBottomSheet();
|
||||
}
|
||||
}));
|
||||
|
||||
|
||||
@@ -64,7 +64,7 @@ public class MappingUtil {
|
||||
bundle.putLong("starred", media.getStarred() != null ? media.getStarred().getTime() : 0);
|
||||
bundle.putString("albumId", media.getAlbumId());
|
||||
bundle.putString("artistId", media.getArtistId());
|
||||
bundle.putString("type", media.getType());
|
||||
bundle.putString("type", Constants.MEDIA_TYPE_MUSIC);
|
||||
bundle.putLong("bookmarkPosition", media.getBookmarkPosition() != null ? media.getBookmarkPosition() : 0);
|
||||
bundle.putInt("originalWidth", media.getOriginalWidth() != null ? media.getOriginalWidth() : 0);
|
||||
bundle.putInt("originalHeight", media.getOriginalHeight() != null ? media.getOriginalHeight() : 0);
|
||||
@@ -82,6 +82,8 @@ public class MappingUtil {
|
||||
.setArtist(MusicUtil.getReadableString(media.getArtist()))
|
||||
.setArtworkUri(artworkUri)
|
||||
.setExtras(bundle)
|
||||
.setIsBrowsable(false)
|
||||
.setIsPlayable(true)
|
||||
.build()
|
||||
)
|
||||
.setRequestMetadata(
|
||||
@@ -116,6 +118,8 @@ public class MappingUtil {
|
||||
.setReleaseYear(media.getYear() != null ? media.getYear() : 0)
|
||||
.setAlbumTitle(MusicUtil.getReadableString(media.getAlbum()))
|
||||
.setArtist(MusicUtil.getReadableString(media.getArtist()))
|
||||
.setIsBrowsable(false)
|
||||
.setIsPlayable(true)
|
||||
.build()
|
||||
)
|
||||
.setRequestMetadata(
|
||||
@@ -145,6 +149,8 @@ public class MappingUtil {
|
||||
.setTitle(internetRadioStation.getName())
|
||||
.setArtist(internetRadioStation.getStreamUrl())
|
||||
.setExtras(bundle)
|
||||
.setIsBrowsable(false)
|
||||
.setIsPlayable(true)
|
||||
.build()
|
||||
)
|
||||
.setRequestMetadata(
|
||||
@@ -169,31 +175,18 @@ public class MappingUtil {
|
||||
bundle.putString("title", podcastEpisode.getTitle());
|
||||
bundle.putString("album", podcastEpisode.getAlbum());
|
||||
bundle.putString("artist", podcastEpisode.getArtist());
|
||||
bundle.putInt("track", podcastEpisode.getTrack() != null ? podcastEpisode.getTrack() : 0);
|
||||
bundle.putInt("year", podcastEpisode.getYear() != null ? podcastEpisode.getYear() : 0);
|
||||
bundle.putString("genre", podcastEpisode.getGenre());
|
||||
bundle.putString("coverArtId", podcastEpisode.getCoverArtId());
|
||||
bundle.putLong("size", podcastEpisode.getSize() != null ? podcastEpisode.getSize() : 0);
|
||||
bundle.putString("contentType", podcastEpisode.getContentType());
|
||||
bundle.putString("suffix", podcastEpisode.getSuffix());
|
||||
bundle.putString("transcodedContentType", podcastEpisode.getTranscodedContentType());
|
||||
bundle.putString("transcodedSuffix", podcastEpisode.getTranscodedSuffix());
|
||||
bundle.putInt("duration", podcastEpisode.getDuration() != null ? podcastEpisode.getDuration() : 0);
|
||||
bundle.putInt("bitrate", podcastEpisode.getBitrate() != null ? podcastEpisode.getBitrate() : 0);
|
||||
bundle.putString("path", podcastEpisode.getPath());
|
||||
bundle.putBoolean("isVideo", podcastEpisode.isVideo());
|
||||
bundle.putInt("userRating", podcastEpisode.getUserRating() != null ? podcastEpisode.getUserRating() : 0);
|
||||
bundle.putDouble("averageRating", podcastEpisode.getAverageRating() != null ? podcastEpisode.getAverageRating() : 0);
|
||||
bundle.putLong("playCount", podcastEpisode.getPlayCount() != null ? podcastEpisode.getPlayCount() : 0);
|
||||
bundle.putInt("discNumber", podcastEpisode.getDiscNumber() != null ? podcastEpisode.getDiscNumber() : 0);
|
||||
bundle.putLong("created", podcastEpisode.getCreated() != null ? podcastEpisode.getCreated().getTime() : 0);
|
||||
bundle.putLong("starred", podcastEpisode.getStarred() != null ? podcastEpisode.getStarred().getTime() : 0);
|
||||
bundle.putString("albumId", podcastEpisode.getAlbumId());
|
||||
bundle.putString("artistId", podcastEpisode.getArtistId());
|
||||
bundle.putString("type", podcastEpisode.getType());
|
||||
bundle.putLong("bookmarkPosition", podcastEpisode.getBookmarkPosition() != null ? podcastEpisode.getBookmarkPosition() : 0);
|
||||
bundle.putInt("originalWidth", podcastEpisode.getOriginalWidth() != null ? podcastEpisode.getOriginalWidth() : 0);
|
||||
bundle.putInt("originalHeight", podcastEpisode.getOriginalHeight() != null ? podcastEpisode.getOriginalHeight() : 0);
|
||||
bundle.putString("description", podcastEpisode.getDescription());
|
||||
bundle.putString("type", Constants.MEDIA_TYPE_PODCAST);
|
||||
bundle.putString("uri", uri.toString());
|
||||
|
||||
MediaItem item = new MediaItem.Builder()
|
||||
@@ -201,13 +194,13 @@ public class MappingUtil {
|
||||
.setMediaMetadata(
|
||||
new MediaMetadata.Builder()
|
||||
.setTitle(MusicUtil.getReadableString(podcastEpisode.getTitle()))
|
||||
.setTrackNumber(podcastEpisode.getTrack() != null ? podcastEpisode.getTrack() : 0)
|
||||
.setDiscNumber(podcastEpisode.getDiscNumber() != null ? podcastEpisode.getDiscNumber() : 0)
|
||||
.setReleaseYear(podcastEpisode.getYear() != null ? podcastEpisode.getYear() : 0)
|
||||
.setAlbumTitle(MusicUtil.getReadableString(podcastEpisode.getAlbum()))
|
||||
.setArtist(MusicUtil.getReadableString(podcastEpisode.getArtist()))
|
||||
.setArtworkUri(artworkUri)
|
||||
.setExtras(bundle)
|
||||
.setIsBrowsable(false)
|
||||
.setIsPlayable(true)
|
||||
.build()
|
||||
)
|
||||
.setRequestMetadata(
|
||||
@@ -216,12 +209,6 @@ public class MappingUtil {
|
||||
.setExtras(bundle)
|
||||
.build()
|
||||
)
|
||||
/* .setClippingConfiguration(
|
||||
new MediaItem.ClippingConfiguration.Builder()
|
||||
.setStartPositionMs(0)
|
||||
.setEndPositionMs(podcastEpisode.getDuration() * 1000)
|
||||
.build()
|
||||
) */
|
||||
.setMimeType(MimeTypes.BASE_TYPE_AUDIO)
|
||||
.setUri(uri)
|
||||
.build();
|
||||
|
||||
@@ -128,16 +128,18 @@ public class MusicUtil {
|
||||
}
|
||||
|
||||
|
||||
public static String getReadableDurationString(long duration, boolean millis) {
|
||||
public static String getReadableDurationString(Long duration, boolean millis) {
|
||||
long lenght = duration != null ? duration : 0;
|
||||
|
||||
long minutes;
|
||||
long seconds;
|
||||
|
||||
if (millis) {
|
||||
minutes = (duration / 1000) / 60;
|
||||
seconds = (duration / 1000) % 60;
|
||||
minutes = (lenght / 1000) / 60;
|
||||
seconds = (lenght / 1000) % 60;
|
||||
} else {
|
||||
minutes = duration / 60;
|
||||
seconds = duration % 60;
|
||||
minutes = lenght / 60;
|
||||
seconds = lenght % 60;
|
||||
}
|
||||
|
||||
if (minutes < 60) {
|
||||
@@ -149,6 +151,11 @@ public class MusicUtil {
|
||||
}
|
||||
}
|
||||
|
||||
public static String getReadableDurationString(Integer duration, boolean millis) {
|
||||
long lenght = duration != null ? duration : 0;
|
||||
return getReadableDurationString(lenght, millis);
|
||||
}
|
||||
|
||||
public static String getReadablePodcastDurationString(long duration) {
|
||||
long minutes = duration / 60;
|
||||
|
||||
@@ -307,4 +314,17 @@ public class MusicUtil {
|
||||
private static ConnectivityManager getConnectivityManager() {
|
||||
return (ConnectivityManager) App.getContext().getSystemService(Context.CONNECTIVITY_SERVICE);
|
||||
}
|
||||
}
|
||||
|
||||
public static void ratingFilter(List<Child> toFilter) {
|
||||
if (toFilter == null || toFilter.isEmpty()) return;
|
||||
|
||||
List<Child> filtered = toFilter
|
||||
.stream()
|
||||
.filter(child -> (child.getUserRating() != null && child.getUserRating() >= Preferences.getMinStarRatingAccepted()) || (child.getUserRating() == null))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
toFilter.clear();
|
||||
|
||||
toFilter.addAll(filtered);
|
||||
}
|
||||
}
|
||||
@@ -40,7 +40,14 @@ object Preferences {
|
||||
private const val MAX_BITRATE_DOWNLOAD = "max_bitrate_download"
|
||||
private const val AUDIO_TRANSCODE_FORMAT_DOWNLOAD = "audio_transcode_format_download"
|
||||
private const val SHARE = "share"
|
||||
private const val SCROBBLING = "scrobbling"
|
||||
private const val ESTIMATE_CONTENT_LENGTH = "estimate_content_length"
|
||||
private const val BUFFERING_STRATEGY = "buffering_strategy"
|
||||
private const val SKIP_MIN_STAR_RATING = "skip_min_star_rating"
|
||||
private const val MIN_STAR_RATING = "min_star_rating"
|
||||
private const val ARTIST_ALBUM_LAYOUT = "artist_album_layout"
|
||||
private const val ALWAYS_ON_DISPLAY = "always_on_display"
|
||||
|
||||
|
||||
@JvmStatic
|
||||
fun getServer(): String? {
|
||||
@@ -321,8 +328,39 @@ object Preferences {
|
||||
return App.getInstance().preferences.getBoolean(SHARE, false)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun isScrobblingEnabled(): Boolean {
|
||||
return App.getInstance().preferences.getBoolean(SCROBBLING, true)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun askForEstimateContentLength(): Boolean {
|
||||
return App.getInstance().preferences.getBoolean(ESTIMATE_CONTENT_LENGTH, false)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun getBufferingStrategy(): Double {
|
||||
return App.getInstance().preferences.getString(BUFFERING_STRATEGY, "1")!!.toDouble()
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun getMinStarRatingAccepted(): Int {
|
||||
return App.getInstance().preferences.getInt(MIN_STAR_RATING, 0)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun isArtistAlbumLayoutHorizontal(): Boolean {
|
||||
return App.getInstance().preferences.getBoolean(ARTIST_ALBUM_LAYOUT, true)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun setArtistAlbumLayout(isArtistAlbumLayoutHorizontal: Boolean) {
|
||||
App.getInstance().preferences.edit().putBoolean(ARTIST_ALBUM_LAYOUT, isArtistAlbumLayoutHorizontal)
|
||||
.apply()
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun isDisplayAlwaysOn(): Boolean {
|
||||
return App.getInstance().preferences.getBoolean(ALWAYS_ON_DISPLAY, false)
|
||||
}
|
||||
}
|
||||
@@ -22,6 +22,7 @@ public class AlbumCatalogueViewModel extends AndroidViewModel {
|
||||
private final MutableLiveData<List<AlbumID3>> albumList = new MutableLiveData<>(new ArrayList<>());
|
||||
|
||||
private int page = 0;
|
||||
private Status status = Status.STOPPED;
|
||||
|
||||
public AlbumCatalogueViewModel(@NonNull Application application) {
|
||||
super(application);
|
||||
@@ -31,7 +32,18 @@ public class AlbumCatalogueViewModel extends AndroidViewModel {
|
||||
return albumList;
|
||||
}
|
||||
|
||||
public void loadAlbums(int size) {
|
||||
public void loadAlbums() {
|
||||
page = 0;
|
||||
status = Status.RUNNING;
|
||||
albumList.setValue(new ArrayList<>());
|
||||
loadAlbums(500);
|
||||
}
|
||||
|
||||
public void stopLoading() {
|
||||
status = Status.STOPPED;
|
||||
}
|
||||
|
||||
private void loadAlbums(int size) {
|
||||
retrieveAlbums(new MediaCallback() {
|
||||
@Override
|
||||
public void onError(Exception exception) {
|
||||
@@ -39,15 +51,19 @@ public class AlbumCatalogueViewModel extends AndroidViewModel {
|
||||
|
||||
@Override
|
||||
public void onLoadMedia(List<?> media) {
|
||||
List<AlbumID3> liveAlbum = albumList.getValue();
|
||||
if (status == Status.STOPPED) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (liveAlbum == null) liveAlbum = new ArrayList<>();
|
||||
List<AlbumID3> liveAlbum = albumList.getValue();
|
||||
|
||||
liveAlbum.addAll((List<AlbumID3>) media);
|
||||
albumList.setValue(liveAlbum);
|
||||
|
||||
if (media.size() == size) {
|
||||
loadAlbums(size);
|
||||
} else {
|
||||
status = Status.STOPPED;
|
||||
}
|
||||
}
|
||||
}, size, size * page++);
|
||||
@@ -61,9 +77,8 @@ public class AlbumCatalogueViewModel extends AndroidViewModel {
|
||||
.enqueue(new Callback<ApiResponse>() {
|
||||
@Override
|
||||
public void onResponse(@NonNull Call<ApiResponse> call, @NonNull retrofit2.Response<ApiResponse> response) {
|
||||
if (response.isSuccessful() && response.body() != null && response.body().getSubsonicResponse().getAlbumList2() != null) {
|
||||
List<AlbumID3> albumList = new ArrayList<>();
|
||||
albumList.addAll(response.body().getSubsonicResponse().getAlbumList2().getAlbums());
|
||||
if (response.isSuccessful() && response.body() != null && response.body().getSubsonicResponse().getAlbumList2() != null && response.body().getSubsonicResponse().getAlbumList2().getAlbums() != null) {
|
||||
List<AlbumID3> albumList = new ArrayList<>(response.body().getSubsonicResponse().getAlbumList2().getAlbums());
|
||||
callback.onLoadMedia(albumList);
|
||||
}
|
||||
}
|
||||
@@ -74,4 +89,9 @@ public class AlbumCatalogueViewModel extends AndroidViewModel {
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private enum Status {
|
||||
RUNNING,
|
||||
STOPPED
|
||||
}
|
||||
}
|
||||
@@ -15,8 +15,6 @@ public class IndexViewModel extends AndroidViewModel {
|
||||
|
||||
private MusicFolder musicFolder;
|
||||
|
||||
private MutableLiveData<Indexes> indexes = new MutableLiveData<>(null);
|
||||
|
||||
public IndexViewModel(@NonNull Application application) {
|
||||
super(application);
|
||||
|
||||
|
||||
@@ -42,6 +42,7 @@ public class PlayerBottomSheetViewModel extends AndroidViewModel {
|
||||
private final FavoriteRepository favoriteRepository;
|
||||
|
||||
private final MutableLiveData<String> lyricsLiveData = new MutableLiveData<>(null);
|
||||
private final MutableLiveData<String> descriptionLiveData = new MutableLiveData<>(null);
|
||||
|
||||
private final MutableLiveData<Child> liveMedia = new MutableLiveData<>(null);
|
||||
private final MutableLiveData<ArtistID3> liveArtist = new MutableLiveData<>(null);
|
||||
@@ -137,6 +138,7 @@ public class PlayerBottomSheetViewModel extends AndroidViewModel {
|
||||
switch (mediaType) {
|
||||
case Constants.MEDIA_TYPE_MUSIC:
|
||||
songRepository.getSong(mediaId).observe(owner, liveMedia::postValue);
|
||||
descriptionLiveData.postValue(null);
|
||||
break;
|
||||
case Constants.MEDIA_TYPE_PODCAST:
|
||||
liveMedia.postValue(null);
|
||||
@@ -162,6 +164,14 @@ public class PlayerBottomSheetViewModel extends AndroidViewModel {
|
||||
}
|
||||
}
|
||||
|
||||
public void setLiveDescription(String description) {
|
||||
descriptionLiveData.postValue(description);
|
||||
}
|
||||
|
||||
public LiveData<String> getLiveDescription() {
|
||||
return descriptionLiveData;
|
||||
}
|
||||
|
||||
public LiveData<List<Child>> getMediaInstantMix(LifecycleOwner owner, Child media) {
|
||||
instantMix.setValue(Collections.emptyList());
|
||||
|
||||
@@ -186,8 +196,4 @@ public class PlayerBottomSheetViewModel extends AndroidViewModel {
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void emptyPlayQueue() {
|
||||
queueRepository.savePlayQueue(null, null, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,359 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/now_playing_media_controller_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<androidx.constraintlayout.widget.Guideline
|
||||
android:id="@+id/vertical_guideline"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
app:layout_constraintGuide_percent="0.45" />
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/player_media_quality_sector"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginVertical="8dp"
|
||||
android:gravity="center"
|
||||
android:orientation="horizontal"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@+id/vertical_guideline"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<com.google.android.material.chip.Chip
|
||||
android:id="@+id/player_media_extension"
|
||||
style="@style/Widget.Material3.Chip.Suggestion"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:checked="true"
|
||||
android:clickable="false"
|
||||
android:text="Unknown"
|
||||
app:chipStrokeWidth="0dp"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@id/player_media_bitrate"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintHorizontal_chainStyle="packed"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/player_media_bitrate"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
app:layout_constraintTop_toTopOf="@id/player_media_extension"
|
||||
app:layout_constraintBottom_toBottomOf="@id/player_media_extension"
|
||||
app:layout_constraintStart_toEndOf="@id/player_media_extension"
|
||||
app:layout_constraintEnd_toEndOf="parent"/>
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/player_info_track"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="16dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:background="?attr/selectableItemBackgroundBorderless"
|
||||
android:scaleType="fitCenter"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@id/player_media_extension"
|
||||
app:layout_constraintBottom_toBottomOf="@id/player_media_extension"
|
||||
app:srcCompat="@drawable/ic_info_stream"
|
||||
app:tint="?attr/colorOnPrimaryContainer" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<androidx.viewpager2.widget.ViewPager2
|
||||
android:id="@+id/player_media_cover_view_pager"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_margin="12dp"
|
||||
android:clipToPadding="false"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@+id/vertical_guideline"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/player_media_title_label"
|
||||
style="@style/HeadlineLarge"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="24dp"
|
||||
android:layout_marginEnd="24dp"
|
||||
android:ellipsize="marquee"
|
||||
android:singleLine="true"
|
||||
android:text="@string/label_placeholder"
|
||||
app:layout_constraintVertical_chainStyle="packed"
|
||||
app:layout_constraintBottom_toTopOf="@+id/player_artist_name_label"
|
||||
app:layout_constraintEnd_toStartOf="@+id/button_favorite"
|
||||
app:layout_constraintStart_toEndOf="@+id/vertical_guideline"
|
||||
app:layout_constraintTop_toBottomOf="@+id/player_media_quality_sector" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/player_artist_name_label"
|
||||
style="@style/TitleMedium"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="24dp"
|
||||
android:layout_marginEnd="24dp"
|
||||
android:ellipsize="marquee"
|
||||
android:singleLine="true"
|
||||
android:text="@string/label_placeholder"
|
||||
app:layout_constraintEnd_toStartOf="@+id/button_favorite"
|
||||
app:layout_constraintStart_toEndOf="@+id/vertical_guideline"
|
||||
app:layout_constraintTop_toBottomOf="@+id/player_media_title_label"
|
||||
app:layout_constraintBottom_toTopOf="@id/exo_progress"/>
|
||||
|
||||
<ToggleButton
|
||||
android:id="@+id/button_favorite"
|
||||
android:layout_width="26dp"
|
||||
android:layout_height="26dp"
|
||||
android:layout_marginEnd="24dp"
|
||||
android:background="@drawable/button_favorite_selector"
|
||||
android:checked="false"
|
||||
android:text=""
|
||||
android:textOff=""
|
||||
android:textOn=""
|
||||
app:layout_constraintBottom_toBottomOf="@+id/player_media_title_label"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@+id/player_media_title_label" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/exo_position"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginStart="24dp"
|
||||
android:paddingBottom="4dp"
|
||||
android:text="@string/label_placeholder"
|
||||
android:textColor="@color/titleTextColor"
|
||||
android:textSize="12sp"
|
||||
app:layout_constraintStart_toEndOf="@+id/vertical_guideline"
|
||||
app:layout_constraintTop_toBottomOf="@+id/exo_progress" />
|
||||
|
||||
<androidx.media3.ui.DefaultTimeBar
|
||||
android:id="@id/exo_progress"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="20dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
app:bar_height="2dp"
|
||||
app:buffered_color="?attr/colorOnSecondaryContainer"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@+id/vertical_guideline"
|
||||
app:layout_constraintTop_toBottomOf="@+id/player_artist_name_label"
|
||||
app:layout_constraintBottom_toTopOf="@+id/player_play_pause_placeholder_view"
|
||||
app:played_color="?attr/colorOnPrimaryContainer"
|
||||
app:scrubber_color="?attr/colorOnPrimaryContainer"
|
||||
app:unplayed_color="?attr/colorPrimaryContainer" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/exo_duration"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginEnd="24dp"
|
||||
android:paddingBottom="4dp"
|
||||
android:text="@string/label_placeholder"
|
||||
android:textColor="@color/titleTextColor"
|
||||
android:textSize="12sp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/exo_progress" />
|
||||
|
||||
<View
|
||||
android:id="@+id/placeholder_view_left"
|
||||
android:layout_width="32dp"
|
||||
android:layout_height="32dp"
|
||||
android:layout_marginStart="24dp"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/placeholder_view_middle_left"
|
||||
app:layout_constraintEnd_toStartOf="@+id/placeholder_view_middle_left"
|
||||
app:layout_constraintStart_toEndOf="@+id/vertical_guideline"
|
||||
app:layout_constraintTop_toTopOf="@+id/placeholder_view_middle_left" />
|
||||
|
||||
<View
|
||||
android:id="@+id/placeholder_view_middle_left"
|
||||
android:layout_width="42dp"
|
||||
android:layout_height="42dp"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/player_play_pause_placeholder_view"
|
||||
app:layout_constraintEnd_toStartOf="@+id/player_play_pause_placeholder_view"
|
||||
app:layout_constraintStart_toEndOf="@id/placeholder_view_left"
|
||||
app:layout_constraintTop_toTopOf="@+id/player_play_pause_placeholder_view" />
|
||||
|
||||
<View
|
||||
android:id="@+id/player_play_pause_placeholder_view"
|
||||
android:layout_width="70dp"
|
||||
android:layout_height="70dp"
|
||||
android:layout_marginTop="36dp"
|
||||
app:layout_constraintBottom_toTopOf="@+id/player_quick_action_view"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@+id/vertical_guideline"
|
||||
app:layout_constraintTop_toBottomOf="@+id/exo_progress"/>
|
||||
|
||||
<View
|
||||
android:id="@+id/placeholder_view_middle_right"
|
||||
android:layout_width="42dp"
|
||||
android:layout_height="42dp"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/player_play_pause_placeholder_view"
|
||||
app:layout_constraintEnd_toStartOf="@id/placeholder_view_right"
|
||||
app:layout_constraintStart_toEndOf="@+id/player_play_pause_placeholder_view"
|
||||
app:layout_constraintTop_toTopOf="@+id/player_play_pause_placeholder_view" />
|
||||
|
||||
<View
|
||||
android:id="@+id/placeholder_view_right"
|
||||
android:layout_width="32dp"
|
||||
android:layout_height="32dp"
|
||||
android:layout_marginEnd="24dp"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/placeholder_view_middle_right"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@+id/placeholder_view_middle_right"
|
||||
app:layout_constraintTop_toTopOf="@+id/placeholder_view_middle_right" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/player_playback_speed_button"
|
||||
style="@style/Widget.Material3.Button.TextButton"
|
||||
android:layout_width="64dp"
|
||||
android:layout_height="64dp"
|
||||
android:layout_marginStart="24dp"
|
||||
android:insetLeft="0dp"
|
||||
android:insetTop="0dp"
|
||||
android:insetRight="0dp"
|
||||
android:insetBottom="0dp"
|
||||
app:cornerRadius="30dp"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/placeholder_view_middle_left"
|
||||
app:layout_constraintEnd_toStartOf="@+id/placeholder_view_middle_left"
|
||||
app:layout_constraintStart_toEndOf="@+id/vertical_guideline"
|
||||
app:layout_constraintTop_toTopOf="@+id/placeholder_view_middle_left"
|
||||
app:tint="?attr/colorOnPrimaryContainer" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/exo_shuffle"
|
||||
android:layout_width="32dp"
|
||||
android:layout_height="32dp"
|
||||
android:layout_marginStart="24dp"
|
||||
android:background="?attr/selectableItemBackgroundBorderless"
|
||||
android:scaleType="fitCenter"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/placeholder_view_middle_left"
|
||||
app:layout_constraintEnd_toStartOf="@+id/placeholder_view_middle_left"
|
||||
app:layout_constraintStart_toEndOf="@+id/vertical_guideline"
|
||||
app:layout_constraintTop_toTopOf="@+id/placeholder_view_middle_left"
|
||||
app:srcCompat="@drawable/ic_shuffle"
|
||||
app:tint="?attr/colorOnPrimaryContainer" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/exo_rew"
|
||||
android:layout_width="42dp"
|
||||
android:layout_height="42dp"
|
||||
android:background="?attr/selectableItemBackgroundBorderless"
|
||||
android:scaleType="fitCenter"
|
||||
android:src="@drawable/ic_replay"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/player_play_pause_placeholder_view"
|
||||
app:layout_constraintEnd_toStartOf="@+id/player_play_pause_placeholder_view"
|
||||
app:layout_constraintStart_toEndOf="@id/placeholder_view_left"
|
||||
app:layout_constraintTop_toTopOf="@+id/player_play_pause_placeholder_view"
|
||||
app:tint="?attr/colorOnPrimaryContainer" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/exo_prev"
|
||||
android:layout_width="42dp"
|
||||
android:layout_height="42dp"
|
||||
android:background="?attr/selectableItemBackgroundBorderless"
|
||||
android:scaleType="fitCenter"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/player_play_pause_placeholder_view"
|
||||
app:layout_constraintEnd_toStartOf="@+id/player_play_pause_placeholder_view"
|
||||
app:layout_constraintStart_toEndOf="@id/placeholder_view_left"
|
||||
app:layout_constraintTop_toTopOf="@+id/player_play_pause_placeholder_view"
|
||||
app:srcCompat="@drawable/ic_skip_previous"
|
||||
app:tint="?attr/colorOnPrimaryContainer" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@id/exo_play_pause"
|
||||
style="@style/ExoStyledControls.Button.Center.PlayPause"
|
||||
android:layout_width="80dp"
|
||||
android:layout_height="80dp"
|
||||
app:layout_constraintTop_toTopOf="@+id/player_play_pause_placeholder_view"
|
||||
app:layout_constraintEnd_toEndOf="@+id/player_play_pause_placeholder_view"
|
||||
app:layout_constraintStart_toStartOf="@+id/player_play_pause_placeholder_view"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/player_play_pause_placeholder_view"
|
||||
app:tint="?attr/colorOnPrimaryContainer" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/exo_next"
|
||||
android:layout_width="42dp"
|
||||
android:layout_height="42dp"
|
||||
android:background="?attr/selectableItemBackgroundBorderless"
|
||||
android:scaleType="fitCenter"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/player_play_pause_placeholder_view"
|
||||
app:layout_constraintEnd_toStartOf="@id/placeholder_view_right"
|
||||
app:layout_constraintStart_toEndOf="@+id/player_play_pause_placeholder_view"
|
||||
app:layout_constraintTop_toTopOf="@+id/player_play_pause_placeholder_view"
|
||||
app:srcCompat="@drawable/ic_skip_next"
|
||||
app:tint="?attr/colorOnPrimaryContainer" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/exo_ffwd"
|
||||
android:layout_width="42dp"
|
||||
android:layout_height="42dp"
|
||||
android:background="?attr/selectableItemBackgroundBorderless"
|
||||
android:scaleType="fitCenter"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/player_play_pause_placeholder_view"
|
||||
app:layout_constraintEnd_toStartOf="@id/placeholder_view_right"
|
||||
app:layout_constraintStart_toEndOf="@+id/player_play_pause_placeholder_view"
|
||||
app:layout_constraintTop_toTopOf="@+id/player_play_pause_placeholder_view"
|
||||
app:srcCompat="@drawable/ic_forward"
|
||||
app:tint="?attr/colorOnPrimaryContainer" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/exo_repeat_toggle"
|
||||
android:layout_width="32dp"
|
||||
android:layout_height="32dp"
|
||||
android:layout_marginEnd="24dp"
|
||||
android:background="?attr/selectableItemBackgroundBorderless"
|
||||
android:scaleType="fitCenter"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/placeholder_view_middle_right"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@+id/placeholder_view_middle_right"
|
||||
app:layout_constraintTop_toTopOf="@+id/placeholder_view_middle_right"
|
||||
app:srcCompat="@drawable/ic_repeat"
|
||||
app:tint="?attr/colorOnPrimaryContainer" />
|
||||
|
||||
<ToggleButton
|
||||
android:id="@+id/player_skip_silence_toggle_button"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:layout_marginEnd="24dp"
|
||||
android:background="@drawable/button_skip_silence_selector"
|
||||
android:text=""
|
||||
android:textOff=""
|
||||
android:textOn=""
|
||||
app:layout_constraintBottom_toBottomOf="@+id/placeholder_view_middle_right"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@+id/placeholder_view_middle_right"
|
||||
app:layout_constraintTop_toTopOf="@+id/placeholder_view_middle_right"
|
||||
app:tint="?attr/colorOnPrimaryContainer" />
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/player_quick_action_view"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="@dimen/now_playing_bottom_peek_height"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@+id/vertical_guideline"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
android:visibility="gone">
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/player_open_queue_button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="16dp"
|
||||
android:background="?attr/selectableItemBackgroundBorderless"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:srcCompat="@drawable/ic_queue" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@@ -3,6 +3,7 @@
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="?attr/colorSurface"
|
||||
android:orientation="vertical">
|
||||
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
@@ -31,9 +32,9 @@
|
||||
android:id="@+id/bottom_navigation"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="80dp"
|
||||
android:layout_gravity="bottom"
|
||||
android:paddingStart="24dp"
|
||||
android:paddingEnd="24dp"
|
||||
android:layout_gravity="bottom"
|
||||
android:visibility="gone"
|
||||
app:menu="@menu/bottom_nav_menu" />
|
||||
|
||||
@@ -48,4 +49,4 @@
|
||||
android:text="@string/activity_info_offline_mode"
|
||||
android:textSize="6sp"
|
||||
android:visibility="gone" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
15
app/src/main/res/layout/dialog_download_directory.xml
Normal file
15
app/src/main/res/layout/dialog_download_directory.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/download_directory_dialog_summary" />
|
||||
|
||||
</LinearLayout>
|
||||
@@ -204,17 +204,31 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
style="@style/TitleLarge"
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingTop="8dp"
|
||||
android:paddingEnd="20dp"
|
||||
android:text="@string/artist_page_title_album_section" />
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
style="@style/TitleLarge"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="8dp"
|
||||
android:text="@string/artist_page_title_album_section" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/artist_page_albums_switch_layout_text_view_clickable"
|
||||
style="@style/TitleMedium"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingEnd="16dp"
|
||||
android:text="@string/artist_page_switch_layout_button" />
|
||||
</LinearLayout>
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/albums_recycler_view"
|
||||
android:id="@+id/albums_horizontal_recycler_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginTop="8dp"
|
||||
@@ -223,7 +237,21 @@
|
||||
android:paddingStart="16dp"
|
||||
android:paddingTop="8dp"
|
||||
android:paddingEnd="8dp"
|
||||
android:paddingBottom="8dp" />
|
||||
android:paddingBottom="8dp"
|
||||
android:visibility="visible"/>
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/albums_vertical_recycler_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:clipToPadding="false"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingTop="8dp"
|
||||
android:paddingEnd="16dp"
|
||||
android:paddingBottom="8dp"
|
||||
android:visibility="gone"/>
|
||||
</LinearLayout>
|
||||
|
||||
<include
|
||||
|
||||
@@ -76,11 +76,20 @@
|
||||
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" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/shuffle_downloaded_text_view_clickable"
|
||||
style="@style/TitleMedium"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/download_shuffle_all_subtitle"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/downloaded_text_view_refreshable"/>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/downloaded_go_back_image_view"
|
||||
android:layout_width="24dp"
|
||||
@@ -108,12 +117,12 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:clipToPadding="false"
|
||||
android:nestedScrollingEnabled="false"
|
||||
android:paddingTop="8dp"
|
||||
android:paddingTop="12dp"
|
||||
android:paddingBottom="8dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/downloaded_text_view_refreshable" />
|
||||
app:layout_constraintTop_toBottomOf="@id/shuffle_downloaded_text_view_clickable" />
|
||||
|
||||
<include
|
||||
android:id="@+id/download_downloaded_placeholder"
|
||||
|
||||
@@ -82,7 +82,7 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="24dp"
|
||||
android:layout_marginTop="24dp"
|
||||
android:layout_marginEnd="12dp"
|
||||
android:layout_marginEnd="24dp"
|
||||
android:ellipsize="marquee"
|
||||
android:singleLine="true"
|
||||
android:text="@string/label_placeholder"
|
||||
@@ -90,6 +90,20 @@
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/guideline" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/player_artist_name_label"
|
||||
style="@style/TitleMedium"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="24dp"
|
||||
android:layout_marginEnd="24dp"
|
||||
android:ellipsize="marquee"
|
||||
android:singleLine="true"
|
||||
android:text="@string/label_placeholder"
|
||||
app:layout_constraintEnd_toStartOf="@+id/button_favorite"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/player_media_title_label" />
|
||||
|
||||
<ToggleButton
|
||||
android:id="@+id/button_favorite"
|
||||
android:layout_width="26dp"
|
||||
@@ -104,20 +118,6 @@
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@+id/player_media_title_label" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/player_artist_name_label"
|
||||
style="@style/TitleMedium"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="24dp"
|
||||
android:layout_marginEnd="12dp"
|
||||
android:ellipsize="marquee"
|
||||
android:singleLine="true"
|
||||
android:text="@string/label_placeholder"
|
||||
app:layout_constraintEnd_toStartOf="@+id/button_favorite"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/player_media_title_label" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/exo_position"
|
||||
android:layout_width="wrap_content"
|
||||
@@ -136,7 +136,7 @@
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="12dp"
|
||||
android:layout_marginTop="20dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
app:bar_height="2dp"
|
||||
app:buffered_color="?attr/colorOnSecondaryContainer"
|
||||
@@ -344,8 +344,9 @@
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/player_open_queue_button"
|
||||
android:layout_width="20dp"
|
||||
android:layout_height="20dp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="16dp"
|
||||
android:background="?attr/selectableItemBackgroundBorderless"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
@@ -10,14 +9,34 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/player_queue_recycler_view"
|
||||
<androidx.core.widget.NestedScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:clipToPadding="false"
|
||||
android:orientation="vertical"
|
||||
android:paddingTop="8dp"
|
||||
android:paddingBottom="@dimen/global_padding_bottom" />
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:paddingTop="8dp"
|
||||
android:paddingBottom="@dimen/global_padding_bottom">
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/player_queue_recycler_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/player_clean_queue_button"
|
||||
style="@style/TitleMedium"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:layout_marginTop="24dp"
|
||||
android:text="@string/player_queue_clean_all_button"/>
|
||||
|
||||
</LinearLayout>
|
||||
</androidx.core.widget.NestedScrollView>
|
||||
</com.cappielloantonio.tempo.helper.recyclerview.NestedScrollableHost>
|
||||
|
||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
@@ -27,6 +46,6 @@
|
||||
android:layout_gravity="bottom|end"
|
||||
android:layout_margin="16dp"
|
||||
app:layout_behavior="com.google.android.material.behavior.HideBottomViewOnScrollBehavior"
|
||||
app:srcCompat="@drawable/ic_shuffle"/>
|
||||
app:srcCompat="@drawable/ic_shuffle" />
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
|
||||
|
||||
@@ -73,7 +73,8 @@
|
||||
android:id="@+id/downloaded_item_more_button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="12dp"
|
||||
android:paddingVertical="12dp"
|
||||
android:paddingStart="12dp"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/item_cover_image_view"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@+id/item_cover_image_view" >
|
||||
|
||||
@@ -21,14 +21,15 @@
|
||||
style="@style/LabelMedium"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="10dp"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"
|
||||
android:paddingStart="8dp"
|
||||
android:paddingEnd="8dp"
|
||||
app:layout_constraintVertical_chainStyle="packed"
|
||||
app:layout_constraintEnd_toStartOf="@+id/placeholder_left_view"
|
||||
app:layout_constraintStart_toEndOf="@+id/player_header_media_cover_image"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
app:layout_constraintTop_toTopOf="@+id/player_header_media_cover_image"
|
||||
app:layout_constraintBottom_toTopOf="@+id/player_header_media_artist_label"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/player_header_media_artist_label"
|
||||
@@ -41,7 +42,8 @@
|
||||
android:paddingEnd="8dp"
|
||||
app:layout_constraintEnd_toStartOf="@+id/placeholder_left_view"
|
||||
app:layout_constraintStart_toEndOf="@+id/player_header_media_cover_image"
|
||||
app:layout_constraintTop_toBottomOf="@+id/player_header_media_title_label" />
|
||||
app:layout_constraintTop_toBottomOf="@+id/player_header_media_title_label"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/player_header_media_cover_image"/>
|
||||
|
||||
<View
|
||||
android:id="@+id/placeholder_left_view"
|
||||
|
||||
@@ -5,5 +5,6 @@
|
||||
android:id="@+id/action_download_directory"
|
||||
android:icon="@drawable/ic_file_download"
|
||||
android:title="@string/menu_download_all_button"
|
||||
android:visible="false"
|
||||
app:showAsAction="never" />
|
||||
</menu>
|
||||
@@ -144,14 +144,14 @@
|
||||
|
||||
<string-array name="replay_gain_titles">
|
||||
<item>Deaktiviert</item>
|
||||
<item>Track</item>
|
||||
<item>Titel</item>
|
||||
<item>Album</item>
|
||||
<item>Auto</item>
|
||||
<item>Automatisch</item>
|
||||
</string-array>
|
||||
<string-array name="replay_gain_values">
|
||||
<item>disabled</item>
|
||||
<item>track</item>
|
||||
<item>album</item>
|
||||
<item>auto</item>
|
||||
<item>Deaktiviert</item>
|
||||
<item>Titel</item>
|
||||
<item>Album</item>
|
||||
<item>Automatisch</item>
|
||||
</string-array>
|
||||
</resources>
|
||||
@@ -1,11 +1,8 @@
|
||||
<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_summary">Bitte deaktiviere die Batterieoptimierung, damit die Medienwiedergabe bei ausgeschaltetem Bildschirm richtig funktioniert.</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>
|
||||
@@ -13,6 +10,7 @@
|
||||
<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_bottom_sheet_share">Teilen</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>
|
||||
@@ -27,6 +25,7 @@
|
||||
<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_adapter_radio_station_starting">Suche…</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>
|
||||
@@ -38,31 +37,41 @@
|
||||
<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_switch_layout_button">Layout umschalten</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="battery_optimization_negative_button">Ignorieren</string>
|
||||
<string name="battery_optimization_neutral_button">Nicht wieder fragen</string>
|
||||
<string name="battery_optimization_positive_button">Ausschalten</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="connection_alert_dialog_title">Wi-Fi ist nicht verbuden</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="description_empty_title">Keine Beschreibung verfügbar</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_summary">Das Ändern des Speicherorts löscht alle Inhalte im zuvor gewählten Speicherort.</string>
|
||||
<string name="download_storage_dialog_title">Wähle den Speicherort aus</string>
|
||||
<string name="download_storage_external_dialog_positive_button">Extern</string>
|
||||
<string name="download_storage_internal_dialog_negative_button">Intern</string>
|
||||
<string name="download_title_section">Downloads</string>
|
||||
<string name="downloaded_bottom_sheet_add_to_queue">Zur Warteschlange hinzufügen</string>
|
||||
<string name="downloaded_bottom_sheet_play_next">Nächsten Titel spielen</string>
|
||||
<string name="downloaded_bottom_sheet_remove">Entfernen</string>
|
||||
<string name="downloaded_bottom_sheet_remove_all">Alle entfernen</string>
|
||||
<string name="downloaded_bottom_sheet_shuffle">Mischen</string>
|
||||
<string name="empty_string" />
|
||||
<string name="error_required">Benötigt</string>
|
||||
<string name="error_server_prefix">http or https prefix benötigt</string>
|
||||
@@ -72,23 +81,32 @@
|
||||
<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_best_of">Top Tracks Deiner Lieblingskünstler</string>
|
||||
<string name="home_subtitle_made_for_you">Ein Mix von einem deiner Lieblingslieder erstellen</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_sync_starred_subtitle">Das Herunterladen dieser Tracks kann erheblichen Datenverbrauch verursachen</string>
|
||||
<string name="home_sync_starred_title">Einige Lieblingslieder müssen synchronisiert werden</string>
|
||||
<string name="home_title_best_of">Best Of</string>
|
||||
<string name="home_title_discovery">Entdeckungsreise</string>
|
||||
<string name="home_title_discovery_shuffle_all_button">Alle mischen</string>
|
||||
<string name="home_title_flashback">Flashback</string>
|
||||
<string name="home_title_internet_radio_station">Internet Radios</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_last_week">Letzte Woche</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_new_releases">Neue Releases</string>
|
||||
<string name="home_title_newest_podcasts">Neueste Podcasts</string>
|
||||
<string name="home_title_podcast_channels">Kanäle</string>
|
||||
<string name="home_title_podcast_channels_see_all_button">Alle zeigen</string>
|
||||
<string name="home_title_radio_station">Radio Stationen</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_shares">Shares</string>
|
||||
<string name="home_title_starred_albums">★ Lieblingsalben</string>
|
||||
<string name="home_title_starred_albums_see_all_button">Alle zeigen</string>
|
||||
@@ -96,15 +114,14 @@
|
||||
<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="home_title_top_songs">Deine Top Songs</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_music_folder">Sammlung</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>
|
||||
@@ -114,10 +131,21 @@
|
||||
<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_filter_all">Alle</string>
|
||||
<string name="menu_filter_download">Heruntergeladen</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_track">Track</string>
|
||||
<string name="menu_group_by_year">Jahr</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="menu_sort_artist">Künstler</string>
|
||||
<string name="menu_sort_name">Name</string>
|
||||
<string name="menu_sort_random">Zufall</string>
|
||||
<string name="menu_sort_year">Jahr</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>
|
||||
@@ -136,28 +164,34 @@
|
||||
<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_bottom_sheet_add_to_queue">Zur Warteschlange hinzufügen</string>
|
||||
<string name="podcast_bottom_sheet_delete">Löschen</string>
|
||||
<string name="podcast_bottom_sheet_download">Download</string>
|
||||
<string name="podcast_bottom_sheet_go_to_channel">Zum Kanal gehen</string>
|
||||
<string name="podcast_bottom_sheet_play_next">Nächsten Titel spielen</string>
|
||||
<string name="podcast_bottom_sheet_remove">Entfernen</string>
|
||||
<string name="podcast_channel_catalogue_title">Kanäle</string>
|
||||
<string name="podcast_channel_catalogue_title_expanded">Kanäle durchsuchen</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_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_button">Hier klicken, um den Bereich auszublenden\nAnwendungsneustart ist notwendig</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="podcast_release_date_duration_formatter">%1$s • %2$s</string>
|
||||
<string name="radio_editor_dialog_hint_homepage_url">Radio Homepage URL</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_button">Hier klicken, um den Bereich auszublenden\nAnwendungsneustart ist notwendig</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>
|
||||
@@ -178,15 +212,17 @@
|
||||
<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="server_unreachable_dialog_title">Server nicht erreichbar</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_format">Transkodierungs-Format</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_estimate_content_length_summary">Diese Option bittet den Server um die geschätzte Länge des Titels.</string>
|
||||
<string name="settings_audio_transcode_estimate_content_length_title">Titellänge schätzen</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>
|
||||
@@ -196,8 +232,8 @@
|
||||
<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_delete_download_storage_title">Gespeicherte Inhalte löschen</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>
|
||||
@@ -208,27 +244,30 @@
|
||||
<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_max_bitrate_wifi">Bitrate bei Wi-Fi 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_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_queue_syncing_title">Warteschlange für diesen User synchronisieren</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_rounded_corner_summary">Abgerundete Ecken für alle gerenderten Cover. Anwendungsneustart ist notwendig.</string>
|
||||
<string name="settings_scan_title">Sammlung scannen</string>
|
||||
<string name="settings_share_title">Teilen von Musik aktivieren</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_share">Diese Option erlaubt es dem Benutzer, Musik mit einem Link zu teilen. Die Funktionalität muss vom Server unterstützt und aktiviert sein und ist auf einzelne Titel, Alben und Wiedergabelisten beschränkt.</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_estimate_content_length">When der Titel während des Abspielens transkodiert wird, When the file is transcoded on the fly, zeigt die App normalerweise keine Titellänge an. Es ist möglich einen Server, der dies unterstützt, zu bitten, die Titellänge zu schätzen. Die Antwortzeiten könnten sich verlängern.</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>
|
||||
@@ -236,14 +275,25 @@
|
||||
<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_share">Teilen</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="settings_wifi_only_title">Warnung bei Streamen ohne Wi-Fi</string>
|
||||
<string name="share_bottom_sheet_copy_link">Link kopieren</string>
|
||||
<string name="share_bottom_sheet_delete">Share löschen</string>
|
||||
<string name="share_bottom_sheet_update">Share aktualisieren</string>
|
||||
<string name="share_subtitle_item">Ablaufdatum: %1$s</string>
|
||||
<string name="share_update_dialog_negative_button">Abbrechen</string>
|
||||
<string name="share_update_dialog_positive_button">Sichern</string>
|
||||
<string name="share_update_dialog_hint_description">Beschreibung</string>
|
||||
<string name="share_update_dialog_hint_expiration_date">Ablaufdatum</string>
|
||||
<string name="share_update_dialog_title">Teilen</string>
|
||||
<string name="share_unsupported_error">Sharing ist nicht aktiviert oder unterstützt.</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>
|
||||
@@ -251,6 +301,7 @@
|
||||
<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_share">Teilen</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>
|
||||
@@ -268,39 +319,30 @@
|
||||
<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>
|
||||
<string name="downloaded_bottom_sheet_shuffle">Mischen</string>
|
||||
<string name="downloaded_bottom_sheet_play_next">Nächsten Titel spielen</string>
|
||||
<string name="downloaded_bottom_sheet_add_to_queue">Zur Warteschlange hinzufügen</string>
|
||||
<string name="downloaded_bottom_sheet_remove">Entfernen</string>
|
||||
<string name="downloaded_bottom_sheet_remove_all">Alle entfernen</string>
|
||||
<string name="undraw_url">https://undraw.co/</string>
|
||||
<string name="track_info_dialog_positive_button">OK</string>
|
||||
<string name="track_info_dialog_title">Titel Info</string>
|
||||
<string name="track_info_title">Titel</string>
|
||||
<string name="track_info_album">Album</string>
|
||||
<string name="track_info_artist">Künstler</string>
|
||||
<string name="track_info_track_number">Track Nummer</string>
|
||||
<string name="track_info_year">Jahr</string>
|
||||
<string name="track_info_genre">Genre</string>
|
||||
<string name="track_info_size">Größe</string>
|
||||
<string name="track_info_content_type">Inhaltstyp</string>
|
||||
<string name="track_info_suffix">Suffix</string>
|
||||
<string name="track_info_transcoded_content_type">Transkodierter Inhaltstyp</string>
|
||||
<string name="track_info_transcoded_suffix">Transkodiertes Suffix</string>
|
||||
<string name="track_info_duration">Länge</string>
|
||||
<string name="track_info_bitrate">Bitrate</string>
|
||||
<string name="track_info_path">Pfad</string>
|
||||
<string name="track_info_disc_number">Disk Nummer</string>
|
||||
<string name="track_info_summary_downloaded_file">Diese Datei wurde mit den Subsonic APIs heruntergeladen. Der Codec und die Bitrate sind unverändert zur original Datei.</string>
|
||||
<string name="track_info_summary_server_prioritized">Die Qualität des abzuspielenden Titels wird vom Server bestimmt. Tempo stellt keinen Codec und keine Bitrate für eine potentielle Transkodierung sicher.</string>
|
||||
<string name="track_info_summary_original_file">Tempo wird nur die original Datei, so wie sie vom Server geliefert wird, lesen. Die Anwendung wird den Server explizit nach einer nicht-transkodierten Version der Datei mit der Bitrate der original Datei fragen.</string>
|
||||
<string name="track_info_summary_transcoding_codec">Tempo wird den Server bitten, die Datei zu transkodieren. Der vom Benutzer gewünschte Codec ist %1$s, die Bitrate wird dieselbe wie bei der original Datei sein. Die potentielle Transkodierung der Datei in das gewünschte Format ist vom Server abhängig. Dieser kann die Operation gegebenenfalls nicht unterstützen.</string>
|
||||
<string name="track_info_summary_transcoding_bitrate">Tempo wird den Server bitten, die Bitrate der Datei zu erändern. Die vom Benutzer gewünschte Bitrate ist %1$s, der Codec der Originaldatei wird nicht verändert. Änderungen an der Bitrate der Datei werden vom Server ausgeführt, dieser kann die Operation gegebenenfalls nicht unterstützen.</string>
|
||||
<string name="track_info_summary_full_transcode">Die Anwendung wird den Server bitten die Datei zu transkodieren und die Bitrate zu verändern. Der vom Benutzer gewünschte Codec ist %1$s, mit der Bitrate %2$s. Änderungen am Codec und an der Bitrate der Datei werden vom Server ausgeführt, dieser kann die Operation gegebenenfalls nicht unterstützen.</string>
|
||||
</resources>
|
||||
242
app/src/main/res/values-fr/arrays.xml
Normal file
242
app/src/main/res/values-fr/arrays.xml
Normal file
@@ -0,0 +1,242 @@
|
||||
<resources>
|
||||
<string-array name="theme_list_titles">
|
||||
<item>Clair</item>
|
||||
<item>Sombre</item>
|
||||
<item>Système</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>Grand</item>
|
||||
<item>Moyen</item>
|
||||
<item>Petit</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>Haute</item>
|
||||
<item>Moyenne</item>
|
||||
<item>Basse</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="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">
|
||||
<item>Lecture directe</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>Lecture directe</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="audio_transcode_format_download_list_titles">
|
||||
<item>Téléchargement direct</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">
|
||||
<item>Dix secondes</item>
|
||||
<item>Cinq secondes</item>
|
||||
<item>Deux secondes</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>Grand</item>
|
||||
<item>Moyen</item>
|
||||
<item>Petit</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>Désactivé</item>
|
||||
<item>Piste</item>
|
||||
<item>Album</item>
|
||||
<item>Auto</item>
|
||||
</string-array>
|
||||
<string-array name="replay_gain_values">
|
||||
<item>disabled</item>
|
||||
<item>track</item>
|
||||
<item>album</item>
|
||||
<item>auto</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="transcoded_download_option_list_titles">
|
||||
<item>Ne pas transcoder</item>
|
||||
<item>Réglages du serveur</item>
|
||||
<item>Format de transcodage (Wi-FI)</item>
|
||||
<item>Format de transcodage (Mobile)</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>
|
||||
|
||||
<string-array name="buffering_strategy_titles">
|
||||
<item>Minimum</item>
|
||||
<item>Modérée</item>
|
||||
<item>Agressive</item>
|
||||
<item>Extrême</item>
|
||||
</string-array>
|
||||
<string-array name="buffering_strategy_values">
|
||||
<item>.1</item>
|
||||
<item>1</item>
|
||||
<item>4</item>
|
||||
<item>8</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="skip_min_star_rating_titles">
|
||||
<item>0 étoile minimum</item>
|
||||
<item>1 étoile minimum</item>
|
||||
<item>2 étoiles minimum</item>
|
||||
<item>3 étoiles minimum</item>
|
||||
<item>4 étoiles minimum</item>
|
||||
</string-array>
|
||||
<string-array name="skip_min_star_rating_values">
|
||||
<item>0</item>
|
||||
<item>1</item>
|
||||
<item>2</item>
|
||||
<item>3</item>
|
||||
<item>4</item>
|
||||
</string-array>
|
||||
</resources>
|
||||
363
app/src/main/res/values-fr/strings.xml
Normal file
363
app/src/main/res/values-fr/strings.xml
Normal file
@@ -0,0 +1,363 @@
|
||||
<resources>
|
||||
<string name="activity_battery_optimizations_conclusion">Si vous rencontrez un problème, visitez https://dontkillmyapp.com. Des instructions pour désactiver les fonctions de sauvegarde d\'énergie qui pourrait affecter les performance de l\'app y sont disponibles.</string>
|
||||
<string name="activity_battery_optimizations_summary">Veuillez désactiver les optimisations de la batterie pour permettre la lecture des médias lorsque l\'écran est éteint.</string>
|
||||
<string name="activity_battery_optimizations_title">Optimisations de la batterie</string>
|
||||
<string name="activity_info_offline_mode">Mode hors-ligne</string>
|
||||
<string name="album_bottom_sheet_add_to_queue">Ajouter à la file d\'attente</string>
|
||||
<string name="album_bottom_sheet_download_all">Télécharger tout</string>
|
||||
<string name="album_bottom_sheet_go_to_artist">Aller à l\'artiste</string>
|
||||
<string name="album_bottom_sheet_instant_mix">Mix instantané</string>
|
||||
<string name="album_bottom_sheet_play_next">Lire après</string>
|
||||
<string name="album_bottom_sheet_remove_all">Retirer tout</string>
|
||||
<string name="album_bottom_sheet_share">Partager</string>
|
||||
<string name="album_bottom_sheet_shuffle">Mélanger</string>
|
||||
<string name="album_catalogue_title">Albums</string>
|
||||
<string name="album_catalogue_title_expanded">Parcourir les Albums</string>
|
||||
<string name="album_error_retrieving_artist">Erreur de récupération de l\'artiste</string>
|
||||
<string name="album_list_page_downloaded">Albums téléchargés</string>
|
||||
<string name="album_list_page_most_played">Albums les plus joués</string>
|
||||
<string name="album_list_page_new_releases">Nouvelles sorties</string>
|
||||
<string name="album_list_page_recently_added">Albums récemment ajoutés</string>
|
||||
<string name="album_list_page_recently_played">Albums récemment joués</string>
|
||||
<string name="album_list_page_starred">Albums favoris</string>
|
||||
<string name="album_list_page_title">Albums</string>
|
||||
<string name="album_page_extra_info_button">Similaire</string>
|
||||
<string name="album_page_play_button">Lire</string>
|
||||
<string name="album_page_shuffle_button">Mélanger</string>
|
||||
<string name="app_name">Tempo</string>
|
||||
<string name="artist_adapter_radio_station_starting">Recherche…</string>
|
||||
<string name="artist_bottom_sheet_instant_mix">Mix instantané</string>
|
||||
<string name="artist_bottom_sheet_shuffle">Mélanger</string>
|
||||
<string name="artist_catalogue_title">Artistes</string>
|
||||
<string name="artist_catalogue_title_expanded">Parcourir les artistes</string>
|
||||
<string name="artist_error_retrieving_radio">Erreur de récupération de la radio de l\'artiste</string>
|
||||
<string name="artist_error_retrieving_tracks">Erreur de récupération des titres de l\'artiste</string>
|
||||
<string name="artist_list_page_downloaded">Artistes téléchargés</string>
|
||||
<string name="artist_list_page_starred">Artistes favoris</string>
|
||||
<string name="artist_list_page_title">Artistes</string>
|
||||
<string name="artist_page_radio_button">Radio</string>
|
||||
<string name="artist_page_shuffle_button">Mélanger</string>
|
||||
<string name="artist_page_switch_layout_button">Changer la disposition</string>
|
||||
<string name="artist_page_title_album_more_like_this_button">Similaire</string>
|
||||
<string name="artist_page_title_album_section">Albums</string>
|
||||
<string name="artist_page_title_biography_more_button">Plus</string>
|
||||
<string name="artist_page_title_biography_section">Biographie</string>
|
||||
<string name="artist_page_title_most_streamed_song_section">Musiques les plus streamées</string>
|
||||
<string name="artist_page_title_most_streamed_song_see_all_button">Voir tout</string>
|
||||
<string name="battery_optimization_negative_button">Ignorer</string>
|
||||
<string name="battery_optimization_neutral_button">Ne pas me redemander</string>
|
||||
<string name="battery_optimization_positive_button">Désactiver</string>
|
||||
<string name="connection_alert_dialog_negative_button">Annuler</string>
|
||||
<string name="connection_alert_dialog_neutral_button">Activer l\'économie de données</string>
|
||||
<string name="connection_alert_dialog_positive_button">OK</string>
|
||||
<string name="connection_alert_dialog_summary">L\'accès au serveur Subsonic sur des connexions autres que le Wi-Fi ont été bloquées. Pour empêcher cette alerte de réapparaître, désactiver la vérification de la connexion dans les paramètres de l\'app.</string>
|
||||
<string name="connection_alert_dialog_title">Wi-Fi déconnecté</string>
|
||||
<string name="delete_download_storage_dialog_negative_button">Annuler</string>
|
||||
<string name="delete_download_storage_dialog_positive_button">Continuer</string>
|
||||
<string name="delete_download_storage_dialog_summary">Sachez que la poursuite de cette action entraînera la suppression permanente de tous les éléments sauvegardés et téléchargés à partir de tous les serveurs</string>
|
||||
<string name="delete_download_storage_dialog_title">Supprimer les éléments téléchargés</string>
|
||||
<string name="description_empty_title">Aucune description disponible</string>
|
||||
<string name="download_directory_dialog_negative_button">Annuler</string>
|
||||
<string name="download_directory_dialog_positive_button">Télécharger</string>
|
||||
<string name="download_directory_dialog_summary">Toutes les pistes dans ce dossier seront téléchargées. Les pistes dans les sous-dossiers ne seront pas téléchargées.</string>
|
||||
<string name="download_directory_dialog_title">Télécharger toutes les pistes.</string>
|
||||
<string name="download_info_empty_subtitle">Dès que vous téléchargerez une musique, vous la trouverez ici</string>
|
||||
<string name="download_info_empty_title">Aucun téléchargement pour l\'instant</string>
|
||||
<string name="download_item_multiple_subtitle_formatter">%1$s • %2$s éléments</string>
|
||||
<string name="download_item_single_subtitle_formatter">%1$s éléments</string>
|
||||
<string name="download_storage_dialog_sub_summary">Redémarrez l\'application pour appliquer les changements.</string>
|
||||
<string name="download_storage_dialog_summary">Changer la destination des téléchargements d\'un espace de stockage à un autre résultera en la suppression immédiate de tous les fichiers précédemment téléchargés dans l\'autre espace de stockage.</string>
|
||||
<string name="download_storage_dialog_title">Sélectionnez l\'option de stockage</string>
|
||||
<string name="download_storage_external_dialog_positive_button">Externe</string>
|
||||
<string name="download_storage_internal_dialog_negative_button">Interne</string>
|
||||
<string name="download_title_section">Téléchargements</string>
|
||||
<string name="downloaded_bottom_sheet_add_to_queue">Ajouter à la liste d\'attente</string>
|
||||
<string name="downloaded_bottom_sheet_play_next">Lire juste après</string>
|
||||
<string name="downloaded_bottom_sheet_remove">Retirer</string>
|
||||
<string name="downloaded_bottom_sheet_remove_all">Retirer tout</string>
|
||||
<string name="downloaded_bottom_sheet_shuffle">Mélanger</string>
|
||||
<string name="empty_string" />
|
||||
<string name="error_required">Requis</string>
|
||||
<string name="error_server_prefix">prefix http ou https requis</string>
|
||||
<string name="exo_download_notification_channel_name">Téléchargements</string>
|
||||
<string name="filter_info_selection">Sélectionnez deux filtres ou plus</string>
|
||||
<string name="filter_title">Filtrer</string>
|
||||
<string name="filter_title_expanded">Filtrer par genre</string>
|
||||
<string name="genre_catalogue_title">Catalogue des Genres</string>
|
||||
<string name="genre_catalogue_title_expanded">Parcourir les Genres</string>
|
||||
<string name="home_subtitle_best_of">Meilleurs morceaux de vos artistes préférés</string>
|
||||
<string name="home_subtitle_made_for_you">Commencez le mix à partir d\'une chanson que vous aimez</string>
|
||||
<string name="home_subtitle_new_internet_radio_station">Ajouter une radio</string>
|
||||
<string name="home_subtitle_new_podcast_channel">Ajouter une chaîne de podcasts</string>
|
||||
<string name="home_sync_starred_cancel">Annuler</string>
|
||||
<string name="home_sync_starred_download">Télécharger</string>
|
||||
<string name="home_sync_starred_subtitle">Télécharger ces titres peut entraîner une utilisation importante de données</string>
|
||||
<string name="home_sync_starred_title">On dirait qu\'il y a des titres favoris à synchroniser</string>
|
||||
<string name="home_title_best_of">Best of</string>
|
||||
<string name="home_title_discovery">Découverte</string>
|
||||
<string name="home_title_discovery_shuffle_all_button">Tout mélanger</string>
|
||||
<string name="home_title_flashback">Flashback</string>
|
||||
<string name="home_title_internet_radio_station">Stations Radio Internet</string>
|
||||
<string name="home_title_last_played">Écouté dernièrement</string>
|
||||
<string name="home_title_last_played_see_all_button">Voir tout</string>
|
||||
<string name="home_title_last_week">Sur la dernière semaine</string>
|
||||
<string name="home_title_made_for_you">Faits pour vous</string>
|
||||
<string name="home_title_most_played">Les plus écoutés</string>
|
||||
<string name="home_title_most_played_see_all_button">Voir tout</string>
|
||||
<string name="home_title_new_releases">Nouvelles sorties</string>
|
||||
<string name="home_title_newest_podcasts">Nouveau podcasts</string>
|
||||
<string name="home_title_podcast_channels">Chaînes</string>
|
||||
<string name="home_title_podcast_channels_see_all_button">Voir tout</string>
|
||||
<string name="home_title_radio_station">Stations radio</string>
|
||||
<string name="home_title_recently_added">Ajouté récemment</string>
|
||||
<string name="home_title_recently_added_see_all_button">Voir tout</string>
|
||||
<string name="home_title_shares">Partages</string>
|
||||
<string name="home_title_starred_albums">★ Albums favoris</string>
|
||||
<string name="home_title_starred_albums_see_all_button">Voir tout</string>
|
||||
<string name="home_title_starred_artists">★ Artistes favoris</string>
|
||||
<string name="home_title_starred_artists_see_all_button">Voir tout</string>
|
||||
<string name="home_title_starred_tracks">★ Titres favoris</string>
|
||||
<string name="home_title_starred_tracks_see_all_button">Voir tout</string>
|
||||
<string name="home_title_top_songs">Vos morceaux préférés</string>
|
||||
<string name="library_title_album">Albums</string>
|
||||
<string name="library_title_album_see_all_button">Voir tout</string>
|
||||
<string name="library_title_artist">Artistes</string>
|
||||
<string name="library_title_artist_see_all_button">Voir tout</string>
|
||||
<string name="library_title_genre">Genres</string>
|
||||
<string name="library_title_genre_see_all_button">Voir tout</string>
|
||||
<string name="library_title_music_folder">Dossiers de musiques</string>
|
||||
<string name="library_title_playlist">Playlists</string>
|
||||
<string name="library_title_playlist_see_all_button">Voir tout</string>
|
||||
<string name="login_empty">Aucun serveur ajouté</string>
|
||||
<string name="login_title">Serveurs Subsonic</string>
|
||||
<string name="login_title_expanded">Serveurs Subsonic</string>
|
||||
<string name="media_route_menu_title">Cast</string>
|
||||
<string name="menu_add_button">Ajouter</string>
|
||||
<string name="menu_download_all_button">Télécharger tout</string>
|
||||
<string name="menu_download_label">Téléchargé</string>
|
||||
<string name="menu_filter_all">Tout</string>
|
||||
<string name="menu_filter_download">Téléchargé</string>
|
||||
<string name="menu_group_by_album">Album</string>
|
||||
<string name="menu_group_by_artist">Artiste</string>
|
||||
<string name="menu_group_by_genre">Genre</string>
|
||||
<string name="menu_group_by_track">Piste</string>
|
||||
<string name="menu_group_by_year">Année</string>
|
||||
<string name="menu_home_label">Home</string>
|
||||
<string name="menu_library_label">Librairie</string>
|
||||
<string name="menu_search_button">Rechercher</string>
|
||||
<string name="menu_settings_button">Paramètres</string>
|
||||
<string name="menu_sort_artist">Artiste</string>
|
||||
<string name="menu_sort_name">Nom</string>
|
||||
<string name="menu_sort_random">Aléatoire</string>
|
||||
<string name="menu_sort_year">Année</string>
|
||||
<string name="player_playback_speed">%1$.2fx</string>
|
||||
<string name="player_queue_clean_all_button">Vider la file d\'attente</string>
|
||||
<string name="player_server_priority">Priorité serveur</string>
|
||||
<string name="playlist_catalogue_title">Catalogue des Playlists</string>
|
||||
<string name="playlist_catalogue_title_expanded">Parcourir les playlists</string>
|
||||
<string name="playlist_chooser_dialog_empty">Pas de playlist</string>
|
||||
<string name="playlist_chooser_dialog_negative_button">Annuler</string>
|
||||
<string name="playlist_chooser_dialog_neutral_button">Créer</string>
|
||||
<string name="playlist_chooser_dialog_title">Ajouter à une playlist</string>
|
||||
<string name="playlist_counted_tracks">%1$d titres • %2$s</string>
|
||||
<string name="playlist_duration">Durée • %1$s</string>
|
||||
<string name="playlist_editor_dialog_hint_name">Nom de la playlist</string>
|
||||
<string name="playlist_editor_dialog_negative_button">Annuler</string>
|
||||
<string name="playlist_editor_dialog_neutral_button">Supprimer</string>
|
||||
<string name="playlist_editor_dialog_positive_button">Enregistrer</string>
|
||||
<string name="playlist_editor_dialog_title">Créer une playlist</string>
|
||||
<string name="playlist_page_play_button">Lire</string>
|
||||
<string name="playlist_page_shuffle_button">Mélanger</string>
|
||||
<string name="playlist_song_count">Playlist • %1$d titres</string>
|
||||
<string name="podcast_bottom_sheet_add_to_queue">Ajouter à la liste d\'attente</string>
|
||||
<string name="podcast_bottom_sheet_delete">Supprimer</string>
|
||||
<string name="podcast_bottom_sheet_download">Télécharger</string>
|
||||
<string name="podcast_bottom_sheet_go_to_channel">Aller à la chaîne</string>
|
||||
<string name="podcast_bottom_sheet_play_next">Lire juste après</string>
|
||||
<string name="podcast_bottom_sheet_remove">Retirer</string>
|
||||
<string name="podcast_channel_catalogue_title">Chaînes</string>
|
||||
<string name="podcast_channel_catalogue_title_expanded">Parcourir les chaînes</string>
|
||||
<string name="podcast_channel_editor_dialog_hint_rss_url">Url RSS</string>
|
||||
<string name="podcast_channel_editor_dialog_title">Chaîne</string>
|
||||
<string name="podcast_channel_page_title_description_section">Description</string>
|
||||
<string name="podcast_channel_page_title_episode_section">Épisodes</string>
|
||||
<string name="podcast_channel_page_title_no_episode_available">Aucun épisode disponible</string>
|
||||
<string name="podcast_episode_download_request_snackbar">Votre requête a été envoyée au serveur</string>
|
||||
<string name="podcast_info_empty_button">Cliquez pour cacher la section\nLes changements seront visibles au redémarrage de l\'app</string>
|
||||
<string name="podcast_info_empty_subtitle">Dès que vous ajouterez une chaîne, vous la retrouverez ici</string>
|
||||
<string name="podcast_info_empty_title">Aucun podcast trouvé!</string>
|
||||
<string name="podcast_release_date_duration_formatter">%1$s • %2$s</string>
|
||||
<string name="radio_editor_dialog_hint_homepage_url">URL de la page d\'accueil de la radio</string>
|
||||
<string name="radio_editor_dialog_hint_name">Nom de la radio</string>
|
||||
<string name="radio_editor_dialog_hint_stream_url">URL du flux radio</string>
|
||||
<string name="radio_editor_dialog_negative_button">Annuler</string>
|
||||
<string name="radio_editor_dialog_neutral_button">Supprimer</string>
|
||||
<string name="radio_editor_dialog_positive_button">Enregistrer</string>
|
||||
<string name="radio_editor_dialog_title">Station Radio Internet</string>
|
||||
<string name="radio_station_info_empty_button">Cliquez pour cacher la section\nLes changements seront visibles au redémarrage de l\'app</string>
|
||||
<string name="radio_station_info_empty_subtitle">Dès que vous ajouterez une station radio, vous la retrouverez ici</string>
|
||||
<string name="radio_station_info_empty_title">Aucune station trouvée!</string>
|
||||
<string name="rating_dialog_negative_button">Annuler</string>
|
||||
<string name="rating_dialog_positive_button">Enregistrer</string>
|
||||
<string name="rating_dialog_title">Noter</string>
|
||||
<string name="search_hint">Rechercher des titres, artistes ou albums</string>
|
||||
<string name="search_info_minimum_characters">Entrez 3 charactères minimum</string>
|
||||
<string name="search_title_album">Albums</string>
|
||||
<string name="search_title_artist">Artistes</string>
|
||||
<string name="search_title_song">Pistes</string>
|
||||
<string name="server_signup_dialog_action_low_security">Sécurité basse</string>
|
||||
<string name="server_signup_dialog_hint_name">Nom du serveur</string>
|
||||
<string name="server_signup_dialog_hint_password">Mot de passe</string>
|
||||
<string name="server_signup_dialog_hint_url">URL du serveur</string>
|
||||
<string name="server_signup_dialog_hint_username">Nom d\'utilisateur</string>
|
||||
<string name="server_signup_dialog_negative_button">Annuler</string>
|
||||
<string name="server_signup_dialog_neutral_button">Supprimer</string>
|
||||
<string name="server_signup_dialog_positive_button">Enregistrer</string>
|
||||
<string name="server_signup_dialog_title">Ajouter un serveur</string>
|
||||
<string name="server_unreachable_dialog_negative_button">Annuler</string>
|
||||
<string name="server_unreachable_dialog_neutral_button">Aller à la connexion</string>
|
||||
<string name="server_unreachable_dialog_positive_button">Continuer quand même</string>
|
||||
<string name="server_unreachable_dialog_summary">Le serveur est injoignable. Si vous décidez de continuer, cette fenêtre n\'apparaîtra plus pendant une heure.</string>
|
||||
<string name="server_unreachable_dialog_title">Serveur injoignable</string>
|
||||
<string name="settings_about_summary">Tempo est un client open source et léger pour Subsonic, développé et build nativement pour Android.</string>
|
||||
<string name="settings_about_title">À propos</string>
|
||||
<string name="settings_audio_transcode_download_format">Format de transcodage</string>
|
||||
<string name="settings_audio_transcode_download_priority_summary">Si activé, Tempo ne forcera pas le téléchargement de la piste avec les paramètre de transcodage ci-dessous.</string>
|
||||
<string name="settings_audio_transcode_download_priority_title">Prioriser les paramètres du serveurs, utilisés pour le streaming, dans les téléchargements</string>
|
||||
<string name="settings_audio_transcode_download_summary">Si activé, Tempo téléchargera les pistes transcodées.</string>
|
||||
<string name="settings_audio_transcode_download_title">Télécharger les pistes transcodées</string>
|
||||
<string name="settings_audio_transcode_estimate_content_length_summary">Si activé, une estimation de la durée de la piste sera demandée au serveur.</string>
|
||||
<string name="settings_audio_transcode_estimate_content_length_title">Estimer la durée du contenu</string>
|
||||
<string name="settings_audio_transcode_format_download">Format de transcodage pour les téléchargements</string>
|
||||
<string name="settings_audio_transcode_format_mobile">Format de transcodage (mobile)</string>
|
||||
<string name="settings_audio_transcode_format_wifi">Format de transcodage (Wi-Fi)</string>
|
||||
<string name="settings_audio_transcode_priority_summary">Si activé, Tempo ne forcera pas le streaming des pistes avec les paramètres ci-dessous.</string>
|
||||
<string name="settings_audio_transcode_priority_title">Prioriser les paramètres de transcodage du serveur</string>
|
||||
<string name="settings_audio_transcode_priority_toast">La priorité au transcodage de la piste est donnée au serveur</string>
|
||||
<string name="settings_buffering_strategy">Stratégie de mise en mémoire tampon</string>
|
||||
<string name="settings_buffering_strategy_summary">Redémarrez l\'application pour appliquer les changements.</string>
|
||||
<string name="settings_covers_cache">Taille du cache des illustrations</string>
|
||||
<string name="settings_data_saving_mode_summary">Pour réduire la consommation de données, éviter de télécharger les illustrations.</string>
|
||||
<string name="settings_data_saving_mode_title">Limiter l\'utilisation des données mobiles</string>
|
||||
<string name="settings_delete_download_storage_summary">Continuer entraînera la suppression irréversible de tous les éléments sauvegardés.</string>
|
||||
<string name="settings_delete_download_storage_title">Supprimer les éléments sauvegardés</string>
|
||||
<string name="settings_download_storage_title">Stockage des téléchargements</string>
|
||||
<string name="settings_equalizer_summary">Ajuster les paramètres audios</string>
|
||||
<string name="settings_equalizer_title">Égaliseur</string>
|
||||
<string name="settings_github_link">https://github.com/CappielloAntonio/tempo</string>
|
||||
<string name="settings_github_summary">Suivre le développement</string>
|
||||
<string name="settings_github_title">Github</string>
|
||||
<string name="settings_image_size">Définir la résolution des images</string>
|
||||
<string name="settings_language">Langue</string>
|
||||
<string name="settings_logout_title">Se déconnecter</string>
|
||||
<string name="settings_max_bitrate_download">Bitrate pour les téléchargements</string>
|
||||
<string name="settings_max_bitrate_mobile">Bitrate en données mobile</string>
|
||||
<string name="settings_max_bitrate_wifi">Bitrate en Wi-Fi</string>
|
||||
<string name="settings_media_cache">Taille du cache des fichiers audios</string>
|
||||
<string name="settings_music_directory">Afficher les dossiers</string>
|
||||
<string name="settings_music_directory_summary">Si activé, rend possible la navigation dans les répertoires. À noter que pour que la navigation dans les dossiers fonctionne correctement, le serveur doit supporter cette fonctionnalité.</string>
|
||||
<string name="settings_podcast">Voir les podcasts</string>
|
||||
<string name="settings_podcast_summary">Si activé, rend visible la section Podcast</string>
|
||||
<string name="settings_queue_syncing_countdown">Minuteur de synchronisation</string>
|
||||
<string name="settings_queue_syncing_summary">Si activé, l\'utilisateur pourra sauvegarder sa file d\'attente et la recharger au démarrage de l\'application.</string>
|
||||
<string name="settings_queue_syncing_title">Synchroniser la file d\'attente pour cet utilisateur</string>
|
||||
<string name="settings_radio">Voir les radios</string>
|
||||
<string name="settings_radio_summary">Si activé, rend visible la section Radio</string>
|
||||
<string name="settings_replay_gain">Ajuster le Replay Gain</string>
|
||||
<string name="settings_rounded_corner">Coins arrondis</string>
|
||||
<string name="settings_rounded_corner_size">Taille des arrondis</string>
|
||||
<string name="settings_rounded_corner_size_summary">Définit l\'ampleur de l\'angle de courbure.</string>
|
||||
<string name="settings_rounded_corner_summary">Si activé, arrondi les angles des illustrations. Les modifications prendront effet au redémarrage.</string>
|
||||
<string name="settings_scan_title">Scanner la bibliothèque</string>
|
||||
<string name="settings_scrobble_title">Activer le scrobbling</string>
|
||||
<string name="settings_share_title">Activer le partage de musique</string>
|
||||
<string name="settings_sub_summary_scrobble">À noter que le scrobbling doit être activé sur le serveur pour qu\'il puisse recevoir ces données</string>
|
||||
<string name="settings_summary_skip_min_star_rating">Lors de l\'écoute de la radio d\'un artiste, d\'un mix instantané ou de tout la bibliothèque en aléatoire, les pistes en dessous d\'une certaine note seront ignorées.</string>
|
||||
<string name="settings_summary_replay_gain">Le Replay Gain est une fonctionnalité qui vous permet d\'ajuster le volume des pistes audio pour une expérience d\'écoute cohérente. Fonctionne uniquement si la piste contient les métadonnées nécessaires.</string>
|
||||
<string name="settings_summary_scrobble">Le scrobbling permet à votre appareil d\'envoyer des informations sur les musiques que vous écoutez au serveur afin de créer des recommendations personnalisées basées sur vos préférences musicales.</string>
|
||||
<string name="settings_summary_share">Permet à l\'utilisateur de partager de la musique via un lien. Cette fonctionnalité doit être supportée et activée sur le serveur et est limitée aux pistes, albums et playlists individuellement.</string>
|
||||
<string name="settings_summary_syncing">Renvoie l\'état de la file d\'attente de cet utilisateur. Cela inclut les pistes dans la file, la piste actuellement écoutée et la position dans la piste. Cette fonctionnalité doit être supportée par le serveur.</string>
|
||||
<string name="settings_summary_transcoding">Le mode de transcodage à prioriser. Si reglé sur \"Lecture directe\", le bitrate du fichier ne sera pas modifié.</string>
|
||||
<string name="settings_summary_transcoding_download">Télécharge les médias transcodés. Si activé, les paramètres de transcodage suivants seront utilisés pour les téléchargements.\n\n Si le format de transcodage est reglé à \"Téléchargement direct\", le bitrate du fichier ne sera pas modifé.</string>
|
||||
<string name="settings_summary_transcoding_estimate_content_length">Quand le fichier est transcodé à la volé, en général, le client n\'affiche pas la durée de la piste. Il est possible de demander aux serveurs qui le supportent d\'estimer la durée de la piste écoutée, mais les temps de réponses peuvent être plus longs.</string>
|
||||
<string name="settings_sync_starred_tracks_for_offline_use_summary">Si activé, les pistes favorites seront téléchargées pour l\'écoute hors-ligne</string>
|
||||
<string name="settings_sync_starred_tracks_for_offline_use_title">Synchronisation des pistes favorites pour écoute hors-ligne</string>
|
||||
<string name="settings_theme">Thème</string>
|
||||
<string name="settings_title_data">Données</string>
|
||||
<string name="settings_title_general">Géneral</string>
|
||||
<string name="settings_title_rating">Note</string>
|
||||
<string name="settings_title_replay_gain">Replay Gain</string>
|
||||
<string name="settings_title_scrobble">Scrobble</string>
|
||||
<string name="settings_title_skip_min_star_rating">Ignorer des musiques selon leur note</string>
|
||||
<string name="settings_title_skip_min_star_rating_dialog">Musiques avec une note de:</string>
|
||||
<string name="settings_title_share">Partage</string>
|
||||
<string name="settings_title_syncing">Synchronisation</string>
|
||||
<string name="settings_title_transcoding">Transcodage</string>
|
||||
<string name="settings_title_transcoding_download">Transcodage des téléchargements</string>
|
||||
<string name="settings_title_ui">UI</string>
|
||||
<string name="settings_transcoded_download">Transcodage des téléchargements</string>
|
||||
<string name="settings_version_title">Version</string>
|
||||
<string name="settings_wifi_only_summary">Demander confirmation à l\'utilisateur avant de streamer sur le réseau mobile.</string>
|
||||
<string name="settings_wifi_only_title">Alerte de streaming sur données mobiles</string>
|
||||
<string name="share_bottom_sheet_copy_link">Copier le lien</string>
|
||||
<string name="share_bottom_sheet_delete">Supprimer le partage</string>
|
||||
<string name="share_bottom_sheet_update">Mettre à jour le partage</string>
|
||||
<string name="share_subtitle_item">Date d\'expiration : %1$s</string>
|
||||
<string name="share_unsupported_error">Le partage n\'est pas supporté ou pas activé</string>
|
||||
<string name="share_update_dialog_hint_description">Description</string>
|
||||
<string name="share_update_dialog_hint_expiration_date">Date d\'expiration</string>
|
||||
<string name="share_update_dialog_negative_button">Annuler</string>
|
||||
<string name="share_update_dialog_positive_button">Enregistrer</string>
|
||||
<string name="share_update_dialog_title">Partager</string>
|
||||
<string name="song_bottom_sheet_add_to_playlist">Ajouter à une playlist</string>
|
||||
<string name="song_bottom_sheet_add_to_queue">Ajouter à la fil d\'attente</string>
|
||||
<string name="song_bottom_sheet_download">Télécharger</string>
|
||||
<string name="song_bottom_sheet_error_retrieving_album">Erreur de récupération de l\'album</string>
|
||||
<string name="song_bottom_sheet_error_retrieving_artist">Erreur de récupération de l\'artiste</string>
|
||||
<string name="song_bottom_sheet_go_to_album">Aller à l\'album</string>
|
||||
<string name="song_bottom_sheet_go_to_artist">Aller à l\'artiste</string>
|
||||
<string name="song_bottom_sheet_instant_mix">Mix instantané</string>
|
||||
<string name="song_bottom_sheet_play_next">Lire juste après</string>
|
||||
<string name="song_bottom_sheet_rate">Noter</string>
|
||||
<string name="song_bottom_sheet_remove">Retirer</string>
|
||||
<string name="song_bottom_sheet_share">Partager</string>
|
||||
<string name="song_list_page_downloaded">Téléchargé</string>
|
||||
<string name="song_list_page_most_played">Titres les plus joués</string>
|
||||
<string name="song_list_page_recently_added">Titres ajoutés récemment</string>
|
||||
<string name="song_list_page_recently_played">Titrés joués récemment</string>
|
||||
<string name="song_list_page_starred">Titres favoris</string>
|
||||
<string name="song_list_page_top">Les meilleurs titres de %1$s</string>
|
||||
<string name="song_list_page_year">Année %1$d</string>
|
||||
<string name="song_subtitle_formatter">%1$s • %2$s</string>
|
||||
<string name="starred_sync_dialog_negative_button">Annuler</string>
|
||||
<string name="starred_sync_dialog_neutral_button">Continuer</string>
|
||||
<string name="starred_sync_dialog_positive_button">Continuer et télécharger</string>
|
||||
<string name="starred_sync_dialog_summary">Le téléchargement des titres favoris pourrer utiliser beaucoup de données.</string>
|
||||
<string name="starred_sync_dialog_title">Synchroniser les titres favoris</string>
|
||||
<string name="track_info_album">Album</string>
|
||||
<string name="track_info_artist">Artiste</string>
|
||||
<string name="track_info_bitrate">Bitrate</string>
|
||||
<string name="track_info_content_type">Type de contenu</string>
|
||||
<string name="track_info_dialog_positive_button">OK</string>
|
||||
<string name="track_info_dialog_title">Infos piste</string>
|
||||
<string name="track_info_disc_number">Numéro de disque</string>
|
||||
<string name="track_info_duration">Durée</string>
|
||||
<string name="track_info_genre">Genre</string>
|
||||
<string name="track_info_path">Chemin</string>
|
||||
<string name="track_info_size">Taille</string>
|
||||
<string name="track_info_suffix">Suffixe</string>
|
||||
<string name="track_info_summary_downloaded_file">Le fichier a été téléchargé depuis les APIs Subsonic. Le codec et le bitrate du fichier demeure inchangé du fichier d\'origine.</string>
|
||||
<string name="track_info_summary_full_transcode">L\'application demandera au serveur de transcoder le fichier et de modifier son bitrate. Le codec demandé par l\'utilisateur est %1$s, avec un bitrate de %2$s. Toute modification éventuelle du codec et du bitrate du fichier dans le format choisi sera gérée par le serveur, qui peut ou non prendre en charge l\'opération.</string>
|
||||
<string name="track_info_summary_original_file">L\'application ne lira que le fichier original tel que fourni par le serveur. L\'application demandera explicitement au serveur le fichier non transcodé avec le bitrate de la source originale.</string>
|
||||
<string name="track_info_summary_server_prioritized">La qualité du fichier à lire est laissée à l\'appréciation du serveur. L\'application n\'impose pas le choix du codec et du bitrate pour un éventuel transcodage.</string>
|
||||
<string name="track_info_summary_transcoding_bitrate">L\'application demandera au serveur de modifier le bitrate du fichier. L\'utilisateur a choisi un bitrate de %1$s, tandis que le codec du fichier restera le même. Toute modification du bitrate du fichier dans le format choisi sera effectuée par le serveur, qui peut ou non prendre en charge l\'opération. </string>
|
||||
<string name="track_info_summary_transcoding_codec">L\'application demandera au serveur de transcoder le fichier. Le codec choisi par l\'utilisateur est le %1$s, tandis que le bitrate sera le même que celui du fichier source. Le transcodage éventuel du fichier dans le codec choisi dépend du serveur, qui peut ou non prendre en charge l\'opération.</string>
|
||||
<string name="track_info_title">Titre</string>
|
||||
<string name="track_info_track_number">Numéro de piste</string>
|
||||
<string name="track_info_transcoded_content_type">Transcodé type de contenu</string>
|
||||
<string name="track_info_transcoded_suffix">Transcodé suffixe</string>
|
||||
<string name="track_info_year">Année</string>
|
||||
<string name="undraw_page">unDraw</string>
|
||||
<string name="undraw_thanks">Un grand merci à unDraw, nous n\'aurions pas pu rendre cette application aussi belle sans leurs illustrations.</string>
|
||||
<string name="undraw_url">https://undraw.co/</string>
|
||||
</resources>
|
||||
242
app/src/main/res/values-ko/arrays.xml
Normal file
242
app/src/main/res/values-ko/arrays.xml
Normal file
@@ -0,0 +1,242 @@
|
||||
<resources>
|
||||
<string-array name="theme_list_titles">
|
||||
<item>라이트</item>
|
||||
<item>다크</item>
|
||||
<item>시스템 기본</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>높음</item>
|
||||
<item>중간</item>
|
||||
<item>낮음</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>높음</item>
|
||||
<item>중간</item>
|
||||
<item>낮음</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>원본</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>원본</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="max_bitrate_download_list_titles">
|
||||
<item>원본</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">
|
||||
<item>직접 재생</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>직접 재생</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="audio_transcode_format_download_list_titles">
|
||||
<item>직접 다운로드</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">
|
||||
<item>10초</item>
|
||||
<item>5초</item>
|
||||
<item>2초</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>높음</item>
|
||||
<item>중간</item>
|
||||
<item>낮음</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>비활성</item>
|
||||
<item>트랙</item>
|
||||
<item>앨범</item>
|
||||
<item>자동</item>
|
||||
</string-array>
|
||||
<string-array name="replay_gain_values">
|
||||
<item>disabled</item>
|
||||
<item>track</item>
|
||||
<item>album</item>
|
||||
<item>auto</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="transcoded_download_option_list_titles">
|
||||
<item>트랜스코딩 하지 않음</item>
|
||||
<item>서버 셋팅</item>
|
||||
<item>Wi-Fi 트랜스코딩 포맷</item>
|
||||
<item>모바일 트랜스코딩 포맷</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>
|
||||
|
||||
<string-array name="buffering_strategy_titles">
|
||||
<item>최소한</item>
|
||||
<item>보통</item>
|
||||
<item>적극적</item>
|
||||
<item>최대한</item>
|
||||
</string-array>
|
||||
<string-array name="buffering_strategy_values">
|
||||
<item>.1</item>
|
||||
<item>1</item>
|
||||
<item>4</item>
|
||||
<item>8</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="skip_min_star_rating_titles">
|
||||
<item>최소 별점 0</item>
|
||||
<item>최소 별점 1</item>
|
||||
<item>최소 별점 2</item>
|
||||
<item>최소 별점 3</item>
|
||||
<item>최소 별점 4</item>
|
||||
</string-array>
|
||||
<string-array name="skip_min_star_rating_values">
|
||||
<item>0</item>
|
||||
<item>1</item>
|
||||
<item>2</item>
|
||||
<item>3</item>
|
||||
<item>4</item>
|
||||
</string-array>
|
||||
</resources>
|
||||
366
app/src/main/res/values-ko/strings.xml
Normal file
366
app/src/main/res/values-ko/strings.xml
Normal file
@@ -0,0 +1,366 @@
|
||||
<resources>
|
||||
<string name="activity_battery_optimizations_conclusion">문제가 있을 시 https://dontkillmyapp.com에 방문해 주세요. 앱 성능에 영향을 줄 수 있는 절전 기능을 비활성화하는 방법에 대한 자세한 설명을 찾을 수 있습니다.</string>
|
||||
<string name="activity_battery_optimizations_summary">화면이 꺼진 상태에서 음악 재생을 하기 위해서는 배터리 최적화를 비활성화 해주세요.</string>
|
||||
<string name="activity_battery_optimizations_title">배터리 최적화</string>
|
||||
<string name="activity_info_offline_mode">오프라인 모드</string>
|
||||
<string name="album_bottom_sheet_add_to_queue">재생목록에 추가</string>
|
||||
<string name="album_bottom_sheet_download_all">모두 다운로드</string>
|
||||
<string name="album_bottom_sheet_go_to_artist">아티스트로 이동</string>
|
||||
<string name="album_bottom_sheet_instant_mix">인스턴트 믹스</string>
|
||||
<string name="album_bottom_sheet_play_next">다음 재생</string>
|
||||
<string name="album_bottom_sheet_remove_all">모두 제거</string>
|
||||
<string name="album_bottom_sheet_share">공유</string>
|
||||
<string name="album_bottom_sheet_shuffle">셔플</string>
|
||||
<string name="album_catalogue_title">앨범</string>
|
||||
<string name="album_catalogue_title_expanded">앨범 찾아보기</string>
|
||||
<string name="album_error_retrieving_artist">아티스트를 검색하던 중 오류가 발생했습니다.</string>
|
||||
<string name="album_list_page_downloaded">다운로드한 앨범</string>
|
||||
<string name="album_list_page_most_played">가장 많이 재생한 앨범</string>
|
||||
<string name="album_list_page_new_releases">New releases</string>
|
||||
<string name="album_list_page_recently_added">최근 추가한 앨범</string>
|
||||
<string name="album_list_page_recently_played">최근 재생한 앨범</string>
|
||||
<string name="album_list_page_starred">즐겨찾기한 앨범</string>
|
||||
<string name="album_list_page_title">앨범</string>
|
||||
<string name="album_page_extra_info_button">유사항목 더 보기</string>
|
||||
<string name="album_page_play_button">재생</string>
|
||||
<string name="album_page_shuffle_button">셔플</string>
|
||||
<string name="app_name">Tempo</string>
|
||||
<string name="artist_adapter_radio_station_starting">탐색 중…</string>
|
||||
<string name="artist_bottom_sheet_instant_mix">인스턴트 믹스</string>
|
||||
<string name="artist_bottom_sheet_shuffle">셔플</string>
|
||||
<string name="artist_catalogue_title">아티스트</string>
|
||||
<string name="artist_catalogue_title_expanded">아티스트 찾아보기</string>
|
||||
<string name="artist_error_retrieving_radio">아티스트의 라디오를 검색하는 중에 오류가 발생했습니다.</string>
|
||||
<string name="artist_error_retrieving_tracks">아티스트의 트랙을 검색하는 중에 오류가 발생했습니다.</string>
|
||||
<string name="artist_list_page_downloaded">다운로드한 아티스트</string>
|
||||
<string name="artist_list_page_starred">즐겨찾기한 아티스트</string>
|
||||
<string name="artist_list_page_title">아티스트</string>
|
||||
<string name="artist_page_radio_button">라디오</string>
|
||||
<string name="artist_page_shuffle_button">셔플</string>
|
||||
<string name="artist_page_switch_layout_button">레이아웃 전환</string>
|
||||
<string name="artist_page_title_album_more_like_this_button">유사한 항목 더 보기</string>
|
||||
<string name="artist_page_title_album_section">앨범</string>
|
||||
<string name="artist_page_title_biography_more_button">더 보기</string>
|
||||
<string name="artist_page_title_biography_section">약력</string>
|
||||
<string name="artist_page_title_most_streamed_song_section">가장 많이 스트리밍한 음악</string>
|
||||
<string name="artist_page_title_most_streamed_song_see_all_button">모두 보기</string>
|
||||
<string name="battery_optimization_negative_button">무시</string>
|
||||
<string name="battery_optimization_neutral_button">다시 묻지 않기</string>
|
||||
<string name="battery_optimization_positive_button">비활성</string>
|
||||
<string name="connection_alert_dialog_negative_button">취소</string>
|
||||
<string name="connection_alert_dialog_neutral_button">데이터 세이버 활성</string>
|
||||
<string name="connection_alert_dialog_positive_button">OK</string>
|
||||
<string name="connection_alert_dialog_summary">Wi-Fi가 연결되지 않은 상태에서 Subsonic 서버에 대한 액세스가 제한되었습니다. 이 경고를 다시 보지 않으려면 앱 설정에서 연결 확인을 비활성화 해주세요.</string>
|
||||
<string name="connection_alert_dialog_title">Wi-Fi가 연결되지 않음</string>
|
||||
<string name="delete_download_storage_dialog_negative_button">취소</string>
|
||||
<string name="delete_download_storage_dialog_positive_button">계속</string>
|
||||
<string name="delete_download_storage_dialog_summary">계속할 시 서버에서 다운로드한 모든 저장 항목이 영구적으로 삭제됩니다.</string>
|
||||
<string name="delete_download_storage_dialog_title">저장된 항목 삭제</string>
|
||||
<string name="description_empty_title">설명 란이 비어있습니다.</string>
|
||||
<string name="download_directory_dialog_negative_button">취소</string>
|
||||
<string name="download_directory_dialog_positive_button">다운로드</string>
|
||||
<string name="download_directory_dialog_summary">하위 폴더를 제외한 해당 폴더의 모든 트랙이 다운로드됩니다.</string>
|
||||
<string name="download_directory_dialog_title">트랙 다운로드</string>
|
||||
<string name="download_info_empty_subtitle">음악을 다운로드하면 여기 표시됩니다.</string>
|
||||
<string name="download_info_empty_title">다운로드 하지 않음</string>
|
||||
<string name="download_item_multiple_subtitle_formatter">%1$s • %2$s 항목</string>
|
||||
<string name="download_item_single_subtitle_formatter">%1$s 항목</string>
|
||||
<string name="download_storage_dialog_sub_summary">변경 사항을 저장하려면 앱을 다시 시작하세요.</string>
|
||||
<string name="download_storage_dialog_summary">>다운로드한 파일을 다른 저장소로 변경하면 기존 저장소에서 다운로드한 파일은 즉시 삭제됩니다.</string>
|
||||
<string name="download_storage_dialog_title">저장소 선택 옵션</string>
|
||||
<string name="download_storage_external_dialog_positive_button">외부</string>
|
||||
<string name="download_storage_internal_dialog_negative_button">내부</string>
|
||||
<string name="download_title_section">다운로드</string>
|
||||
<string name="downloaded_bottom_sheet_add_to_queue">재생목록에 추가</string>
|
||||
<string name="downloaded_bottom_sheet_play_next">다음 재생</string>
|
||||
<string name="downloaded_bottom_sheet_remove">제거</string>
|
||||
<string name="downloaded_bottom_sheet_remove_all">모두 제거</string>
|
||||
<string name="downloaded_bottom_sheet_shuffle">셔플</string>
|
||||
<string name="empty_string" />
|
||||
<string name="error_required">필수 항목</string>
|
||||
<string name="error_server_prefix">http 또는 https 접두사가 필요합니다.</string>
|
||||
<string name="exo_download_notification_channel_name">다운로드</string>
|
||||
<string name="filter_info_selection">둘 이상의 필터를 선택해 주세요.</string>
|
||||
<string name="filter_title">필터</string>
|
||||
<string name="filter_title_expanded">장르 필터링</string>
|
||||
<string name="genre_catalogue_title">장르 카탈로그</string>
|
||||
<string name="genre_catalogue_title_expanded">장르 찾아보기</string>
|
||||
<string name="home_subtitle_best_of">최애 아티스트의 인기곡</string>
|
||||
<string name="home_subtitle_made_for_you">좋아하는 음악으로 믹스를 시작해 보세요.</string>
|
||||
<string name="home_subtitle_new_internet_radio_station">새 라디오 추가</string>
|
||||
<string name="home_subtitle_new_podcast_channel">새 팟캐스트 채널 추가</string>
|
||||
<string name="home_sync_starred_cancel">취소</string>
|
||||
<string name="home_sync_starred_download">다운로드</string>
|
||||
<string name="home_sync_starred_subtitle">트랙 다운로드 시 많은 데이터 사용량이 발생할 수 있습니다.</string>
|
||||
<string name="home_sync_starred_title">동기화가 필요한 즐겨찾기 트랙이 있는 것 같습니다.</string>
|
||||
<string name="home_title_best_of">Best of</string>
|
||||
<string name="home_title_discovery">Discovery</string>
|
||||
<string name="home_title_discovery_shuffle_all_button">모두 셔플</string>
|
||||
<string name="home_title_flashback">Flashback</string>
|
||||
<string name="home_title_internet_radio_station">인터넷 라디오 스테이션</string>
|
||||
<string name="home_title_last_played">최근 재생</string>
|
||||
<string name="home_title_last_played_see_all_button">모두 보기</string>
|
||||
<string name="home_title_last_week">지난 주</string>
|
||||
<string name="home_title_made_for_you">Made for you</string>
|
||||
<string name="home_title_most_played">가장 많이 재생</string>
|
||||
<string name="home_title_most_played_see_all_button">모두 보기</string>
|
||||
<string name="home_title_new_releases">New releases</string>
|
||||
<string name="home_title_newest_podcasts">새로운 팟캐스트</string>
|
||||
<string name="home_title_podcast_channels">채널</string>
|
||||
<string name="home_title_podcast_channels_see_all_button">모두 보기</string>
|
||||
<string name="home_title_radio_station">라디오 스테이션</string>
|
||||
<string name="home_title_recently_added">최근 추가</string>
|
||||
<string name="home_title_recently_added_see_all_button">모두 보기</string>
|
||||
<string name="home_title_shares">공유</string>
|
||||
<string name="home_title_starred_albums">★ 즐겨찾기한 앨범</string>
|
||||
<string name="home_title_starred_albums_see_all_button">모두 보기</string>
|
||||
<string name="home_title_starred_artists">★ 즐겨찾기한 아티스트</string>
|
||||
<string name="home_title_starred_artists_see_all_button">모두 보기</string>
|
||||
<string name="home_title_starred_tracks">★ 즐겨찾기한 트랙</string>
|
||||
<string name="home_title_starred_tracks_see_all_button">모두 보기</string>
|
||||
<string name="home_title_top_songs">자주 플레이한 음악</string>
|
||||
<string name="label_dot_separator" translatable="false">•</string>
|
||||
<string name="label_placeholder" translatable="false">--</string>
|
||||
<string name="library_title_album">앨범</string>
|
||||
<string name="library_title_album_see_all_button">모두 보기</string>
|
||||
<string name="library_title_artist">아티스트</string>
|
||||
<string name="library_title_artist_see_all_button">모두 보기</string>
|
||||
<string name="library_title_genre">장르</string>
|
||||
<string name="library_title_genre_see_all_button">모두 보기</string>
|
||||
<string name="library_title_music_folder">음악 폴더</string>
|
||||
<string name="library_title_playlist">플레이리스트</string>
|
||||
<string name="library_title_playlist_see_all_button">모두 보기</string>
|
||||
<string name="login_empty">서버가 없습니다.</string>
|
||||
<string name="login_title">Subsonic 서버</string>
|
||||
<string name="login_title_expanded">Subsonic 서버</string>
|
||||
<string name="media_route_menu_title">Cast</string>
|
||||
<string name="menu_add_button">추가</string>
|
||||
<string name="menu_download_all_button">모두 다운로드</string>
|
||||
<string name="menu_download_label">다운로드</string>
|
||||
<string name="menu_filter_all">모두</string>
|
||||
<string name="menu_filter_download">다운로드한</string>
|
||||
<string name="menu_group_by_album">앨범</string>
|
||||
<string name="menu_group_by_artist">아티스트</string>
|
||||
<string name="menu_group_by_genre">장르</string>
|
||||
<string name="menu_group_by_track">트랙</string>
|
||||
<string name="menu_group_by_year">년도</string>
|
||||
<string name="menu_home_label">홈으로</string>
|
||||
<string name="menu_library_label">라이브러리</string>
|
||||
<string name="menu_search_button">검색</string>
|
||||
<string name="menu_settings_button">셋팅</string>
|
||||
<string name="menu_sort_artist">아티스트</string>
|
||||
<string name="menu_sort_name">이름</string>
|
||||
<string name="menu_sort_random">랜덤</string>
|
||||
<string name="menu_sort_year">년도</string>
|
||||
<string name="player_playback_speed">%1$.2fx</string>
|
||||
<string name="player_queue_clean_all_button">재생목록 비우기</string>
|
||||
<string name="player_server_priority">서버 우선순위</string>
|
||||
<string name="playlist_catalogue_title">플레이리스트 카탈로그</string>
|
||||
<string name="playlist_catalogue_title_expanded">플레이리스트 찾아보기</string>
|
||||
<string name="playlist_chooser_dialog_empty">플레이리스트가 없습니다.</string>
|
||||
<string name="playlist_chooser_dialog_negative_button">취소</string>
|
||||
<string name="playlist_chooser_dialog_neutral_button">생성</string>
|
||||
<string name="playlist_chooser_dialog_title">플레이리스트 추가</string>
|
||||
<string name="playlist_counted_tracks">%1$d 트랙 • %2$s</string>
|
||||
<string name="playlist_duration">재생시간 • %1$s</string>
|
||||
<string name="playlist_editor_dialog_hint_name">플레이리스트 이름</string>
|
||||
<string name="playlist_editor_dialog_negative_button">취소</string>
|
||||
<string name="playlist_editor_dialog_neutral_button">삭제</string>
|
||||
<string name="playlist_editor_dialog_positive_button">저장</string>
|
||||
<string name="playlist_editor_dialog_title">플레이리스트 수정</string>
|
||||
<string name="playlist_page_play_button">재생</string>
|
||||
<string name="playlist_page_shuffle_button">셔플</string>
|
||||
<string name="playlist_song_count">플레이리스트 • %1$d 곡</string>
|
||||
<string name="podcast_bottom_sheet_add_to_queue">재생목록에 추가</string>
|
||||
<string name="podcast_bottom_sheet_delete">제거</string>
|
||||
<string name="podcast_bottom_sheet_download">다운로드</string>
|
||||
<string name="podcast_bottom_sheet_go_to_channel">채널로 이동</string>
|
||||
<string name="podcast_bottom_sheet_play_next">다음 재생</string>
|
||||
<string name="podcast_bottom_sheet_remove">제거</string>
|
||||
<string name="podcast_channel_catalogue_title">채널</string>
|
||||
<string name="podcast_channel_catalogue_title_expanded">채널 찾아보기</string>
|
||||
<string name="podcast_channel_editor_dialog_hint_rss_url">RSS Url</string>
|
||||
<string name="podcast_channel_editor_dialog_title">팟캐스트 채널</string>
|
||||
<string name="podcast_channel_page_title_description_section">설명</string>
|
||||
<string name="podcast_channel_page_title_episode_section">에피소드</string>
|
||||
<string name="podcast_channel_page_title_no_episode_available">가능한 에피소드가 없습니다.</string>
|
||||
<string name="podcast_episode_download_request_snackbar">요청이 서버로 전송되었습니다.</string>
|
||||
<string name="podcast_info_empty_button">섹션을 숨기려면 클릭하세요.\n다시 시작하면 적용됩니다.</string>
|
||||
<string name="podcast_info_empty_subtitle">채널을 추가하면 표시됩니다.</string>
|
||||
<string name="podcast_info_empty_title">팟캐스트를 찾을 수 없습니다.</string>
|
||||
<string name="podcast_release_date_duration_formatter">%1$s • %2$s</string>
|
||||
<string name="radio_editor_dialog_hint_homepage_url">라디오 홈페이지 URL</string>
|
||||
<string name="radio_editor_dialog_hint_name">라디오 이름</string>
|
||||
<string name="radio_editor_dialog_hint_stream_url">라디오 스트리밍 URL</string>
|
||||
<string name="radio_editor_dialog_negative_button">취소</string>
|
||||
<string name="radio_editor_dialog_neutral_button">삭제</string>
|
||||
<string name="radio_editor_dialog_positive_button">저장</string>
|
||||
<string name="radio_editor_dialog_title">인터넷 라디오 섹션</string>
|
||||
<string name="radio_station_info_empty_button">섹션을 숨기려면 클릭하세요.\n다시 시작하면 적용됩니다.</string>
|
||||
<string name="radio_station_info_empty_subtitle">라디오 스테이션을 추가하면 표시됩니다.</string>
|
||||
<string name="radio_station_info_empty_title">스테이션을 찾을 수 없습니다.</string>
|
||||
<string name="rating_dialog_negative_button">취소</string>
|
||||
<string name="rating_dialog_positive_button">저장</string>
|
||||
<string name="rating_dialog_title">별점</string>
|
||||
<string name="search_hint">제목, 아티스트, 앨범 검색</string>
|
||||
<string name="search_info_minimum_characters">3자 이상 입력하세요.</string>
|
||||
<string name="search_title_album">앨범</string>
|
||||
<string name="search_title_artist">아티스트</string>
|
||||
<string name="search_title_song">음악</string>
|
||||
<string name="server_signup_dialog_action_low_security">낮은 보안</string>
|
||||
<string name="server_signup_dialog_hint_name">서버 이름</string>
|
||||
<string name="server_signup_dialog_hint_password">암호</string>
|
||||
<string name="server_signup_dialog_hint_url">서버 URL</string>
|
||||
<string name="server_signup_dialog_hint_username">사용자 이름</string>
|
||||
<string name="server_signup_dialog_negative_button">취소</string>
|
||||
<string name="server_signup_dialog_neutral_button">삭제</string>
|
||||
<string name="server_signup_dialog_positive_button">저장</string>
|
||||
<string name="server_signup_dialog_title">서버 추가</string>
|
||||
<string name="server_unreachable_dialog_negative_button">취소</string>
|
||||
<string name="server_unreachable_dialog_neutral_button">로그인으로 이동</string>
|
||||
<string name="server_unreachable_dialog_positive_button">무시하고 계속</string>
|
||||
<string name="server_unreachable_dialog_summary">요청한 서버를 사용할 수 없습니다. 계속하면 한 시간 동안 더 이상 경고하지 않습니다.</string>
|
||||
<string name="server_unreachable_dialog_title">서버에 연결할 수 없음</string>
|
||||
<string name="settings_about_summary">Tempo는 안드로이드용 경량 Subsonic 오픈 소스 음악 클라이언트입니다.</string>
|
||||
<string name="settings_about_title">About</string>
|
||||
<string name="settings_audio_transcode_download_format">트랜스코딩 포맷</string>
|
||||
<string name="settings_audio_transcode_download_priority_summary">활성화 시, 아래 트랜스코딩 설정으로 트랙 강제 다운로드를 하지 않습니다.</string>
|
||||
<string name="settings_audio_transcode_download_priority_title">다운로드 스트리밍에 사용할 서버 설정 우선순위 지정</string>
|
||||
<string name="settings_audio_transcode_download_summary">활성화 시, 트랜스코딩된 트랙을 다운로드합니다.</string>
|
||||
<string name="settings_audio_transcode_download_title">트랜스코딩 트랙 다운로드</string>
|
||||
<string name="settings_audio_transcode_estimate_content_length_summary">활성화 시, 서버가 트랙의 예상 재생 시간을 묻습니다.</string>
|
||||
<string name="settings_audio_transcode_estimate_content_length_title">예상 재생 시간</string>
|
||||
<string name="settings_audio_transcode_format_download">다운로드용 트랜스코딩 포맷</string>
|
||||
<string name="settings_audio_transcode_format_mobile">데이터 사용 시 트랜스코딩 포맷</string>
|
||||
<string name="settings_audio_transcode_format_wifi">Wi-Fi 사용 시 트랜스코딩 포맷</string>
|
||||
<string name="settings_audio_transcode_priority_summary">활성화 시, 아래 트랜스코딩 설정으로 트랙 강제 스트리밍을 하지 않습니다.</string>
|
||||
<string name="settings_audio_transcode_priority_title">서버 트랜스코딩 우선순위 설정</string>
|
||||
<string name="settings_audio_transcode_priority_toast">서버에 적용할 트랙 트랜스코딩 우선순위</string>
|
||||
<string name="settings_buffering_strategy">버퍼링 전략</string>
|
||||
<string name="settings_buffering_strategy_summary">변경 사항을 적용하려면 앱을 수동으로 다시 시작해야 합니다.</string>
|
||||
<string name="settings_covers_cache">앨범 커버 캐시 크기</string>
|
||||
<string name="settings_data_saving_mode_summary">데이터 소비를 줄이려면 앨범 커버 다운로드를 피하세요.</string>
|
||||
<string name="settings_data_saving_mode_title">모바일 데이터 사용량 제한</string>
|
||||
<string name="settings_delete_download_storage_summary">계속하면 저장된 모든 항목을 완전히 삭제합니다.</string>
|
||||
<string name="settings_delete_download_storage_title">저장된 항목 삭제</string>
|
||||
<string name="settings_download_storage_title">스토리지 다운로드</string>
|
||||
<string name="settings_equalizer_summary">오디오 설정 적용</string>
|
||||
<string name="settings_equalizer_title">이퀄라이저</string>
|
||||
<string name="settings_github_link">https://github.com/CappielloAntonio/tempo</string>
|
||||
<string name="settings_github_summary">Follow the development</string>
|
||||
<string name="settings_github_title">Github</string>
|
||||
<string name="settings_image_size">이미지 해상도 설정</string>
|
||||
<string name="settings_language">언어</string>
|
||||
<string name="settings_logout_title">로그아웃</string>
|
||||
<string name="settings_max_bitrate_download">다운로드 비트 전송률</string>
|
||||
<string name="settings_max_bitrate_mobile">데이터 사용 시 비트 전송률</string>
|
||||
<string name="settings_max_bitrate_wifi">Wi-Fi 사용 시 비트 전송률</string>
|
||||
<string name="settings_media_cache">미디어 파일 캐시 크기</string>
|
||||
<string name="settings_music_directory">음악 디렉토리 보기</string>
|
||||
<string name="settings_music_directory_summary">활성화 시, 음악 디렉터리 섹션을 표시합니다. 폴더 탐색이 제대로 작동하려면 서버가 이 기능을 지원해야 합니다.</string>
|
||||
<string name="settings_podcast">팟캐스트 보기</string>
|
||||
<string name="settings_podcast_summary">활성화 시, 팟캐스트 섹션을 표시합니다.</string>
|
||||
<string name="settings_queue_syncing_countdown">동기화 타이머</string>
|
||||
<string name="settings_queue_syncing_summary">활성화 시, 재생목록을 저장하여 재실행 시 상태를 불러올 수 있습니다.</string>
|
||||
<string name="settings_queue_syncing_title">사용자의 재생목록 동기화</string>
|
||||
<string name="settings_radio">라디오 보기</string>
|
||||
<string name="settings_radio_summary">활성화 시, 라디오 섹션을 표시합니다.</string>
|
||||
<string name="settings_replay_gain">replay gain 모드 설정</string>
|
||||
<string name="settings_rounded_corner">모서리를 둥글게 하기</string>
|
||||
<string name="settings_rounded_corner_size">모서리 크기</string>
|
||||
<string name="settings_rounded_corner_size_summary">모서리의 곡률 각도를 설정합니다.</string>
|
||||
<string name="settings_rounded_corner_summary">활성화 시, 렌더링된 모든 앨범 커버의 곡률 각도를 설정합니다. 다시 시작하면 적용됩니다.</string>
|
||||
<string name="settings_scan_title">라이브러리 스캔</string>
|
||||
<string name="settings_scrobble_title">음악 스크로블링 활성화</string>
|
||||
<string name="settings_share_title">음악 공유 활성화</string>
|
||||
<string name="settings_sub_summary_scrobble">스크로블링은 이 데이터를 수신할 수 있는 서버에 의존합니다.</string>
|
||||
<string name="settings_summary_skip_min_star_rating">아티스트의 라디오를 들을 때, 인스턴트 믹스를 들을 때, 전체를 셔플할 때 특정 별점 이하의 트랙은 무시됩니다.</string>
|
||||
<string name="settings_summary_replay_gain">Replay gain은 일관된 청취 경험을 위해 오디오 트랙의 볼륨 레벨을 조정할 수 있는 기능입니다. 이 설정은 필요한 메타데이터가 트랙에 포함된 경우에만 유효합니다.</string>
|
||||
<string name="settings_summary_scrobble">스크로블링은 기기에서 들은 음악 정보를 음악 서버로 보내는 기능입니다. 이 정보는 음악 선호도에 따른 맞춤 추천을 생성하는 데 사용합니다.</string>
|
||||
<string name="settings_summary_share">링크를 통해 음악을 공유할 수 있습니다. 이 기능은 서버 측에서 지원 및 활성화되어야 하며 개별 트랙, 앨범, 재생 목록으로 제한됩니다.</string>
|
||||
<string name="settings_summary_syncing">사용자의 재생목록의 상태를 반환합니다. 재생목록의 트랙, 현재 재생 중인 트랙, 트랙 번호가 포함됩니다. 서버가 이 기능을 지원해야 합니다.</string>
|
||||
<string name="settings_summary_transcoding">트랜스코딩 모드에 우선순위가 부여됩니다. \"직접 재생\"으로 설정하면 파일의 비트 전송률이 변경되지 않습니다.</string>
|
||||
<string name="settings_summary_transcoding_download">트랜스코딩된 미디어를 다운로드합니다. 활성화하면 다운로드 endpoint를 사용하지 않고 다음 설정이 사용됩니다. \n\n \"다운로드용 트랜스코딩 포맷\"이 \"직접 다운로드\"로 설정된 경우 파일의 비트 전송률은 변경되지 않습니다.</string>
|
||||
<string name="settings_summary_transcoding_estimate_content_length">파일이 즉시 트랜스코딩되면 일반적으로 트랙 길이를 표시하지 않습니다. 트랙의 재생시간을 추정하는 기능을 지원한다면 서버에 요청할 수 있지만 응답 시간이 필요할 수 있습니다.</string>
|
||||
<string name="settings_sync_starred_tracks_for_offline_use_summary">활성화 시, 즐겨찾기 트랙을 오프라인으로 사용할 수 있도록 다운로드합니다.</string>
|
||||
<string name="settings_sync_starred_tracks_for_offline_use_title">오프라인 사용을 위해 즐겨찾기 트랙 동기화</string>
|
||||
<string name="settings_theme">테마</string>
|
||||
<string name="settings_title_data">데이터</string>
|
||||
<string name="settings_title_general">일반</string>
|
||||
<string name="settings_title_rating">별점</string>
|
||||
<string name="settings_title_replay_gain">Replay Gain</string>
|
||||
<string name="settings_title_scrobble">스크로블링</string>
|
||||
<string name="settings_title_skip_min_star_rating">별점 기준으로 트랙 무시</string>
|
||||
<string name="settings_title_skip_min_star_rating_dialog">음악 별점:</string>
|
||||
<string name="settings_title_share">공유</string>
|
||||
<string name="settings_title_syncing">동기화</string>
|
||||
<string name="settings_title_transcoding">트랜스코딩</string>
|
||||
<string name="settings_title_transcoding_download">트랜스코딩 다운로드</string>
|
||||
<string name="settings_title_ui">UI</string>
|
||||
<string name="settings_transcoded_download">트랜스코딩된 다운로드</string>
|
||||
<string name="settings_version_summary" translatable="false">3.1.0</string>
|
||||
<string name="settings_version_title">버전</string>
|
||||
<string name="settings_wifi_only_summary">모바일 데이터로 스트리밍하려 할 시 확인창을 띄웁니다.</string>
|
||||
<string name="settings_wifi_only_title">Wi-Fi로만 스트리밍 확인창</string>
|
||||
<string name="share_bottom_sheet_copy_link">링크 복사</string>
|
||||
<string name="share_bottom_sheet_delete">공유 삭제</string>
|
||||
<string name="share_bottom_sheet_update">공유 업데이트</string>
|
||||
<string name="share_subtitle_item">만료일: %1$s</string>
|
||||
<string name="share_unsupported_error">공유 기능을 지원하지 않거나 활성화되지 않았습니다.</string>
|
||||
<string name="share_update_dialog_hint_description">설명</string>
|
||||
<string name="share_update_dialog_hint_expiration_date">만료일</string>
|
||||
<string name="share_update_dialog_negative_button">취소</string>
|
||||
<string name="share_update_dialog_positive_button">저장</string>
|
||||
<string name="share_update_dialog_title">공유</string>
|
||||
<string name="song_bottom_sheet_add_to_playlist">플레이리스트에 추가</string>
|
||||
<string name="song_bottom_sheet_add_to_queue">재생목록에 추가</string>
|
||||
<string name="song_bottom_sheet_download">다운로드</string>
|
||||
<string name="song_bottom_sheet_error_retrieving_album">앨범 검색 중 오류가 발생했습니다.</string>
|
||||
<string name="song_bottom_sheet_error_retrieving_artist">아티스트 검색 중 오류가 발생했습니다.</string>
|
||||
<string name="song_bottom_sheet_go_to_album">앨범으로 이동</string>
|
||||
<string name="song_bottom_sheet_go_to_artist">아티스트로 이동</string>
|
||||
<string name="song_bottom_sheet_instant_mix">인스턴트 믹스</string>
|
||||
<string name="song_bottom_sheet_play_next">다음 재생</string>
|
||||
<string name="song_bottom_sheet_rate">별점</string>
|
||||
<string name="song_bottom_sheet_remove">제거</string>
|
||||
<string name="song_bottom_sheet_share">공유</string>
|
||||
<string name="song_list_page_downloaded">다운로드됨</string>
|
||||
<string name="song_list_page_most_played">가장 많이 재생한 트랙</string>
|
||||
<string name="song_list_page_recently_added">최근 추가한 트랙</string>
|
||||
<string name="song_list_page_recently_played">최근 재생한 트랙</string>
|
||||
<string name="song_list_page_starred">즐겨찾기한 트랙</string>
|
||||
<string name="song_list_page_top">%1$s\의 top tracks</string>
|
||||
<string name="song_list_page_year">년도 %1$d</string>
|
||||
<string name="song_subtitle_formatter">%1$s • %2$s</string>
|
||||
<string name="starred_sync_dialog_negative_button">취소</string>
|
||||
<string name="starred_sync_dialog_neutral_button">계속</string>
|
||||
<string name="starred_sync_dialog_positive_button">계속해서 다운로드</string>
|
||||
<string name="starred_sync_dialog_summary">즐겨찾기한 트랙을 다운로드할 시 많은 양의 데이터가 필요할 수 있습니다.</string>
|
||||
<string name="starred_sync_dialog_title">즐겨찾기 한 트랙 동기화</string>
|
||||
<string name="track_info_album">앨범</string>
|
||||
<string name="track_info_artist">아티스트</string>
|
||||
<string name="track_info_bitrate">비트 전송률</string>
|
||||
<string name="track_info_content_type">컨텐츠 타입</string>
|
||||
<string name="track_info_dialog_positive_button">OK</string>
|
||||
<string name="track_info_dialog_title">트랙 정보</string>
|
||||
<string name="track_info_disc_number">디스크 번호</string>
|
||||
<string name="track_info_duration">재생 시간</string>
|
||||
<string name="track_info_genre">장르</string>
|
||||
<string name="track_info_path">경로</string>
|
||||
<string name="track_info_size">크기</string>
|
||||
<string name="track_info_suffix">접미사</string>
|
||||
<string name="track_info_summary_downloaded_file">파일은 Subsonic API를 사용하여 다운로드되었습니다. 파일의 코덱과 비트 전송률은 소스 파일과 동일하게 유지됩니다.</string>
|
||||
<string name="track_info_summary_full_transcode">서버에 파일을 트랜스코딩하고 비트 전송률을 수정하도록 요청합니다. 사용자가 요청한 코덱은 %1$s이고 비트 전송률은 %2$s입니다. 선택한 형식의 파일 코덱 및 비트 전송률에 대한 변경 사항은 서버에서 수행하며 서버가 이를 지원하지 않을 수 있습니다.</string>
|
||||
<string name="track_info_summary_original_file">서버에서 제공한 원본 파일만 읽습니다. 앱은 원본 소스의 비트 전송률을 사용하여 트랜스코딩되지 않은 파일을 서버에 명시적으로 요청합니다.</string>
|
||||
<string name="track_info_summary_server_prioritized">파일의 품질을 서버 설정에 맞춥니다. 앱은 잠재적인 트랜스코딩에 대해 코덱 및 비트 전송률 선택을 강제하지 않습니다.</string>
|
||||
<string name="track_info_summary_transcoding_bitrate">서버에 파일의 비트 전송률을 수정하도록 요청합니다. 사용자가 %1$s의 비트 전송률을 요청해도 소스 파일의 코덱은 동일하게 유지됩니다. 선택한 형식의 파일 비트 전송률에 대한 변경 사항은 서버에서 수행하며 서버가 이를 지원하지 않을 수 있습니다.</string>
|
||||
<string name="track_info_summary_transcoding_codec">서버에 파일을 트랜스코딩 하도록 요청합니다. 사용자가 요청한 코덱은 %1$s이고 비트 전송률은 소스 파일과 동일합니다. 서버가 기능을 지원할 시에만 선택한 형식으로 트랜스코딩 됩니다.</string>
|
||||
<string name="track_info_title">타이틀</string>
|
||||
<string name="track_info_track_number">트랙 번호</string>
|
||||
<string name="track_info_transcoded_content_type">트랜스코딩된 콘텐츠 유형</string>
|
||||
<string name="track_info_transcoded_suffix">트랜스코딩된 접미사</string>
|
||||
<string name="track_info_year">년도</string>
|
||||
<string name="undraw_page">unDraw</string>
|
||||
<string name="undraw_thanks">이 앱을 일러스트로 더 다채롭게 꾸밀 수 있도록 해준 unDraw 에 특별히 감사드립니다.</string>
|
||||
<string name="undraw_url">https://undraw.co/</string>
|
||||
</resources>
|
||||
227
app/src/main/res/values-zh/arrays.xml
Normal file
227
app/src/main/res/values-zh/arrays.xml
Normal file
@@ -0,0 +1,227 @@
|
||||
<resources>
|
||||
<string-array name="theme_list_titles">
|
||||
<item>浅色</item>
|
||||
<item>深色</item>
|
||||
<item>跟随系统</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>高</item>
|
||||
<item>中</item>
|
||||
<item>低</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>高</item>
|
||||
<item>中</item>
|
||||
<item>低</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>原始</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>原始</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="max_bitrate_download_list_titles">
|
||||
<item>原始</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">
|
||||
<item>播放原始</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>播放原始</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="audio_transcode_format_download_list_titles">
|
||||
<item>下载原始</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">
|
||||
<item>10秒</item>
|
||||
<item>5秒</item>
|
||||
<item>2秒</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>高</item>
|
||||
<item>中</item>
|
||||
<item>低</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>禁用</item>
|
||||
<item>曲目</item>
|
||||
<item>专辑</item>
|
||||
<item>自动</item>
|
||||
</string-array>
|
||||
<string-array name="replay_gain_values">
|
||||
<item>disabled</item>
|
||||
<item>track</item>
|
||||
<item>album</item>
|
||||
<item>auto</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="transcoded_download_option_list_titles">
|
||||
<item>不转码</item>
|
||||
<item>服务器设置</item>
|
||||
<item>Wi-Fi转码设置</item>
|
||||
<item>移动数据转码设置</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>
|
||||
|
||||
<string-array name="buffering_strategy_titles">
|
||||
<item>最小</item>
|
||||
<item>适中</item>
|
||||
<item>积极</item>
|
||||
<item>极端</item>
|
||||
</string-array>
|
||||
<string-array name="buffering_strategy_values">
|
||||
<item>.1</item>
|
||||
<item>1</item>
|
||||
<item>4</item>
|
||||
<item>8</item>
|
||||
</string-array>
|
||||
</resources>
|
||||
362
app/src/main/res/values-zh/strings.xml
Normal file
362
app/src/main/res/values-zh/strings.xml
Normal file
@@ -0,0 +1,362 @@
|
||||
<resources>
|
||||
<string name="activity_battery_optimizations_conclusion">如果遇到问题,请访问 https://dontkillmyapp.com。 省电优化选项可能会影响应用的性能,网站上提供了如何禁用这些选项的详细说明。</string>
|
||||
<string name="activity_battery_optimizations_summary">请禁用针对媒体锁屏播放的电池优化。</string>
|
||||
<string name="activity_battery_optimizations_title">电池优化</string>
|
||||
<string name="activity_info_offline_mode">离线模式</string>
|
||||
<string name="album_bottom_sheet_add_to_queue">添加到队列</string>
|
||||
<string name="album_bottom_sheet_download_all">全部下载</string>
|
||||
<string name="album_bottom_sheet_go_to_artist">查看该艺术家</string>
|
||||
<string name="album_bottom_sheet_instant_mix">即时混合</string>
|
||||
<string name="album_bottom_sheet_play_next">下一首播放</string>
|
||||
<string name="album_bottom_sheet_remove_all">移除所有</string>
|
||||
<string name="album_bottom_sheet_share">分享</string>
|
||||
<string name="album_bottom_sheet_shuffle">随机播放</string>
|
||||
<string name="album_catalogue_title">专辑</string>
|
||||
<string name="album_catalogue_title_expanded">浏览专辑</string>
|
||||
<string name="album_error_retrieving_artist">检索艺术家时出错</string>
|
||||
<string name="album_list_page_downloaded">已下载的专辑</string>
|
||||
<string name="album_list_page_most_played">最常播放的专辑</string>
|
||||
<string name="album_list_page_new_releases">新发行</string>
|
||||
<string name="album_list_page_recently_added">最近添加的专辑</string>
|
||||
<string name="album_list_page_recently_played">最近播放的专辑</string>
|
||||
<string name="album_list_page_starred">收藏的专辑</string>
|
||||
<string name="album_list_page_title">专辑</string>
|
||||
<string name="album_page_extra_info_button">更多相似</string>
|
||||
<string name="album_page_play_button">播放</string>
|
||||
<string name="album_page_shuffle_button">随机播放</string>
|
||||
<string name="app_name">Tempo</string>
|
||||
<string name="artist_adapter_radio_station_starting">正在搜索...</string>
|
||||
<string name="artist_bottom_sheet_instant_mix">即时混合</string>
|
||||
<string name="artist_bottom_sheet_shuffle">随机播放</string>
|
||||
<string name="artist_catalogue_title">艺术家</string>
|
||||
<string name="artist_catalogue_title_expanded">浏览艺术家</string>
|
||||
<string name="artist_error_retrieving_radio">检索艺术家的电台时出错</string>
|
||||
<string name="artist_error_retrieving_tracks">检索艺术家曲目时出错</string>
|
||||
<string name="artist_list_page_downloaded">已下载的艺术家</string>
|
||||
<string name="artist_list_page_starred">收藏的艺人</string>
|
||||
<string name="artist_list_page_title">艺术家</string>
|
||||
<string name="artist_page_radio_button">电台</string>
|
||||
<string name="artist_page_shuffle_button">随机播放</string>
|
||||
<string name="artist_page_switch_layout_button">切换布局</string>
|
||||
<string name="artist_page_title_album_more_like_this_button">更多相似</string>
|
||||
<string name="artist_page_title_album_section">专辑</string>
|
||||
<string name="artist_page_title_biography_more_button">更多</string>
|
||||
<string name="artist_page_title_biography_section">个人简介</string>
|
||||
<string name="artist_page_title_most_streamed_song_section">最常播放的歌曲</string>
|
||||
<string name="artist_page_title_most_streamed_song_see_all_button">查看全部</string>
|
||||
<string name="battery_optimization_negative_button">忽略</string>
|
||||
<string name="battery_optimization_neutral_button">不要再问</string>
|
||||
<string name="battery_optimization_positive_button">禁用</string>
|
||||
<string name="connection_alert_dialog_negative_button">取消</string>
|
||||
<string name="connection_alert_dialog_neutral_button">启用流量节省</string>
|
||||
<string name="connection_alert_dialog_positive_button">确定</string>
|
||||
<string name="connection_alert_dialog_summary">已限制通过 Wi-Fi 以外的连接访问 Subsonic 服务器。 要阻止此警告对话框再次出现,请在应用程序设置中禁用连接检查。</string>
|
||||
<string name="connection_alert_dialog_title">Wi-Fi网络未连接</string>
|
||||
<string name="delete_download_storage_dialog_negative_button">取消</string>
|
||||
<string name="delete_download_storage_dialog_positive_button">继续</string>
|
||||
<string name="delete_download_storage_dialog_summary">请注意,继续执行此操作将永久删除从所有服务器下载的所有已保存的项目。</string>
|
||||
<string name="delete_download_storage_dialog_title">删除已保存的项目</string>
|
||||
<string name="description_empty_title">没有可用的描述</string>
|
||||
<string name="download_directory_dialog_negative_button">取消</string>
|
||||
<string name="download_directory_dialog_positive_button">下载</string>
|
||||
<string name="download_directory_dialog_summary">该文件夹中的所有曲目将被下载。 子文件夹中的曲目将不会被下载。</string>
|
||||
<string name="download_directory_dialog_title">下载曲目</string>
|
||||
<string name="download_info_empty_subtitle">下载歌曲后,您可以在这里找到它。</string>
|
||||
<string name="download_info_empty_title">还没有下载!</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_storage_dialog_sub_summary">要使更改生效,请重新启动应用程序。</string>
|
||||
<string name="download_storage_dialog_summary">更改已下载文件的目录将会立即删除以前已下载的所有文件。</string>
|
||||
<string name="download_storage_dialog_title">选择存储选项</string>
|
||||
<string name="download_storage_external_dialog_positive_button">外部</string>
|
||||
<string name="download_storage_internal_dialog_negative_button">内部</string>
|
||||
<string name="download_title_section">下载</string>
|
||||
<string name="downloaded_bottom_sheet_add_to_queue">添加到队列</string>
|
||||
<string name="downloaded_bottom_sheet_play_next">下一首播放</string>
|
||||
<string name="downloaded_bottom_sheet_remove">移除</string>
|
||||
<string name="downloaded_bottom_sheet_remove_all">移除所有</string>
|
||||
<string name="downloaded_bottom_sheet_shuffle">随机播放</string>
|
||||
<string name="empty_string"></string>
|
||||
<string name="error_required">必需</string>
|
||||
<string name="error_server_prefix">必须是 http 或 https 前缀</string>
|
||||
<string name="exo_download_notification_channel_name">下载</string>
|
||||
<string name="filter_info_selection">选择两个或多个过滤器</string>
|
||||
<string name="filter_title">筛选</string>
|
||||
<string name="filter_title_expanded">筛选流派</string>
|
||||
<string name="genre_catalogue_title">流派目录</string>
|
||||
<string name="genre_catalogue_title_expanded">浏览流派</string>
|
||||
<string name="home_subtitle_best_of">您最喜欢的艺术家的热门歌曲</string>
|
||||
<string name="home_subtitle_made_for_you">从您喜欢的歌曲开始混音</string>
|
||||
<string name="home_subtitle_new_internet_radio_station">添加新的电台</string>
|
||||
<string name="home_subtitle_new_podcast_channel">添加新的播客频道</string>
|
||||
<string name="home_sync_starred_cancel">取消</string>
|
||||
<string name="home_sync_starred_download">下载</string>
|
||||
<string name="home_sync_starred_subtitle">下载这些曲目可能需要大量移动数据</string>
|
||||
<string name="home_sync_starred_title">似乎有一些已收藏的曲目需要同步</string>
|
||||
<string name="home_title_best_of">最佳</string>
|
||||
<string name="home_title_discovery">发现</string>
|
||||
<string name="home_title_discovery_shuffle_all_button">全部随机播放</string>
|
||||
<string name="home_title_flashback">闪回</string>
|
||||
<string name="home_title_internet_radio_station">网络广播电台</string>
|
||||
<string name="home_title_last_played">最近播放</string>
|
||||
<string name="home_title_last_played_see_all_button">查看全部</string>
|
||||
<string name="home_title_last_week">上周</string>
|
||||
<string name="home_title_made_for_you">为您定制</string>
|
||||
<string name="home_title_most_played">最常播放</string>
|
||||
<string name="home_title_most_played_see_all_button">查看全部</string>
|
||||
<string name="home_title_new_releases">新发行</string>
|
||||
<string name="home_title_newest_podcasts">最新播客</string>
|
||||
<string name="home_title_podcast_channels">频道</string>
|
||||
<string name="home_title_podcast_channels_see_all_button">查看全部</string>
|
||||
<string name="home_title_radio_station">广播电台</string>
|
||||
<string name="home_title_recently_added">最近添加</string>
|
||||
<string name="home_title_recently_added_see_all_button">查看全部</string>
|
||||
<string name="home_title_shares">分享</string>
|
||||
<string name="home_title_starred_albums">★ 收藏的专辑</string>
|
||||
<string name="home_title_starred_albums_see_all_button">查看全部</string>
|
||||
<string name="home_title_starred_artists">★ 收藏的艺术家</string>
|
||||
<string name="home_title_starred_artists_see_all_button">查看全部</string>
|
||||
<string name="home_title_starred_tracks">★ 收藏的曲目</string>
|
||||
<string name="home_title_starred_tracks_see_all_button">查看全部</string>
|
||||
<string name="home_title_top_songs">你最喜欢的歌曲</string>
|
||||
<string name="label_dot_separator" translatable="false">•</string>
|
||||
<string name="label_placeholder" translatable="false">--</string>
|
||||
<string name="library_title_album">专辑</string>
|
||||
<string name="library_title_album_see_all_button">查看全部</string>
|
||||
<string name="library_title_artist">艺术家</string>
|
||||
<string name="library_title_artist_see_all_button">查看全部</string>
|
||||
<string name="library_title_genre">流派</string>
|
||||
<string name="library_title_genre_see_all_button">查看全部</string>
|
||||
<string name="library_title_music_folder">音乐文件夹</string>
|
||||
<string name="library_title_playlist">播放列表</string>
|
||||
<string name="library_title_playlist_see_all_button">查看全部</string>
|
||||
<string name="login_empty">尚未添加服务器</string>
|
||||
<string name="login_title">Subsonic 服务器</string>
|
||||
<string name="login_title_expanded">Subsonic 服务器</string>
|
||||
<string name="media_route_menu_title">投送</string>
|
||||
<string name="menu_add_button">添加</string>
|
||||
<string name="menu_download_all_button">全部下载</string>
|
||||
<string name="menu_download_label">下载</string>
|
||||
<string name="menu_filter_all">全部</string>
|
||||
<string name="menu_filter_download">已下载</string>
|
||||
<string name="menu_group_by_album">专辑</string>
|
||||
<string name="menu_group_by_artist">艺术家</string>
|
||||
<string name="menu_group_by_genre">流派</string>
|
||||
<string name="menu_group_by_track">曲目</string>
|
||||
<string name="menu_group_by_year">年份</string>
|
||||
<string name="menu_home_label">首页</string>
|
||||
<string name="menu_library_label">曲库</string>
|
||||
<string name="menu_search_button">搜索</string>
|
||||
<string name="menu_settings_button">设置</string>
|
||||
<string name="menu_sort_artist">艺术家</string>
|
||||
<string name="menu_sort_name">姓名</string>
|
||||
<string name="menu_sort_random">随机</string>
|
||||
<string name="menu_sort_year">年份</string>
|
||||
<string name="player_playback_speed">%1$.2fx</string>
|
||||
<string name="player_queue_clean_all_button">清空队列</string>
|
||||
<string name="player_server_priority">服务器优先级</string>
|
||||
<string name="playlist_catalogue_title">播放列表目录</string>
|
||||
<string name="playlist_catalogue_title_expanded">浏览播放列表</string>
|
||||
<string name="playlist_chooser_dialog_empty">尚未创建播放列表</string>
|
||||
<string name="playlist_chooser_dialog_negative_button">取消</string>
|
||||
<string name="playlist_chooser_dialog_neutral_button">新建</string>
|
||||
<string name="playlist_chooser_dialog_title">添加到播放列表</string>
|
||||
<string name="playlist_counted_tracks">%1$d tracks • %2$s</string>
|
||||
<string name="playlist_duration">持续时间 • %1$s</string>
|
||||
<string name="playlist_editor_dialog_hint_name">播放列表名称</string>
|
||||
<string name="playlist_editor_dialog_negative_button">取消</string>
|
||||
<string name="playlist_editor_dialog_neutral_button">删除</string>
|
||||
<string name="playlist_editor_dialog_positive_button">保存</string>
|
||||
<string name="playlist_editor_dialog_title">编辑播放列表</string>
|
||||
<string name="playlist_page_play_button">播放</string>
|
||||
<string name="playlist_page_shuffle_button">随机播放</string>
|
||||
<string name="playlist_song_count">播放列表 • %1$d songs</string>
|
||||
<string name="podcast_bottom_sheet_add_to_queue">添加到队列</string>
|
||||
<string name="podcast_bottom_sheet_delete">删除</string>
|
||||
<string name="podcast_bottom_sheet_download">下载</string>
|
||||
<string name="podcast_bottom_sheet_go_to_channel">前往该频道</string>
|
||||
<string name="podcast_bottom_sheet_play_next">下一首播放</string>
|
||||
<string name="podcast_bottom_sheet_remove">移除</string>
|
||||
<string name="podcast_channel_catalogue_title">频道</string>
|
||||
<string name="podcast_channel_catalogue_title_expanded">浏览频道</string>
|
||||
<string name="podcast_channel_editor_dialog_hint_rss_url">RSS 网址</string>
|
||||
<string name="podcast_channel_editor_dialog_title">播客频道</string>
|
||||
<string name="podcast_channel_page_title_description_section">描述</string>
|
||||
<string name="podcast_channel_page_title_episode_section">剧集</string>
|
||||
<string name="podcast_channel_page_title_no_episode_available">没有可用的剧集</string>
|
||||
<string name="podcast_episode_download_request_snackbar">您的请求已发送至服务器</string>
|
||||
<string name="podcast_info_empty_button">单击以隐藏该部分\n重启应用后生效</string>
|
||||
<string name="podcast_info_empty_subtitle">添加频道后,您将在此处找到它</string>
|
||||
<string name="podcast_info_empty_title">未找到播客!</string>
|
||||
<string name="podcast_release_date_duration_formatter">%1$s • %2$s</string>
|
||||
<string name="radio_editor_dialog_hint_homepage_url">电台主页 URL</string>
|
||||
<string name="radio_editor_dialog_hint_name">电台名称</string>
|
||||
<string name="radio_editor_dialog_hint_stream_url">广播流 URL</string>
|
||||
<string name="radio_editor_dialog_negative_button">取消</string>
|
||||
<string name="radio_editor_dialog_neutral_button">删除</string>
|
||||
<string name="radio_editor_dialog_positive_button">保存</string>
|
||||
<string name="radio_editor_dialog_title">网络广播电台</string>
|
||||
<string name="radio_station_info_empty_button">单击以隐藏该部分\n重启应用后生效</string>
|
||||
<string name="radio_station_info_empty_subtitle">添加广播电台后,您可以在此处找到它</string>
|
||||
<string name="radio_station_info_empty_title">没有找到电台!</string>
|
||||
<string name="rating_dialog_negative_button">取消</string>
|
||||
<string name="rating_dialog_positive_button">保存</string>
|
||||
<string name="rating_dialog_title">评分</string>
|
||||
<string name="search_hint">搜索标题、艺术家或专辑</string>
|
||||
<string name="search_info_minimum_characters">输入至少三个字符</string>
|
||||
<string name="search_title_album">专辑</string>
|
||||
<string name="search_title_artist">艺术家</string>
|
||||
<string name="search_title_song">歌曲</string>
|
||||
<string name="server_signup_dialog_action_low_security">低安全性</string>
|
||||
<string name="server_signup_dialog_hint_name">服务器名称</string>
|
||||
<string name="server_signup_dialog_hint_password">密码</string>
|
||||
<string name="server_signup_dialog_hint_url">服务器地址</string>
|
||||
<string name="server_signup_dialog_hint_username">用户名</string>
|
||||
<string name="server_signup_dialog_negative_button">取消</string>
|
||||
<string name="server_signup_dialog_neutral_button">删除</string>
|
||||
<string name="server_signup_dialog_positive_button">保存</string>
|
||||
<string name="server_signup_dialog_title">添加服务器</string>
|
||||
<string name="server_unreachable_dialog_negative_button">取消</string>
|
||||
<string name="server_unreachable_dialog_neutral_button">前往登录</string>
|
||||
<string name="server_unreachable_dialog_positive_button">仍然继续</string>
|
||||
<string name="server_unreachable_dialog_summary">请求的服务器不可用。 如果您选择继续,此对话框在接下来的一个小时内将不会再次出现。</string>
|
||||
<string name="server_unreachable_dialog_title">服务器无法访问</string>
|
||||
<string name="settings_about_summary">Tempo 是 Subsonic 的开源轻量级音乐客户端,专为 Android 设计和构建。</string>
|
||||
<string name="settings_about_title">关于</string>
|
||||
<string name="settings_audio_transcode_download_format">转码格式</string>
|
||||
<string name="settings_audio_transcode_download_priority_summary">如果启用,Tempo 将不会强制使用下面的转码设置下载曲目。</string>
|
||||
<string name="settings_audio_transcode_download_priority_title">优先考虑服务器上用于流式传输的设置</string>
|
||||
<string name="settings_audio_transcode_download_summary">如果启用,Tempo 将下载转码后的曲目。</string>
|
||||
<string name="settings_audio_transcode_download_title">下载转码后的曲目</string>
|
||||
<string name="settings_audio_transcode_estimate_content_length_summary">如果启用,将发送请求到服务器以查询曲目的估计持续时间。</string>
|
||||
<string name="settings_audio_transcode_estimate_content_length_title">估计内容长度</string>
|
||||
<string name="settings_audio_transcode_format_download">用于下载的转码格式</string>
|
||||
<string name="settings_audio_transcode_format_mobile">移动数据下的转码格式</string>
|
||||
<string name="settings_audio_transcode_format_wifi">Wi-Fi 下的转码格式</string>
|
||||
<string name="settings_audio_transcode_priority_summary">如果启用,Tempo 将不会强制使用下面的转码设置流式传输曲目。</string>
|
||||
<string name="settings_audio_transcode_priority_title">优先考虑服务器转码设置</string>
|
||||
<string name="settings_audio_transcode_priority_toast">曲目转码设置优先级设置为服务器</string>
|
||||
<string name="settings_buffering_strategy">缓存策略</string>
|
||||
<string name="settings_buffering_strategy_summary">为了使更改生效,您必须手动重新启动应用程序。</string>
|
||||
<string name="settings_covers_cache">图片缓存大小</string>
|
||||
<string name="settings_data_saving_mode_summary">为了减少数据消耗,请避免下载封面。</string>
|
||||
<string name="settings_data_saving_mode_title">限制移动数据使用</string>
|
||||
<string name="settings_delete_download_storage_summary">继续当前操作将导致所有已保存的项目被永久删除。</string>
|
||||
<string name="settings_delete_download_storage_title">删除已保存的项目</string>
|
||||
<string name="settings_download_storage_title">下载存储</string>
|
||||
<string name="settings_equalizer_summary">调整音频设置</string>
|
||||
<string name="settings_equalizer_title">均衡器</string>
|
||||
<string name="settings_github_link">https://github.com/CappielloAntonio/tempo</string>
|
||||
<string name="settings_github_summary">关注开发进展</string>
|
||||
<string name="settings_github_title">Github</string>
|
||||
<string name="settings_image_size">设置图像分辨率</string>
|
||||
<string name="settings_language">语言</string>
|
||||
<string name="settings_logout_title">注销登录</string>
|
||||
<string name="settings_max_bitrate_download">用于下载的比特率</string>
|
||||
<string name="settings_max_bitrate_mobile">移动数据下的比特率</string>
|
||||
<string name="settings_max_bitrate_wifi">Wi-Fi 下的比特率</string>
|
||||
<string name="settings_media_cache">媒体文件缓存大小</string>
|
||||
<string name="settings_music_directory">显示音乐目录</string>
|
||||
<string name="settings_music_directory_summary">如果启用,则显示音乐目录部分。 请注意,要使文件夹导航正常工作,服务器必须支持此功能。</string>
|
||||
<string name="settings_podcast">显示播客</string>
|
||||
<string name="settings_podcast_summary">如果启用,则显示播客部分。</string>
|
||||
<string name="settings_queue_syncing_countdown">同步定时器</string>
|
||||
<string name="settings_queue_syncing_summary">如果启用,将允许当前用户保存其播放队列,并能够在打开应用程序时加载保存状态。</string>
|
||||
<string name="settings_queue_syncing_title">同步当前用户的播放队列</string>
|
||||
<string name="settings_radio">显示广播</string>
|
||||
<string name="settings_radio_summary">如果启用,则显示电台部分。</string>
|
||||
<string name="settings_replay_gain">设置播放增益模式</string>
|
||||
<string name="settings_rounded_corner">圆角</string>
|
||||
<string name="settings_rounded_corner_size">圆角大小</string>
|
||||
<string name="settings_rounded_corner_size_summary">设置圆角的大小。</string>
|
||||
<string name="settings_rounded_corner_summary">如果启用,则为所有渲染的封面设置圆角。 更改将在应用重新启动后生效。</string>
|
||||
<string name="settings_scan_title">扫描曲库</string>
|
||||
<string name="settings_scrobble_title">启用音乐记录</string>
|
||||
<string name="settings_share_title">启用音乐共享</string>
|
||||
<string name="settings_sub_summary_scrobble">请注意,音乐记录同时也依赖于服务器是否能够接收这些数据。</string>
|
||||
<string name="settings_summary_replay_gain">播放增益(Replay gain)允许您通过调整音轨的音量,以获得始终如一的聆听体验。 仅当曲目标签包含必要的元数据时,此设置才有效。</string>
|
||||
<string name="settings_summary_scrobble">音乐记录(Scrobbling)允许您的设备将您收听的歌曲的相关信息发送到音乐服务器。 这些信息有助于基于您的音乐偏好生成个性化推荐。</string>
|
||||
<string name="settings_summary_share">允许用户通过链接共享音乐。 该功能需要服务器端支持并启用,并且仅限于单个曲目、专辑和队列。</string>
|
||||
<string name="settings_summary_syncing">返回当前用户的播放队列状态。 这包括播放队列中的曲目、正在播放的曲目以及曲目播放进度。需要服务器支持此功能。</string>
|
||||
<string name="settings_summary_transcoding">转码模式优先级设置。 如果设置为“播放原始”,文件的比特率将不会更改。</string>
|
||||
<string name="settings_summary_transcoding_download">下载转码后的媒体。 如果启用,将不会下载原始数据,而是使用以下设置。\n如果“用于下载的转码格式”设置为“下载原始”,则文件的比特率不会更改。</string>
|
||||
<string name="settings_summary_transcoding_estimate_content_length">当文件即时转码时,客户端通常不会显示曲目长度。 可以向支持该功能的服务器发送请求,估计正在播放的曲目的持续时间,但可能响应变慢。</string>
|
||||
<string name="settings_sync_starred_tracks_for_offline_use_summary">如果启用,将下载已收藏的曲目以供离线使用。</string>
|
||||
<string name="settings_sync_starred_tracks_for_offline_use_title">同步已收藏的曲目以供离线使用</string>
|
||||
<string name="settings_theme">主题</string>
|
||||
<string name="settings_title_data">数据</string>
|
||||
<string name="settings_title_general">通用</string>
|
||||
<string name="settings_title_replay_gain">播放增益</string>
|
||||
<string name="settings_title_scrobble">音乐记录</string>
|
||||
<string name="settings_title_share">分享</string>
|
||||
<string name="settings_title_syncing">同步</string>
|
||||
<string name="settings_title_transcoding">转码</string>
|
||||
<string name="settings_title_transcoding_download">转码下载</string>
|
||||
<string name="settings_title_ui">界面</string>
|
||||
<string name="settings_transcoded_download">转码下载</string>
|
||||
<string name="settings_version_summary" translatable="false">3.1.0</string>
|
||||
<string name="settings_version_title">版本</string>
|
||||
<string name="settings_wifi_only_summary">在通过移动网络进行流式传输之前请求用户确认。</string>
|
||||
<string name="settings_wifi_only_title">提示仅通过 Wi-Fi 进行流式传输</string>
|
||||
<string name="share_bottom_sheet_copy_link">复制链接</string>
|
||||
<string name="share_bottom_sheet_delete">删除分享</string>
|
||||
<string name="share_bottom_sheet_update">更新分享</string>
|
||||
<string name="share_subtitle_item">到期日期:%1$s</string>
|
||||
<string name="share_unsupported_error">不支持分享或未启用</string>
|
||||
<string name="share_update_dialog_hint_description">描述</string>
|
||||
<string name="share_update_dialog_hint_expiration_date">截止日期</string>
|
||||
<string name="share_update_dialog_negative_button">取消</string>
|
||||
<string name="share_update_dialog_positive_button">保存</string>
|
||||
<string name="share_update_dialog_title">分享</string>
|
||||
<string name="song_bottom_sheet_add_to_playlist">添加到播放列表</string>
|
||||
<string name="song_bottom_sheet_add_to_queue">添加到队列</string>
|
||||
<string name="song_bottom_sheet_download">下载</string>
|
||||
<string name="song_bottom_sheet_error_retrieving_album">检索相册时出错</string>
|
||||
<string name="song_bottom_sheet_error_retrieving_artist">检索艺术家时出错</string>
|
||||
<string name="song_bottom_sheet_go_to_album">前往该专辑</string>
|
||||
<string name="song_bottom_sheet_go_to_artist">前往该艺术家</string>
|
||||
<string name="song_bottom_sheet_instant_mix">即时混合</string>
|
||||
<string name="song_bottom_sheet_play_next">下一首播放</string>
|
||||
<string name="song_bottom_sheet_rate">评分</string>
|
||||
<string name="song_bottom_sheet_remove">移除</string>
|
||||
<string name="song_bottom_sheet_share">分享</string>
|
||||
<string name="song_list_page_downloaded">已下载</string>
|
||||
<string name="song_list_page_most_played">最常播放的曲目</string>
|
||||
<string name="song_list_page_recently_added">最近添加的曲目</string>
|
||||
<string name="song_list_page_recently_played">最近播放的曲目</string>
|
||||
<string name="song_list_page_starred">已收藏的曲目</string>
|
||||
<string name="song_list_page_top">%1$s 的热门曲目</string>
|
||||
<string name="song_list_page_year">年份 %1$d</string>
|
||||
<string name="song_subtitle_formatter">%1$s • %2$s</string>
|
||||
<string name="starred_sync_dialog_negative_button">取消</string>
|
||||
<string name="starred_sync_dialog_neutral_button">继续</string>
|
||||
<string name="starred_sync_dialog_positive_button">继续并下载</string>
|
||||
<string name="starred_sync_dialog_summary">下载收藏曲目可能需要大量数据。</string>
|
||||
<string name="starred_sync_dialog_title">同步已收藏的曲目</string>
|
||||
<string name="track_info_album">专辑</string>
|
||||
<string name="track_info_artist">艺术家</string>
|
||||
<string name="track_info_bitrate">比特率</string>
|
||||
<string name="track_info_content_type">内容类型</string>
|
||||
<string name="track_info_dialog_positive_button">确定</string>
|
||||
<string name="track_info_dialog_title">曲目信息</string>
|
||||
<string name="track_info_disc_number">碟片编号</string>
|
||||
<string name="track_info_duration">持续时间</string>
|
||||
<string name="track_info_genre">流派</string>
|
||||
<string name="track_info_path">路径</string>
|
||||
<string name="track_info_size">大小</string>
|
||||
<string name="track_info_suffix">后缀</string>
|
||||
<string name="track_info_summary_downloaded_file">该文件已使用 Subsonic API 下载。 文件的编码和比特率与源文件一致。</string>
|
||||
<string name="track_info_summary_full_transcode">本应用将请求服务器对文件进行转码并修改其比特率。 用户请求的编解码器是%1$s,比特率为%2$s。 对所选格式的文件的编码和比特率的任何潜在更改都将由服务器处理,服务器可能支持也可能不支持该操作。</string>
|
||||
<string name="track_info_summary_original_file">本应用只会读取服务器提供的原始文件。 本应用将明确向服务器请求具有原始源比特率的未转码文件。</string>
|
||||
<string name="track_info_summary_server_prioritized">要播放的文件质量取决于服务器设置。 本应用不会强制选择任何用于潜在转码的编码和比特率。</string>
|
||||
<string name="track_info_summary_transcoding_bitrate">本应用将请求服务器修改文件的比特率。 用户请求的比特率为%1$s,而源文件的编码将保持不变。 对所选格式的文件比特率的任何更改都将由服务器完成,服务器可能支持也可能不支持该操作。</string>
|
||||
<string name="track_info_summary_transcoding_codec">本应用将请求服务器对文件进行转码。 用户请求的编解码器是%1$s,而比特率将与源文件相同。 将文件转码为所选格式的可能性取决于服务器,因为它可能支持也可能不支持该操作。</string>
|
||||
<string name="track_info_title">标题</string>
|
||||
<string name="track_info_track_number">曲目编号</string>
|
||||
<string name="track_info_transcoded_content_type">转码内容类型</string>
|
||||
<string name="track_info_transcoded_suffix">转码后缀</string>
|
||||
<string name="track_info_year">年份</string>
|
||||
<string name="undraw_page">unDraw</string>
|
||||
<string name="undraw_thanks">特别感谢 unDraw,没有它提供的插图,我们的应用不可能会如此精美。</string>
|
||||
<string name="undraw_url">https://undraw.co/</string>
|
||||
</resources>
|
||||
@@ -211,4 +211,32 @@
|
||||
<item>2</item>
|
||||
<item>3</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="buffering_strategy_titles">
|
||||
<item>Minimum</item>
|
||||
<item>Moderate</item>
|
||||
<item>Aggressive</item>
|
||||
<item>Extreme</item>
|
||||
</string-array>
|
||||
<string-array name="buffering_strategy_values">
|
||||
<item>.1</item>
|
||||
<item>1</item>
|
||||
<item>4</item>
|
||||
<item>8</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="skip_min_star_rating_titles">
|
||||
<item>0 star minimum</item>
|
||||
<item>1 star minimum</item>
|
||||
<item>2 stars minimum</item>
|
||||
<item>3 stars minimum</item>
|
||||
<item>4 stars minimum</item>
|
||||
</string-array>
|
||||
<string-array name="skip_min_star_rating_values">
|
||||
<item>0</item>
|
||||
<item>1</item>
|
||||
<item>2</item>
|
||||
<item>3</item>
|
||||
<item>4</item>
|
||||
</string-array>
|
||||
</resources>
|
||||
@@ -1,19 +1,16 @@
|
||||
<resources>
|
||||
<string name="activity_battery_optimizations_summary">Please disable battery optimizations for media playback while the screen is off.</string>
|
||||
<string name="activity_battery_optimizations_conclusion">If in trouble visit https://dontkillmyapp.com. It provides detailed instructions on how to disable any power-saving features that may affect app\'s performance.</string>
|
||||
<string name="activity_battery_optimizations_summary">Please disable battery optimizations for media playback while the screen is off.</string>
|
||||
<string name="activity_battery_optimizations_title">Battery Optimizations</string>
|
||||
<string name="activity_info_offline_mode">Offline mode</string>
|
||||
<string name="battery_optimization_negative_button">Ignore</string>
|
||||
<string name="battery_optimization_positive_button">Disable</string>
|
||||
<string name="battery_optimization_neutral_button">Don\'t ask again</string>
|
||||
<string name="album_bottom_sheet_add_to_queue">Add to queue</string>
|
||||
<string name="album_bottom_sheet_download_all">Download all</string>
|
||||
<string name="album_bottom_sheet_go_to_artist">Go to artist</string>
|
||||
<string name="album_bottom_sheet_instant_mix">Instant mix</string>
|
||||
<string name="album_bottom_sheet_play_next">Play next</string>
|
||||
<string name="album_bottom_sheet_remove_all">Remove all</string>
|
||||
<string name="album_bottom_sheet_shuffle">Shuffle</string>
|
||||
<string name="album_bottom_sheet_share">Share</string>
|
||||
<string name="album_bottom_sheet_shuffle">Shuffle</string>
|
||||
<string name="album_catalogue_title">Albums</string>
|
||||
<string name="album_catalogue_title_expanded">Browse Albums</string>
|
||||
<string name="album_error_retrieving_artist">Error retrieving artist</string>
|
||||
@@ -28,6 +25,7 @@
|
||||
<string name="album_page_play_button">Play</string>
|
||||
<string name="album_page_shuffle_button">Shuffle</string>
|
||||
<string name="app_name">Tempo</string>
|
||||
<string name="artist_adapter_radio_station_starting">Searching…</string>
|
||||
<string name="artist_bottom_sheet_instant_mix">Instant mix</string>
|
||||
<string name="artist_bottom_sheet_shuffle">Shuffle</string>
|
||||
<string name="artist_catalogue_title">Artists</string>
|
||||
@@ -39,31 +37,46 @@
|
||||
<string name="artist_list_page_title">Artists</string>
|
||||
<string name="artist_page_radio_button">Radio</string>
|
||||
<string name="artist_page_shuffle_button">Shuffle</string>
|
||||
<string name="artist_page_switch_layout_button">Switch layout</string>
|
||||
<string name="artist_page_title_album_more_like_this_button">More like this</string>
|
||||
<string name="artist_page_title_album_section">Albums</string>
|
||||
<string name="artist_page_title_biography_more_button">More</string>
|
||||
<string name="artist_page_title_biography_section">Biography</string>
|
||||
<string name="artist_page_title_most_streamed_song_section">Most Streamed Songs</string>
|
||||
<string name="artist_page_title_most_streamed_song_see_all_button">See all</string>
|
||||
<string name="battery_optimization_negative_button">Ignore</string>
|
||||
<string name="battery_optimization_neutral_button">Don\'t ask again</string>
|
||||
<string name="battery_optimization_positive_button">Disable</string>
|
||||
<string name="connection_alert_dialog_negative_button">Cancel</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_summary">Access to the Subsonic server on connections other than Wi-Fi has been restricted. To prevent this alert dialog from reappearing, disable the connection check in the app settings.</string>
|
||||
<string name="connection_alert_dialog_title">Wi-Fi not connected</string>
|
||||
<string name="connection_alert_dialog_summary">Access to the Subsonic server on connections other than Wi-Fi has been restricted. To prevent this alert dialod from reappearing, disable the connection check in the app settings.</string>
|
||||
<string name="delete_download_storage_dialog_negative_button">Cancel</string>
|
||||
<string name="delete_download_storage_dialog_positive_button">Continue</string>
|
||||
<string name="delete_download_storage_dialog_summary">Please be aware that continuing with this action will result in the permanent deletion of all saved items downloaded from all servers.</string>
|
||||
<string name="delete_download_storage_dialog_title">Delete saved items</string>
|
||||
<string name="description_empty_title">No description available</string>
|
||||
<string name="download_directory_dialog_negative_button">Cancel</string>
|
||||
<string name="download_directory_dialog_positive_button">Download</string>
|
||||
<string name="download_directory_dialog_summary">All tracks in this folder will be downloaded. Tracks present in subfolders will not be downloaded.</string>
|
||||
<string name="download_directory_dialog_title">Download the tracks</string>
|
||||
<string name="download_info_empty_subtitle">Once you download a song, you\'ll find it here</string>
|
||||
<string name="download_info_empty_title">No downloads yet!</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">Internal</string>
|
||||
<string name="download_storage_external_dialog_positive_button">External</string>
|
||||
<string name="download_storage_dialog_summary">Changing the destination of downloaded files from one storage to another will result in the immediate deletion of any previously downloaded files in the other storage.</string>
|
||||
<string name="download_shuffle_all_subtitle">Shuffle all</string>
|
||||
<string name="download_storage_dialog_sub_summary">For the changes to take effect, restart the app.</string>
|
||||
<string name="download_storage_dialog_summary">Changing the destination of downloaded files from one storage to another will result in the immediate deletion of any previously downloaded files in the other storage.</string>
|
||||
<string name="download_storage_dialog_title">Select storage option</string>
|
||||
<string name="download_storage_external_dialog_positive_button">External</string>
|
||||
<string name="download_storage_internal_dialog_negative_button">Internal</string>
|
||||
<string name="download_title_section">Downloads</string>
|
||||
<string name="downloaded_bottom_sheet_add_to_queue">Add to queue</string>
|
||||
<string name="downloaded_bottom_sheet_play_next">Play next</string>
|
||||
<string name="downloaded_bottom_sheet_remove">Remove</string>
|
||||
<string name="downloaded_bottom_sheet_remove_all">Remove all</string>
|
||||
<string name="downloaded_bottom_sheet_shuffle">Shuffle</string>
|
||||
<string name="empty_string" />
|
||||
<string name="error_required">Required</string>
|
||||
<string name="error_server_prefix">http or https prefix required</string>
|
||||
@@ -73,23 +86,32 @@
|
||||
<string name="filter_title_expanded">Filter Genres</string>
|
||||
<string name="genre_catalogue_title">Genre Catalogue</string>
|
||||
<string name="genre_catalogue_title_expanded">Browse Genres</string>
|
||||
<string name="home_subtitle_best_of">Top songs of your favorite artists</string>
|
||||
<string name="home_subtitle_made_for_you">Start mix from a song you liked</string>
|
||||
<string name="home_subtitle_new_internet_radio_station">Add a new radio</string>
|
||||
<string name="home_subtitle_new_podcast_channel">Add a new podcast channel</string>
|
||||
<string name="home_subtitle_made_for_you">Start mix from a song you liked</string>
|
||||
<string name="home_sync_starred_title">Looks like there are some starred tracks to sync</string>
|
||||
<string name="home_sync_starred_subtitle">Downloading these tracks may involve significant data usage</string>
|
||||
<string name="home_sync_starred_cancel">Cancel</string>
|
||||
<string name="home_sync_starred_download">Download</string>
|
||||
<string name="home_sync_starred_subtitle">Downloading these tracks may involve significant data usage</string>
|
||||
<string name="home_sync_starred_title">Looks like there are some starred tracks to sync</string>
|
||||
<string name="home_title_best_of">Best of</string>
|
||||
<string name="home_title_discovery">Discovery</string>
|
||||
<string name="home_title_discovery_shuffle_all_button">Shuffle all</string>
|
||||
<string name="home_title_flashback">Flashback</string>
|
||||
<string name="home_title_internet_radio_station">Internet radio stations</string>
|
||||
<string name="home_title_last_played">Last played</string>
|
||||
<string name="home_title_last_played_see_all_button">See all</string>
|
||||
<string name="home_title_last_week">Last week</string>
|
||||
<string name="home_title_made_for_you">Made for you</string>
|
||||
<string name="home_title_most_played">Most played</string>
|
||||
<string name="home_title_most_played_see_all_button">See all</string>
|
||||
<string name="home_title_discovery">Discovery</string>
|
||||
<string name="home_title_new_releases">New releases</string>
|
||||
<string name="home_title_newest_podcasts">Newest podcasts</string>
|
||||
<string name="home_title_podcast_channels">Channels</string>
|
||||
<string name="home_title_podcast_channels_see_all_button">See all</string>
|
||||
<string name="home_title_radio_station">Radio stations</string>
|
||||
<string name="home_title_recently_added">Recently added</string>
|
||||
<string name="home_title_recently_added_see_all_button">See all</string>
|
||||
<string name="home_title_discovery_shuffle_all_button">Shuffle all</string>
|
||||
<string name="home_title_shares">Shares</string>
|
||||
<string name="home_title_starred_albums">★ Starred albums</string>
|
||||
<string name="home_title_starred_albums_see_all_button">See all</string>
|
||||
@@ -97,17 +119,16 @@
|
||||
<string name="home_title_starred_artists_see_all_button">See all</string>
|
||||
<string name="home_title_starred_tracks">★ Starred tracks</string>
|
||||
<string name="home_title_starred_tracks_see_all_button">See all</string>
|
||||
<string name="home_title_internet_radio_station">Internet radio stations</string>
|
||||
<string name="home_title_podcast_channels_see_all_button">See all</string>
|
||||
<string name="home_title_top_songs">Your top songs</string>
|
||||
<string name="label_dot_separator" translatable="false">•</string>
|
||||
<string name="label_placeholder" translatable="false">--</string>
|
||||
<string name="library_title_music_folder">Music folders</string>
|
||||
<string name="library_title_album">Albums</string>
|
||||
<string name="library_title_album_see_all_button">See all</string>
|
||||
<string name="library_title_artist">Artists</string>
|
||||
<string name="library_title_artist_see_all_button">See all</string>
|
||||
<string name="library_title_genre">Genres</string>
|
||||
<string name="library_title_genre_see_all_button">See all</string>
|
||||
<string name="library_title_music_folder">Music folders</string>
|
||||
<string name="library_title_playlist">Playlists</string>
|
||||
<string name="library_title_playlist_see_all_button">See all</string>
|
||||
<string name="login_empty">No server added</string>
|
||||
@@ -117,11 +138,23 @@
|
||||
<string name="menu_add_button">Add</string>
|
||||
<string name="menu_download_all_button">Download all</string>
|
||||
<string name="menu_download_label">Download</string>
|
||||
<string name="menu_filter_all">All</string>
|
||||
<string name="menu_filter_download">Downloaded</string>
|
||||
<string name="menu_group_by_album">Album</string>
|
||||
<string name="menu_group_by_artist">Artist</string>
|
||||
<string name="menu_group_by_genre">Genre</string>
|
||||
<string name="menu_group_by_track">Track</string>
|
||||
<string name="menu_group_by_year">Year</string>
|
||||
<string name="menu_home_label">Home</string>
|
||||
<string name="menu_library_label">Library</string>
|
||||
<string name="menu_search_button">Search</string>
|
||||
<string name="menu_settings_button">Settings</string>
|
||||
<string name="menu_sort_artist">Artist</string>
|
||||
<string name="menu_sort_name">Name</string>
|
||||
<string name="menu_sort_random">Random</string>
|
||||
<string name="menu_sort_year">Year</string>
|
||||
<string name="player_playback_speed">%1$.2fx</string>
|
||||
<string name="player_queue_clean_all_button">Clean play queue</string>
|
||||
<string name="player_server_priority">Server Priority</string>
|
||||
<string name="playlist_catalogue_title">Playlist Catalogue</string>
|
||||
<string name="playlist_catalogue_title_expanded">Browse Playlists</string>
|
||||
@@ -135,32 +168,38 @@
|
||||
<string name="playlist_editor_dialog_negative_button">Cancel</string>
|
||||
<string name="playlist_editor_dialog_neutral_button">Delete</string>
|
||||
<string name="playlist_editor_dialog_positive_button">Save</string>
|
||||
<string name="playlist_editor_dialog_title">Create playlist</string>
|
||||
<string name="playlist_editor_dialog_title">Edit playlist</string>
|
||||
<string name="playlist_page_play_button">Play</string>
|
||||
<string name="playlist_page_shuffle_button">Shuffle</string>
|
||||
<string name="playlist_song_count">Playlist • %1$d songs</string>
|
||||
<string name="podcast_channel_catalogue_title_expanded">Browse Channels</string>
|
||||
<string name="podcast_bottom_sheet_add_to_queue">Add to queue</string>
|
||||
<string name="podcast_bottom_sheet_delete">Delete</string>
|
||||
<string name="podcast_bottom_sheet_download">Download</string>
|
||||
<string name="podcast_bottom_sheet_go_to_channel">Go to channel</string>
|
||||
<string name="podcast_bottom_sheet_play_next">Play next</string>
|
||||
<string name="podcast_bottom_sheet_remove">Remove</string>
|
||||
<string name="podcast_channel_catalogue_title">Channels</string>
|
||||
<string name="podcast_channel_catalogue_title_expanded">Browse Channels</string>
|
||||
<string name="podcast_channel_editor_dialog_hint_rss_url">RSS Url</string>
|
||||
<string name="podcast_channel_editor_dialog_title">Podcast Channel</string>
|
||||
<string name="podcast_channel_page_title_description_section">Description</string>
|
||||
<string name="podcast_channel_page_title_episode_section">Episodes</string>
|
||||
<string name="podcast_channel_page_title_no_episode_available">No episodes available</string>
|
||||
<string name="podcast_channel_editor_dialog_hint_rss_url">RSS Url</string>
|
||||
<string name="podcast_channel_editor_dialog_title">Podcast Channel</string>
|
||||
<string name="podcast_release_date_duration_formatter">%1$s • %2$s</string>
|
||||
<string name="podcast_episode_download_request_snackbar">Your request has been sent to the server</string>
|
||||
<string name="podcast_info_empty_button">Click to hide the section\nThe effects will be visible on restart</string>
|
||||
<string name="podcast_info_empty_subtitle">Once you add a channel, you\'ll find it here</string>
|
||||
<string name="podcast_info_empty_title">No podcasts found!</string>
|
||||
<string name="podcast_info_empty_button">Click to hide the section\nThe effects will be visible on restart</string>
|
||||
<string name="podcast_release_date_duration_formatter">%1$s • %2$s</string>
|
||||
<string name="radio_editor_dialog_hint_homepage_url">Radio Homepage URL</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">Cancel</string>
|
||||
<string name="radio_editor_dialog_neutral_button">Delete</string>
|
||||
<string name="radio_editor_dialog_positive_button">Save</string>
|
||||
<string name="radio_editor_dialog_title">Internet Radio Station</string>
|
||||
<string name="radio_station_info_empty_button">Click to hide the section\nThe effects will be visible on restart</string>
|
||||
<string name="radio_station_info_empty_subtitle">Once you add a radio station, you\'ll find it here</string>
|
||||
<string name="radio_station_info_empty_title">No stations found!</string>
|
||||
<string name="radio_station_info_empty_button">Click to hide the section\nThe effects will be visible on restart</string>
|
||||
<string name="rating_dialog_negative_button">Cancel</string>
|
||||
<string name="rating_dialog_positive_button">Save</string>
|
||||
<string name="rating_dialog_title">Rate</string>
|
||||
@@ -181,28 +220,31 @@
|
||||
<string name="server_unreachable_dialog_negative_button">Cancel</string>
|
||||
<string name="server_unreachable_dialog_neutral_button">Go to login</string>
|
||||
<string name="server_unreachable_dialog_positive_button">Continue anyway</string>
|
||||
<string name="server_unreachable_dialog_title">Server unreachable</string>
|
||||
<string name="server_unreachable_dialog_summary">The requested server is unavailable. If you choose to continue this dialog will not appear for the next hour.</string>
|
||||
<string name="server_unreachable_dialog_title">Server unreachable</string>
|
||||
<string name="settings_about_summary">Tempo is an open source and lightweight music client for Subsonic, designed and built natively for Android.</string>
|
||||
<string name="settings_about_title">About</string>
|
||||
<string name="settings_always_on_display">Always on display</string>
|
||||
<string name="settings_audio_transcode_download_format">Transcode format</string>
|
||||
<string name="settings_audio_transcode_download_priority_summary">If enabled, Tempo will not force download the track with the transcode settings below.</string>
|
||||
<string name="settings_audio_transcode_download_priority_title">Prioritize server settings used for streaming in downloads</string>
|
||||
<string name="settings_audio_transcode_download_summary">If enabled, Tempo will download transcoded tracks.</string>
|
||||
<string name="settings_audio_transcode_download_title">Download transcoded tracks</string>
|
||||
<string name="settings_audio_transcode_download_format">Transcode format</string>
|
||||
<string name="settings_audio_transcode_estimate_content_length_summary">If enabled, the server will be asked for the estimated duration of the track.</string>
|
||||
<string name="settings_audio_transcode_estimate_content_length_title">Estimate content length</string>
|
||||
<string name="settings_audio_transcode_priority_summary">If enabled, Tempo will not force stream the track with the transcode settings below.</string>
|
||||
<string name="settings_audio_transcode_priority_title">Prioritize server transcode settings</string>
|
||||
<string name="settings_audio_transcode_priority_toast">Priority on transcoding of track given to server</string>
|
||||
<string name="settings_audio_transcode_format_download">Transcode format for downloads</string>
|
||||
<string name="settings_audio_transcode_format_mobile">Transcode format in mobile</string>
|
||||
<string name="settings_audio_transcode_format_wifi">Transcode format in Wi-Fi</string>
|
||||
<string name="settings_audio_transcode_priority_summary">If enabled, Tempo will not force stream the track with the transcode settings below.</string>
|
||||
<string name="settings_audio_transcode_priority_title">Prioritize server transcode settings</string>
|
||||
<string name="settings_audio_transcode_priority_toast">Priority on transcoding of track given to server</string>
|
||||
<string name="settings_buffering_strategy">Buffering strategy</string>
|
||||
<string name="settings_buffering_strategy_summary">For the change to take effect you must manually restart the app.</string>
|
||||
<string name="settings_covers_cache">Size of artwork cache</string>
|
||||
<string name="settings_data_saving_mode_summary">In order to reduce data consumption, avoid downloading covers.</string>
|
||||
<string name="settings_data_saving_mode_title">Limit mobile data usage</string>
|
||||
<string name="settings_delete_download_storage_title">Delete saved items</string>
|
||||
<string name="settings_delete_download_storage_summary">Proceeding will result in the irreversible deletion of all saved items.</string>
|
||||
<string name="settings_delete_download_storage_title">Delete saved items</string>
|
||||
<string name="settings_download_storage_title">Download storage</string>
|
||||
<string name="settings_equalizer_summary">Adjust audio settings</string>
|
||||
<string name="settings_equalizer_title">Equalizer</string>
|
||||
@@ -213,37 +255,45 @@
|
||||
<string name="settings_language">Language</string>
|
||||
<string name="settings_logout_title">Log out</string>
|
||||
<string name="settings_max_bitrate_download">Bitrate for downloads</string>
|
||||
<string name="settings_max_bitrate_wifi">Bitrate in Wi-Fi</string>
|
||||
<string name="settings_max_bitrate_mobile">Bitrate in mobile</string>
|
||||
<string name="settings_max_bitrate_wifi">Bitrate in Wi-Fi</string>
|
||||
<string name="settings_media_cache">Size of media file cache</string>
|
||||
<string name="settings_music_directory">Show music directories</string>
|
||||
<string name="settings_music_directory_summary">If enabled, show the music directory section. Please note that for folder navigation to work properly, the server must support this feature.</string>
|
||||
<string name="settings_queue_syncing_title">Sync play queue for this user</string>
|
||||
<string name="settings_podcast">Show podcast</string>
|
||||
<string name="settings_podcast_summary">If enabled, show the podcast section. Restart the app for it to take full effect.</string>
|
||||
<string name="settings_queue_syncing_countdown">Sync timer</string>
|
||||
<string name="settings_queue_syncing_summary">If enabled, the user will have the ability to save their play queue and will have the ability to load state when opening the application.</string>
|
||||
<string name="settings_podcast">Show podcast</string>
|
||||
<string name="settings_podcast_summary">If enabled, show the podcast section.</string>
|
||||
<string name="settings_queue_syncing_title">Sync play queue for this user</string>
|
||||
<string name="settings_radio">Show radio</string>
|
||||
<string name="settings_radio_summary">If enabled, show the radio section.</string>
|
||||
<string name="settings_radio_summary">If enabled, show the radio section. Restart the app for it to take full effect.</string>
|
||||
<string name="settings_replay_gain">Set replay gain mode</string>
|
||||
<string name="settings_rounded_corner">Rounded corners</string>
|
||||
<string name="settings_rounded_corner_summary">If enabled, sets a curvature angle for all rendered covers. The changes will take effect on restart.</string>
|
||||
<string name="settings_rounded_corner_size">Corners size</string>
|
||||
<string name="settings_rounded_corner_size_summary">Sets the magnitude of the curvature angle.</string>
|
||||
<string name="settings_rounded_corner_summary">If enabled, sets a curvature angle for all rendered covers. The changes will take effect on restart.</string>
|
||||
<string name="settings_scan_title">Scan library</string>
|
||||
<string name="settings_scrobble_title">Enable music scrobbling</string>
|
||||
<string name="settings_share_title">Enable music sharing</string>
|
||||
<string name="settings_sub_summary_scrobble">It\'s important to note that scrobbling also relies on the server being enabled to receive this data.</string>
|
||||
<string name="settings_summary_skip_min_star_rating">When listening to an artist\'s radio, an instant mix or when shuffling all, tracks below a certain user rating will be ignored.</string>
|
||||
<string name="settings_summary_replay_gain">Replay gain is a feature that allows you to adjust the volume level of audio tracks for a consistent listening experience. This setting is only effective if the track contains the necessary metadata.</string>
|
||||
<string name="settings_summary_scrobble">Scrobbling is a feature that allows your device to send information about the songs you listen to the music server. This information helps create personalized recommendations based on your music preferences.</string>
|
||||
<string name="settings_summary_share">Allows the user to share music via a link. The functionality must be supported and enabled server-side and is limited to individual tracks, albums and playlists.</string>
|
||||
<string name="settings_summary_syncing">Returns the state of the play queue for this user. This includes the tracks in the play queue, the currently playing track, and the position within this track. The server must support this feature.</string>
|
||||
<string name="settings_summary_transcoding">Priority given to the transcoding mode. If set to \"Direct play\" the bitrate of the file will not be changed.</string>
|
||||
<string name="settings_summary_transcoding_download">Download transcoded media. If enabled, the download endpoint will not be used, but the following settings. \n\n If \"Transcode format for donwloads\" is set to \"Direct download\" the bitrate of the file will not be changed.</string>
|
||||
<string name="settings_summary_transcoding_estimate_content_length">When the file is transcoded on the fly, the client usually does not show the track length. It is possible to request the servers that support the functionality to estimate the duration of the track being played, but the response times may take longer.</string>
|
||||
<string name="settings_summary_transcoding_download">Download transcoded media. If enabled, the download endpoint will not be used, but the following settings. \n\n If \"Transcode format\" is set to \"Direct play\" the bitrate of the file will not be changed.</string>
|
||||
<string name="settings_sync_starred_tracks_for_offline_use_summary">If enabled, starred tracks will be downloaded for offline use.</string>
|
||||
<string name="settings_sync_starred_tracks_for_offline_use_title">Sync starred tracks for offline use</string>
|
||||
<string name="settings_theme">Theme</string>
|
||||
<string name="settings_title_data">Data</string>
|
||||
<string name="settings_title_general">General</string>
|
||||
<string name="settings_title_rating">Rating</string>
|
||||
<string name="settings_title_replay_gain">Replay Gain</string>
|
||||
<string name="settings_title_scrobble">Scrobble</string>
|
||||
<string name="settings_title_skip_min_star_rating">Ignore tracks based on rating</string>
|
||||
<string name="settings_title_skip_min_star_rating_dialog">Songs with a rating of:</string>
|
||||
<string name="settings_title_share">Share</string>
|
||||
<string name="settings_title_syncing">Syncing</string>
|
||||
<string name="settings_title_transcoding">Transcoding</string>
|
||||
@@ -252,16 +302,17 @@
|
||||
<string name="settings_transcoded_download">Transcoded download</string>
|
||||
<string name="settings_version_summary" translatable="false">3.1.0</string>
|
||||
<string name="settings_version_title">Version</string>
|
||||
<string name="settings_wifi_only_title">Stream via Wi-Fi only alert</string>
|
||||
<string name="settings_wifi_only_summary">Ask for user confirmation before streaming over mobile network.</string>
|
||||
<string name="settings_wifi_only_title">Stream via Wi-Fi only alert</string>
|
||||
<string name="share_bottom_sheet_copy_link">Copy link</string>
|
||||
<string name="share_bottom_sheet_delete">Delete share</string>
|
||||
<string name="share_bottom_sheet_update">Update share</string>
|
||||
<string name="share_subtitle_item">Expiration date: %1$s</string>
|
||||
<string name="share_update_dialog_negative_button">Cancel</string>
|
||||
<string name="share_update_dialog_positive_button">Save</string>
|
||||
<string name="share_unsupported_error">Sharing is not supported or not enabled</string>
|
||||
<string name="share_update_dialog_hint_description">Description</string>
|
||||
<string name="share_update_dialog_hint_expiration_date">Expiration date</string>
|
||||
<string name="share_update_dialog_negative_button">Cancel</string>
|
||||
<string name="share_update_dialog_positive_button">Save</string>
|
||||
<string name="share_update_dialog_title">Share</string>
|
||||
<string name="song_bottom_sheet_add_to_playlist">Add to playlist</string>
|
||||
<string name="song_bottom_sheet_add_to_queue">Add to queue</string>
|
||||
@@ -270,11 +321,11 @@
|
||||
<string name="song_bottom_sheet_error_retrieving_artist">Error retrieving artist</string>
|
||||
<string name="song_bottom_sheet_go_to_album">Go to album</string>
|
||||
<string name="song_bottom_sheet_go_to_artist">Go to artist</string>
|
||||
<string name="song_bottom_sheet_share">Share</string>
|
||||
<string name="song_bottom_sheet_instant_mix">Instant mix</string>
|
||||
<string name="song_bottom_sheet_play_next">Play next</string>
|
||||
<string name="song_bottom_sheet_rate">Rate</string>
|
||||
<string name="song_bottom_sheet_remove">Remove</string>
|
||||
<string name="song_bottom_sheet_share">Share</string>
|
||||
<string name="song_list_page_downloaded">Downloaded</string>
|
||||
<string name="song_list_page_most_played">Most played tracks</string>
|
||||
<string name="song_list_page_recently_added">Recently added tracks</string>
|
||||
@@ -288,62 +339,30 @@
|
||||
<string name="starred_sync_dialog_positive_button">Continue and download</string>
|
||||
<string name="starred_sync_dialog_summary">Downloading starry tracks may require a large amount of data.</string>
|
||||
<string name="starred_sync_dialog_title">Sync starred tracks</string>
|
||||
<string name="undraw_url">https://undraw.co/</string>
|
||||
<string name="undraw_page">unDraw</string>
|
||||
<string name="undraw_thanks">A special thanks goes to unDraw without whose illustrations we could not have made this application more beautiful.</string>
|
||||
<string name="home_title_radio_station">Radio stations</string>
|
||||
<string name="home_title_last_week">Last week</string>
|
||||
<string name="home_title_top_songs">Your top songs</string>
|
||||
<string name="home_title_new_releases">New releases</string>
|
||||
<string name="home_title_best_of">Best of</string>
|
||||
<string name="home_subtitle_best_of">Top songs of your favorite artists</string>
|
||||
<string name="home_title_newest_podcasts">Newest podcasts</string>
|
||||
<string name="home_title_podcast_channels">Channels</string>
|
||||
<string name="artist_adapter_radio_station_starting">Searching…</string>
|
||||
<string name="podcast_bottom_sheet_go_to_channel">Go to channel</string>
|
||||
<string name="podcast_bottom_sheet_delete">Delete</string>
|
||||
<string name="podcast_bottom_sheet_remove">Remove</string>
|
||||
<string name="podcast_bottom_sheet_download">Download</string>
|
||||
<string name="podcast_bottom_sheet_add_to_queue">Add to queue</string>
|
||||
<string name="podcast_bottom_sheet_play_next">Play next</string>
|
||||
<string name="menu_sort_year">Year</string>
|
||||
<string name="menu_sort_artist">Artist</string>
|
||||
<string name="menu_sort_name">Name</string>
|
||||
<string name="menu_sort_random">Random</string>
|
||||
<string name="description_empty_title">No description available</string>
|
||||
<string name="menu_filter_download">Downloaded</string>
|
||||
<string name="menu_filter_all">All</string>
|
||||
<string name="menu_group_by_track">Track</string>
|
||||
<string name="menu_group_by_album">Album</string>
|
||||
<string name="menu_group_by_artist">Artist</string>
|
||||
<string name="menu_group_by_genre">Genre</string>
|
||||
<string name="menu_group_by_year">Year</string>
|
||||
<string name="downloaded_bottom_sheet_shuffle">Shuffle</string>
|
||||
<string name="downloaded_bottom_sheet_play_next">Play next</string>
|
||||
<string name="downloaded_bottom_sheet_add_to_queue">Add to queue</string>
|
||||
<string name="downloaded_bottom_sheet_remove">Remove</string>
|
||||
<string name="downloaded_bottom_sheet_remove_all">Remove all</string>
|
||||
<string name="track_info_dialog_positive_button">OK</string>
|
||||
<string name="track_info_dialog_title">Track info</string>
|
||||
<string name="track_info_title">Title</string>
|
||||
<string name="track_info_album">Album</string>
|
||||
<string name="track_info_artist">Artist</string>
|
||||
<string name="track_info_track_number">Track number</string>
|
||||
<string name="track_info_year">Year</string>
|
||||
<string name="track_info_genre">Genre</string>
|
||||
<string name="track_info_size">Size</string>
|
||||
<string name="track_info_bitrate">Bitrate</string>
|
||||
<string name="track_info_content_type">Content Type</string>
|
||||
<string name="track_info_dialog_positive_button">OK</string>
|
||||
<string name="track_info_dialog_title">Track info</string>
|
||||
<string name="track_info_disc_number">Disc number</string>
|
||||
<string name="track_info_duration">Duration</string>
|
||||
<string name="track_info_genre">Genre</string>
|
||||
<string name="track_info_path">Path</string>
|
||||
<string name="track_info_size">Size</string>
|
||||
<string name="track_info_suffix">Suffix</string>
|
||||
<string name="track_info_summary_downloaded_file">The file has been downloaded using the Subsonic APIs. The codec and bitrate of the file remain unchanged from the source file.</string>
|
||||
<string name="track_info_summary_full_transcode">The application will request the server to transcode the file and modify its bitrate. The user requested codec is %1$s, with a bitrate of %2$s. Any potential changes to the codec and bitrate of the file in the chosen format will be handled by the server, which may or may not support the operation.</string>
|
||||
<string name="track_info_summary_original_file">The application will only read the original file as provided by the server. The app will explicitly request the server for the untranscoded file with the bitrate of the original source.</string>
|
||||
<string name="track_info_summary_server_prioritized">The quality of the file to be played is left up to the server\'s decision. The app will not enforce the choice of codec and bitrate for any potential transcoding.</string>
|
||||
<string name="track_info_summary_transcoding_bitrate">The application will request the server to modify the bitrate of the file. The user requested a bitrate of %1$s, while the codec of the source file will remain the same. Any changes to the bitrate of the file in the chosen format will be done by the server, which may or may not support the operation.</string>
|
||||
<string name="track_info_summary_transcoding_codec">The application will request the server to transcode the file. The requested codec by the user is %1$s, while the bitrate will be the same as the source file. The potential transcoding of the file into the chosen format is dependent on the server, as it may or may not support the operation.</string>
|
||||
<string name="track_info_title">Title</string>
|
||||
<string name="track_info_track_number">Track number</string>
|
||||
<string name="track_info_transcoded_content_type">Transcoded content type</string>
|
||||
<string name="track_info_transcoded_suffix">Transcoded suffix</string>
|
||||
<string name="track_info_duration">Duration</string>
|
||||
<string name="track_info_bitrate">Bitrate</string>
|
||||
<string name="track_info_path">Path</string>
|
||||
<string name="track_info_disc_number">Disc number</string>
|
||||
<string name="track_info_summary_downloaded_file">The file has been downloaded using the Subsonic APIs. The codec and bitrate of the file remain unchanged from the source file.</string>
|
||||
<string name="track_info_summary_server_prioritized">The quality of the file to be played is left up to the server\'s decision. The app will not enforce the choice of codec and bitrate for any potential transcoding.</string>
|
||||
<string name="track_info_summary_original_file">The application will only read the original file as provided by the server. The app will explicitly request the server for the untranscoded file with the bitrate of the original source.</string>
|
||||
<string name="track_info_summary_transcoding_codec">The application will request the server to transcode the file. The requested codec by the user is %1$s, while the bitrate will be the same as the source file. The potential transcoding of the file into the chosen format is dependent on the server, as it may or may not support the operation.</string>
|
||||
<string name="track_info_summary_transcoding_bitrate">The application will request the server to modify the bitrate of the file. The user requested a bitrate of %1$s, while the codec of the source file will remain the same. Any changes to the bitrate of the file in the chosen format will be done by the server, which may or may not support the operation.</string>
|
||||
<string name="track_info_summary_full_transcode">The application will request the server to transcode the file and modify its bitrate. The user requested codec is %1$s, with a bitrate of %2$s. Any potential changes to the codec and bitrate of the file in the chosen format will be handled by the server, which may or may not support the operation.</string>
|
||||
</resources>
|
||||
<string name="track_info_year">Year</string>
|
||||
<string name="undraw_page">unDraw</string>
|
||||
<string name="undraw_thanks">A special thanks goes to unDraw without whose illustrations we could not have made this application more beautiful.</string>
|
||||
<string name="undraw_url">https://undraw.co/</string>
|
||||
</resources>
|
||||
|
||||
4
app/src/main/res/xml/auto_app_desc.xml
Normal file
4
app/src/main/res/xml/auto_app_desc.xml
Normal file
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<automotiveApp>
|
||||
<uses name="media" />
|
||||
</automotiveApp>
|
||||
@@ -31,6 +31,11 @@
|
||||
app:title="@string/settings_theme"
|
||||
app:useSimpleSummaryProvider="true" />
|
||||
|
||||
<SwitchPreference
|
||||
android:title="@string/settings_always_on_display"
|
||||
android:defaultValue="false"
|
||||
android:key="always_on_display" />
|
||||
|
||||
<SwitchPreference
|
||||
android:title="@string/settings_rounded_corner"
|
||||
android:defaultValue="true"
|
||||
@@ -102,6 +107,16 @@
|
||||
android:summary="@string/settings_sync_starred_tracks_for_offline_use_summary"
|
||||
android:key="sync_starred_tracks_for_offline_use" />
|
||||
|
||||
<ListPreference
|
||||
app:defaultValue="1"
|
||||
app:dialogTitle="@string/settings_buffering_strategy"
|
||||
app:entries="@array/buffering_strategy_titles"
|
||||
app:entryValues="@array/buffering_strategy_values"
|
||||
app:key="buffering_strategy"
|
||||
app:title="@string/settings_buffering_strategy"
|
||||
app:summary="@string/settings_buffering_strategy_summary"
|
||||
app:useSimpleSummaryProvider="false" />
|
||||
|
||||
<Preference
|
||||
android:key="download_storage"
|
||||
app:title="@string/settings_download_storage_title" />
|
||||
@@ -222,6 +237,35 @@
|
||||
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory app:title="@string/settings_title_rating">
|
||||
<SeekBarPreference
|
||||
android:key="min_star_rating"
|
||||
app:defaultValue="0"
|
||||
app:min="0"
|
||||
android:max="4"
|
||||
app:seekBarIncrement="1"
|
||||
app:showSeekBarValue="true"
|
||||
app:summary="@string/settings_summary_skip_min_star_rating"
|
||||
app:title="@string/settings_title_skip_min_star_rating" />
|
||||
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory app:title="@string/settings_title_scrobble">
|
||||
<Preference
|
||||
app:selectable="false"
|
||||
app:summary="@string/settings_summary_scrobble" />
|
||||
|
||||
<Preference
|
||||
app:selectable="false"
|
||||
app:summary="@string/settings_sub_summary_scrobble" />
|
||||
|
||||
<SwitchPreference
|
||||
android:title="@string/settings_scrobble_title"
|
||||
android:defaultValue="true"
|
||||
android:key="scrobbling" />
|
||||
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory app:title="@string/settings_title_share">
|
||||
<Preference
|
||||
app:selectable="false"
|
||||
|
||||
@@ -2,4 +2,7 @@
|
||||
<locale-config xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<locale android:name="en"/> <!-- English -->
|
||||
<locale android:name="de-DE"/> <!-- German -->
|
||||
</locale-config>
|
||||
<locale android:name="fr-FR"/> <!-- French -->
|
||||
<locale android:name="zh-CN"/> <!-- Simplified Chinese-->
|
||||
<locale android:name="ko-KR"/> <!-- Korean-->
|
||||
</locale-config>
|
||||
|
||||
9
app/src/main/res/xml/network_security_config.xml
Normal file
9
app/src/main/res/xml/network_security_config.xml
Normal file
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<network-security-config>
|
||||
<base-config cleartextTrafficPermitted="true">
|
||||
<trust-anchors>
|
||||
<certificates src="system" />
|
||||
<certificates src="user" />
|
||||
</trust-anchors>
|
||||
</base-config>
|
||||
</network-security-config>
|
||||
@@ -183,6 +183,8 @@ class MediaService : MediaLibraryService() {
|
||||
player.currentMediaItem,
|
||||
player.currentPosition
|
||||
)
|
||||
} else {
|
||||
MediaManager.scrobble(player.currentMediaItem, false)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -192,7 +194,7 @@ class MediaService : MediaLibraryService() {
|
||||
playbackState == Player.STATE_ENDED &&
|
||||
player.mediaMetadata.extras?.getString("type") == Constants.MEDIA_TYPE_MUSIC
|
||||
) {
|
||||
MediaManager.scrobble(player.currentMediaItem)
|
||||
MediaManager.scrobble(player.currentMediaItem, true)
|
||||
MediaManager.saveChronology(player.currentMediaItem)
|
||||
}
|
||||
}
|
||||
@@ -206,7 +208,7 @@ class MediaService : MediaLibraryService() {
|
||||
|
||||
if (reason == Player.DISCONTINUITY_REASON_AUTO_TRANSITION) {
|
||||
if (oldPosition.mediaItem?.mediaMetadata?.extras?.getString("type") == Constants.MEDIA_TYPE_MUSIC) {
|
||||
MediaManager.scrobble(oldPosition.mediaItem)
|
||||
MediaManager.scrobble(oldPosition.mediaItem, true)
|
||||
MediaManager.saveChronology(oldPosition.mediaItem)
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,467 @@
|
||||
package com.cappielloantonio.tempo.service
|
||||
|
||||
import android.net.Uri
|
||||
import androidx.media3.common.MediaItem
|
||||
import androidx.media3.common.MediaItem.SubtitleConfiguration
|
||||
import androidx.media3.common.MediaMetadata
|
||||
import androidx.media3.session.LibraryResult
|
||||
import com.cappielloantonio.tempo.repository.AutomotiveRepository
|
||||
import com.google.common.collect.ImmutableList
|
||||
import com.google.common.util.concurrent.Futures
|
||||
import com.google.common.util.concurrent.ListenableFuture
|
||||
import com.google.common.util.concurrent.SettableFuture
|
||||
|
||||
object MediaBrowserTree {
|
||||
|
||||
private lateinit var automotiveRepository: AutomotiveRepository
|
||||
|
||||
private var treeNodes: MutableMap<String, MediaItemNode> = mutableMapOf()
|
||||
|
||||
private var isInitialized = false
|
||||
|
||||
// Root
|
||||
private const val ROOT_ID = "[rootID]"
|
||||
|
||||
// First level
|
||||
private const val HOME_ID = "[homeID]"
|
||||
private const val LIBRARY_ID = "[libraryID]"
|
||||
private const val OTHER_ID = "[otherID]"
|
||||
|
||||
// Second level HOME_ID
|
||||
private const val MOST_PLAYED_ID = "[mostPlayedID]"
|
||||
private const val LAST_PLAYED_ID = "[lastPlayedID]"
|
||||
private const val RECENTLY_ADDED_ID = "[recentlyAddedID]"
|
||||
private const val MADE_FOR_YOU_ID = "[madeForYouID]"
|
||||
private const val STARRED_TRACKS_ID = "[starredTracksID]"
|
||||
private const val STARRED_ALBUMS_ID = "[starredAlbumsID]"
|
||||
private const val STARRED_ARTISTS_ID = "[starredArtistsID]"
|
||||
|
||||
// Second level LIBRARY_ID
|
||||
private const val FOLDER_ID = "[folderID]"
|
||||
private const val INDEX_ID = "[indexID]"
|
||||
private const val DIRECTORY_ID = "[directoryID]"
|
||||
private const val PLAYLIST_ID = "[playlistID]"
|
||||
|
||||
// Second level OTHER_ID
|
||||
private const val PODCAST_ID = "[podcastID]"
|
||||
private const val RADIO_ID = "[radioID]"
|
||||
|
||||
private const val ALBUM_ID = "[albumID]"
|
||||
private const val ARTIST_ID = "[artistID]"
|
||||
|
||||
private class MediaItemNode(val item: MediaItem) {
|
||||
private val children: MutableList<MediaItem> = ArrayList()
|
||||
|
||||
fun addChild(childID: String) {
|
||||
this.children.add(treeNodes[childID]!!.item)
|
||||
}
|
||||
|
||||
fun getChildren(): ListenableFuture<LibraryResult<ImmutableList<MediaItem>>> {
|
||||
val listenableFuture = SettableFuture.create<LibraryResult<ImmutableList<MediaItem>>>()
|
||||
val libraryResult = LibraryResult.ofItemList(children, null)
|
||||
|
||||
listenableFuture.set(libraryResult)
|
||||
|
||||
return listenableFuture
|
||||
}
|
||||
}
|
||||
|
||||
private fun buildMediaItem(
|
||||
title: String,
|
||||
mediaId: String,
|
||||
isPlayable: Boolean,
|
||||
isBrowsable: Boolean,
|
||||
mediaType: @MediaMetadata.MediaType Int,
|
||||
subtitleConfigurations: List<SubtitleConfiguration> = mutableListOf(),
|
||||
album: String? = null,
|
||||
artist: String? = null,
|
||||
genre: String? = null,
|
||||
sourceUri: Uri? = null,
|
||||
imageUri: Uri? = null
|
||||
): MediaItem {
|
||||
val metadata =
|
||||
MediaMetadata.Builder()
|
||||
.setAlbumTitle(album)
|
||||
.setTitle(title)
|
||||
.setArtist(artist)
|
||||
.setGenre(genre)
|
||||
.setIsBrowsable(isBrowsable)
|
||||
.setIsPlayable(isPlayable)
|
||||
.setArtworkUri(imageUri)
|
||||
.setMediaType(mediaType)
|
||||
.build()
|
||||
|
||||
return MediaItem.Builder()
|
||||
.setMediaId(mediaId)
|
||||
.setSubtitleConfigurations(subtitleConfigurations)
|
||||
.setMediaMetadata(metadata)
|
||||
.setUri(sourceUri)
|
||||
.build()
|
||||
}
|
||||
|
||||
fun initialize(automotiveRepository: AutomotiveRepository) {
|
||||
this.automotiveRepository = automotiveRepository
|
||||
|
||||
if (isInitialized) return
|
||||
|
||||
isInitialized = true
|
||||
|
||||
// Root level
|
||||
|
||||
treeNodes[ROOT_ID] =
|
||||
MediaItemNode(
|
||||
buildMediaItem(
|
||||
title = "Root Folder",
|
||||
mediaId = ROOT_ID,
|
||||
isPlayable = false,
|
||||
isBrowsable = true,
|
||||
mediaType = MediaMetadata.MEDIA_TYPE_FOLDER_MIXED
|
||||
)
|
||||
)
|
||||
|
||||
// First level
|
||||
|
||||
treeNodes[HOME_ID] =
|
||||
MediaItemNode(
|
||||
buildMediaItem(
|
||||
title = "Home",
|
||||
mediaId = HOME_ID,
|
||||
isPlayable = false,
|
||||
isBrowsable = true,
|
||||
mediaType = MediaMetadata.MEDIA_TYPE_FOLDER_MIXED
|
||||
)
|
||||
)
|
||||
|
||||
treeNodes[LIBRARY_ID] =
|
||||
MediaItemNode(
|
||||
buildMediaItem(
|
||||
title = "Library",
|
||||
mediaId = LIBRARY_ID,
|
||||
isPlayable = false,
|
||||
isBrowsable = true,
|
||||
mediaType = MediaMetadata.MEDIA_TYPE_FOLDER_MIXED
|
||||
)
|
||||
)
|
||||
|
||||
treeNodes[OTHER_ID] =
|
||||
MediaItemNode(
|
||||
buildMediaItem(
|
||||
title = "Other",
|
||||
mediaId = OTHER_ID,
|
||||
isPlayable = false,
|
||||
isBrowsable = true,
|
||||
mediaType = MediaMetadata.MEDIA_TYPE_FOLDER_MIXED
|
||||
)
|
||||
)
|
||||
|
||||
treeNodes[ROOT_ID]!!.addChild(HOME_ID)
|
||||
treeNodes[ROOT_ID]!!.addChild(LIBRARY_ID)
|
||||
treeNodes[ROOT_ID]!!.addChild(OTHER_ID)
|
||||
|
||||
// Second level HOME_ID
|
||||
|
||||
treeNodes[MOST_PLAYED_ID] =
|
||||
MediaItemNode(
|
||||
buildMediaItem(
|
||||
title = "Most played",
|
||||
mediaId = MOST_PLAYED_ID,
|
||||
isPlayable = false,
|
||||
isBrowsable = true,
|
||||
mediaType = MediaMetadata.MEDIA_TYPE_FOLDER_ALBUMS
|
||||
)
|
||||
)
|
||||
|
||||
treeNodes[LAST_PLAYED_ID] =
|
||||
MediaItemNode(
|
||||
buildMediaItem(
|
||||
title = "Last played",
|
||||
mediaId = LAST_PLAYED_ID,
|
||||
isPlayable = false,
|
||||
isBrowsable = true,
|
||||
mediaType = MediaMetadata.MEDIA_TYPE_FOLDER_ALBUMS
|
||||
)
|
||||
)
|
||||
|
||||
treeNodes[RECENTLY_ADDED_ID] =
|
||||
MediaItemNode(
|
||||
buildMediaItem(
|
||||
title = "Recently added",
|
||||
mediaId = RECENTLY_ADDED_ID,
|
||||
isPlayable = false,
|
||||
isBrowsable = true,
|
||||
mediaType = MediaMetadata.MEDIA_TYPE_FOLDER_ALBUMS
|
||||
)
|
||||
)
|
||||
|
||||
treeNodes[MADE_FOR_YOU_ID] =
|
||||
MediaItemNode(
|
||||
buildMediaItem(
|
||||
title = "Made for you",
|
||||
mediaId = MADE_FOR_YOU_ID,
|
||||
isPlayable = false,
|
||||
isBrowsable = true,
|
||||
mediaType = MediaMetadata.MEDIA_TYPE_FOLDER_PLAYLISTS
|
||||
)
|
||||
)
|
||||
|
||||
treeNodes[STARRED_TRACKS_ID] =
|
||||
MediaItemNode(
|
||||
buildMediaItem(
|
||||
title = "Starred tracks",
|
||||
mediaId = STARRED_TRACKS_ID,
|
||||
isPlayable = false,
|
||||
isBrowsable = true,
|
||||
mediaType = MediaMetadata.MEDIA_TYPE_FOLDER_MIXED
|
||||
)
|
||||
)
|
||||
|
||||
treeNodes[STARRED_ALBUMS_ID] =
|
||||
MediaItemNode(
|
||||
buildMediaItem(
|
||||
title = "Starred albums",
|
||||
mediaId = STARRED_ALBUMS_ID,
|
||||
isPlayable = false,
|
||||
isBrowsable = true,
|
||||
mediaType = MediaMetadata.MEDIA_TYPE_FOLDER_ALBUMS
|
||||
)
|
||||
)
|
||||
|
||||
treeNodes[STARRED_ARTISTS_ID] =
|
||||
MediaItemNode(
|
||||
buildMediaItem(
|
||||
title = "Starred artists",
|
||||
mediaId = STARRED_ARTISTS_ID,
|
||||
isPlayable = false,
|
||||
isBrowsable = true,
|
||||
mediaType = MediaMetadata.MEDIA_TYPE_FOLDER_ARTISTS
|
||||
)
|
||||
)
|
||||
|
||||
treeNodes[HOME_ID]!!.addChild(MOST_PLAYED_ID)
|
||||
treeNodes[HOME_ID]!!.addChild(LAST_PLAYED_ID)
|
||||
treeNodes[HOME_ID]!!.addChild(RECENTLY_ADDED_ID)
|
||||
treeNodes[HOME_ID]!!.addChild(MADE_FOR_YOU_ID)
|
||||
treeNodes[HOME_ID]!!.addChild(STARRED_TRACKS_ID)
|
||||
treeNodes[HOME_ID]!!.addChild(STARRED_ALBUMS_ID)
|
||||
treeNodes[HOME_ID]!!.addChild(STARRED_ARTISTS_ID)
|
||||
|
||||
// Second level LIBRARY_ID
|
||||
|
||||
treeNodes[FOLDER_ID] =
|
||||
MediaItemNode(
|
||||
buildMediaItem(
|
||||
title = "Folders",
|
||||
mediaId = FOLDER_ID,
|
||||
isPlayable = false,
|
||||
isBrowsable = true,
|
||||
mediaType = MediaMetadata.MEDIA_TYPE_FOLDER_MIXED
|
||||
)
|
||||
)
|
||||
|
||||
treeNodes[PLAYLIST_ID] =
|
||||
MediaItemNode(
|
||||
buildMediaItem(
|
||||
title = "Playlists",
|
||||
mediaId = PLAYLIST_ID,
|
||||
isPlayable = false,
|
||||
isBrowsable = true,
|
||||
mediaType = MediaMetadata.MEDIA_TYPE_FOLDER_PLAYLISTS
|
||||
)
|
||||
)
|
||||
|
||||
treeNodes[LIBRARY_ID]!!.addChild(FOLDER_ID)
|
||||
treeNodes[LIBRARY_ID]!!.addChild(PLAYLIST_ID)
|
||||
|
||||
// Second level OTHER_ID
|
||||
|
||||
treeNodes[PODCAST_ID] =
|
||||
MediaItemNode(
|
||||
buildMediaItem(
|
||||
title = "Podcasts",
|
||||
mediaId = PODCAST_ID,
|
||||
isPlayable = false,
|
||||
isBrowsable = true,
|
||||
mediaType = MediaMetadata.MEDIA_TYPE_FOLDER_PODCASTS
|
||||
)
|
||||
)
|
||||
|
||||
treeNodes[RADIO_ID] =
|
||||
MediaItemNode(
|
||||
buildMediaItem(
|
||||
title = "Radio stations",
|
||||
mediaId = RADIO_ID,
|
||||
isPlayable = false,
|
||||
isBrowsable = true,
|
||||
mediaType = MediaMetadata.MEDIA_TYPE_FOLDER_RADIO_STATIONS
|
||||
)
|
||||
)
|
||||
|
||||
treeNodes[OTHER_ID]!!.addChild(PODCAST_ID)
|
||||
treeNodes[OTHER_ID]!!.addChild(RADIO_ID)
|
||||
}
|
||||
|
||||
fun getRootItem(): MediaItem {
|
||||
return treeNodes[ROOT_ID]!!.item
|
||||
}
|
||||
|
||||
fun getChildren(
|
||||
id: String
|
||||
): ListenableFuture<LibraryResult<ImmutableList<MediaItem>>> {
|
||||
return when (id) {
|
||||
ROOT_ID -> treeNodes[ROOT_ID]?.getChildren()!!
|
||||
HOME_ID -> treeNodes[HOME_ID]?.getChildren()!!
|
||||
LIBRARY_ID -> treeNodes[LIBRARY_ID]?.getChildren()!!
|
||||
OTHER_ID -> treeNodes[OTHER_ID]?.getChildren()!!
|
||||
|
||||
MOST_PLAYED_ID -> automotiveRepository.getAlbums(id, "frequent", 100)
|
||||
LAST_PLAYED_ID -> automotiveRepository.getAlbums(id, "recent", 100)
|
||||
RECENTLY_ADDED_ID -> automotiveRepository.getAlbums(id, "newest", 100)
|
||||
MADE_FOR_YOU_ID -> automotiveRepository.getStarredArtists(id)
|
||||
STARRED_TRACKS_ID -> automotiveRepository.starredSongs
|
||||
STARRED_ALBUMS_ID -> automotiveRepository.getStarredAlbums(id)
|
||||
STARRED_ARTISTS_ID -> automotiveRepository.getStarredArtists(id)
|
||||
FOLDER_ID -> automotiveRepository.getMusicFolders(id)
|
||||
PLAYLIST_ID -> automotiveRepository.getPlaylists(id)
|
||||
PODCAST_ID -> automotiveRepository.getNewestPodcastEpisodes(100)
|
||||
RADIO_ID -> automotiveRepository.internetRadioStations
|
||||
|
||||
else -> {
|
||||
if (id.startsWith(MOST_PLAYED_ID)) {
|
||||
return automotiveRepository.getAlbumTracks(
|
||||
id.removePrefix(
|
||||
MOST_PLAYED_ID
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
if (id.startsWith(LAST_PLAYED_ID)) {
|
||||
return automotiveRepository.getAlbumTracks(
|
||||
id.removePrefix(
|
||||
LAST_PLAYED_ID
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
if (id.startsWith(RECENTLY_ADDED_ID)) {
|
||||
return automotiveRepository.getAlbumTracks(
|
||||
id.removePrefix(
|
||||
RECENTLY_ADDED_ID
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
if (id.startsWith(MADE_FOR_YOU_ID)) {
|
||||
return automotiveRepository.getMadeForYou(
|
||||
id.removePrefix(
|
||||
MADE_FOR_YOU_ID
|
||||
),
|
||||
20
|
||||
)
|
||||
}
|
||||
|
||||
if (id.startsWith(STARRED_ALBUMS_ID)) {
|
||||
return automotiveRepository.getAlbumTracks(
|
||||
id.removePrefix(
|
||||
STARRED_ALBUMS_ID
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
if (id.startsWith(STARRED_ARTISTS_ID)) {
|
||||
return automotiveRepository.getArtistAlbum(
|
||||
STARRED_ALBUMS_ID,
|
||||
id.removePrefix(
|
||||
STARRED_ARTISTS_ID
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
if (id.startsWith(FOLDER_ID)) {
|
||||
return automotiveRepository.getIndexes(
|
||||
INDEX_ID,
|
||||
id.removePrefix(
|
||||
FOLDER_ID
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
if (id.startsWith(INDEX_ID)) {
|
||||
return automotiveRepository.getDirectories(
|
||||
DIRECTORY_ID,
|
||||
id.removePrefix(
|
||||
INDEX_ID
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
if (id.startsWith(DIRECTORY_ID)) {
|
||||
return automotiveRepository.getDirectories(
|
||||
DIRECTORY_ID,
|
||||
id.removePrefix(
|
||||
DIRECTORY_ID
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
if (id.startsWith(PLAYLIST_ID)) {
|
||||
return automotiveRepository.getPlaylistSongs(
|
||||
id.removePrefix(
|
||||
PLAYLIST_ID
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
if (id.startsWith(ALBUM_ID)) {
|
||||
return automotiveRepository.getAlbumTracks(
|
||||
id.removePrefix(
|
||||
ALBUM_ID
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
if (id.startsWith(ARTIST_ID)) {
|
||||
return automotiveRepository.getArtistAlbum(
|
||||
ALBUM_ID,
|
||||
id.removePrefix(
|
||||
ARTIST_ID
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
return Futures.immediateFuture(LibraryResult.ofError(LibraryResult.RESULT_ERROR_BAD_VALUE))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// https://github.com/androidx/media/issues/156
|
||||
fun getItems(mediaItems: List<MediaItem>): List<MediaItem> {
|
||||
val updatedMediaItems = ArrayList<MediaItem>()
|
||||
|
||||
mediaItems.forEach {
|
||||
if (it.localConfiguration?.uri != null) {
|
||||
updatedMediaItems.add(it)
|
||||
} else {
|
||||
val sessionMediaItem = automotiveRepository.getSessionMediaItem(it.mediaId)
|
||||
|
||||
if (sessionMediaItem != null) {
|
||||
var toAdd = automotiveRepository.getMetadatas(sessionMediaItem.timestamp!!)
|
||||
val index = toAdd.indexOfFirst { mediaItem -> mediaItem.mediaId == it.mediaId }
|
||||
|
||||
toAdd = toAdd.subList(index, toAdd.size)
|
||||
|
||||
updatedMediaItems.addAll(toAdd)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return updatedMediaItems
|
||||
}
|
||||
|
||||
fun search(query: String): ListenableFuture<LibraryResult<ImmutableList<MediaItem>>> {
|
||||
return automotiveRepository.search(
|
||||
query,
|
||||
ALBUM_ID,
|
||||
ARTIST_ID
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,162 @@
|
||||
package com.cappielloantonio.tempo.service
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Bundle
|
||||
import androidx.annotation.OptIn
|
||||
import androidx.media3.common.MediaItem
|
||||
import androidx.media3.common.util.UnstableApi
|
||||
import androidx.media3.session.CommandButton
|
||||
import androidx.media3.session.LibraryResult
|
||||
import androidx.media3.session.MediaLibraryService
|
||||
import androidx.media3.session.MediaSession
|
||||
import androidx.media3.session.SessionCommand
|
||||
import androidx.media3.session.SessionResult
|
||||
import com.cappielloantonio.tempo.R
|
||||
import com.cappielloantonio.tempo.repository.AutomotiveRepository
|
||||
import com.google.common.collect.ImmutableList
|
||||
import com.google.common.util.concurrent.Futures
|
||||
import com.google.common.util.concurrent.ListenableFuture
|
||||
|
||||
open class MediaLibrarySessionCallback(
|
||||
context: Context,
|
||||
automotiveRepository: AutomotiveRepository
|
||||
) :
|
||||
MediaLibraryService.MediaLibrarySession.Callback {
|
||||
|
||||
init {
|
||||
MediaBrowserTree.initialize(automotiveRepository)
|
||||
}
|
||||
|
||||
private val customLayoutCommandButtons: List<CommandButton> = listOf(
|
||||
CommandButton.Builder()
|
||||
.setDisplayName(context.getString(R.string.exo_controls_shuffle_on_description))
|
||||
.setSessionCommand(
|
||||
SessionCommand(
|
||||
CUSTOM_COMMAND_TOGGLE_SHUFFLE_MODE_ON, Bundle.EMPTY
|
||||
)
|
||||
).setIconResId(R.drawable.exo_icon_shuffle_off).build(),
|
||||
|
||||
CommandButton.Builder()
|
||||
.setDisplayName(context.getString(R.string.exo_controls_shuffle_off_description))
|
||||
.setSessionCommand(
|
||||
SessionCommand(
|
||||
CUSTOM_COMMAND_TOGGLE_SHUFFLE_MODE_OFF, Bundle.EMPTY
|
||||
)
|
||||
).setIconResId(R.drawable.exo_icon_shuffle_on).build()
|
||||
)
|
||||
|
||||
@OptIn(UnstableApi::class)
|
||||
val mediaNotificationSessionCommands =
|
||||
MediaSession.ConnectionResult.DEFAULT_SESSION_AND_LIBRARY_COMMANDS.buildUpon()
|
||||
.also { builder ->
|
||||
customLayoutCommandButtons.forEach { commandButton ->
|
||||
commandButton.sessionCommand?.let { builder.add(it) }
|
||||
}
|
||||
}.build()
|
||||
|
||||
@OptIn(UnstableApi::class)
|
||||
override fun onConnect(
|
||||
session: MediaSession, controller: MediaSession.ControllerInfo
|
||||
): MediaSession.ConnectionResult {
|
||||
if (session.isMediaNotificationController(controller) || session.isAutomotiveController(
|
||||
controller
|
||||
) || session.isAutoCompanionController(controller)
|
||||
) {
|
||||
val customLayout =
|
||||
customLayoutCommandButtons[if (session.player.shuffleModeEnabled) 1 else 0]
|
||||
|
||||
return MediaSession.ConnectionResult.AcceptedResultBuilder(session)
|
||||
.setAvailableSessionCommands(mediaNotificationSessionCommands)
|
||||
.setCustomLayout(ImmutableList.of(customLayout)).build()
|
||||
}
|
||||
|
||||
return MediaSession.ConnectionResult.AcceptedResultBuilder(session).build()
|
||||
}
|
||||
|
||||
@OptIn(UnstableApi::class)
|
||||
override fun onCustomCommand(
|
||||
session: MediaSession,
|
||||
controller: MediaSession.ControllerInfo,
|
||||
customCommand: SessionCommand,
|
||||
args: Bundle
|
||||
): ListenableFuture<SessionResult> {
|
||||
if (CUSTOM_COMMAND_TOGGLE_SHUFFLE_MODE_ON == customCommand.customAction) {
|
||||
session.player.shuffleModeEnabled = true
|
||||
session.setCustomLayout(
|
||||
session.mediaNotificationControllerInfo!!,
|
||||
ImmutableList.of(customLayoutCommandButtons[1])
|
||||
)
|
||||
|
||||
return Futures.immediateFuture(SessionResult(SessionResult.RESULT_SUCCESS))
|
||||
} else if (CUSTOM_COMMAND_TOGGLE_SHUFFLE_MODE_OFF == customCommand.customAction) {
|
||||
session.player.shuffleModeEnabled = false
|
||||
session.setCustomLayout(
|
||||
session.mediaNotificationControllerInfo!!,
|
||||
ImmutableList.of(customLayoutCommandButtons[0])
|
||||
)
|
||||
|
||||
return Futures.immediateFuture(SessionResult(SessionResult.RESULT_SUCCESS))
|
||||
}
|
||||
|
||||
return Futures.immediateFuture(SessionResult(SessionResult.RESULT_ERROR_NOT_SUPPORTED))
|
||||
}
|
||||
|
||||
override fun onGetLibraryRoot(
|
||||
session: MediaLibraryService.MediaLibrarySession,
|
||||
browser: MediaSession.ControllerInfo,
|
||||
params: MediaLibraryService.LibraryParams?
|
||||
): ListenableFuture<LibraryResult<MediaItem>> {
|
||||
return Futures.immediateFuture(LibraryResult.ofItem(MediaBrowserTree.getRootItem(), params))
|
||||
}
|
||||
|
||||
override fun onGetChildren(
|
||||
session: MediaLibraryService.MediaLibrarySession,
|
||||
browser: MediaSession.ControllerInfo,
|
||||
parentId: String,
|
||||
page: Int,
|
||||
pageSize: Int,
|
||||
params: MediaLibraryService.LibraryParams?
|
||||
): ListenableFuture<LibraryResult<ImmutableList<MediaItem>>> {
|
||||
return MediaBrowserTree.getChildren(parentId)
|
||||
}
|
||||
|
||||
override fun onAddMediaItems(
|
||||
mediaSession: MediaSession,
|
||||
controller: MediaSession.ControllerInfo,
|
||||
mediaItems: List<MediaItem>
|
||||
): ListenableFuture<List<MediaItem>> {
|
||||
return super.onAddMediaItems(
|
||||
mediaSession,
|
||||
controller,
|
||||
MediaBrowserTree.getItems(mediaItems)
|
||||
)
|
||||
}
|
||||
|
||||
override fun onSearch(
|
||||
session: MediaLibraryService.MediaLibrarySession,
|
||||
browser: MediaSession.ControllerInfo,
|
||||
query: String,
|
||||
params: MediaLibraryService.LibraryParams?
|
||||
): ListenableFuture<LibraryResult<Void>> {
|
||||
session.notifySearchResultChanged(browser, query, 60, params)
|
||||
return Futures.immediateFuture(LibraryResult.ofVoid())
|
||||
}
|
||||
|
||||
override fun onGetSearchResult(
|
||||
session: MediaLibraryService.MediaLibrarySession,
|
||||
browser: MediaSession.ControllerInfo,
|
||||
query: String,
|
||||
page: Int,
|
||||
pageSize: Int,
|
||||
params: MediaLibraryService.LibraryParams?
|
||||
): ListenableFuture<LibraryResult<ImmutableList<MediaItem>>> {
|
||||
return MediaBrowserTree.search(query)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val CUSTOM_COMMAND_TOGGLE_SHUFFLE_MODE_ON =
|
||||
"android.media3.session.demo.SHUFFLE_ON"
|
||||
private const val CUSTOM_COMMAND_TOGGLE_SHUFFLE_MODE_OFF =
|
||||
"android.media3.session.demo.SHUFFLE_OFF"
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user