Compare commits
130 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8aaa6b207e | ||
|
|
72d560e4eb | ||
|
|
31219ea754 | ||
|
|
52c411ead0 | ||
|
|
0edbd15d47 | ||
|
|
342241963a | ||
|
|
f5b381eb35 | ||
|
|
c415db0cc5 | ||
|
|
35576c3d6f | ||
|
|
a11fbfa829 | ||
|
|
26d1b144e4 | ||
|
|
16b63bf13c | ||
|
|
52434f3aa9 | ||
|
|
7aa325f914 | ||
|
|
5a8a631449 | ||
|
|
e6bbd7b2bf | ||
|
|
3721484dff | ||
|
|
6698052ba5 | ||
|
|
f6f24acfdf | ||
|
|
ca8bcba0d7 | ||
|
|
9a36f8541f | ||
|
|
0026dc287f | ||
|
|
c65077172d | ||
|
|
6e6c261f35 | ||
|
|
130cbbd7dd | ||
|
|
63668f5a8c | ||
|
|
193b551773 | ||
|
|
76a0e12222 | ||
|
|
887d8c85ee | ||
|
|
6a90f06084 | ||
|
|
f7a21cbb52 | ||
|
|
3c6c240b9d | ||
|
|
2553c06a9f | ||
|
|
62a10d142e | ||
|
|
955dc1b015 | ||
|
|
6124ec66f3 | ||
|
|
2c6287405e | ||
|
|
0be309fb22 | ||
|
|
a79543569d | ||
|
|
e3d7120193 | ||
|
|
80a3a54476 | ||
|
|
6448cc598d | ||
|
|
c4bd30d512 | ||
|
|
b2e3596d87 | ||
|
|
ccfe74a6ea | ||
|
|
f4ffdc985e | ||
|
|
33981f9885 | ||
|
|
1bc93cce0e | ||
|
|
ec0eee9d3f | ||
|
|
140546ca4d | ||
|
|
fd075a02c5 | ||
|
|
4d1d953a3a | ||
|
|
9dd509be22 | ||
|
|
efaae35976 | ||
|
|
35784216bc | ||
|
|
8ce0a82506 | ||
|
|
51883cd82b | ||
|
|
829c5d85f6 | ||
|
|
748a19ef44 | ||
|
|
e987226954 | ||
|
|
59e2f4a7fa | ||
|
|
817b53efaa | ||
|
|
12c7ec86a9 | ||
|
|
611b5001be | ||
|
|
923cfd5bc9 | ||
|
|
36d2320e70 | ||
|
|
17713ee400 | ||
|
|
ee3465868e | ||
|
|
9e8141a8d9 | ||
|
|
043e1b39b0 | ||
|
|
938c1de906 | ||
|
|
a0dfe63660 | ||
|
|
42b7441467 | ||
|
|
28c2f87b26 | ||
|
|
9ab22bfede | ||
|
|
20900fb557 | ||
|
|
7457c5b6e3 | ||
|
|
e5a928ec0f | ||
|
|
147c8360a6 | ||
|
|
d5d504fc64 | ||
|
|
24eead2d0a | ||
|
|
2644fa52b6 | ||
|
|
38c144c073 | ||
|
|
1dca1ef68d | ||
|
|
ba94d7e5cc | ||
|
|
0028872e3f | ||
|
|
be9eec625a | ||
|
|
b335ddec01 | ||
|
|
8ad35ce83a | ||
|
|
eb5c4721d1 | ||
|
|
b0e8fa75ca | ||
|
|
27d7288ee9 | ||
|
|
287921de09 | ||
|
|
e5cb8793b0 | ||
|
|
f25e7f250a | ||
|
|
911acc3c2d | ||
|
|
4b7f60bb8c | ||
|
|
d35146dba3 | ||
|
|
6c3897a400 | ||
|
|
1002499d92 | ||
|
|
4464b5b34d | ||
|
|
77c0b86dac | ||
|
|
0abdfc6b19 | ||
|
|
52b2ca8fa7 | ||
|
|
7f66124614 | ||
|
|
9930537486 | ||
|
|
5d51132921 | ||
|
|
4b2e963a81 | ||
|
|
4c865e199d | ||
|
|
fc58869354 | ||
|
|
5e1a2b41e9 | ||
|
|
77bdd71d79 | ||
|
|
7267c13ee0 | ||
|
|
4ab1f034d8 | ||
|
|
4bd8bbfa4c | ||
|
|
3fc03114e2 | ||
|
|
576c93e6cb | ||
|
|
ac674d937a | ||
|
|
0ed329022e | ||
|
|
b8b4a77349 | ||
|
|
de14663b25 | ||
|
|
747af0d81c | ||
|
|
c95e7cc5e0 | ||
|
|
0c3b43c5dc | ||
|
|
830e9076f1 | ||
|
|
cd9ae97bc7 | ||
|
|
1b59a8e8ef | ||
|
|
391405fc76 | ||
|
|
4bdcbacf62 | ||
|
|
6cbae700bf |
1
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -26,6 +26,7 @@ Outline the steps required to reproduce the bug, including any specific actions,
|
||||
- Android device: [Device Model]
|
||||
- Android OS version: [Android Version]
|
||||
- App version: [App Version]
|
||||
- App variant: [goole play services, degoogled]
|
||||
- Other relevant details: [e.g., specific network conditions, external dependencies]
|
||||
|
||||
## Logs or Screenshots
|
||||
|
||||
3
.github/ISSUE_TEMPLATE/crash-report.md
vendored
@@ -23,10 +23,11 @@ Please provide the steps to reproduce the crash:
|
||||
- Android device: [Device Model]
|
||||
- Android OS version: [Android Version]
|
||||
- App version: [App Version]
|
||||
- App variant: [goole play services, degoogled]
|
||||
- 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 ```). Please use the unsigned apk (app-tempo-debug.apk), as the logs would be illegible and therefore useless for this purpose. -->
|
||||
<!-- 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-tempus-debug.apk), as the logs would be illegible and therefore useless for this purpose. -->
|
||||
|
||||
## Screenshots
|
||||
<!-- If applicable, add screenshots to help explain the problem. -->
|
||||
|
||||
149
.github/workflows/github_release.yml
vendored
@@ -3,7 +3,7 @@ name: Github Release Workflow
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'v[0-9]+.[0-9]+.[0-9]+'
|
||||
- '[0-9]+.[0-9]+.[0-9]+'
|
||||
|
||||
jobs:
|
||||
build:
|
||||
@@ -35,17 +35,17 @@ jobs:
|
||||
echo "BUILD_TOOL_VERSION=$BUILD_TOOL_VERSION" >> $GITHUB_ENV
|
||||
echo Last build tool version is: $BUILD_TOOL_VERSION
|
||||
|
||||
- name: Build All APKs
|
||||
- name: Build All Release APKs
|
||||
id: build
|
||||
run: |
|
||||
# Build release variants
|
||||
# Only build release variants (removed debug builds)
|
||||
bash ./gradlew assembleTempusRelease
|
||||
bash ./gradlew assembleDegoggledRelease
|
||||
# Build debug variants
|
||||
bash ./gradlew assembleTempusDebug
|
||||
bash ./gradlew assembleDegoggledDebug
|
||||
bash ./gradlew assembleDegoogledRelease
|
||||
|
||||
- name: Sign All Tempus Release APKs
|
||||
- name: Create Artifact Staging Directory
|
||||
run: mkdir -p release-artifacts
|
||||
|
||||
- name: Sign Tempus Release APKs
|
||||
id: sign_tempus_release
|
||||
uses: r0adkll/sign-android-release@v1
|
||||
with:
|
||||
@@ -54,11 +54,30 @@ jobs:
|
||||
alias: ${{ secrets.KEY_ALIAS_GITHUB }}
|
||||
keyStorePassword: ${{ secrets.KEYSTORE_PASSWORD }}
|
||||
keyPassword: ${{ secrets.KEY_PASSWORD_GITHUB }}
|
||||
apkPath: "**/*.apk"
|
||||
env:
|
||||
BUILD_TOOLS_VERSION: ${{ env.BUILD_TOOL_VERSION }}
|
||||
|
||||
- name: Sign All Degoggled Release APKs
|
||||
- name: Prepare Signed Tempus APKs for Release
|
||||
run: |
|
||||
TEMPUS_PATH=app/build/outputs/apk/tempus/release
|
||||
|
||||
echo "--- Tempus Files BEFORE Move ---"
|
||||
ls -la $TEMPUS_PATH
|
||||
echo "--------------------------------"
|
||||
|
||||
# FIX: Use find/xargs for robust file matching and moving.
|
||||
|
||||
# Renaming 64-bit APK and moving to safe staging directory
|
||||
find $TEMPUS_PATH -name '*arm64-v8a*signed.apk' -print0 | xargs -0 mv -t ./release-artifacts/
|
||||
mv ./release-artifacts/*arm64-v8a*signed.apk ./release-artifacts/app-tempus-arm64-v8a-release.apk
|
||||
|
||||
# Renaming 32-bit APK and moving to safe staging directory
|
||||
find $TEMPUS_PATH -name '*armeabi-v7a*signed.apk' -print0 | xargs -0 mv -t ./release-artifacts/
|
||||
mv ./release-artifacts/*armeabi-v7a*signed.apk ./release-artifacts/app-tempus-armeabi-v7a-release.apk
|
||||
|
||||
echo "Prepared Tempus APKs."
|
||||
|
||||
- name: Sign Degoogled Release APKs
|
||||
id: sign_degoogled_release
|
||||
uses: r0adkll/sign-android-release@v1
|
||||
with:
|
||||
@@ -67,104 +86,44 @@ jobs:
|
||||
alias: ${{ secrets.KEY_ALIAS_GITHUB }}
|
||||
keyStorePassword: ${{ secrets.KEYSTORE_PASSWORD }}
|
||||
keyPassword: ${{ secrets.KEY_PASSWORD_GITHUB }}
|
||||
apkPath: "**/*.apk"
|
||||
env:
|
||||
BUILD_TOOLS_VERSION: ${{ env.BUILD_TOOL_VERSION }}
|
||||
|
||||
- name: Rename and Prepare APK Files
|
||||
- name: Prepare Signed Degoogled APKs for Release
|
||||
run: |
|
||||
# Copy and rename tempus APKs
|
||||
for file in app/build/outputs/apk/tempus/release/*.apk; do
|
||||
if [[ $file == *"arm64-v8a"* ]]; then
|
||||
cp "$file" "./app-tempus-arm64-v8a-release.apk"
|
||||
echo "Created: app-tempus-arm64-v8a-release.apk"
|
||||
elif [[ $file == *"armeabi-v7a"* ]]; then
|
||||
cp "$file" "./app-tempus-armeabi-v7a-release.apk"
|
||||
echo "Created: app-tempus-armeabi-v7a-release.apk"
|
||||
fi
|
||||
done
|
||||
DEGOOGLED_PATH=app/build/outputs/apk/degoogled/release
|
||||
|
||||
# Copy and rename degoogled APKs
|
||||
for file in app/build/outputs/apk/degoogled/release/*.apk; do
|
||||
if [[ $file == *"arm64-v8a"* ]]; then
|
||||
cp "$file" "./app-degoogled-arm64-v8a-release.apk"
|
||||
echo "Created: app-degoogled-arm64-v8a-release.apk"
|
||||
elif [[ $file == *"armeabi-v7a"* ]]; then
|
||||
cp "$file" "./app-degoogled-armeabi-v7a-release.apk"
|
||||
echo "Created: app-degoogled-armeabi-v7a-release.apk"
|
||||
fi
|
||||
done
|
||||
echo "--- Degoogled Files BEFORE Move ---"
|
||||
ls -la $DEGOOGLED_PATH
|
||||
echo "--------------------------------"
|
||||
|
||||
# List the created files for verification
|
||||
echo "Final APK files:"
|
||||
ls -la *.apk
|
||||
# FIX: Use find/xargs for robust file matching and moving.
|
||||
|
||||
# Renaming 64-bit APK and moving to safe staging directory
|
||||
find $DEGOOGLED_PATH -name '*arm64-v8a*signed.apk' -print0 | xargs -0 mv -t ./release-artifacts/
|
||||
mv ./release-artifacts/*arm64-v8a*signed.apk ./release-artifacts/app-degoogled-arm64-v8a-release.apk
|
||||
|
||||
# Renaming 32-bit APK and moving to safe staging directory
|
||||
find $DEGOOGLED_PATH -name '*armeabi-v7a*signed.apk' -print0 | xargs -0 mv -t ./release-artifacts/
|
||||
mv ./release-artifacts/*armeabi-v7a*signed.apk ./release-artifacts/app-degoogled-armeabi-v7a-release.apk
|
||||
|
||||
echo "Prepared Degoogled APKs."
|
||||
ls -la ./release-artifacts/
|
||||
|
||||
- name: Create Release
|
||||
id: create_release
|
||||
uses: actions/create-release@v1
|
||||
uses: softprops/action-gh-release@v1
|
||||
with:
|
||||
tag_name: ${{ github.ref }}
|
||||
release_name: Release v${{ github.ref }}
|
||||
tag_name: ${{ github.ref_name }}
|
||||
name: ${{ github.ref_name }}
|
||||
body: '> Changelog coming soon'
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ github.token }}
|
||||
draft: false
|
||||
prerelease: false
|
||||
files: ./release-artifacts/*.apk
|
||||
|
||||
- name: Upload Tempus 64-bit Release APK
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ github.token }}
|
||||
with:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
asset_path: ./app-tempus-arm64-v8a-release.apk
|
||||
asset_name: app-tempus-arm64-v8a-release.apk
|
||||
asset_content_type: application/vnd.android.package-archive
|
||||
|
||||
- name: Upload Tempus 32-bit Release APK
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ github.token }}
|
||||
with:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
asset_path: ./app-tempus-armeabi-v7a-release.apk
|
||||
asset_name: app-tempus-armeabi-v7a-release.apk
|
||||
asset_content_type: application/vnd.android.package-archive
|
||||
|
||||
- name: Upload Degoggled 64-bit Release APK
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ github.token }}
|
||||
with:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
asset_path: ./app-degoogled-arm64-v8a-release.apk
|
||||
asset_name: app-degoogled-arm64-v8a-release.apk
|
||||
asset_content_type: application/vnd.android.package-archive
|
||||
|
||||
- name: Upload Degoggled 32-bit Release APK
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ github.token }}
|
||||
with:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
asset_path: ./app-degoogled-armeabi-v7a-release.apk
|
||||
asset_name: app-degoogled-armeabi-v7a-release.apk
|
||||
asset_content_type: application/vnd.android.package-archive
|
||||
|
||||
- name: Upload Debug APKs as artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: debug-apks
|
||||
path: |
|
||||
app/build/outputs/apk/tempus/debug/
|
||||
app/build/outputs/apk/degoogled/debug/
|
||||
retention-days: 30
|
||||
|
||||
- name: Upload Release APKs as artifacts
|
||||
- name: Upload Release APKs as artifacts (For easy pipeline access)
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: release-apks
|
||||
path: |
|
||||
./app-tempus-arm64-v8a-release.apk
|
||||
./app-tempus-armeabi-v7a-release.apk
|
||||
./app-degoogled-arm64-v8a-release.apk
|
||||
./app-degoogled-armeabi-v7a-release.apk
|
||||
path: ./release-artifacts/*.apk
|
||||
retention-days: 30
|
||||
|
||||
101
CHANGELOG.md
@@ -1,6 +1,103 @@
|
||||
# Changelog
|
||||
|
||||
***This log is for this fork to detail updates since 3.9.0 from the main repo.***
|
||||
## Pending release...
|
||||
|
||||
## [4.2.6](https://github.com/eddyizm/tempo/releases/tag/v4.2.6) (2025-11-22)
|
||||
## What's Changed
|
||||
* fix: Fix player queue soft-lock by @shrapnelnet in https://github.com/eddyizm/tempus/pull/266
|
||||
* chore: Add Catalan i18n by @marcriera in https://github.com/eddyizm/tempus/pull/268
|
||||
* chore: Refactor MediaService by @pca006132 in https://github.com/eddyizm/tempus/pull/267
|
||||
* chore(i18n): Update Spanish translation by @jaime-grj in https://github.com/eddyizm/tempus/pull/272
|
||||
* chore(i18n): Update Italian translation by @66Bunz in https://github.com/eddyizm/tempus/pull/278
|
||||
|
||||
## New Contributors
|
||||
* @marcriera made their first contribution in https://github.com/eddyizm/tempus/pull/268
|
||||
* @66Bunz made their first contribution in https://github.com/eddyizm/tempus/pull/278
|
||||
|
||||
**Full Changelog**: https://github.com/eddyizm/tempus/compare/v4.2.4...v4.2.6
|
||||
|
||||
## [4.2.4](https://github.com/eddyizm/tempo/releases/tag/v4.2.4) (2025-11-15)
|
||||
## What's Changed
|
||||
* chore: Update russian strings.xml by @Sevinfolds in https://github.com/eddyizm/tempus/pull/249
|
||||
* fix: disallow duplicate songs in queue by @eddyizm in https://github.com/eddyizm/tempus/pull/252
|
||||
* fix:github release check by @eddyizm in https://github.com/eddyizm/tempus/pull/253
|
||||
* fix: Fixed crash when viewing share by @drakeerv in https://github.com/eddyizm/tempus/pull/255
|
||||
* chore: Update Polish translation by @skajmer in https://github.com/eddyizm/tempus/pull/257
|
||||
* fix: add podcast/radio channel visible when empty podcasts/radio by @eddyizm in https://github.com/eddyizm/tempus/pull/260
|
||||
|
||||
## New Contributors
|
||||
* @Sevinfolds made their first contribution in https://github.com/eddyizm/tempus/pull/249
|
||||
* @drakeerv made their first contribution in https://github.com/eddyizm/tempus/pull/255
|
||||
|
||||
**Full Changelog**: https://github.com/eddyizm/tempus/compare/v4.2.0...v4.2.4
|
||||
## [4.2.0](https://github.com/eddyizm/tempo/releases/tag/v4.2.0) (2025-11-09)
|
||||
## What's Changed
|
||||
* fix: Equalizer fix in main build variant by @jaime-grj in https://github.com/eddyizm/tempus/pull/239
|
||||
* fix: Images not filling holder by @eddyizm in https://github.com/eddyizm/tempus/pull/244
|
||||
* feat: Make artist and album clickable by @eddyizm in https://github.com/eddyizm/tempus/pull/243
|
||||
* feat: implement scroll to currently playing feature by @shrapnelnet in https://github.com/eddyizm/tempus/pull/247
|
||||
* fix: shuffling genres only queuing 25 songs by @shrapnelnet in https://github.com/eddyizm/tempus/pull/246
|
||||
|
||||
## New Contributors
|
||||
* @shrapnelnet made their first contribution in https://github.com/eddyizm/tempus/pull/247
|
||||
|
||||
**Full Changelog**: https://github.com/eddyizm/tempus/compare/v4.1.3...v4.2.0
|
||||
|
||||
## [4.1.3](https://github.com/eddyizm/tempo/releases/tag/v4.1.3) (2025-11-06)
|
||||
## What's Changed
|
||||
* [fix: equalizer missing referenced value](https://github.com/eddyizm/tempus/commit/923cfd5bc97ed7db28c90348e3619d0a784fc434)
|
||||
* Fix: Album track list bug by @eddyizm in https://github.com/eddyizm/tempus/pull/237
|
||||
* fix: Add listener to enable equalizer when audioSessionId changes by @jaime-grj in https://github.com/eddyizm/tempus/pull/235
|
||||
|
||||
**Full Changelog**: https://github.com/eddyizm/tempus/compare/v4.1.0...v4.1.3
|
||||
|
||||
## [4.1.0](https://github.com/eddyizm/tempo/releases/tag/v4.1.0) (2025-11-05)
|
||||
## What's Changed
|
||||
* chore(i18n): Update Spanish (es-ES) translation by @jaime-grj in https://github.com/eddyizm/tempus/pull/205
|
||||
* shuffle for artists without using `getTopSongs` by @pca006132 in https://github.com/eddyizm/tempus/pull/207
|
||||
* Update USAGE.md with instant mix details by @zc-devs in https://github.com/eddyizm/tempus/pull/220
|
||||
* feat: sort artists by album count by @pca006132 in https://github.com/eddyizm/tempus/pull/206
|
||||
* Fix downloaded tab performance by @pca006132 in https://github.com/eddyizm/tempus/pull/210
|
||||
* fix: remove NestedScrollViews for fragment_album_page by @pca006132 in https://github.com/eddyizm/tempus/pull/216
|
||||
* fix: playlist page should not snap by @pca006132 in https://github.com/eddyizm/tempus/pull/218
|
||||
* fix: do not override getItemViewType and getItemId by @pca006132 in https://github.com/eddyizm/tempus/pull/221
|
||||
* chore: update media3 dependencies by @pca006132 in https://github.com/eddyizm/tempus/pull/217
|
||||
* fix: update MediaItems after network change by @pca006132 in https://github.com/eddyizm/tempus/pull/222
|
||||
* fix: skip mapping downloaded item by @pca006132 in https://github.com/eddyizm/tempus/pull/228
|
||||
|
||||
## New Contributors
|
||||
* @pca006132 made their first contribution in https://github.com/eddyizm/tempus/pull/207
|
||||
|
||||
**Full Changelog**: https://github.com/eddyizm/tempus/compare/v4.0.7...v4.1.0
|
||||
|
||||
## [4.0.7](https://github.com/eddyizm/tempo/releases/tag/v4.0.7) (2025-10-28)
|
||||
## What's Changed
|
||||
* chore: updated tempo references to tempus including github check by @eddyizm in https://github.com/eddyizm/tempus/pull/197
|
||||
* fix: Crash on share no expiration date or field returned from api by @eddyizm in https://github.com/eddyizm/tempus/pull/199
|
||||
|
||||
**Full Changelog**: https://github.com/eddyizm/tempus/compare/v4.0.6...v4.0.7
|
||||
|
||||
## [4.0.6](https://github.com/eddyizm/tempo/releases/tag/v4.0.6) (2025-10-26)
|
||||
## Attention
|
||||
This release will not update previous installs as it is considered a new app, no longer `Tempo`, new icon, new app id, and new app name. Hoping it will not be a huge inconvenience but was necessary in order to publish to app stores like IzzyDroid and FDroid.
|
||||
|
||||
**Android Auto**
|
||||
Support should be the same as before, however, I was not able to test any of the icons/visuals, so please let me know if there are any remnants of the tempo logo/icon as I believe I removed them all and replaced them successfully.
|
||||
|
||||
## What's Changed
|
||||
* Check also underlying transport by @zc-devs in https://github.com/eddyizm/tempus/pull/90
|
||||
* fix: updated workflow for 32/64 bit apks by @eddyizm in https://github.com/eddyizm/tempus/pull/176
|
||||
* Unhide genre from album details view by @sebaFlame in https://github.com/eddyizm/tempus/pull/161
|
||||
* fix: persist album sorting on resume by @eddyizm in https://github.com/eddyizm/tempus/pull/181
|
||||
* chore: update readme and usage references to tempus. added new banner… by @eddyizm in https://github.com/eddyizm/tempus/pull/182
|
||||
* Tempus rebrand by @eddyizm in https://github.com/eddyizm/tempus/pull/183
|
||||
* Update Polish translation by @skajmer in https://github.com/eddyizm/tempus/pull/188
|
||||
|
||||
## New Contributors
|
||||
* @zc-devs made their first contribution in https://github.com/eddyizm/tempus/pull/90
|
||||
* @sebaFlame made their first contribution in https://github.com/eddyizm/tempus/pull/161
|
||||
|
||||
**Full Changelog**: https://github.com/eddyizm/tempus/compare/v3.17.14...v4.0.1
|
||||
|
||||
## [3.17.14](https://github.com/eddyizm/tempo/releases/tag/v3.17.14) (2025-10-16)
|
||||
## What's Changed
|
||||
@@ -170,3 +267,5 @@
|
||||
[\#400](https://github.com/CappielloAntonio/tempo/pull/400)
|
||||
- [Chore] Spanish translation [\#374](https://github.com/CappielloAntonio/tempo/pull/374)
|
||||
- [Chore] Polish translation [\#378](https://github.com/CappielloAntonio/tempo/pull/378)
|
||||
|
||||
***This log is for this fork to detail updates since 3.9.0 from the main repo.***
|
||||
29
README.md
@@ -2,17 +2,27 @@
|
||||
<img alt="Tempus" title="Tempus" src="mockup/svg/tempus_horizontal_logo.png" width="250">
|
||||
</p>
|
||||
|
||||
---
|
||||
|
||||
<p align="center">
|
||||
<b>Access your music library on all your android devices</b>
|
||||
</p>
|
||||
|
||||
<div align="center">
|
||||
|
||||
<!-- Reproducible build -->
|
||||
[<img src="https://shields.rbtlog.dev/simple/com.eddyizm.degoogled.tempus" alt="RB Status">](https://shields.rbtlog.dev/com.eddyizm.degoogled.tempus)
|
||||
|
||||
</div>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://github.com/eddyizm/tempo/releases"><img src="https://i.ibb.co/q0mdc4Z/get-it-on-github.png" width="200"></a>
|
||||
<a href="https://github.com/eddyizm/tempus/releases"><img src="https://i.ibb.co/q0mdc4Z/get-it-on-github.png" width="200"></a>
|
||||
<a href="https://apt.izzysoft.de/fdroid/index/apk/com.eddyizm.degoogled.tempus"><img src="https://gitlab.com/IzzyOnDroid/repo/-/raw/master/assets/IzzyOnDroid.png" width="200"></a>
|
||||
</p>
|
||||
<!-- <p align="center">
|
||||
<!--
|
||||
<a href="https://f-droid.org/packages/com.cappielloantonio.notquitemy.tempo"><img src="https://fdroid.gitlab.io/artwork/badge/get-it-on.png" width="200"></a>
|
||||
<a href="https://apt.izzysoft.de/fdroid/index/apk/com.cappielloantonio.tempo"><img src="https://gitlab.com/IzzyOnDroid/repo/-/raw/master/assets/IzzyOnDroid.png" width="200"></a>
|
||||
</p> -->
|
||||
-->
|
||||
|
||||
|
||||
**Tempus** is an open-source and lightweight music client for Subsonic, designed and built natively for Android. It provides a seamless and intuitive music streaming experience, allowing you to access and play your Subsonic music library directly from your Android device.
|
||||
|
||||
@@ -33,11 +43,9 @@ Please note the two variants in the release assets include release/debug and 32/
|
||||
|
||||
`app-tempus` <- The github release with all the android auto/chromecast features
|
||||
|
||||
`app-degoogled*` <- The f-droid release that goes without any of the google stuff. It was last released at 3.8.1 from the original repo. Since I don't have access to that original repo, I am releasing the apk's here on github.
|
||||
`app-degoogled*` <- The izzyOnDroid release that goes without any of the google stuff. It is now available on izzyOnDroid (64bit) I am releasing the both 32/64bit apk's here on github for those who need a 32bit version.
|
||||
|
||||
Moved details to [CHANGELOG.md](CHANGELOG.md)
|
||||
|
||||
Fork [**sponsorship here**](https://ko-fi.com/eddyizm).
|
||||
[CHANGELOG.md](CHANGELOG.md)
|
||||
|
||||
## Usage
|
||||
|
||||
@@ -103,6 +111,11 @@ Currently there are no tests but I would love to start on some unit tests.
|
||||
|
||||
Not a hard requirement but any new feature/change should ideally include an update to the nacent documention.
|
||||
|
||||
## Support
|
||||
|
||||
[**Buy me a coffee**](https://ko-fi.com/eddyizm)
|
||||
bitcoin: `3QVHSSCJvn6yXEcJ3A3cxYLMmbvFsrnUs5`
|
||||
|
||||
## License
|
||||
|
||||
Tempus is released under the [GNU General Public License v3.0](LICENSE). Feel free to modify, distribute, and use the app in accordance with the terms of the license. Contributions to the project are also welcome.
|
||||
|
||||
19
USAGE.md
@@ -6,6 +6,7 @@
|
||||
- [Getting Started](#getting-started)
|
||||
- [Server Configuration](#server-configuration)
|
||||
- [Main Features](#main-features)
|
||||
|
||||
- [Navigation](#navigation)
|
||||
- [Playback Controls](#playback-controls)
|
||||
- [Favorites](#favorites)
|
||||
@@ -27,6 +28,8 @@ This app works with any service that implements the Subsonic API, including:
|
||||
- [LMS - Lightweight Music Server](https://github.com/epoupon/lms) - *personal fave and my backend*
|
||||
- [Navidrome](https://www.navidrome.org/)
|
||||
- [Gonic](https://github.com/sentriz/gonic)
|
||||
- [Ampache](https://github.com/ampache/ampache)
|
||||
- [NextCloud Music](https://apps.nextcloud.com/apps/music)
|
||||
|
||||
|
||||
|
||||
@@ -78,9 +81,23 @@ On the main player control screen, tapping on the artwork will reveal a small co
|
||||
1. Downloads the track (there is a notification if the android screen but not a pop toast currently )
|
||||
2. Adds track to playlist - pops up playlist dialog.
|
||||
3. Adds tracks to the queue via instant mix function
|
||||
* TBD: what is the _instant mix function_?
|
||||
* Uses [getSimilarSongs](https://opensubsonic.netlify.app/docs/endpoints/getsimilarsongs/) of OpenSubsonic API.
|
||||
Which tracks to be mixed depends on the server implementation. For example, Navidrome gets 15 similar artists from LastFM, then 20 top songs from each.
|
||||
4. Saves play queue (if the feature is enabled in the settings)
|
||||
* if the setting is not enabled, it toggles a view of the lyrics if available (slides to the right)
|
||||
|
||||
### Podcasts
|
||||
If your server supports it - add a podcast rss feed
|
||||
<p align="left">
|
||||
<img src="mockup/usage/add_podcast_feed.png" width=317>
|
||||
</p>
|
||||
|
||||
### Radio Stations
|
||||
If your server supports it - add a internet radio station feed
|
||||
<p align="left">
|
||||
<img src="mockup/usage/add_radio_station.png" width=326>
|
||||
</p>
|
||||
|
||||
## Navigation
|
||||
|
||||
@@ -163,4 +180,4 @@ For additional help:
|
||||
|
||||
---
|
||||
|
||||
*Note: This app requires a pre-existing Subsonic-compatible server with music content.*
|
||||
*Note: This app requires a pre-existing Subsonic-compatible server with music content.*
|
||||
|
||||
@@ -10,8 +10,8 @@ android {
|
||||
minSdkVersion 24
|
||||
targetSdk 35
|
||||
|
||||
versionCode 1
|
||||
versionName '4.0.0'
|
||||
versionCode 7
|
||||
versionName '4.2.6'
|
||||
testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
|
||||
|
||||
javaCompileOptions {
|
||||
@@ -35,7 +35,12 @@ android {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
dependenciesInfo {
|
||||
// Disables dependency metadata when building APKs (for IzzyOnDroid/F-Droid)
|
||||
includeInApk = false
|
||||
// Disables dependency metadata when building Android App Bundles (for Google Play)
|
||||
includeInBundle = false
|
||||
}
|
||||
|
||||
flavorDimensions += "default"
|
||||
|
||||
@@ -105,12 +110,12 @@ dependencies {
|
||||
implementation 'com.github.bumptech.glide:annotations:4.16.0'
|
||||
|
||||
// Media3
|
||||
implementation 'androidx.media3:media3-session:1.5.1'
|
||||
implementation 'androidx.media3:media3-common:1.5.1'
|
||||
implementation 'androidx.media3:media3-exoplayer:1.5.1'
|
||||
implementation 'androidx.media3:media3-ui:1.5.1'
|
||||
implementation 'androidx.media3:media3-exoplayer-hls:1.5.1'
|
||||
tempusImplementation 'androidx.media3:media3-cast:1.5.1'
|
||||
implementation 'androidx.media3:media3-session:1.8.0'
|
||||
implementation 'androidx.media3:media3-common:1.8.0'
|
||||
implementation 'androidx.media3:media3-exoplayer:1.8.0'
|
||||
implementation 'androidx.media3:media3-ui:1.8.0'
|
||||
implementation 'androidx.media3:media3-exoplayer-hls:1.8.0'
|
||||
tempusImplementation 'androidx.media3:media3-cast:1.8.0'
|
||||
|
||||
|
||||
annotationProcessor 'com.github.bumptech.glide:compiler:4.16.0'
|
||||
|
||||
@@ -1,503 +1,6 @@
|
||||
package com.cappielloantonio.tempo.service
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.PendingIntent.FLAG_IMMUTABLE
|
||||
import android.app.PendingIntent.FLAG_UPDATE_CURRENT
|
||||
import android.app.TaskStackBuilder
|
||||
import android.content.Intent
|
||||
import android.os.Binder
|
||||
import android.os.Bundle
|
||||
import android.os.IBinder
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import androidx.media3.common.*
|
||||
import androidx.media3.common.util.UnstableApi
|
||||
import androidx.media3.exoplayer.DefaultLoadControl
|
||||
import androidx.media3.exoplayer.ExoPlayer
|
||||
import androidx.media3.exoplayer.source.MediaSource
|
||||
import androidx.media3.exoplayer.source.TrackGroupArray
|
||||
import androidx.media3.exoplayer.trackselection.TrackSelectionArray
|
||||
import androidx.media3.session.*
|
||||
import androidx.media3.session.MediaSession.ControllerInfo
|
||||
import com.cappielloantonio.tempo.R
|
||||
import com.cappielloantonio.tempo.repository.QueueRepository
|
||||
import com.cappielloantonio.tempo.ui.activity.MainActivity
|
||||
import com.cappielloantonio.tempo.util.AssetLinkUtil
|
||||
import com.cappielloantonio.tempo.util.Constants
|
||||
import com.cappielloantonio.tempo.util.DownloadUtil
|
||||
import com.cappielloantonio.tempo.util.DynamicMediaSourceFactory
|
||||
import com.cappielloantonio.tempo.util.MappingUtil
|
||||
import com.cappielloantonio.tempo.util.Preferences
|
||||
import com.cappielloantonio.tempo.util.ReplayGainUtil
|
||||
import com.cappielloantonio.tempo.widget.WidgetUpdateManager
|
||||
import com.google.common.collect.ImmutableList
|
||||
import com.google.common.util.concurrent.Futures
|
||||
import com.google.common.util.concurrent.ListenableFuture
|
||||
|
||||
|
||||
@UnstableApi
|
||||
class MediaService : MediaLibraryService() {
|
||||
private val librarySessionCallback = CustomMediaLibrarySessionCallback()
|
||||
|
||||
private lateinit var player: ExoPlayer
|
||||
private lateinit var mediaLibrarySession: MediaLibrarySession
|
||||
private lateinit var shuffleCommands: List<CommandButton>
|
||||
private lateinit var repeatCommands: List<CommandButton>
|
||||
lateinit var equalizerManager: EqualizerManager
|
||||
|
||||
private var customLayout = ImmutableList.of<CommandButton>()
|
||||
private val widgetUpdateHandler = Handler(Looper.getMainLooper())
|
||||
private var widgetUpdateScheduled = false
|
||||
private val widgetUpdateRunnable = object : Runnable {
|
||||
override fun run() {
|
||||
if (!player.isPlaying) {
|
||||
widgetUpdateScheduled = false
|
||||
return
|
||||
}
|
||||
updateWidget()
|
||||
widgetUpdateHandler.postDelayed(this, WIDGET_UPDATE_INTERVAL_MS)
|
||||
}
|
||||
}
|
||||
|
||||
inner class LocalBinder : Binder() {
|
||||
fun getEqualizerManager(): EqualizerManager {
|
||||
return this@MediaService.equalizerManager
|
||||
}
|
||||
}
|
||||
|
||||
private val binder = LocalBinder()
|
||||
|
||||
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"
|
||||
private const val CUSTOM_COMMAND_TOGGLE_REPEAT_MODE_OFF =
|
||||
"android.media3.session.demo.REPEAT_OFF"
|
||||
private const val CUSTOM_COMMAND_TOGGLE_REPEAT_MODE_ONE =
|
||||
"android.media3.session.demo.REPEAT_ONE"
|
||||
private const val CUSTOM_COMMAND_TOGGLE_REPEAT_MODE_ALL =
|
||||
"android.media3.session.demo.REPEAT_ALL"
|
||||
const val ACTION_BIND_EQUALIZER = "com.cappielloantonio.tempo.service.BIND_EQUALIZER"
|
||||
}
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
|
||||
initializeCustomCommands()
|
||||
initializePlayer()
|
||||
initializeMediaLibrarySession()
|
||||
restorePlayerFromQueue()
|
||||
initializePlayerListener()
|
||||
initializeEqualizerManager()
|
||||
|
||||
setPlayer(player)
|
||||
}
|
||||
|
||||
override fun onGetSession(controllerInfo: ControllerInfo): MediaLibrarySession {
|
||||
return mediaLibrarySession
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
equalizerManager.release()
|
||||
stopWidgetUpdates()
|
||||
releasePlayer()
|
||||
super.onDestroy()
|
||||
}
|
||||
|
||||
override fun onBind(intent: Intent?): IBinder? {
|
||||
// Check if the intent is for our custom equalizer binder
|
||||
if (intent?.action == ACTION_BIND_EQUALIZER) {
|
||||
return binder
|
||||
}
|
||||
// Otherwise, handle it as a normal MediaLibraryService connection
|
||||
return super.onBind(intent)
|
||||
}
|
||||
|
||||
private inner class CustomMediaLibrarySessionCallback : MediaLibrarySession.Callback {
|
||||
|
||||
override fun onConnect(
|
||||
session: MediaSession,
|
||||
controller: ControllerInfo
|
||||
): MediaSession.ConnectionResult {
|
||||
val connectionResult = super.onConnect(session, controller)
|
||||
val availableSessionCommands = connectionResult.availableSessionCommands.buildUpon()
|
||||
|
||||
(shuffleCommands + repeatCommands).forEach { commandButton ->
|
||||
commandButton.sessionCommand?.let { availableSessionCommands.add(it) }
|
||||
}
|
||||
|
||||
customLayout = buildCustomLayout(session.player)
|
||||
|
||||
return MediaSession.ConnectionResult.AcceptedResultBuilder(session)
|
||||
.setAvailableSessionCommands(availableSessionCommands.build())
|
||||
.setAvailablePlayerCommands(connectionResult.availablePlayerCommands)
|
||||
.setCustomLayout(customLayout)
|
||||
.build()
|
||||
}
|
||||
|
||||
override fun onPostConnect(session: MediaSession, controller: ControllerInfo) {
|
||||
if (!customLayout.isEmpty() && controller.controllerVersion != 0) {
|
||||
ignoreFuture(mediaLibrarySession.setCustomLayout(controller, customLayout))
|
||||
}
|
||||
}
|
||||
|
||||
fun buildCustomLayout(player: Player): ImmutableList<CommandButton> {
|
||||
val shuffle = shuffleCommands[if (player.shuffleModeEnabled) 1 else 0]
|
||||
val repeat = when (player.repeatMode) {
|
||||
Player.REPEAT_MODE_ONE -> repeatCommands[1]
|
||||
Player.REPEAT_MODE_ALL -> repeatCommands[2]
|
||||
else -> repeatCommands[0]
|
||||
}
|
||||
return ImmutableList.of(shuffle, repeat)
|
||||
}
|
||||
|
||||
override fun onCustomCommand(
|
||||
session: MediaSession,
|
||||
controller: ControllerInfo,
|
||||
customCommand: SessionCommand,
|
||||
args: Bundle
|
||||
): ListenableFuture<SessionResult> {
|
||||
when (customCommand.customAction) {
|
||||
CUSTOM_COMMAND_TOGGLE_SHUFFLE_MODE_ON -> player.shuffleModeEnabled = true
|
||||
CUSTOM_COMMAND_TOGGLE_SHUFFLE_MODE_OFF -> player.shuffleModeEnabled = false
|
||||
CUSTOM_COMMAND_TOGGLE_REPEAT_MODE_OFF,
|
||||
CUSTOM_COMMAND_TOGGLE_REPEAT_MODE_ALL,
|
||||
CUSTOM_COMMAND_TOGGLE_REPEAT_MODE_ONE -> {
|
||||
val nextMode = when (player.repeatMode) {
|
||||
Player.REPEAT_MODE_ONE -> Player.REPEAT_MODE_ALL
|
||||
Player.REPEAT_MODE_OFF -> Player.REPEAT_MODE_ONE
|
||||
else -> Player.REPEAT_MODE_OFF
|
||||
}
|
||||
player.repeatMode = nextMode
|
||||
}
|
||||
}
|
||||
|
||||
customLayout = librarySessionCallback.buildCustomLayout(player)
|
||||
session.setCustomLayout(customLayout)
|
||||
|
||||
return Futures.immediateFuture(SessionResult(SessionResult.RESULT_SUCCESS))
|
||||
}
|
||||
|
||||
override fun onAddMediaItems(
|
||||
mediaSession: MediaSession,
|
||||
controller: ControllerInfo,
|
||||
mediaItems: List<MediaItem>
|
||||
): ListenableFuture<List<MediaItem>> {
|
||||
val updatedMediaItems = mediaItems.map { mediaItem ->
|
||||
val mediaMetadata = mediaItem.mediaMetadata
|
||||
|
||||
val newMetadata = mediaMetadata.buildUpon()
|
||||
.setArtist(
|
||||
if (mediaMetadata.artist != null) mediaMetadata.artist
|
||||
else mediaMetadata.extras?.getString("uri") ?: ""
|
||||
)
|
||||
.build()
|
||||
|
||||
mediaItem.buildUpon()
|
||||
.setUri(mediaItem.requestMetadata.mediaUri)
|
||||
.setMediaMetadata(newMetadata)
|
||||
.setMimeType(MimeTypes.BASE_TYPE_AUDIO)
|
||||
.build()
|
||||
}
|
||||
return Futures.immediateFuture(updatedMediaItems)
|
||||
}
|
||||
}
|
||||
|
||||
private fun initializeCustomCommands() {
|
||||
shuffleCommands = listOf(
|
||||
getShuffleCommandButton(
|
||||
SessionCommand(CUSTOM_COMMAND_TOGGLE_SHUFFLE_MODE_ON, Bundle.EMPTY)
|
||||
),
|
||||
getShuffleCommandButton(
|
||||
SessionCommand(CUSTOM_COMMAND_TOGGLE_SHUFFLE_MODE_OFF, Bundle.EMPTY)
|
||||
)
|
||||
)
|
||||
|
||||
repeatCommands = listOf(
|
||||
getRepeatCommandButton(
|
||||
SessionCommand(CUSTOM_COMMAND_TOGGLE_REPEAT_MODE_OFF, Bundle.EMPTY)
|
||||
),
|
||||
getRepeatCommandButton(
|
||||
SessionCommand(CUSTOM_COMMAND_TOGGLE_REPEAT_MODE_ONE, Bundle.EMPTY)
|
||||
),
|
||||
getRepeatCommandButton(
|
||||
SessionCommand(CUSTOM_COMMAND_TOGGLE_REPEAT_MODE_ALL, Bundle.EMPTY)
|
||||
)
|
||||
)
|
||||
|
||||
customLayout = ImmutableList.of(shuffleCommands[0], repeatCommands[0])
|
||||
}
|
||||
|
||||
private fun initializePlayer() {
|
||||
player = ExoPlayer.Builder(this)
|
||||
.setRenderersFactory(getRenderersFactory())
|
||||
.setMediaSourceFactory(getMediaSourceFactory())
|
||||
.setAudioAttributes(AudioAttributes.DEFAULT, true)
|
||||
.setHandleAudioBecomingNoisy(true)
|
||||
.setWakeMode(C.WAKE_MODE_NETWORK)
|
||||
.setLoadControl(initializeLoadControl())
|
||||
.build()
|
||||
|
||||
player.shuffleModeEnabled = Preferences.isShuffleModeEnabled()
|
||||
player.repeatMode = Preferences.getRepeatMode()
|
||||
}
|
||||
|
||||
private fun initializeEqualizerManager() {
|
||||
equalizerManager = EqualizerManager()
|
||||
val audioSessionId = player.audioSessionId
|
||||
if (equalizerManager.attachToSession(audioSessionId)) {
|
||||
val enabled = Preferences.isEqualizerEnabled()
|
||||
equalizerManager.setEnabled(enabled)
|
||||
|
||||
val bands = equalizerManager.getNumberOfBands()
|
||||
val savedLevels = Preferences.getEqualizerBandLevels(bands)
|
||||
for (i in 0 until bands) {
|
||||
equalizerManager.setBandLevel(i.toShort(), savedLevels[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun initializeMediaLibrarySession() {
|
||||
val sessionActivityPendingIntent =
|
||||
TaskStackBuilder.create(this).run {
|
||||
addNextIntent(Intent(this@MediaService, MainActivity::class.java))
|
||||
getPendingIntent(0, FLAG_IMMUTABLE or FLAG_UPDATE_CURRENT)
|
||||
}
|
||||
|
||||
mediaLibrarySession =
|
||||
MediaLibrarySession.Builder(this, player, librarySessionCallback)
|
||||
.setSessionActivity(sessionActivityPendingIntent)
|
||||
.build()
|
||||
|
||||
if (!customLayout.isEmpty()) {
|
||||
mediaLibrarySession.setCustomLayout(customLayout)
|
||||
}
|
||||
}
|
||||
|
||||
private fun restorePlayerFromQueue() {
|
||||
if (player.mediaItemCount > 0) return
|
||||
|
||||
val queueRepository = QueueRepository()
|
||||
val storedQueue = queueRepository.media
|
||||
if (storedQueue.isNullOrEmpty()) return
|
||||
|
||||
val mediaItems = MappingUtil.mapMediaItems(storedQueue)
|
||||
if (mediaItems.isEmpty()) return
|
||||
|
||||
val lastIndex = try {
|
||||
queueRepository.lastPlayedMediaIndex
|
||||
} catch (_: Exception) {
|
||||
0
|
||||
}.coerceIn(0, mediaItems.size - 1)
|
||||
|
||||
val lastPosition = try {
|
||||
queueRepository.lastPlayedMediaTimestamp
|
||||
} catch (_: Exception) {
|
||||
0L
|
||||
}.let { if (it < 0L) 0L else it }
|
||||
|
||||
player.setMediaItems(mediaItems, lastIndex, lastPosition)
|
||||
player.prepare()
|
||||
updateWidget()
|
||||
}
|
||||
|
||||
private fun initializePlayerListener() {
|
||||
player.addListener(object : Player.Listener {
|
||||
override fun onMediaItemTransition(mediaItem: MediaItem?, reason: Int) {
|
||||
if (mediaItem == null) return
|
||||
|
||||
if (reason == Player.MEDIA_ITEM_TRANSITION_REASON_SEEK || reason == Player.MEDIA_ITEM_TRANSITION_REASON_AUTO) {
|
||||
MediaManager.setLastPlayedTimestamp(mediaItem)
|
||||
}
|
||||
updateWidget()
|
||||
}
|
||||
|
||||
override fun onTracksChanged(tracks: Tracks) {
|
||||
ReplayGainUtil.setReplayGain(player, tracks)
|
||||
val currentMediaItem = player.currentMediaItem
|
||||
if (currentMediaItem != null && currentMediaItem.mediaMetadata.extras != null) {
|
||||
MediaManager.scrobble(currentMediaItem, false)
|
||||
}
|
||||
|
||||
if (player.currentMediaItemIndex + 1 == player.mediaItemCount)
|
||||
MediaManager.continuousPlay(player.currentMediaItem)
|
||||
}
|
||||
|
||||
override fun onIsPlayingChanged(isPlaying: Boolean) {
|
||||
if (!isPlaying) {
|
||||
MediaManager.setPlayingPausedTimestamp(
|
||||
player.currentMediaItem,
|
||||
player.currentPosition
|
||||
)
|
||||
} else {
|
||||
MediaManager.scrobble(player.currentMediaItem, false)
|
||||
}
|
||||
if (isPlaying) {
|
||||
scheduleWidgetUpdates()
|
||||
} else {
|
||||
stopWidgetUpdates()
|
||||
}
|
||||
updateWidget()
|
||||
}
|
||||
|
||||
override fun onPlaybackStateChanged(playbackState: Int) {
|
||||
super.onPlaybackStateChanged(playbackState)
|
||||
if (!player.hasNextMediaItem() &&
|
||||
playbackState == Player.STATE_ENDED &&
|
||||
player.mediaMetadata.extras?.getString("type") == Constants.MEDIA_TYPE_MUSIC
|
||||
) {
|
||||
MediaManager.scrobble(player.currentMediaItem, true)
|
||||
MediaManager.saveChronology(player.currentMediaItem)
|
||||
}
|
||||
updateWidget()
|
||||
}
|
||||
|
||||
override fun onPositionDiscontinuity(
|
||||
oldPosition: Player.PositionInfo,
|
||||
newPosition: Player.PositionInfo,
|
||||
reason: Int
|
||||
) {
|
||||
super.onPositionDiscontinuity(oldPosition, newPosition, reason)
|
||||
|
||||
if (reason == Player.DISCONTINUITY_REASON_AUTO_TRANSITION) {
|
||||
if (oldPosition.mediaItem?.mediaMetadata?.extras?.getString("type") == Constants.MEDIA_TYPE_MUSIC) {
|
||||
MediaManager.scrobble(oldPosition.mediaItem, true)
|
||||
MediaManager.saveChronology(oldPosition.mediaItem)
|
||||
}
|
||||
|
||||
if (newPosition.mediaItem?.mediaMetadata?.extras?.getString("type") == Constants.MEDIA_TYPE_MUSIC) {
|
||||
MediaManager.setLastPlayedTimestamp(newPosition.mediaItem)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onShuffleModeEnabledChanged(shuffleModeEnabled: Boolean) {
|
||||
Preferences.setShuffleModeEnabled(shuffleModeEnabled)
|
||||
customLayout = librarySessionCallback.buildCustomLayout(player)
|
||||
mediaLibrarySession.setCustomLayout(customLayout)
|
||||
}
|
||||
|
||||
override fun onRepeatModeChanged(repeatMode: Int) {
|
||||
Preferences.setRepeatMode(repeatMode)
|
||||
customLayout = librarySessionCallback.buildCustomLayout(player)
|
||||
mediaLibrarySession.setCustomLayout(customLayout)
|
||||
}
|
||||
})
|
||||
if (player.isPlaying) {
|
||||
scheduleWidgetUpdates()
|
||||
}
|
||||
}
|
||||
|
||||
private fun setPlayer(player: Player) {
|
||||
mediaLibrarySession.player = player
|
||||
}
|
||||
|
||||
private fun releasePlayer() {
|
||||
player.release()
|
||||
mediaLibrarySession.release()
|
||||
}
|
||||
|
||||
@SuppressLint("PrivateResource")
|
||||
private fun getShuffleCommandButton(sessionCommand: SessionCommand): CommandButton {
|
||||
val isOn = sessionCommand.customAction == CUSTOM_COMMAND_TOGGLE_SHUFFLE_MODE_ON
|
||||
return CommandButton.Builder()
|
||||
.setDisplayName(
|
||||
getString(
|
||||
if (isOn) R.string.exo_controls_shuffle_on_description
|
||||
else R.string.exo_controls_shuffle_off_description
|
||||
)
|
||||
)
|
||||
.setSessionCommand(sessionCommand)
|
||||
.setIconResId(if (isOn) R.drawable.exo_icon_shuffle_off else R.drawable.exo_icon_shuffle_on)
|
||||
.build()
|
||||
}
|
||||
|
||||
@SuppressLint("PrivateResource")
|
||||
private fun getRepeatCommandButton(sessionCommand: SessionCommand): CommandButton {
|
||||
val icon = when (sessionCommand.customAction) {
|
||||
CUSTOM_COMMAND_TOGGLE_REPEAT_MODE_ONE -> R.drawable.exo_icon_repeat_one
|
||||
CUSTOM_COMMAND_TOGGLE_REPEAT_MODE_ALL -> R.drawable.exo_icon_repeat_all
|
||||
else -> R.drawable.exo_icon_repeat_off
|
||||
}
|
||||
val description = when (sessionCommand.customAction) {
|
||||
CUSTOM_COMMAND_TOGGLE_REPEAT_MODE_ONE -> R.string.exo_controls_repeat_one_description
|
||||
CUSTOM_COMMAND_TOGGLE_REPEAT_MODE_ALL -> R.string.exo_controls_repeat_all_description
|
||||
else -> R.string.exo_controls_repeat_off_description
|
||||
}
|
||||
return CommandButton.Builder()
|
||||
.setDisplayName(getString(description))
|
||||
.setSessionCommand(sessionCommand)
|
||||
.setIconResId(icon)
|
||||
.build()
|
||||
}
|
||||
|
||||
private fun ignoreFuture(@Suppress("UNUSED_PARAMETER") customLayout: ListenableFuture<SessionResult>) {
|
||||
/* Do nothing. */
|
||||
}
|
||||
|
||||
private fun initializeLoadControl(): DefaultLoadControl {
|
||||
return DefaultLoadControl.Builder()
|
||||
.setBufferDurationsMs(
|
||||
(DefaultLoadControl.DEFAULT_MIN_BUFFER_MS * Preferences.getBufferingStrategy()).toInt(),
|
||||
(DefaultLoadControl.DEFAULT_MAX_BUFFER_MS * Preferences.getBufferingStrategy()).toInt(),
|
||||
DefaultLoadControl.DEFAULT_BUFFER_FOR_PLAYBACK_MS,
|
||||
DefaultLoadControl.DEFAULT_BUFFER_FOR_PLAYBACK_AFTER_REBUFFER_MS
|
||||
)
|
||||
.build()
|
||||
}
|
||||
|
||||
private fun updateWidget() {
|
||||
val mi = player.currentMediaItem
|
||||
val title = mi?.mediaMetadata?.title?.toString()
|
||||
?: mi?.mediaMetadata?.extras?.getString("title")
|
||||
val artist = mi?.mediaMetadata?.artist?.toString()
|
||||
?: mi?.mediaMetadata?.extras?.getString("artist")
|
||||
val album = mi?.mediaMetadata?.albumTitle?.toString()
|
||||
?: mi?.mediaMetadata?.extras?.getString("album")
|
||||
val extras = mi?.mediaMetadata?.extras
|
||||
val coverId = extras?.getString("coverArtId")
|
||||
val songLink = extras?.getString("assetLinkSong")
|
||||
?: AssetLinkUtil.buildLink(AssetLinkUtil.TYPE_SONG, extras?.getString("id"))
|
||||
val albumLink = extras?.getString("assetLinkAlbum")
|
||||
?: AssetLinkUtil.buildLink(AssetLinkUtil.TYPE_ALBUM, extras?.getString("albumId"))
|
||||
val artistLink = extras?.getString("assetLinkArtist")
|
||||
?: AssetLinkUtil.buildLink(AssetLinkUtil.TYPE_ARTIST, extras?.getString("artistId"))
|
||||
val position = player.currentPosition.takeIf { it != C.TIME_UNSET } ?: 0L
|
||||
val duration = player.duration.takeIf { it != C.TIME_UNSET } ?: 0L
|
||||
WidgetUpdateManager.updateFromState(
|
||||
this,
|
||||
title ?: "",
|
||||
artist ?: "",
|
||||
album ?: "",
|
||||
coverId,
|
||||
player.isPlaying,
|
||||
player.shuffleModeEnabled,
|
||||
player.repeatMode,
|
||||
position,
|
||||
duration,
|
||||
songLink,
|
||||
albumLink,
|
||||
artistLink
|
||||
)
|
||||
}
|
||||
|
||||
private fun scheduleWidgetUpdates() {
|
||||
if (widgetUpdateScheduled) return
|
||||
widgetUpdateHandler.postDelayed(widgetUpdateRunnable, WIDGET_UPDATE_INTERVAL_MS)
|
||||
widgetUpdateScheduled = true
|
||||
}
|
||||
|
||||
private fun stopWidgetUpdates() {
|
||||
if (!widgetUpdateScheduled) return
|
||||
widgetUpdateHandler.removeCallbacks(widgetUpdateRunnable)
|
||||
widgetUpdateScheduled = false
|
||||
}
|
||||
|
||||
|
||||
private fun getRenderersFactory() = DownloadUtil.buildRenderersFactory(this, false)
|
||||
|
||||
private fun getMediaSourceFactory(): MediaSource.Factory = DynamicMediaSourceFactory(this)
|
||||
}
|
||||
|
||||
private const val WIDGET_UPDATE_INTERVAL_MS = 1000L
|
||||
class MediaService : BaseMediaService()
|
||||
|
||||
@@ -3,8 +3,8 @@ package com.cappielloantonio.tempo.github;
|
||||
import com.cappielloantonio.tempo.github.api.release.ReleaseClient;
|
||||
|
||||
public class Github {
|
||||
private static final String OWNER = "CappielloAntonio";
|
||||
private static final String REPO = "Tempo";
|
||||
private static final String OWNER = "eddyizm";
|
||||
private static final String REPO = "Tempus";
|
||||
private ReleaseClient releaseClient;
|
||||
|
||||
public ReleaseClient getReleaseClient() {
|
||||
|
||||
@@ -7,10 +7,11 @@ public class UpdateUtil {
|
||||
|
||||
public static boolean showUpdateDialog(LatestRelease release) {
|
||||
if (release.getTagName() == null) return false;
|
||||
String remoteTag = release.getTagName().replaceAll("^\\D+", "");
|
||||
|
||||
try {
|
||||
String[] local = BuildConfig.VERSION_NAME.split("\\.");
|
||||
String[] remote = release.getTagName().split("\\.");
|
||||
String[] remote = remoteTag.split("\\.");
|
||||
|
||||
for (int i = 0; i < local.length; i++) {
|
||||
int localPart = Integer.parseInt(local[i]);
|
||||
|
||||
@@ -13,9 +13,11 @@ import com.cappielloantonio.tempo.subsonic.models.Child;
|
||||
import com.cappielloantonio.tempo.subsonic.models.IndexID3;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import retrofit2.Call;
|
||||
import retrofit2.Callback;
|
||||
@@ -312,24 +314,42 @@ public class ArtistRepository {
|
||||
|
||||
App.getSubsonicClientInstance(false)
|
||||
.getBrowsingClient()
|
||||
.getTopSongs(artist.getName(), count)
|
||||
.getArtist(artist.getId())
|
||||
.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().getTopSongs() != null && response.body().getSubsonicResponse().getTopSongs().getSongs() != null) {
|
||||
List<Child> songs = response.body().getSubsonicResponse().getTopSongs().getSongs();
|
||||
if (response.isSuccessful() && response.body() != null &&
|
||||
response.body().getSubsonicResponse().getArtist() != null &&
|
||||
response.body().getSubsonicResponse().getArtist().getAlbums() != null) {
|
||||
|
||||
if (songs != null && !songs.isEmpty()) {
|
||||
Collections.shuffle(songs);
|
||||
List<AlbumID3> albums = response.body().getSubsonicResponse().getArtist().getAlbums();
|
||||
Log.d("ArtistRepository", "Got albums directly: " + albums.size());
|
||||
if (albums.isEmpty()) {
|
||||
Log.d("ArtistRepository", "No albums found in artist response");
|
||||
return;
|
||||
}
|
||||
|
||||
randomSongs.setValue(songs);
|
||||
Collections.shuffle(albums);
|
||||
int[] counts = albums.stream().mapToInt(AlbumID3::getSongCount).toArray();
|
||||
Arrays.parallelPrefix(counts, Integer::sum);
|
||||
int albumLimit = 0;
|
||||
int multiplier = 4; // get more than the limit so we can shuffle them
|
||||
while (albumLimit < albums.size() && counts[albumLimit] < count * multiplier)
|
||||
albumLimit++;
|
||||
Log.d("ArtistRepository", String.format("Retaining %d/%d albums", albumLimit, albums.size()));
|
||||
|
||||
fetchAllAlbumSongsWithCallback(albums.stream().limit(albumLimit).collect(Collectors.toList()), songs -> {
|
||||
Collections.shuffle(songs);
|
||||
randomSongs.setValue(songs.stream().limit(count).collect(Collectors.toList()));
|
||||
});
|
||||
} else {
|
||||
Log.d("ArtistRepository", "Failed to get artist info");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NonNull Call<ApiResponse> call, @NonNull Throwable t) {
|
||||
|
||||
Log.d("ArtistRepository", "Error getting artist info: " + t.getMessage());
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -121,6 +121,15 @@ public class QueueRepository {
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isMediaInQueue(List<Queue> queue, Child media) {
|
||||
if (queue == null || media == null) return false;
|
||||
|
||||
return queue.stream().anyMatch(queueItem ->
|
||||
queueItem != null && media.getId() != null &&
|
||||
queueItem.getId().equals(media.getId())
|
||||
);
|
||||
}
|
||||
|
||||
public void insertAll(List<Child> toAdd, boolean reset, int afterIndex) {
|
||||
try {
|
||||
List<Queue> media = new ArrayList<>();
|
||||
@@ -134,8 +143,14 @@ public class QueueRepository {
|
||||
media = getMediaThreadSafe.getMedia();
|
||||
}
|
||||
|
||||
for (int i = 0; i < toAdd.size(); i++) {
|
||||
Queue queueItem = new Queue(toAdd.get(i));
|
||||
List<Child> filteredToAdd = toAdd;
|
||||
final List<Queue> finalMedia = media;
|
||||
filteredToAdd = toAdd.stream()
|
||||
.filter(child -> !isMediaInQueue(finalMedia, child))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
for (int i = 0; i < filteredToAdd.size(); i++) {
|
||||
Queue queueItem = new Queue(filteredToAdd.get(i));
|
||||
media.add(afterIndex + i, queueItem);
|
||||
}
|
||||
|
||||
|
||||
@@ -100,6 +100,33 @@ public class SongRepository {
|
||||
return randomSongsSample;
|
||||
}
|
||||
|
||||
public MutableLiveData<List<Child>> getRandomSampleWithGenre(int number, Integer fromYear, Integer toYear, String genre) {
|
||||
MutableLiveData<List<Child>> randomSongsSample = new MutableLiveData<>();
|
||||
|
||||
App.getSubsonicClientInstance(false)
|
||||
.getAlbumSongListClient()
|
||||
.getRandomSongs(number, fromYear, toYear, genre)
|
||||
.enqueue(new Callback<ApiResponse>() {
|
||||
@Override
|
||||
public void onResponse(@NonNull Call<ApiResponse> call, @NonNull Response<ApiResponse> response) {
|
||||
List<Child> songs = new ArrayList<>();
|
||||
|
||||
if (response.isSuccessful() && response.body() != null && response.body().getSubsonicResponse().getRandomSongs() != null && response.body().getSubsonicResponse().getRandomSongs().getSongs() != null) {
|
||||
songs.addAll(response.body().getSubsonicResponse().getRandomSongs().getSongs());
|
||||
}
|
||||
|
||||
randomSongsSample.setValue(songs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NonNull Call<ApiResponse> call, @NonNull Throwable t) {
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
return randomSongsSample;
|
||||
}
|
||||
|
||||
public void scrobble(String id, boolean submission) {
|
||||
App.getSubsonicClientInstance(false)
|
||||
.getMediaAnnotationClient()
|
||||
|
||||
@@ -0,0 +1,590 @@
|
||||
package com.cappielloantonio.tempo.service
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.PendingIntent.FLAG_IMMUTABLE
|
||||
import android.app.PendingIntent.FLAG_UPDATE_CURRENT
|
||||
import android.app.TaskStackBuilder
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.net.ConnectivityManager
|
||||
import android.net.Network
|
||||
import android.net.NetworkCapabilities
|
||||
import android.os.Binder
|
||||
import android.os.Bundle
|
||||
import android.os.IBinder
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.util.Log
|
||||
import androidx.media3.common.*
|
||||
import androidx.media3.common.util.UnstableApi
|
||||
import androidx.media3.exoplayer.DefaultLoadControl
|
||||
import androidx.media3.exoplayer.ExoPlayer
|
||||
import androidx.media3.exoplayer.source.MediaSource
|
||||
import androidx.media3.exoplayer.source.ShuffleOrder.DefaultShuffleOrder
|
||||
import androidx.media3.session.*
|
||||
import androidx.media3.session.MediaSession.ControllerInfo
|
||||
import com.cappielloantonio.tempo.R
|
||||
import com.cappielloantonio.tempo.repository.QueueRepository
|
||||
import com.cappielloantonio.tempo.ui.activity.MainActivity
|
||||
import com.cappielloantonio.tempo.util.*
|
||||
import com.cappielloantonio.tempo.widget.WidgetUpdateManager
|
||||
import com.google.common.collect.ImmutableList
|
||||
import com.google.common.util.concurrent.Futures
|
||||
import com.google.common.util.concurrent.ListenableFuture
|
||||
|
||||
@UnstableApi
|
||||
open class BaseMediaService : MediaLibraryService() {
|
||||
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"
|
||||
private const val CUSTOM_COMMAND_TOGGLE_REPEAT_MODE_OFF =
|
||||
"android.media3.session.demo.REPEAT_OFF"
|
||||
private const val CUSTOM_COMMAND_TOGGLE_REPEAT_MODE_ONE =
|
||||
"android.media3.session.demo.REPEAT_ONE"
|
||||
private const val CUSTOM_COMMAND_TOGGLE_REPEAT_MODE_ALL =
|
||||
"android.media3.session.demo.REPEAT_ALL"
|
||||
const val ACTION_BIND_EQUALIZER = "com.cappielloantonio.tempo.service.BIND_EQUALIZER"
|
||||
const val ACTION_EQUALIZER_UPDATED = "com.cappielloantonio.tempo.service.EQUALIZER_UPDATED"
|
||||
}
|
||||
|
||||
protected lateinit var exoplayer: ExoPlayer
|
||||
protected lateinit var mediaLibrarySession: MediaLibrarySession
|
||||
private lateinit var networkCallback: CustomNetworkCallback
|
||||
private lateinit var equalizerManager: EqualizerManager
|
||||
private val widgetUpdateHandler = Handler(Looper.getMainLooper())
|
||||
private var widgetUpdateScheduled = false
|
||||
private val widgetUpdateRunnable = object : Runnable {
|
||||
override fun run() {
|
||||
val player = mediaLibrarySession.player
|
||||
if (!player.isPlaying) {
|
||||
widgetUpdateScheduled = false
|
||||
return
|
||||
}
|
||||
updateWidget(player)
|
||||
widgetUpdateHandler.postDelayed(this, WIDGET_UPDATE_INTERVAL_MS)
|
||||
}
|
||||
}
|
||||
|
||||
private val binder = LocalBinder()
|
||||
|
||||
open fun playerInitHook() {
|
||||
initializeExoPlayer()
|
||||
initializeMediaLibrarySession(exoplayer)
|
||||
initializePlayerListener(exoplayer)
|
||||
setPlayer(null, exoplayer)
|
||||
}
|
||||
|
||||
open fun getMediaLibrarySessionCallback(): MediaLibrarySession.Callback {
|
||||
return CustomMediaLibrarySessionCallback(baseContext)
|
||||
}
|
||||
|
||||
fun updateMediaItems(player: Player) {
|
||||
Log.d(javaClass.toString(), "update items")
|
||||
val n = player.mediaItemCount
|
||||
val k = player.currentMediaItemIndex
|
||||
val current = player.currentPosition
|
||||
val items = (0..n - 1).map { MappingUtil.mapMediaItem(player.getMediaItemAt(it)) }
|
||||
player.clearMediaItems()
|
||||
player.setMediaItems(items, k, current)
|
||||
}
|
||||
|
||||
fun restorePlayerFromQueue(player: Player) {
|
||||
if (player.mediaItemCount > 0) return
|
||||
|
||||
val queueRepository = QueueRepository()
|
||||
val storedQueue = queueRepository.media
|
||||
if (storedQueue.isNullOrEmpty()) return
|
||||
|
||||
val mediaItems = MappingUtil.mapMediaItems(storedQueue)
|
||||
if (mediaItems.isEmpty()) return
|
||||
|
||||
val lastIndex = try {
|
||||
queueRepository.lastPlayedMediaIndex
|
||||
} catch (_: Exception) {
|
||||
0
|
||||
}.coerceIn(0, mediaItems.size - 1)
|
||||
|
||||
val lastPosition = try {
|
||||
queueRepository.lastPlayedMediaTimestamp
|
||||
} catch (_: Exception) {
|
||||
0L
|
||||
}.let { if (it < 0L) 0L else it }
|
||||
|
||||
player.setMediaItems(mediaItems, lastIndex, lastPosition)
|
||||
player.prepare()
|
||||
updateWidget(player)
|
||||
}
|
||||
|
||||
fun initializePlayerListener(player: Player) {
|
||||
player.addListener(object : Player.Listener {
|
||||
override fun onMediaItemTransition(mediaItem: MediaItem?, reason: Int) {
|
||||
Log.d(javaClass.toString(), "onMediaItemTransition" + player.currentMediaItemIndex)
|
||||
if (mediaItem == null) return
|
||||
|
||||
if (reason == Player.MEDIA_ITEM_TRANSITION_REASON_SEEK || reason == Player.MEDIA_ITEM_TRANSITION_REASON_AUTO) {
|
||||
MediaManager.setLastPlayedTimestamp(mediaItem)
|
||||
}
|
||||
updateWidget(player)
|
||||
}
|
||||
|
||||
override fun onTracksChanged(tracks: Tracks) {
|
||||
Log.d(javaClass.toString(), "onTracksChanged " + player.currentMediaItemIndex)
|
||||
ReplayGainUtil.setReplayGain(player, tracks)
|
||||
val currentMediaItem = player.currentMediaItem
|
||||
if (currentMediaItem != null) {
|
||||
val item = MappingUtil.mapMediaItem(currentMediaItem)
|
||||
if (item.mediaMetadata.extras != null)
|
||||
MediaManager.scrobble(item, false)
|
||||
|
||||
if (player.nextMediaItemIndex == C.INDEX_UNSET)
|
||||
MediaManager.continuousPlay(player.currentMediaItem)
|
||||
}
|
||||
|
||||
if (player is ExoPlayer) {
|
||||
// https://stackoverflow.com/questions/56937283/exoplayer-shuffle-doesnt-reproduce-all-the-songs
|
||||
if (MediaManager.justStarted.get()) {
|
||||
Log.d(javaClass.toString(), "update shuffle order")
|
||||
MediaManager.justStarted.set(false)
|
||||
val shuffledList = IntArray(player.mediaItemCount) { i -> i }
|
||||
shuffledList.shuffle()
|
||||
val index = shuffledList.indexOf(player.currentMediaItemIndex)
|
||||
// swap current media index to the first index
|
||||
if (index > -1 && shuffledList.isNotEmpty()) {
|
||||
val tmp = shuffledList[0]
|
||||
shuffledList[0] = shuffledList[index]
|
||||
shuffledList[index] = tmp
|
||||
}
|
||||
player.shuffleOrder =
|
||||
DefaultShuffleOrder(shuffledList, kotlin.random.Random.nextLong())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onIsPlayingChanged(isPlaying: Boolean) {
|
||||
Log.d(javaClass.toString(), "onIsPlayingChanged " + player.currentMediaItemIndex)
|
||||
if (!isPlaying) {
|
||||
MediaManager.setPlayingPausedTimestamp(
|
||||
player.currentMediaItem,
|
||||
player.currentPosition
|
||||
)
|
||||
} else {
|
||||
MediaManager.scrobble(player.currentMediaItem, false)
|
||||
}
|
||||
if (isPlaying) {
|
||||
scheduleWidgetUpdates()
|
||||
} else {
|
||||
stopWidgetUpdates()
|
||||
}
|
||||
updateWidget(player)
|
||||
}
|
||||
|
||||
override fun onPlaybackStateChanged(playbackState: Int) {
|
||||
Log.d(javaClass.toString(), "onPlaybackStateChanged")
|
||||
super.onPlaybackStateChanged(playbackState)
|
||||
if (!player.hasNextMediaItem() &&
|
||||
playbackState == Player.STATE_ENDED &&
|
||||
player.mediaMetadata.extras?.getString("type") == Constants.MEDIA_TYPE_MUSIC
|
||||
) {
|
||||
MediaManager.scrobble(player.currentMediaItem, true)
|
||||
MediaManager.saveChronology(player.currentMediaItem)
|
||||
}
|
||||
updateWidget(player)
|
||||
}
|
||||
|
||||
override fun onPositionDiscontinuity(
|
||||
oldPosition: Player.PositionInfo,
|
||||
newPosition: Player.PositionInfo,
|
||||
reason: Int
|
||||
) {
|
||||
Log.d(javaClass.toString(), "onPositionDiscontinuity")
|
||||
super.onPositionDiscontinuity(oldPosition, newPosition, reason)
|
||||
|
||||
if (reason == Player.DISCONTINUITY_REASON_AUTO_TRANSITION) {
|
||||
if (oldPosition.mediaItem?.mediaMetadata?.extras?.getString("type") == Constants.MEDIA_TYPE_MUSIC) {
|
||||
MediaManager.scrobble(oldPosition.mediaItem, true)
|
||||
MediaManager.saveChronology(oldPosition.mediaItem)
|
||||
}
|
||||
|
||||
if (newPosition.mediaItem?.mediaMetadata?.extras?.getString("type") == Constants.MEDIA_TYPE_MUSIC) {
|
||||
MediaManager.setLastPlayedTimestamp(newPosition.mediaItem)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onShuffleModeEnabledChanged(shuffleModeEnabled: Boolean) {
|
||||
Preferences.setShuffleModeEnabled(shuffleModeEnabled)
|
||||
}
|
||||
|
||||
override fun onRepeatModeChanged(repeatMode: Int) {
|
||||
Preferences.setRepeatMode(repeatMode)
|
||||
}
|
||||
|
||||
override fun onAudioSessionIdChanged(audioSessionId: Int) {
|
||||
Log.d(javaClass.toString(), "onAudioSessionIdChanged")
|
||||
attachEqualizerIfPossible(audioSessionId)
|
||||
}
|
||||
})
|
||||
if (player.isPlaying) {
|
||||
scheduleWidgetUpdates()
|
||||
}
|
||||
}
|
||||
|
||||
fun setPlayer(oldPlayer: Player?, newPlayer: Player) {
|
||||
if (oldPlayer === newPlayer) return
|
||||
if (oldPlayer != null) {
|
||||
val currentQueue = getQueueFromPlayer(oldPlayer)
|
||||
val currentIndex = oldPlayer.currentMediaItemIndex
|
||||
val currentPosition = oldPlayer.currentPosition
|
||||
val isPlaying = oldPlayer.playWhenReady
|
||||
oldPlayer.stop()
|
||||
newPlayer.setMediaItems(currentQueue, currentIndex, currentPosition)
|
||||
newPlayer.playWhenReady = isPlaying
|
||||
newPlayer.prepare()
|
||||
}
|
||||
mediaLibrarySession.player = newPlayer
|
||||
}
|
||||
|
||||
open fun releasePlayers() {
|
||||
exoplayer.release()
|
||||
}
|
||||
|
||||
fun getQueueFromPlayer(player: Player): List<MediaItem> {
|
||||
return (0..player.mediaItemCount - 1).map(player::getMediaItemAt)
|
||||
}
|
||||
|
||||
override fun onTaskRemoved(rootIntent: Intent?) {
|
||||
val player = mediaLibrarySession.player
|
||||
|
||||
if (!player.playWhenReady || player.mediaItemCount == 0) {
|
||||
stopSelf()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
|
||||
playerInitHook()
|
||||
initializeEqualizerManager()
|
||||
initializeNetworkListener()
|
||||
restorePlayerFromQueue(mediaLibrarySession.player)
|
||||
}
|
||||
|
||||
override fun onGetSession(controllerInfo: ControllerInfo): MediaLibrarySession {
|
||||
return mediaLibrarySession
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
releaseNetworkCallback()
|
||||
equalizerManager.release()
|
||||
stopWidgetUpdates()
|
||||
releasePlayers()
|
||||
mediaLibrarySession.release()
|
||||
super.onDestroy()
|
||||
}
|
||||
|
||||
override fun onBind(intent: Intent?): IBinder? {
|
||||
// Check if the intent is for our custom equalizer binder
|
||||
if (intent?.action == ACTION_BIND_EQUALIZER) {
|
||||
return binder
|
||||
}
|
||||
// Otherwise, handle it as a normal MediaLibraryService connection
|
||||
return super.onBind(intent)
|
||||
}
|
||||
|
||||
private fun initializeExoPlayer() {
|
||||
exoplayer = ExoPlayer.Builder(this)
|
||||
.setRenderersFactory(getRenderersFactory())
|
||||
.setMediaSourceFactory(getMediaSourceFactory())
|
||||
.setAudioAttributes(AudioAttributes.DEFAULT, true)
|
||||
.setHandleAudioBecomingNoisy(true)
|
||||
.setWakeMode(C.WAKE_MODE_NETWORK)
|
||||
.setLoadControl(initializeLoadControl())
|
||||
.build()
|
||||
|
||||
exoplayer.shuffleModeEnabled = Preferences.isShuffleModeEnabled()
|
||||
exoplayer.repeatMode = Preferences.getRepeatMode()
|
||||
}
|
||||
|
||||
private fun initializeEqualizerManager() {
|
||||
equalizerManager = EqualizerManager()
|
||||
val audioSessionId = exoplayer.audioSessionId
|
||||
attachEqualizerIfPossible(audioSessionId)
|
||||
}
|
||||
|
||||
private fun initializeMediaLibrarySession(player: Player) {
|
||||
Log.d(javaClass.toString(), "initializeMediaLibrarySession")
|
||||
val sessionActivityPendingIntent =
|
||||
TaskStackBuilder.create(this).run {
|
||||
addNextIntent(Intent(baseContext, MainActivity::class.java))
|
||||
getPendingIntent(0, FLAG_IMMUTABLE or FLAG_UPDATE_CURRENT)
|
||||
}
|
||||
|
||||
mediaLibrarySession =
|
||||
MediaLibrarySession.Builder(this, player, getMediaLibrarySessionCallback())
|
||||
.setSessionActivity(sessionActivityPendingIntent)
|
||||
.build()
|
||||
}
|
||||
|
||||
private fun initializeNetworkListener() {
|
||||
networkCallback = CustomNetworkCallback()
|
||||
getSystemService(ConnectivityManager::class.java).registerDefaultNetworkCallback(
|
||||
networkCallback
|
||||
)
|
||||
updateMediaItems(mediaLibrarySession.player)
|
||||
}
|
||||
|
||||
private fun initializeLoadControl(): DefaultLoadControl {
|
||||
return DefaultLoadControl.Builder()
|
||||
.setBufferDurationsMs(
|
||||
(DefaultLoadControl.DEFAULT_MIN_BUFFER_MS * Preferences.getBufferingStrategy()).toInt(),
|
||||
(DefaultLoadControl.DEFAULT_MAX_BUFFER_MS * Preferences.getBufferingStrategy()).toInt(),
|
||||
DefaultLoadControl.DEFAULT_BUFFER_FOR_PLAYBACK_MS,
|
||||
DefaultLoadControl.DEFAULT_BUFFER_FOR_PLAYBACK_AFTER_REBUFFER_MS
|
||||
)
|
||||
.build()
|
||||
}
|
||||
|
||||
private fun releaseNetworkCallback() {
|
||||
getSystemService(ConnectivityManager::class.java).unregisterNetworkCallback(networkCallback)
|
||||
}
|
||||
|
||||
private fun updateWidget(player: Player) {
|
||||
val mi = player.currentMediaItem
|
||||
val title = mi?.mediaMetadata?.title?.toString()
|
||||
?: mi?.mediaMetadata?.extras?.getString("title")
|
||||
val artist = mi?.mediaMetadata?.artist?.toString()
|
||||
?: mi?.mediaMetadata?.extras?.getString("artist")
|
||||
val album = mi?.mediaMetadata?.albumTitle?.toString()
|
||||
?: mi?.mediaMetadata?.extras?.getString("album")
|
||||
val extras = mi?.mediaMetadata?.extras
|
||||
val coverId = extras?.getString("coverArtId")
|
||||
val songLink = extras?.getString("assetLinkSong")
|
||||
?: AssetLinkUtil.buildLink(AssetLinkUtil.TYPE_SONG, extras?.getString("id"))
|
||||
val albumLink = extras?.getString("assetLinkAlbum")
|
||||
?: AssetLinkUtil.buildLink(AssetLinkUtil.TYPE_ALBUM, extras?.getString("albumId"))
|
||||
val artistLink = extras?.getString("assetLinkArtist")
|
||||
?: AssetLinkUtil.buildLink(AssetLinkUtil.TYPE_ARTIST, extras?.getString("artistId"))
|
||||
val position = player.currentPosition.takeIf { it != C.TIME_UNSET } ?: 0L
|
||||
val duration = player.duration.takeIf { it != C.TIME_UNSET } ?: 0L
|
||||
WidgetUpdateManager.updateFromState(
|
||||
this,
|
||||
title ?: "",
|
||||
artist ?: "",
|
||||
album ?: "",
|
||||
coverId,
|
||||
player.isPlaying,
|
||||
player.shuffleModeEnabled,
|
||||
player.repeatMode,
|
||||
position,
|
||||
duration,
|
||||
songLink,
|
||||
albumLink,
|
||||
artistLink
|
||||
)
|
||||
}
|
||||
|
||||
private fun scheduleWidgetUpdates() {
|
||||
if (widgetUpdateScheduled) return
|
||||
widgetUpdateHandler.postDelayed(widgetUpdateRunnable, WIDGET_UPDATE_INTERVAL_MS)
|
||||
widgetUpdateScheduled = true
|
||||
}
|
||||
|
||||
private fun stopWidgetUpdates() {
|
||||
if (!widgetUpdateScheduled) return
|
||||
widgetUpdateHandler.removeCallbacks(widgetUpdateRunnable)
|
||||
widgetUpdateScheduled = false
|
||||
}
|
||||
|
||||
private fun attachEqualizerIfPossible(audioSessionId: Int): Boolean {
|
||||
if (audioSessionId == 0 || audioSessionId == -1) return false
|
||||
val attached = equalizerManager.attachToSession(audioSessionId)
|
||||
if (attached) {
|
||||
val enabled = Preferences.isEqualizerEnabled()
|
||||
equalizerManager.setEnabled(enabled)
|
||||
val bands = equalizerManager.getNumberOfBands()
|
||||
val savedLevels = Preferences.getEqualizerBandLevels(bands)
|
||||
for (i in 0 until bands) {
|
||||
equalizerManager.setBandLevel(i.toShort(), savedLevels[i])
|
||||
}
|
||||
sendBroadcast(Intent(ACTION_EQUALIZER_UPDATED))
|
||||
}
|
||||
return attached
|
||||
}
|
||||
|
||||
private fun getRenderersFactory() = DownloadUtil.buildRenderersFactory(this, false)
|
||||
|
||||
private fun getMediaSourceFactory(): MediaSource.Factory = DynamicMediaSourceFactory(this)
|
||||
|
||||
@UnstableApi
|
||||
private class CustomMediaLibrarySessionCallback : MediaLibrarySession.Callback {
|
||||
private val shuffleCommands: List<CommandButton>
|
||||
private val repeatCommands: List<CommandButton>
|
||||
|
||||
constructor(ctx: Context) {
|
||||
shuffleCommands = listOf(
|
||||
CUSTOM_COMMAND_TOGGLE_SHUFFLE_MODE_ON,
|
||||
CUSTOM_COMMAND_TOGGLE_SHUFFLE_MODE_OFF
|
||||
)
|
||||
.map { getShuffleCommandButton(SessionCommand(it, Bundle.EMPTY), ctx) }
|
||||
repeatCommands = listOf(
|
||||
CUSTOM_COMMAND_TOGGLE_REPEAT_MODE_OFF,
|
||||
CUSTOM_COMMAND_TOGGLE_REPEAT_MODE_ONE,
|
||||
CUSTOM_COMMAND_TOGGLE_REPEAT_MODE_ALL
|
||||
)
|
||||
.map { getRepeatCommandButton(SessionCommand(it, Bundle.EMPTY), ctx) }
|
||||
}
|
||||
|
||||
override fun onConnect(
|
||||
session: MediaSession,
|
||||
controller: ControllerInfo
|
||||
): MediaSession.ConnectionResult {
|
||||
val connectionResult = super.onConnect(session, controller)
|
||||
val availableSessionCommands = connectionResult.availableSessionCommands.buildUpon()
|
||||
|
||||
(shuffleCommands + repeatCommands).forEach { commandButton ->
|
||||
commandButton.sessionCommand?.let { availableSessionCommands.add(it) }
|
||||
}
|
||||
|
||||
val result = MediaSession.ConnectionResult.AcceptedResultBuilder(session)
|
||||
.setAvailableSessionCommands(availableSessionCommands.build())
|
||||
.setAvailablePlayerCommands(connectionResult.availablePlayerCommands)
|
||||
.setMediaButtonPreferences(buildCustomLayout(session.player))
|
||||
.build()
|
||||
return result
|
||||
}
|
||||
|
||||
override fun onCustomCommand(
|
||||
session: MediaSession,
|
||||
controller: ControllerInfo,
|
||||
customCommand: SessionCommand,
|
||||
args: Bundle
|
||||
): ListenableFuture<SessionResult> {
|
||||
Log.d(javaClass.toString(), "onCustomCommand")
|
||||
when (customCommand.customAction) {
|
||||
CUSTOM_COMMAND_TOGGLE_SHUFFLE_MODE_ON -> session.player.shuffleModeEnabled = true
|
||||
CUSTOM_COMMAND_TOGGLE_SHUFFLE_MODE_OFF -> session.player.shuffleModeEnabled = false
|
||||
CUSTOM_COMMAND_TOGGLE_REPEAT_MODE_OFF,
|
||||
CUSTOM_COMMAND_TOGGLE_REPEAT_MODE_ALL,
|
||||
CUSTOM_COMMAND_TOGGLE_REPEAT_MODE_ONE -> {
|
||||
val nextMode = when (session.player.repeatMode) {
|
||||
Player.REPEAT_MODE_ONE -> Player.REPEAT_MODE_ALL
|
||||
Player.REPEAT_MODE_OFF -> Player.REPEAT_MODE_ONE
|
||||
else -> Player.REPEAT_MODE_OFF
|
||||
}
|
||||
session.player.repeatMode = nextMode
|
||||
}
|
||||
}
|
||||
|
||||
session.setMediaButtonPreferences(buildCustomLayout(session.player))
|
||||
return Futures.immediateFuture(SessionResult(SessionResult.RESULT_SUCCESS))
|
||||
}
|
||||
|
||||
override fun onAddMediaItems(
|
||||
mediaSession: MediaSession,
|
||||
controller: ControllerInfo,
|
||||
mediaItems: List<MediaItem>
|
||||
): ListenableFuture<List<MediaItem>> {
|
||||
Log.d(javaClass.toString(), "onAddMediaItems")
|
||||
val updatedMediaItems = mediaItems.map { mediaItem ->
|
||||
val mediaMetadata = mediaItem.mediaMetadata
|
||||
val newMetadata = mediaMetadata.buildUpon()
|
||||
.setArtist(
|
||||
if (mediaMetadata.artist != null) mediaMetadata.artist
|
||||
else mediaMetadata.extras?.getString("uri") ?: ""
|
||||
)
|
||||
.build()
|
||||
|
||||
mediaItem.buildUpon()
|
||||
.setUri(mediaItem.requestMetadata.mediaUri)
|
||||
.setMediaMetadata(newMetadata)
|
||||
.setMimeType(MimeTypes.BASE_TYPE_AUDIO)
|
||||
.build()
|
||||
}
|
||||
return Futures.immediateFuture(updatedMediaItems)
|
||||
}
|
||||
|
||||
@SuppressLint("PrivateResource")
|
||||
private fun getShuffleCommandButton(
|
||||
sessionCommand: SessionCommand,
|
||||
ctx: Context
|
||||
): CommandButton {
|
||||
val isOn = sessionCommand.customAction == CUSTOM_COMMAND_TOGGLE_SHUFFLE_MODE_ON
|
||||
return CommandButton.Builder(if (isOn) CommandButton.ICON_SHUFFLE_OFF else CommandButton.ICON_SHUFFLE_ON)
|
||||
.setSessionCommand(sessionCommand)
|
||||
.setDisplayName(
|
||||
ctx.getString(
|
||||
if (isOn) R.string.exo_controls_shuffle_on_description
|
||||
else R.string.exo_controls_shuffle_off_description
|
||||
)
|
||||
)
|
||||
.build()
|
||||
}
|
||||
|
||||
@SuppressLint("PrivateResource")
|
||||
private fun getRepeatCommandButton(
|
||||
sessionCommand: SessionCommand,
|
||||
ctx: Context
|
||||
): CommandButton {
|
||||
val icon = when (sessionCommand.customAction) {
|
||||
CUSTOM_COMMAND_TOGGLE_REPEAT_MODE_ONE -> CommandButton.ICON_REPEAT_ONE
|
||||
CUSTOM_COMMAND_TOGGLE_REPEAT_MODE_ALL -> CommandButton.ICON_REPEAT_ALL
|
||||
else -> CommandButton.ICON_REPEAT_OFF
|
||||
}
|
||||
val description = when (sessionCommand.customAction) {
|
||||
CUSTOM_COMMAND_TOGGLE_REPEAT_MODE_ONE -> R.string.exo_controls_repeat_one_description
|
||||
CUSTOM_COMMAND_TOGGLE_REPEAT_MODE_ALL -> R.string.exo_controls_repeat_all_description
|
||||
else -> R.string.exo_controls_repeat_off_description
|
||||
}
|
||||
return CommandButton.Builder(icon)
|
||||
.setSessionCommand(sessionCommand)
|
||||
.setDisplayName(ctx.getString(description))
|
||||
.build()
|
||||
}
|
||||
|
||||
private fun buildCustomLayout(player: Player): ImmutableList<CommandButton> {
|
||||
val shuffle = shuffleCommands[if (player.shuffleModeEnabled) 1 else 0]
|
||||
val repeat = when (player.repeatMode) {
|
||||
Player.REPEAT_MODE_ONE -> repeatCommands[1]
|
||||
Player.REPEAT_MODE_ALL -> repeatCommands[2]
|
||||
else -> repeatCommands[0]
|
||||
}
|
||||
return ImmutableList.of(shuffle, repeat)
|
||||
}
|
||||
}
|
||||
|
||||
private inner class CustomNetworkCallback : ConnectivityManager.NetworkCallback() {
|
||||
var wasWifi = false
|
||||
|
||||
init {
|
||||
val manager = getSystemService(ConnectivityManager::class.java)
|
||||
val network = manager.activeNetwork
|
||||
val capabilities = manager.getNetworkCapabilities(network)
|
||||
if (capabilities != null)
|
||||
wasWifi = capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)
|
||||
}
|
||||
|
||||
override fun onCapabilitiesChanged(
|
||||
network: Network,
|
||||
networkCapabilities: NetworkCapabilities
|
||||
) {
|
||||
val isWifi = networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)
|
||||
if (isWifi != wasWifi) {
|
||||
wasWifi = isWifi
|
||||
widgetUpdateHandler.post {
|
||||
updateMediaItems(mediaLibrarySession.player)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inner class LocalBinder : Binder() {
|
||||
fun getEqualizerManager(): EqualizerManager {
|
||||
return equalizerManager
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private const val WIDGET_UPDATE_INTERVAL_MS = 1000L
|
||||
|
||||
@@ -36,10 +36,12 @@ import com.google.common.util.concurrent.MoreExecutors;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
public class MediaManager {
|
||||
private static final String TAG = "MediaManager";
|
||||
private static WeakReference<MediaBrowser> attachedBrowserRef = new WeakReference<>(null);
|
||||
public static AtomicBoolean justStarted = new AtomicBoolean(false);
|
||||
|
||||
public static void registerPlaybackObserver(
|
||||
ListenableFuture<MediaBrowser> browserFuture,
|
||||
@@ -179,8 +181,8 @@ public class MediaManager {
|
||||
try {
|
||||
if (mediaBrowserListenableFuture.isDone()) {
|
||||
MediaBrowser browser = mediaBrowserListenableFuture.get();
|
||||
browser.clearMediaItems();
|
||||
browser.setMediaItems(MappingUtil.mapMediaItems(media));
|
||||
justStarted.set(true);
|
||||
browser.setMediaItems(MappingUtil.mapMediaItems(media), startIndex, 0);
|
||||
browser.prepare();
|
||||
|
||||
Player.Listener timelineListener = new Player.Listener() {
|
||||
@@ -210,10 +212,11 @@ public class MediaManager {
|
||||
mediaBrowserListenableFuture.addListener(() -> {
|
||||
try {
|
||||
if (mediaBrowserListenableFuture.isDone()) {
|
||||
mediaBrowserListenableFuture.get().clearMediaItems();
|
||||
mediaBrowserListenableFuture.get().setMediaItem(MappingUtil.mapMediaItem(media));
|
||||
mediaBrowserListenableFuture.get().prepare();
|
||||
mediaBrowserListenableFuture.get().play();
|
||||
MediaBrowser browser = mediaBrowserListenableFuture.get();
|
||||
justStarted.set(true);
|
||||
browser.setMediaItem(MappingUtil.mapMediaItem(media));
|
||||
browser.prepare();
|
||||
browser.play();
|
||||
enqueueDatabase(media, true, 0);
|
||||
}
|
||||
} catch (ExecutionException | InterruptedException e) {
|
||||
@@ -229,7 +232,7 @@ public class MediaManager {
|
||||
try {
|
||||
if (mediaBrowserListenableFuture.isDone()) {
|
||||
MediaBrowser mediaBrowser = mediaBrowserListenableFuture.get();
|
||||
mediaBrowser.clearMediaItems();
|
||||
justStarted.set(true);
|
||||
mediaBrowser.setMediaItem(mediaItem);
|
||||
mediaBrowser.prepare();
|
||||
mediaBrowser.play();
|
||||
@@ -247,10 +250,11 @@ public class MediaManager {
|
||||
mediaBrowserListenableFuture.addListener(() -> {
|
||||
try {
|
||||
if (mediaBrowserListenableFuture.isDone()) {
|
||||
mediaBrowserListenableFuture.get().clearMediaItems();
|
||||
mediaBrowserListenableFuture.get().setMediaItem(MappingUtil.mapInternetRadioStation(internetRadioStation));
|
||||
mediaBrowserListenableFuture.get().prepare();
|
||||
mediaBrowserListenableFuture.get().play();
|
||||
MediaBrowser browser = mediaBrowserListenableFuture.get();
|
||||
justStarted.set(true);
|
||||
browser.setMediaItem(MappingUtil.mapInternetRadioStation(internetRadioStation));
|
||||
browser.prepare();
|
||||
browser.play();
|
||||
}
|
||||
} catch (ExecutionException | InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
@@ -264,10 +268,11 @@ public class MediaManager {
|
||||
mediaBrowserListenableFuture.addListener(() -> {
|
||||
try {
|
||||
if (mediaBrowserListenableFuture.isDone()) {
|
||||
mediaBrowserListenableFuture.get().clearMediaItems();
|
||||
mediaBrowserListenableFuture.get().setMediaItem(MappingUtil.mapMediaItem(podcastEpisode));
|
||||
mediaBrowserListenableFuture.get().prepare();
|
||||
mediaBrowserListenableFuture.get().play();
|
||||
MediaBrowser browser = mediaBrowserListenableFuture.get();
|
||||
justStarted.set(true);
|
||||
browser.setMediaItem(MappingUtil.mapMediaItem(podcastEpisode));
|
||||
browser.prepare();
|
||||
browser.play();
|
||||
}
|
||||
} catch (ExecutionException | InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
@@ -281,9 +286,11 @@ public class MediaManager {
|
||||
mediaBrowserListenableFuture.addListener(() -> {
|
||||
try {
|
||||
if (mediaBrowserListenableFuture.isDone()) {
|
||||
if (playImmediatelyAfter && mediaBrowserListenableFuture.get().getNextMediaItemIndex() != -1) {
|
||||
enqueueDatabase(media, false, mediaBrowserListenableFuture.get().getNextMediaItemIndex());
|
||||
mediaBrowserListenableFuture.get().addMediaItems(mediaBrowserListenableFuture.get().getNextMediaItemIndex(), MappingUtil.mapMediaItems(media));
|
||||
Log.e(TAG, "enqueue");
|
||||
MediaBrowser browser = mediaBrowserListenableFuture.get();
|
||||
if (playImmediatelyAfter && browser.getNextMediaItemIndex() != -1) {
|
||||
enqueueDatabase(media, false, browser.getNextMediaItemIndex());
|
||||
browser.addMediaItems(browser.getNextMediaItemIndex(), MappingUtil.mapMediaItems(media));
|
||||
} else {
|
||||
enqueueDatabase(media, false, mediaBrowserListenableFuture.get().getMediaItemCount());
|
||||
mediaBrowserListenableFuture.get().addMediaItems(MappingUtil.mapMediaItems(media));
|
||||
@@ -301,9 +308,11 @@ public class MediaManager {
|
||||
mediaBrowserListenableFuture.addListener(() -> {
|
||||
try {
|
||||
if (mediaBrowserListenableFuture.isDone()) {
|
||||
if (playImmediatelyAfter && mediaBrowserListenableFuture.get().getNextMediaItemIndex() != -1) {
|
||||
enqueueDatabase(media, false, mediaBrowserListenableFuture.get().getNextMediaItemIndex());
|
||||
mediaBrowserListenableFuture.get().addMediaItem(mediaBrowserListenableFuture.get().getNextMediaItemIndex(), MappingUtil.mapMediaItem(media));
|
||||
Log.e(TAG, "enqueue");
|
||||
MediaBrowser browser = mediaBrowserListenableFuture.get();
|
||||
if (playImmediatelyAfter && browser.getNextMediaItemIndex() != -1) {
|
||||
enqueueDatabase(media, false, browser.getNextMediaItemIndex());
|
||||
browser.addMediaItem(browser.getNextMediaItemIndex(), MappingUtil.mapMediaItem(media));
|
||||
} else {
|
||||
enqueueDatabase(media, false, mediaBrowserListenableFuture.get().getMediaItemCount());
|
||||
mediaBrowserListenableFuture.get().addMediaItem(MappingUtil.mapMediaItem(media));
|
||||
@@ -321,8 +330,10 @@ public class MediaManager {
|
||||
mediaBrowserListenableFuture.addListener(() -> {
|
||||
try {
|
||||
if (mediaBrowserListenableFuture.isDone()) {
|
||||
mediaBrowserListenableFuture.get().removeMediaItems(startIndex, endIndex + 1);
|
||||
mediaBrowserListenableFuture.get().addMediaItems(MappingUtil.mapMediaItems(media).subList(startIndex, endIndex + 1));
|
||||
Log.e(TAG, "shuffle");
|
||||
MediaBrowser browser = mediaBrowserListenableFuture.get();
|
||||
browser.removeMediaItems(startIndex, endIndex + 1);
|
||||
browser.addMediaItems(MappingUtil.mapMediaItems(media).subList(startIndex, endIndex + 1));
|
||||
swapDatabase(media);
|
||||
}
|
||||
} catch (ExecutionException | InterruptedException e) {
|
||||
@@ -337,6 +348,7 @@ public class MediaManager {
|
||||
mediaBrowserListenableFuture.addListener(() -> {
|
||||
try {
|
||||
if (mediaBrowserListenableFuture.isDone()) {
|
||||
Log.e(TAG, "swap");
|
||||
mediaBrowserListenableFuture.get().moveMediaItem(from, to);
|
||||
swapDatabase(media);
|
||||
}
|
||||
@@ -352,6 +364,7 @@ public class MediaManager {
|
||||
mediaBrowserListenableFuture.addListener(() -> {
|
||||
try {
|
||||
if (mediaBrowserListenableFuture.isDone()) {
|
||||
Log.e(TAG, "remove");
|
||||
if (mediaBrowserListenableFuture.get().getMediaItemCount() > 1 && mediaBrowserListenableFuture.get().getCurrentMediaItemIndex() != toRemove) {
|
||||
mediaBrowserListenableFuture.get().removeMediaItem(toRemove);
|
||||
removeDatabase(media, toRemove);
|
||||
@@ -371,6 +384,7 @@ public class MediaManager {
|
||||
mediaBrowserListenableFuture.addListener(() -> {
|
||||
try {
|
||||
if (mediaBrowserListenableFuture.isDone()) {
|
||||
Log.e(TAG, "remove range");
|
||||
mediaBrowserListenableFuture.get().removeMediaItems(fromItem, toItem);
|
||||
removeRangeDatabase(media, fromItem, toItem);
|
||||
}
|
||||
@@ -420,6 +434,7 @@ public class MediaManager {
|
||||
@Override
|
||||
public void onChanged(List<Child> media) {
|
||||
if (media != null) {
|
||||
Log.e(TAG, "continuous play");
|
||||
ListenableFuture<MediaBrowser> mediaBrowserListenableFuture = new MediaBrowser.Builder(
|
||||
App.getContext(),
|
||||
new SessionToken(App.getContext(), new ComponentName(App.getContext(), MediaService.class))
|
||||
|
||||
@@ -7,7 +7,7 @@ import java.util.UUID;
|
||||
public class SubsonicPreferences {
|
||||
private String serverUrl;
|
||||
private String username;
|
||||
private String clientName = "Tempo";
|
||||
private String clientName = "Tempus";
|
||||
private SubsonicAuthentication authentication;
|
||||
|
||||
public String getServerUrl() {
|
||||
|
||||
@@ -34,6 +34,11 @@ public class AlbumSongListClient {
|
||||
return albumSongListService.getRandomSongs(subsonic.getParams(), size, fromYear, toYear);
|
||||
}
|
||||
|
||||
public Call<ApiResponse> getRandomSongs(int size, Integer fromYear, Integer toYear, String genre) {
|
||||
Log.d(TAG, "getRandomSongs()");
|
||||
return albumSongListService.getRandomSongs(subsonic.getParams(), size, fromYear, toYear, genre);
|
||||
}
|
||||
|
||||
public Call<ApiResponse> getSongsByGenre(String genre, int count, int offset) {
|
||||
Log.d(TAG, "getSongsByGenre()");
|
||||
return albumSongListService.getSongsByGenre(subsonic.getParams(), genre, count, offset);
|
||||
|
||||
@@ -19,6 +19,9 @@ public interface AlbumSongListService {
|
||||
@GET("getRandomSongs")
|
||||
Call<ApiResponse> getRandomSongs(@QueryMap Map<String, String> params, @Query("size") int size, @Query("fromYear") Integer fromYear, @Query("toYear") Integer toYear);
|
||||
|
||||
@GET("getRandomSongs")
|
||||
Call<ApiResponse> getRandomSongs(@QueryMap Map<String, String> params, @Query("size") int size, @Query("fromYear") Integer fromYear, @Query("toYear") Integer toYear, @Query("genre") String genre);
|
||||
|
||||
@GET("getSongsByGenre")
|
||||
Call<ApiResponse> getSongsByGenre(@QueryMap Map<String, String> params, @Query("genre") String genre, @Query("count") int count, @Query("offset") int offset);
|
||||
|
||||
|
||||
@@ -438,7 +438,7 @@ public class MainActivity extends BaseActivity {
|
||||
}
|
||||
|
||||
private void checkTempoUpdate() {
|
||||
if (BuildConfig.FLAVOR.equals("tempo") && Preferences.showTempoUpdateDialog()) {
|
||||
if (BuildConfig.FLAVOR.equals("tempus") && Preferences.isGithubUpdateEnabled() && Preferences.showTempusUpdateDialog()) {
|
||||
mainViewModel.checkTempoUpdate().observe(this, latestRelease -> {
|
||||
if (latestRelease != null && UpdateUtil.showUpdateDialog(latestRelease)) {
|
||||
GithubTempoUpdateDialog dialog = new GithubTempoUpdateDialog(latestRelease);
|
||||
|
||||
@@ -151,6 +151,9 @@ public class ArtistCatalogueAdapter extends RecyclerView.Adapter<ArtistCatalogue
|
||||
case Constants.ARTIST_ORDER_BY_RANDOM:
|
||||
Collections.shuffle(artists);
|
||||
break;
|
||||
case Constants.ARTIST_ORDER_BY_ALBUM_COUNT:
|
||||
artists.sort(Comparator.comparing(ArtistID3::getAlbumCount).reversed());
|
||||
break;
|
||||
}
|
||||
|
||||
notifyDataSetChanged();
|
||||
|
||||
@@ -73,6 +73,11 @@ public class DiscoverSongAdapter extends RecyclerView.Adapter<DiscoverSongAdapte
|
||||
this.item = item;
|
||||
|
||||
itemView.setOnClickListener(v -> onClick());
|
||||
|
||||
itemView.setOnLongClickListener(v -> {
|
||||
onLongClick();
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
public void onClick() {
|
||||
@@ -82,6 +87,13 @@ public class DiscoverSongAdapter extends RecyclerView.Adapter<DiscoverSongAdapte
|
||||
|
||||
click.onMediaClick(bundle);
|
||||
}
|
||||
|
||||
private boolean onLongClick() {
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putParcelable(Constants.TRACK_OBJECT, songs.get(getBindingAdapterPosition()));
|
||||
click.onMediaLongClick(bundle);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private void startAnimation(ViewHolder holder) {
|
||||
|
||||
@@ -55,7 +55,7 @@ public class GithubTempoUpdateDialog extends DialogFragment {
|
||||
});
|
||||
|
||||
alertDialog.getButton(AlertDialog.BUTTON_NEGATIVE).setOnClickListener(v -> {
|
||||
Preferences.setTempoUpdateReminder();
|
||||
Preferences.setTempusUpdateReminder();
|
||||
Objects.requireNonNull(getDialog()).dismiss();
|
||||
});
|
||||
|
||||
|
||||
@@ -34,6 +34,7 @@ import com.cappielloantonio.tempo.interfaces.ClickCallback;
|
||||
import com.cappielloantonio.tempo.ui.activity.MainActivity;
|
||||
import com.cappielloantonio.tempo.ui.adapter.ArtistCatalogueAdapter;
|
||||
import com.cappielloantonio.tempo.util.Constants;
|
||||
import com.cappielloantonio.tempo.util.Preferences;
|
||||
import com.cappielloantonio.tempo.viewmodel.ArtistCatalogueViewModel;
|
||||
import com.cappielloantonio.tempo.subsonic.models.ArtistID3;
|
||||
|
||||
@@ -114,7 +115,10 @@ public class ArtistCatalogueFragment extends Fragment implements ClickCallback {
|
||||
artistAdapter = new ArtistCatalogueAdapter(this);
|
||||
artistAdapter.setStateRestorationPolicy(RecyclerView.Adapter.StateRestorationPolicy.PREVENT_WHEN_EMPTY);
|
||||
bind.artistCatalogueRecyclerView.setAdapter(artistAdapter);
|
||||
artistCatalogueViewModel.getArtistList().observe(getViewLifecycleOwner(), artistList -> artistAdapter.setItems(artistList));
|
||||
artistCatalogueViewModel.getArtistList().observe(getViewLifecycleOwner(), artistList -> {
|
||||
artistAdapter.setItems(artistList);
|
||||
artistAdapter.sort(Preferences.getArtistSortOrder());
|
||||
});
|
||||
|
||||
bind.artistCatalogueRecyclerView.setOnTouchListener((v, event) -> {
|
||||
hideKeyboard(v);
|
||||
@@ -192,6 +196,9 @@ public class ArtistCatalogueFragment extends Fragment implements ClickCallback {
|
||||
} else if (menuItem.getItemId() == R.id.menu_artist_sort_random) {
|
||||
artistAdapter.sort(Constants.ARTIST_ORDER_BY_RANDOM);
|
||||
return true;
|
||||
} else if (menuItem.getItemId() == R.id.menu_artist_sort_album_count) {
|
||||
artistAdapter.sort(Constants.ARTIST_ORDER_BY_ALBUM_COUNT);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
@@ -188,8 +188,6 @@ public class ArtistPageFragment extends Fragment implements ClickCallback {
|
||||
} else {
|
||||
if (bind != null)
|
||||
bind.artistPageTopSongsSector.setVisibility(!songs.isEmpty() ? View.VISIBLE : View.GONE);
|
||||
if (bind != null)
|
||||
bind.artistPageShuffleButton.setEnabled(!songs.isEmpty());
|
||||
songHorizontalAdapter.setItems(songs);
|
||||
reapplyPlayback();
|
||||
}
|
||||
|
||||
@@ -117,14 +117,12 @@ public class DownloadFragment extends Fragment implements ClickCallback {
|
||||
if (songs.isEmpty()) {
|
||||
if (bind != null) {
|
||||
bind.emptyDownloadLayout.setVisibility(View.VISIBLE);
|
||||
bind.fragmentDownloadNestedScrollView.setVisibility(View.GONE);
|
||||
bind.downloadDownloadedSector.setVisibility(View.GONE);
|
||||
bind.downloadedGroupByImageView.setVisibility(View.GONE);
|
||||
}
|
||||
} else {
|
||||
if (bind != null) {
|
||||
bind.emptyDownloadLayout.setVisibility(View.GONE);
|
||||
bind.fragmentDownloadNestedScrollView.setVisibility(View.VISIBLE);
|
||||
bind.downloadDownloadedSector.setVisibility(View.VISIBLE);
|
||||
bind.downloadedGroupByImageView.setVisibility(View.VISIBLE);
|
||||
|
||||
|
||||
@@ -3,7 +3,9 @@ package com.cappielloantonio.tempo.ui.fragment
|
||||
import android.content.ComponentName
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.IntentFilter
|
||||
import android.content.ServiceConnection
|
||||
import android.content.BroadcastReceiver
|
||||
import android.os.Bundle
|
||||
import android.os.IBinder
|
||||
import android.view.Gravity
|
||||
@@ -12,10 +14,12 @@ import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.*
|
||||
import androidx.annotation.OptIn
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.media3.common.util.UnstableApi
|
||||
import com.cappielloantonio.tempo.R
|
||||
import com.cappielloantonio.tempo.service.EqualizerManager
|
||||
import com.cappielloantonio.tempo.service.BaseMediaService
|
||||
import com.cappielloantonio.tempo.service.MediaService
|
||||
import com.cappielloantonio.tempo.util.Preferences
|
||||
|
||||
@@ -28,10 +32,21 @@ class EqualizerFragment : Fragment() {
|
||||
private lateinit var safeSpace: Space
|
||||
private val bandSeekBars = mutableListOf<SeekBar>()
|
||||
|
||||
private var receiverRegistered = false
|
||||
private val equalizerUpdatedReceiver = object : BroadcastReceiver() {
|
||||
@OptIn(UnstableApi::class)
|
||||
override fun onReceive(context: Context?, intent: Intent?) {
|
||||
if (intent?.action == BaseMediaService.ACTION_EQUALIZER_UPDATED) {
|
||||
initUI()
|
||||
restoreEqualizerPreferences()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val connection = object : ServiceConnection {
|
||||
@OptIn(UnstableApi::class)
|
||||
override fun onServiceConnected(className: ComponentName, service: IBinder) {
|
||||
val binder = service as MediaService.LocalBinder
|
||||
val binder = service as BaseMediaService.LocalBinder
|
||||
equalizerManager = binder.getEqualizerManager()
|
||||
initUI()
|
||||
restoreEqualizerPreferences()
|
||||
@@ -46,15 +61,32 @@ class EqualizerFragment : Fragment() {
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
Intent(requireContext(), MediaService::class.java).also { intent ->
|
||||
intent.action = MediaService.ACTION_BIND_EQUALIZER
|
||||
intent.action = BaseMediaService.ACTION_BIND_EQUALIZER
|
||||
requireActivity().bindService(intent, connection, Context.BIND_AUTO_CREATE)
|
||||
}
|
||||
if (!receiverRegistered) {
|
||||
ContextCompat.registerReceiver(
|
||||
requireContext(),
|
||||
equalizerUpdatedReceiver,
|
||||
IntentFilter(BaseMediaService.ACTION_EQUALIZER_UPDATED),
|
||||
ContextCompat.RECEIVER_NOT_EXPORTED
|
||||
)
|
||||
receiverRegistered = true
|
||||
}
|
||||
}
|
||||
|
||||
override fun onStop() {
|
||||
super.onStop()
|
||||
requireActivity().unbindService(connection)
|
||||
equalizerManager = null
|
||||
if (receiverRegistered) {
|
||||
try {
|
||||
requireContext().unregisterReceiver(equalizerUpdatedReceiver)
|
||||
} catch (_: Exception) {
|
||||
// ignore if not registered
|
||||
}
|
||||
receiverRegistered = false
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
@@ -234,4 +266,4 @@ class EqualizerFragment : Fragment() {
|
||||
}
|
||||
|
||||
private fun Int.dpToPx(context: Context): Int =
|
||||
(this * context.resources.displayMetrics.density).toInt()
|
||||
(this * context.resources.displayMetrics.density).toInt()
|
||||
|
||||
@@ -2,6 +2,7 @@ package com.cappielloantonio.tempo.ui.fragment;
|
||||
|
||||
import android.content.ComponentName;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
@@ -72,6 +73,16 @@ public class PlayerQueueFragment extends Fragment implements ClickCallback {
|
||||
super.onResume();
|
||||
setMediaBrowserListenableFuture();
|
||||
updateNowPlayingItem();
|
||||
mediaBrowserListenableFuture.addListener(() -> {
|
||||
try {
|
||||
long position = mediaBrowserListenableFuture.get().getCurrentMediaItemIndex();
|
||||
requireActivity().runOnUiThread(() -> {
|
||||
bind.playerQueueRecyclerView.scrollToPosition((int) position);
|
||||
});
|
||||
} catch (Exception e) {
|
||||
Log.e("PlayerQueueFragment", "Failed to get mediaBrowserListenableFuture in onResume", e);
|
||||
}
|
||||
}, MoreExecutors.directExecutor());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -29,6 +29,7 @@ import androidx.navigation.NavOptions;
|
||||
import androidx.navigation.fragment.NavHostFragment;
|
||||
import androidx.preference.ListPreference;
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceCategory;
|
||||
import androidx.preference.PreferenceFragmentCompat;
|
||||
import androidx.preference.SwitchPreference;
|
||||
|
||||
@@ -77,6 +78,13 @@ public class SettingsFragment extends PreferenceFragmentCompat {
|
||||
result -> {}
|
||||
);
|
||||
|
||||
if (!BuildConfig.FLAVOR.equals("tempus")) {
|
||||
PreferenceCategory githubUpdateCategory = findPreference("settings_github_update_category_key");
|
||||
if (githubUpdateCategory != null) {
|
||||
getPreferenceScreen().removePreference(githubUpdateCategory);
|
||||
}
|
||||
}
|
||||
|
||||
directoryPickerLauncher = registerForActivityResult(
|
||||
new ActivityResultContracts.StartActivityForResult(),
|
||||
result -> {
|
||||
|
||||
@@ -189,7 +189,7 @@ public class SongListPageFragment extends Fragment implements ClickCallback {
|
||||
|
||||
bind.songListShuffleImageView.setOnClickListener(v -> {
|
||||
Collections.shuffle(songs);
|
||||
MediaManager.startQueue(mediaBrowserListenableFuture, songs.subList(0, Math.min(25, songs.size())), 0);
|
||||
MediaManager.startQueue(mediaBrowserListenableFuture, songs.subList(0, Math.min(500, songs.size())), 0);
|
||||
activity.setBottomSheetInPeek(true);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -89,6 +89,9 @@ public class ArtistBottomSheetDialog extends BottomSheetDialogFragment implement
|
||||
ArtistRepository artistRepository = new ArtistRepository();
|
||||
|
||||
artistRepository.getInstantMix(artist, 20).observe(getViewLifecycleOwner(), songs -> {
|
||||
// navidrome may return null for this
|
||||
if (songs == null)
|
||||
return;
|
||||
MusicUtil.ratingFilter(songs);
|
||||
|
||||
if (!songs.isEmpty()) {
|
||||
|
||||
@@ -17,6 +17,7 @@ import androidx.media3.common.util.UnstableApi;
|
||||
import com.cappielloantonio.tempo.R;
|
||||
import com.cappielloantonio.tempo.glide.CustomGlideRequest;
|
||||
import com.cappielloantonio.tempo.subsonic.models.Share;
|
||||
import com.cappielloantonio.tempo.subsonic.models.Child;
|
||||
import com.cappielloantonio.tempo.ui.dialog.ShareUpdateDialog;
|
||||
import com.cappielloantonio.tempo.util.Constants;
|
||||
import com.cappielloantonio.tempo.util.UIUtil;
|
||||
@@ -24,6 +25,8 @@ import com.cappielloantonio.tempo.viewmodel.HomeViewModel;
|
||||
import com.cappielloantonio.tempo.viewmodel.ShareBottomSheetViewModel;
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialogFragment;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@UnstableApi
|
||||
public class ShareBottomSheetDialog extends BottomSheetDialogFragment implements View.OnClickListener {
|
||||
|
||||
@@ -50,8 +53,15 @@ public class ShareBottomSheetDialog extends BottomSheetDialogFragment implements
|
||||
private void init(View view) {
|
||||
ImageView shareCover = view.findViewById(R.id.share_cover_image_view);
|
||||
|
||||
String coverArtId = null;
|
||||
List<Child> entries = shareBottomSheetViewModel.getShare().getEntries();
|
||||
|
||||
if (entries != null && !entries.isEmpty()) {
|
||||
coverArtId = entries.get(0).getCoverArtId();
|
||||
}
|
||||
|
||||
CustomGlideRequest.Builder
|
||||
.from(requireContext(), shareBottomSheetViewModel.getShare().getEntries().get(0).getCoverArtId(), CustomGlideRequest.ResourceType.Unknown)
|
||||
.from(requireContext(), coverArtId, CustomGlideRequest.ResourceType.Unknown)
|
||||
.build()
|
||||
.into(shareCover);
|
||||
|
||||
|
||||
@@ -40,6 +40,7 @@ object Constants {
|
||||
const val ARTIST_STARRED = "ARTIST_STARRED"
|
||||
const val ARTIST_ORDER_BY_NAME = "ARTIST_ORDER_BY_NAME"
|
||||
const val ARTIST_ORDER_BY_RANDOM = "ARTIST_ORDER_BY_RANDOM"
|
||||
const val ARTIST_ORDER_BY_ALBUM_COUNT = "ARTIST_ORDER_BY_ALBUM_COUNT"
|
||||
const val ARTIST_ORDER_BY_MOST_RECENTLY_STARRED = "ARTIST_ORDER_BY_MOST_RECENTLY_STARRED"
|
||||
const val ARTIST_ORDER_BY_LEAST_RECENTLY_STARRED = "ARTIST_ORDER_BY_LEAST_RECENTLY_STARRED"
|
||||
|
||||
|
||||
@@ -115,6 +115,29 @@ public class MappingUtil {
|
||||
.build();
|
||||
}
|
||||
|
||||
public static MediaItem mapMediaItem(MediaItem old) {
|
||||
String mediaId = null;
|
||||
if (old.requestMetadata.extras != null)
|
||||
mediaId = old.requestMetadata.extras.getString("id");
|
||||
|
||||
if (mediaId != null && DownloadUtil.getDownloadTracker(App.getContext()).isDownloaded(mediaId)) {
|
||||
return old;
|
||||
}
|
||||
Uri uri = old.requestMetadata.mediaUri == null ? null : MusicUtil.updateStreamUri(old.requestMetadata.mediaUri);
|
||||
return new MediaItem.Builder()
|
||||
.setMediaId(old.mediaId)
|
||||
.setMediaMetadata(old.mediaMetadata)
|
||||
.setRequestMetadata(
|
||||
new MediaItem.RequestMetadata.Builder()
|
||||
.setMediaUri(uri)
|
||||
.setExtras(old.requestMetadata.extras)
|
||||
.build()
|
||||
)
|
||||
.setMimeType(MimeTypes.BASE_TYPE_AUDIO)
|
||||
.setUri(uri)
|
||||
.build();
|
||||
}
|
||||
|
||||
public static List<MediaItem> mapDownloads(List<Child> items) {
|
||||
ArrayList<MediaItem> downloads = new ArrayList<>();
|
||||
|
||||
|
||||
@@ -21,11 +21,16 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class MusicUtil {
|
||||
private static final String TAG = "MusicUtil";
|
||||
|
||||
private static final Pattern BITRATE_PATTERN = Pattern.compile("&maxBitRate=\\d+");
|
||||
private static final Pattern FORMAT_PATTERN = Pattern.compile("&format=\\w+");
|
||||
|
||||
public static Uri getStreamUri(String id) {
|
||||
Map<String, String> params = App.getSubsonicClientInstance(false).getParams();
|
||||
|
||||
@@ -61,6 +66,24 @@ public class MusicUtil {
|
||||
return Uri.parse(uri.toString());
|
||||
}
|
||||
|
||||
public static Uri updateStreamUri(Uri uri) {
|
||||
String s = uri.toString();
|
||||
Matcher m1 = BITRATE_PATTERN.matcher(s);
|
||||
s = m1.replaceAll("");
|
||||
Matcher m2 = FORMAT_PATTERN.matcher(s);
|
||||
s = m2.replaceAll("");
|
||||
s = s.replace("&estimateContentLength=true", "");
|
||||
|
||||
if (!Preferences.isServerPrioritized())
|
||||
s += "&maxBitRate=" + getBitratePreference();
|
||||
if (!Preferences.isServerPrioritized())
|
||||
s += "&format=" + getTranscodingFormatPreference();
|
||||
if (Preferences.askForEstimateContentLength())
|
||||
s += "&estimateContentLength=true";
|
||||
|
||||
return Uri.parse(s);
|
||||
}
|
||||
|
||||
public static Uri getDownloadUri(String id) {
|
||||
StringBuilder uri = new StringBuilder();
|
||||
|
||||
|
||||
@@ -70,6 +70,7 @@ object Preferences {
|
||||
private const val SONG_RATING_PER_ITEM = "song_rating_per_item"
|
||||
private const val RATING_PER_ITEM = "rating_per_item"
|
||||
private const val NEXT_UPDATE_CHECK = "next_update_check"
|
||||
private const val GITHUB_UPDATE_CHECK = "github_update_check"
|
||||
private const val CONTINUOUS_PLAY = "continuous_play"
|
||||
private const val LAST_INSTANT_MIX = "last_instant_mix"
|
||||
private const val ALLOW_PLAYLIST_DUPLICATES = "allow_playlist_duplicates"
|
||||
@@ -79,6 +80,7 @@ object Preferences {
|
||||
private const val ALBUM_DETAIL = "album_detail"
|
||||
private const val ALBUM_SORT_ORDER = "album_sort_order"
|
||||
private const val DEFAULT_ALBUM_SORT_ORDER = Constants.ALBUM_ORDER_BY_NAME
|
||||
private const val ARTIST_SORT_BY_ALBUM_COUNT= "artist_sort_by_album_count"
|
||||
|
||||
@JvmStatic
|
||||
fun getServer(): String? {
|
||||
@@ -573,15 +575,21 @@ object Preferences {
|
||||
return App.getInstance().preferences.getBoolean(RATING_PER_ITEM, false)
|
||||
}
|
||||
|
||||
|
||||
@JvmStatic
|
||||
fun showTempoUpdateDialog(): Boolean {
|
||||
fun isGithubUpdateEnabled(): Boolean {
|
||||
return App.getInstance().preferences.getBoolean(GITHUB_UPDATE_CHECK, true)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun showTempusUpdateDialog(): Boolean {
|
||||
return App.getInstance().preferences.getLong(
|
||||
NEXT_UPDATE_CHECK, 0
|
||||
) + 86400000 < System.currentTimeMillis()
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun setTempoUpdateReminder() {
|
||||
fun setTempusUpdateReminder() {
|
||||
App.getInstance().preferences.edit().putLong(NEXT_UPDATE_CHECK, System.currentTimeMillis()).apply()
|
||||
}
|
||||
|
||||
@@ -656,4 +664,14 @@ object Preferences {
|
||||
fun setAlbumSortOrder(sortOrder: String) {
|
||||
App.getInstance().preferences.edit().putString(ALBUM_SORT_ORDER, sortOrder).apply()
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun getArtistSortOrder(): String {
|
||||
val sort_by_album_count = App.getInstance().preferences.getBoolean(ARTIST_SORT_BY_ALBUM_COUNT, false)
|
||||
Log.d("Preferences", "getSortOrder")
|
||||
if (sort_by_album_count)
|
||||
return Constants.ARTIST_ORDER_BY_ALBUM_COUNT
|
||||
else
|
||||
return Constants.ARTIST_ORDER_BY_NAME
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,12 @@
|
||||
package com.cappielloantonio.tempo.util;
|
||||
|
||||
import androidx.annotation.OptIn;
|
||||
import androidx.media3.common.C;
|
||||
import androidx.media3.common.MediaItem;
|
||||
import androidx.media3.common.Metadata;
|
||||
import androidx.media3.common.Tracks;
|
||||
import androidx.media3.common.util.UnstableApi;
|
||||
import androidx.media3.exoplayer.ExoPlayer;
|
||||
import androidx.media3.common.Player;
|
||||
|
||||
import com.cappielloantonio.tempo.model.ReplayGain;
|
||||
|
||||
@@ -17,7 +18,7 @@ import java.util.Objects;
|
||||
public class ReplayGainUtil {
|
||||
private static final String[] tags = {"REPLAYGAIN_TRACK_GAIN", "REPLAYGAIN_ALBUM_GAIN", "R128_TRACK_GAIN", "R128_ALBUM_GAIN"};
|
||||
|
||||
public static void setReplayGain(ExoPlayer player, Tracks tracks) {
|
||||
public static void setReplayGain(Player player, Tracks tracks) {
|
||||
List<Metadata> metadata = getMetadata(tracks);
|
||||
List<ReplayGain> gains = getReplayGains(metadata);
|
||||
|
||||
@@ -62,7 +63,7 @@ public class ReplayGainUtil {
|
||||
}
|
||||
}
|
||||
|
||||
if (gains.size() == 0) gains.add(0, new ReplayGain());
|
||||
if (gains.isEmpty()) gains.add(0, new ReplayGain());
|
||||
if (gains.size() == 1) gains.add(1, new ReplayGain());
|
||||
|
||||
return gains;
|
||||
@@ -108,7 +109,7 @@ public class ReplayGainUtil {
|
||||
}
|
||||
}
|
||||
|
||||
private static void applyReplayGain(ExoPlayer player, List<ReplayGain> gains) {
|
||||
private static void applyReplayGain(Player player, List<ReplayGain> gains) {
|
||||
if (Objects.equals(Preferences.getReplayGainMode(), "disabled") || gains == null || gains.isEmpty()) {
|
||||
setNoReplayGain(player);
|
||||
return;
|
||||
@@ -137,33 +138,33 @@ public class ReplayGainUtil {
|
||||
setNoReplayGain(player);
|
||||
}
|
||||
|
||||
private static void setNoReplayGain(ExoPlayer player) {
|
||||
private static void setNoReplayGain(Player player) {
|
||||
setReplayGain(player, 0f);
|
||||
}
|
||||
|
||||
private static void setTrackReplayGain(ExoPlayer player, List<ReplayGain> gains) {
|
||||
private static void setTrackReplayGain(Player player, List<ReplayGain> gains) {
|
||||
float trackGain = gains.get(0).getTrackGain() != 0f ? gains.get(0).getTrackGain() : gains.get(1).getTrackGain();
|
||||
|
||||
setReplayGain(player, trackGain != 0f ? trackGain : 0f);
|
||||
}
|
||||
|
||||
private static void setAlbumReplayGain(ExoPlayer player, List<ReplayGain> gains) {
|
||||
private static void setAlbumReplayGain(Player player, List<ReplayGain> gains) {
|
||||
float albumGain = gains.get(0).getAlbumGain() != 0f ? gains.get(0).getAlbumGain() : gains.get(1).getAlbumGain();
|
||||
|
||||
setReplayGain(player, albumGain != 0f ? albumGain : 0f);
|
||||
}
|
||||
|
||||
private static void setAutoReplayGain(ExoPlayer player, List<ReplayGain> gains) {
|
||||
private static void setAutoReplayGain(Player player, List<ReplayGain> gains) {
|
||||
float albumGain = gains.get(0).getAlbumGain() != 0f ? gains.get(0).getAlbumGain() : gains.get(1).getAlbumGain();
|
||||
float trackGain = gains.get(0).getTrackGain() != 0f ? gains.get(0).getTrackGain() : gains.get(1).getTrackGain();
|
||||
|
||||
setReplayGain(player, albumGain != 0f ? albumGain : trackGain);
|
||||
}
|
||||
|
||||
private static boolean areTracksConsecutive(ExoPlayer player) {
|
||||
private static boolean areTracksConsecutive(Player player) {
|
||||
MediaItem currentMediaItem = player.getCurrentMediaItem();
|
||||
int currentMediaItemIndex = player.getCurrentMediaItemIndex();
|
||||
MediaItem pastMediaItem = currentMediaItemIndex > 0 ? player.getMediaItemAt(currentMediaItemIndex - 1) : null;
|
||||
int prevMediaItemIndex = player.getPreviousMediaItemIndex();
|
||||
MediaItem pastMediaItem = prevMediaItemIndex == C.INDEX_UNSET ? null : player.getMediaItemAt(prevMediaItemIndex);
|
||||
|
||||
return currentMediaItem != null &&
|
||||
pastMediaItem != null &&
|
||||
@@ -172,7 +173,7 @@ public class ReplayGainUtil {
|
||||
pastMediaItem.mediaMetadata.albumTitle.toString().equals(currentMediaItem.mediaMetadata.albumTitle.toString());
|
||||
}
|
||||
|
||||
private static void setReplayGain(ExoPlayer player, float gain) {
|
||||
private static void setReplayGain(Player player, float gain) {
|
||||
player.setVolume((float) Math.pow(10f, gain / 20f));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -105,7 +105,11 @@ public class UIUtil {
|
||||
}
|
||||
|
||||
public static String getReadableDate(Date date) {
|
||||
if (date == null) {
|
||||
return App.getContext().getString(R.string.share_no_expiration);
|
||||
}
|
||||
SimpleDateFormat formatter = new SimpleDateFormat("dd MMM, yyyy", Locale.getDefault());
|
||||
return formatter.format(date);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ public class SongListPageViewModel extends AndroidViewModel {
|
||||
|
||||
public int year = 0;
|
||||
public int maxNumberByYear = 500;
|
||||
public int maxNumberByGenre = 100;
|
||||
public int maxNumberByGenre = 500;
|
||||
|
||||
public SongListPageViewModel(@NonNull Application application) {
|
||||
super(application);
|
||||
@@ -51,7 +51,7 @@ public class SongListPageViewModel extends AndroidViewModel {
|
||||
|
||||
switch (title) {
|
||||
case Constants.MEDIA_BY_GENRE:
|
||||
songList = songRepository.getSongsByGenre(genre.getGenre(), 0);
|
||||
songList = songRepository.getRandomSampleWithGenre(maxNumberByGenre, 0, 3000, genre.getGenre());
|
||||
break;
|
||||
case Constants.MEDIA_BY_ARTIST:
|
||||
songList = artistRepository.getTopSongs(artist.getName(), 50);
|
||||
|
||||
@@ -14,22 +14,21 @@
|
||||
app:layout_collapseMode="pin"
|
||||
app:navigationIcon="@drawable/ic_arrow_back" />
|
||||
|
||||
<androidx.core.widget.NestedScrollView
|
||||
android:id="@+id/fragment_album_page_nested_scroll_view"
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_height="match_parent">
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:id="@+id/app_bar_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/album_info_sector"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:clipChildren="false"
|
||||
android:paddingTop="8dp">
|
||||
android:paddingTop="8dp"
|
||||
app:layout_scrollFlags="scroll|exitUntilCollapsed">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/album_cover_image_view"
|
||||
@@ -252,53 +251,15 @@
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/album_page_button_layout" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:paddingBottom="@dimen/global_padding_bottom"
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior">
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/song_recycler_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:clipToPadding="false"
|
||||
android:nestedScrollingEnabled="false"
|
||||
android:paddingTop="8dp" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/similar_album_sector"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:visibility="gone">
|
||||
|
||||
<TextView
|
||||
style="@style/TitleLarge"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingTop="32dp"
|
||||
android:paddingEnd="20dp"
|
||||
android:text="@string/album_page_extra_info_button" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/similar_albums_recycler_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:clipToPadding="false"
|
||||
android:nestedScrollingEnabled="false"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingTop="8dp"
|
||||
android:paddingEnd="8dp"
|
||||
android:paddingBottom="8dp" />
|
||||
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
</androidx.core.widget.NestedScrollView>
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/song_recycler_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:clipToPadding="false"
|
||||
android:paddingTop="8dp"
|
||||
android:paddingBottom="@dimen/global_padding_bottom"
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior"/>
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
</LinearLayout>
|
||||
@@ -1,9 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<fragment
|
||||
android:id="@+id/toolbar_fragment"
|
||||
@@ -26,6 +27,7 @@
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical"
|
||||
android:paddingBottom="@dimen/global_padding_bottom"
|
||||
android:visibility="gone">
|
||||
|
||||
<ImageView
|
||||
@@ -57,92 +59,78 @@
|
||||
android:text="@string/download_info_empty_subtitle" />
|
||||
</LinearLayout>
|
||||
|
||||
<androidx.core.widget.NestedScrollView
|
||||
android:id="@+id/fragment_download_nested_scroll_view"
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/download_downloaded_sector"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingHorizontal="16dp"
|
||||
android:visibility="gone"
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior">
|
||||
tools:visibility="visible">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/download_downloaded_sector"
|
||||
<TextView
|
||||
android:id="@+id/downloaded_text_view_refreshable"
|
||||
style="@style/TitleLarge"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/download_title_section"
|
||||
app:layout_constraintEnd_toStartOf="@+id/downloaded_refresh_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:paddingHorizontal="16dp"
|
||||
android:paddingTop="16dp"
|
||||
android:paddingBottom="@dimen/global_padding_bottom"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible">
|
||||
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" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/downloaded_text_view_refreshable"
|
||||
style="@style/TitleLarge"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/download_title_section"
|
||||
app:layout_constraintEnd_toStartOf="@+id/downloaded_refresh_image_view"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
<ImageView
|
||||
android:id="@+id/downloaded_refresh_image_view"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:layout_marginStart="12dp"
|
||||
android:layout_marginEnd="12dp"
|
||||
android:background="@drawable/ic_refresh"
|
||||
android:contentDescription="@string/download_refresh_button_content_description"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/downloaded_text_view_refreshable"
|
||||
app:layout_constraintEnd_toStartOf="@id/downloaded_go_back_image_view"
|
||||
app:layout_constraintStart_toEndOf="@id/downloaded_text_view_refreshable"
|
||||
app:layout_constraintTop_toTopOf="@+id/downloaded_text_view_refreshable" />
|
||||
|
||||
<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"
|
||||
android:layout_height="24dp"
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginHorizontal="12dp"
|
||||
android:background="@drawable/ic_arrow_back"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/downloaded_text_view_refreshable"
|
||||
app:layout_constraintEnd_toStartOf="@id/downloaded_group_by_image_view"
|
||||
app:layout_constraintStart_toEndOf="@id/downloaded_refresh_image_view"
|
||||
app:layout_constraintTop_toTopOf="@+id/downloaded_text_view_refreshable" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/downloaded_refresh_image_view"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:layout_marginStart="12dp"
|
||||
android:layout_marginEnd="12dp"
|
||||
android:background="@drawable/ic_refresh"
|
||||
android:contentDescription="@string/download_refresh_button_content_description"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/downloaded_text_view_refreshable"
|
||||
app:layout_constraintEnd_toStartOf="@id/downloaded_go_back_image_view"
|
||||
app:layout_constraintStart_toEndOf="@id/downloaded_text_view_refreshable"
|
||||
app:layout_constraintTop_toTopOf="@+id/downloaded_text_view_refreshable" />
|
||||
<ImageView
|
||||
android:id="@+id/downloaded_group_by_image_view"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:layout_gravity="center"
|
||||
android:background="@drawable/ic_filter_list"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/downloaded_text_view_refreshable"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@+id/downloaded_text_view_refreshable" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/downloaded_go_back_image_view"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:layout_marginHorizontal="12dp"
|
||||
android:layout_gravity="center"
|
||||
android:background="@drawable/ic_arrow_back"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/downloaded_text_view_refreshable"
|
||||
app:layout_constraintEnd_toStartOf="@id/downloaded_group_by_image_view"
|
||||
app:layout_constraintStart_toEndOf="@id/downloaded_refresh_image_view"
|
||||
app:layout_constraintTop_toTopOf="@+id/downloaded_text_view_refreshable" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/downloaded_group_by_image_view"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:layout_gravity="center"
|
||||
android:background="@drawable/ic_filter_list"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/downloaded_text_view_refreshable"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@+id/downloaded_text_view_refreshable" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/downloaded_recycler_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:clipToPadding="false"
|
||||
android:nestedScrollingEnabled="false"
|
||||
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/shuffle_downloaded_text_view_clickable" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</androidx.core.widget.NestedScrollView>
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/downloaded_recycler_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:clipToPadding="false"
|
||||
android:nestedScrollingEnabled="false"
|
||||
android:paddingHorizontal="12dp"
|
||||
android:paddingTop="12dp"
|
||||
android:paddingBottom="@dimen/global_padding_bottom" />
|
||||
</LinearLayout>
|
||||
|
||||
|
||||
@@ -20,6 +20,20 @@
|
||||
android:orientation="vertical"
|
||||
android:paddingBottom="@dimen/global_padding_bottom">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/podcast_channels_pre_text_view"
|
||||
style="@style/TitleMedium"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?attr/selectableItemBackground"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingTop="16dp"
|
||||
android:paddingEnd="16dp"
|
||||
android:text="@string/home_subtitle_new_podcast_channel"
|
||||
android:textAllCaps="true"
|
||||
android:textColor="?attr/colorPrimary"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/home_podcast_channels_sector"
|
||||
android:layout_width="match_parent"
|
||||
@@ -29,17 +43,6 @@
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/podcast_channels_pre_text_view"
|
||||
style="@style/TitleMedium"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingTop="16dp"
|
||||
android:paddingEnd="16dp"
|
||||
android:text="@string/home_subtitle_new_podcast_channel"
|
||||
android:textAllCaps="true" />
|
||||
|
||||
<!-- Label and button -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
@@ -169,4 +172,4 @@
|
||||
android:gravity="center"
|
||||
android:text="@string/podcast_info_empty_button" />
|
||||
</LinearLayout>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@@ -1,44 +1,54 @@
|
||||
<?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"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/internet_radio_station_pre_text_view"
|
||||
style="@style/TitleMedium"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?attr/selectableItemBackground"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingTop="16dp"
|
||||
android:paddingEnd="16dp"
|
||||
android:text="@string/home_subtitle_new_internet_radio_station"
|
||||
android:textAllCaps="true"
|
||||
android:textColor="?attr/colorPrimary"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/internet_radio_station_title_text_view"
|
||||
style="@style/TitleLarge"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="16dp"
|
||||
android:text="@string/home_title_internet_radio_station"
|
||||
app:layout_constraintTop_toBottomOf="@id/internet_radio_station_pre_text_view"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent" />
|
||||
|
||||
<androidx.core.widget.NestedScrollView
|
||||
android:id="@+id/home_radio_station_sector"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_height="0dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
app:layout_constraintTop_toBottomOf="@id/internet_radio_station_title_text_view">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:paddingBottom="@dimen/global_padding_bottom">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/internet_radio_station_pre_text_view"
|
||||
style="@style/TitleMedium"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingTop="16dp"
|
||||
android:paddingEnd="16dp"
|
||||
android:text="@string/home_subtitle_new_internet_radio_station"
|
||||
android:textAllCaps="true" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/internet_radio_station_title_text_view"
|
||||
style="@style/TitleLarge"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="16dp"
|
||||
android:text="@string/home_title_internet_radio_station" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/internet_radio_station_recycler_view"
|
||||
android:layout_width="match_parent"
|
||||
@@ -61,7 +71,7 @@
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
app:layout_constraintTop_toBottomOf="@id/internet_radio_station_title_text_view">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/empty_description_image_view"
|
||||
@@ -105,7 +115,4 @@
|
||||
android:gravity="center"
|
||||
android:text="@string/radio_station_info_empty_button" />
|
||||
</LinearLayout>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@@ -27,7 +27,7 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?attr/colorSurface"
|
||||
app:layout_scrollFlags="scroll|exitUntilCollapsed|snap">
|
||||
app:layout_scrollFlags="scroll|exitUntilCollapsed">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/playlist_cover_image_view_top_left"
|
||||
|
||||
@@ -7,7 +7,10 @@
|
||||
<ImageView
|
||||
android:id="@+id/discover_song_cover_image_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="196dp"
|
||||
android:layout_height="match_parent"
|
||||
android:scaleType="centerCrop"
|
||||
android:pivotX="50%"
|
||||
android:pivotY="50%"
|
||||
android:background="?attr/colorSurfaceContainerHighest"
|
||||
android:foreground="@drawable/gradient_discover_background_image" />
|
||||
|
||||
|
||||
@@ -6,4 +6,7 @@
|
||||
<item
|
||||
android:id="@+id/menu_artist_sort_random"
|
||||
android:title="@string/menu_sort_random" />
|
||||
<item
|
||||
android:id="@+id/menu_artist_sort_album_count"
|
||||
android:title="@string/menu_sort_album_count" />
|
||||
</menu>
|
||||
258
app/src/main/res/values-ca/arrays.xml
Normal file
@@ -0,0 +1,258 @@
|
||||
<?xml version="1.0"?>
|
||||
<resources>
|
||||
<string-array name="theme_list_titles">
|
||||
<item>Clar</item>
|
||||
<item>Fosc</item>
|
||||
<item>Valor per defecte del sistema</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>Alta</item>
|
||||
<item>Mitjana</item>
|
||||
<item>Baixa</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>Alta</item>
|
||||
<item>Mitjana</item>
|
||||
<item>Baixa</item>
|
||||
</string-array>
|
||||
<string-array name="pref_image_size_values">
|
||||
<item>-1</item>
|
||||
<item>500</item>
|
||||
<item>300</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="streaming_cache_size_titles">
|
||||
<item>Inhabilitada</item>
|
||||
<item>128 MiB</item>
|
||||
<item>256 MiB</item>
|
||||
<item>512 MiB</item>
|
||||
<item>1024 MiB</item>
|
||||
</string-array>
|
||||
<string-array name="streaming_cache_size_values">
|
||||
<item>0</item>
|
||||
<item>128</item>
|
||||
<item>256</item>
|
||||
<item>512</item>
|
||||
<item>1024</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>Reproducció directa</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>Reproducció directa</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>Baixada directa</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 segons</item>
|
||||
<item>5 segons</item>
|
||||
<item>2 segons</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>Alta</item>
|
||||
<item>Mitjana</item>
|
||||
<item>Baixa</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>Inhabilitat</item>
|
||||
<item>Pista</item>
|
||||
<item>Àlbum</item>
|
||||
<item>Automàtic</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>Sense transcodificació</item>
|
||||
<item>Paràmetres del servidor</item>
|
||||
<item>Format de transcodificació amb wifi</item>
|
||||
<item>Format de transcodificació amb dades mòbils</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>Mínima</item>
|
||||
<item>Moderada</item>
|
||||
<item>Agressiva</item>
|
||||
<item>Extrema</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>Mínim 0 estrelles</item>
|
||||
<item>Mínim 1 estrella</item>
|
||||
<item>Mínim 2 estrelles</item>
|
||||
<item>Mínim 3 estrelles</item>
|
||||
<item>Mínim 4 estrelles</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>
|
||||
537
app/src/main/res/values-ca/strings.xml
Normal file
@@ -0,0 +1,537 @@
|
||||
<?xml version="1.0"?>
|
||||
<resources>
|
||||
<string name="activity_battery_optimizations_conclusion">Si teniu problemes, visiteu https://dontkillmyapp.com. S\'hi proporcionen instruccions detallades sobre com inhabilitar qualsevol característica d\'estalvi de bateria que pugui afectar el rendiment de l\'aplicació.</string>
|
||||
<string name="activity_battery_optimizations_summary">Inhabiliteu les optimitzacions de la bateria per a la reproducció multimèdia mentre la pantalla està apagada.</string>
|
||||
<string name="activity_battery_optimizations_title">Optimitzacions de la bateria</string>
|
||||
<string name="activity_info_offline_mode">Mode fora de línia</string>
|
||||
<string name="album_bottom_sheet_add_to_playlist">Afegeix a la llista de reproducció</string>
|
||||
<string name="album_bottom_sheet_add_to_queue">Afegeix a la cua</string>
|
||||
<string name="album_bottom_sheet_download_all">Baixa-ho tot</string>
|
||||
<string name="album_bottom_sheet_go_to_artist">Ves a l\'artista</string>
|
||||
<string name="album_bottom_sheet_instant_mix">Mescla instantània</string>
|
||||
<string name="album_bottom_sheet_play_next">Reprodueix a continuació</string>
|
||||
<string name="album_bottom_sheet_remove_all">Suprimeix-ho tot</string>
|
||||
<string name="album_bottom_sheet_share">Comparteix</string>
|
||||
<string name="album_bottom_sheet_shuffle">Reprodueix aleatòriament</string>
|
||||
<string name="album_catalogue_title">Àlbums</string>
|
||||
<string name="album_catalogue_title_expanded">Exploració d\'àlbums</string>
|
||||
<string name="album_error_retrieving_artist">S\'ha produït un error en recuperar l\'artista</string>
|
||||
<string name="album_list_page_downloaded">Àlbums baixats</string>
|
||||
<string name="album_list_page_most_played">Àlbums més reproduïts</string>
|
||||
<string name="album_list_page_new_releases">Llançaments nous</string>
|
||||
<string name="album_list_page_recently_added">Àlbums afegits recentment</string>
|
||||
<string name="album_list_page_recently_played">Àlbums reproduïts recentment</string>
|
||||
<string name="album_list_page_starred">Àlbums amb estrelles</string>
|
||||
<string name="album_list_page_title">Àlbums</string>
|
||||
<string name="album_page_extra_info_button">Més com això</string>
|
||||
<string name="album_page_play_button">Reprodueix</string>
|
||||
<string name="album_page_release_date_label">Publicació: %1$s</string>
|
||||
<string name="album_page_release_dates_label">Publicació: %1$s, originalment: %2$s</string>
|
||||
<string name="album_page_shuffle_button">Reprodueix aleatòriament</string>
|
||||
<string name="album_page_tracks_count_and_duration">%1$d cançons • %2$d minuts</string>
|
||||
<string name="app_name">Tempus</string>
|
||||
<string name="artist_adapter_radio_station_starting">S\'està cercant...</string>
|
||||
<string name="artist_bottom_sheet_instant_mix">Mescla instantània</string>
|
||||
<string name="artist_bottom_sheet_shuffle">Reprodueix aleatòriament</string>
|
||||
<string name="artist_catalogue_title">Artistes</string>
|
||||
<string name="artist_catalogue_title_expanded">Exploració d\'artistes</string>
|
||||
<string name="artist_error_retrieving_radio">S\'ha produït un error en recuperar la ràdio de l\'artista</string>
|
||||
<string name="artist_error_retrieving_tracks">S\'ha produït un error en recuperar les pistes de l\'artista</string>
|
||||
<string name="artist_list_page_downloaded">Artistes baixats</string>
|
||||
<string name="artist_list_page_starred">Artistes amb estrelles</string>
|
||||
<string name="artist_list_page_title">Artistes</string>
|
||||
<string name="artist_page_radio_button">Ràdio</string>
|
||||
<string name="artist_page_shuffle_button">Reprodueix aleatòriament</string>
|
||||
<string name="artist_page_switch_layout_button">Canvia de disposició</string>
|
||||
<string name="artist_page_title_album_more_like_this_button">Més com això</string>
|
||||
<string name="artist_page_title_album_section">Àlbums</string>
|
||||
<string name="artist_page_title_biography_more_button">Més</string>
|
||||
<string name="artist_page_title_biography_section">Biografia</string>
|
||||
<string name="artist_page_title_most_streamed_song_section">Cançons més transmeses</string>
|
||||
<string name="artist_page_title_most_streamed_song_see_all_button">Visualitza-ho tot</string>
|
||||
<string name="battery_optimization_negative_button">Ignora</string>
|
||||
<string name="battery_optimization_neutral_button">No ho tornis a preguntar</string>
|
||||
<string name="battery_optimization_positive_button">Inhabilita</string>
|
||||
<string name="connection_alert_dialog_negative_button">Cancel·la</string>
|
||||
<string name="connection_alert_dialog_neutral_button">Habilita l\'estalvi de dades</string>
|
||||
<string name="connection_alert_dialog_positive_button">D\'acord</string>
|
||||
<string name="connection_alert_dialog_summary">S\'ha restringit l\'accés al servidor del Subsonic en connexions que no són wifi. Per a impedir que torni a aparèixer aquesta alerta, inhabiliteu la comprovació de la connexió als paràmetres de l\'aplicació.</string>
|
||||
<string name="connection_alert_dialog_title">Xarxa wifi no connectada</string>
|
||||
<string name="content_description_shuffle_button">Reprodueix aleatòriament</string>
|
||||
<string name="delete_download_storage_dialog_negative_button">Cancel·la</string>
|
||||
<string name="delete_download_storage_dialog_positive_button">Continua</string>
|
||||
<string name="delete_download_storage_dialog_summary">Tingueu en compte que, si continueu, se suprimiran permanentment tots els elements baixats de tots els servidors.</string>
|
||||
<string name="delete_download_storage_dialog_title">Suprimeix els elements desats</string>
|
||||
<string name="description_empty_title">No hi ha cap descripció disponible</string>
|
||||
<string name="disc_titlefull">Disc %1$s - %2$s</string>
|
||||
<string name="disc_titleless">Disc %1$s</string>
|
||||
<string name="download_directory_dialog_negative_button">Cancel·la</string>
|
||||
<string name="download_directory_dialog_positive_button">Baixa</string>
|
||||
<string name="download_directory_dialog_summary">Es baixaran totes les pistes d\'aquesta carpeta. Les pistes en subcarpetes no es baixaran.</string>
|
||||
<string name="download_directory_dialog_title">Baixada de les pistes</string>
|
||||
<string name="download_directory_set">Defineix on es baixa la música</string>
|
||||
<string name="download_info_empty_subtitle">Quan baixeu una cançó, la trobareu aquí.</string>
|
||||
<string name="download_info_empty_title">Encara no hi ha cap baixada.</string>
|
||||
<string name="download_item_multiple_subtitle_formatter">%1$s • %2$s elements</string>
|
||||
<string name="download_item_single_subtitle_formatter">%1$s elements</string>
|
||||
<string name="download_shuffle_all_subtitle">Reprodueix-ho tot aleatòriament</string>
|
||||
<string name="download_storage_dialog_sub_summary">Perquè els canvis tinguin efecte, reinicieu l\'aplicació.</string>
|
||||
<string name="download_storage_dialog_summary">Si canvieu la destinació dels fitxers baixats d\'un emmagatzematge a un altre, se suprimiran immediatament tots els fitxers baixats anteriorment de l\'altre emmagatzematge.</string>
|
||||
<string name="download_storage_dialog_title">Selecció de l\'opció d\'emmagatzematge</string>
|
||||
<string name="download_storage_external_dialog_positive_button">Extern</string>
|
||||
<string name="download_storage_internal_dialog_negative_button">Intern</string>
|
||||
<string name="download_storage_directory_dialog_neutral_button">Carpeta</string>
|
||||
<string name="download_title_section">Baixades</string>
|
||||
<string name="download_refresh_no_directory">Definiu una carpeta de baixades per a actualitzar les baixades.</string>
|
||||
<string name="download_refresh_no_changes">No s\'ha trobat cap baixada que falti.</string>
|
||||
<plurals name="download_refresh_removed">
|
||||
<item quantity="one">S\'ha suprimit %d baixada que faltava.</item>
|
||||
<item quantity="other">S\'han suprimit %d baixades que faltaven.</item>
|
||||
</plurals>
|
||||
<string name="download_refresh_button_content_description">Actualitza els elements baixats</string>
|
||||
<string name="downloaded_bottom_sheet_add_to_queue">Afegeix a la cua</string>
|
||||
<string name="downloaded_bottom_sheet_play_next">Reprodueix a continuació</string>
|
||||
<string name="downloaded_bottom_sheet_remove">Suprimeix</string>
|
||||
<string name="downloaded_bottom_sheet_remove_all">Suprimeix-ho tot</string>
|
||||
<string name="downloaded_bottom_sheet_shuffle">Reprodueix aleatòriament</string>
|
||||
<string name="empty_string"/>
|
||||
<string name="error_required">Obligatori</string>
|
||||
<string name="error_server_prefix">El prefix «http» o «https» és obligatori</string>
|
||||
<string name="exo_download_notification_channel_name">Baixades</string>
|
||||
<string name="exo_controls_heart_off_description">Treu el cor</string>
|
||||
<string name="exo_controls_heart_on_description">Posa un cor</string>
|
||||
<string name="cast_expanded_controller_loading">S\'està carregant...</string>
|
||||
<string name="filter_info_selection">Seleccioneu dos o més filtres</string>
|
||||
<string name="filter_title">Filtre</string>
|
||||
<string name="filter_artist">Filtra per artistes</string>
|
||||
<string name="filter_title_expanded">Filtra per gèneres</string>
|
||||
<string name="generic_list_page_count">(%1$d)</string>
|
||||
<string name="generic_list_page_count_unknown">(+%1$d)</string>
|
||||
<string name="genre_catalogue_title">Catàleg de gèneres</string>
|
||||
<string name="genre_catalogue_title_expanded">Exploració de gèneres</string>
|
||||
<string name="github_update_dialog_negative_button">Recorda-m\'ho més tard</string>
|
||||
<string name="github_update_dialog_neutral_button">Fes una aportació</string>
|
||||
<string name="github_update_dialog_positive_button">Baixa-ho ara</string>
|
||||
<string name="github_update_dialog_summary">Hi ha una versió nova de l\'aplicació disponible a Github.</string>
|
||||
<string name="github_update_dialog_title">Actualització disponible</string>
|
||||
<string name="home_rearrangement_dialog_negative_button">Cancel·la</string>
|
||||
<string name="home_rearrangement_dialog_neutral_button">Reinicialitza</string>
|
||||
<string name="home_rearrangement_dialog_positive_button">Desa</string>
|
||||
<string name="home_rearrangement_dialog_title">Reorganització de l\'inici</string>
|
||||
<string name="home_rearrangement_dialog_subtitle">Tingueu en compte que, perquè els canvis tinguin efecte, cal reiniciar l\'aplicació.</string>
|
||||
<string name="home_section_music">Música</string>
|
||||
<string name="home_section_podcast">Pòdcasts</string>
|
||||
<string name="home_section_radio">Ràdio</string>
|
||||
<string name="home_subtitle_best_of">Les millors cançons dels vostres artistes preferits</string>
|
||||
<string name="home_subtitle_made_for_you">Comenceu una mescla a partir d\'una cançó que us ha agradat</string>
|
||||
<string name="home_subtitle_new_internet_radio_station">Afegeix una ràdio nova</string>
|
||||
<string name="home_subtitle_new_podcast_channel">Afegeix un canal de pòdcasts nou</string>
|
||||
<string name="home_sync_starred_cancel">Cancel·la</string>
|
||||
<string name="home_sync_starred_download">Baixa</string>
|
||||
<string name="home_sync_starred_subtitle">Baixar aquestes pistes pot suposar un ús de dades important</string>
|
||||
<string name="home_sync_starred_title">Sembla que hi ha pistes amb estrelles pendents de sincronitzar</string>
|
||||
<string name="home_sync_starred_albums_title">Sincronitza els àlbums amb estrelles</string>
|
||||
<string name="home_sync_starred_albums_subtitle">Els àlbums marcats amb una estrella estaran disponibles fora de línia</string>
|
||||
<string name="home_sync_starred_artists_title">Sincronització dels artistes amb estrelles</string>
|
||||
<string name="home_sync_starred_artists_subtitle">Teniu artistes amb estrella amb música sense baixar</string>
|
||||
<string name="home_title_best_of">El millor de</string>
|
||||
<string name="home_title_discovery">Descobriment</string>
|
||||
<string name="home_title_discovery_shuffle_all_button">Reprodueix-ho tot aleatòriament</string>
|
||||
<string name="home_title_flashback">Viatge al passat</string>
|
||||
<string name="home_title_internet_radio_station">Emissores de ràdio per Internet</string>
|
||||
<string name="home_title_last_played">Darreres reproduccions</string>
|
||||
<string name="home_title_last_played_see_all_button">Visualitza-ho tot</string>
|
||||
<string name="home_title_last_week">La setmana passada</string>
|
||||
<string name="home_title_last_month">El mes passat</string>
|
||||
<string name="home_title_last_year">L\'any passat</string>
|
||||
<string name="home_title_made_for_you">Fet a mida</string>
|
||||
<string name="home_title_most_played">Més reproduccions</string>
|
||||
<string name="home_title_most_played_see_all_button">Visualitza-ho tot</string>
|
||||
<string name="home_title_new_releases">Llançaments nous</string>
|
||||
<string name="home_title_newest_podcasts">Pòdcasts més recents</string>
|
||||
<string name="home_title_pinned_playlists">Llistes de reproducció</string>
|
||||
<string name="home_title_podcast_channels">Canals</string>
|
||||
<string name="home_title_podcast_channels_see_all_button">Visualitza-ho tot</string>
|
||||
<string name="home_title_radio_station">Emissores de ràdio</string>
|
||||
<string name="home_title_recently_added">Addicions recents</string>
|
||||
<string name="home_title_recently_added_see_all_button">Visualitza-ho tot</string>
|
||||
<string name="home_title_shares">Elements compartits</string>
|
||||
<string name="home_title_starred_albums">★ Àlbums amb estrelles</string>
|
||||
<string name="home_title_starred_albums_see_all_button">Visualitza-ho tot</string>
|
||||
<string name="home_title_starred_artists">★ Artistes amb estrelles</string>
|
||||
<string name="home_title_starred_artists_see_all_button">Visualitza-ho tot</string>
|
||||
<string name="home_title_starred_tracks">★ Pistes amb estrelles</string>
|
||||
<string name="home_title_starred_tracks_see_all_button">Visualitza-ho tot</string>
|
||||
<string name="home_title_top_songs">Les vostres cançons més escoltades</string>
|
||||
<string name="home_option_reorganize">Reorganitza</string>
|
||||
<string name="label_dot_separator" translatable="false">•</string>
|
||||
<string name="label_placeholder" translatable="false">--</string>
|
||||
<string name="library_title_album">Àlbums</string>
|
||||
<string name="library_title_album_see_all_button">Visualitza-ho tot</string>
|
||||
<string name="library_title_artist">Artistes</string>
|
||||
<string name="library_title_artist_see_all_button">Visualitza-ho tot</string>
|
||||
<string name="library_title_genre">Gèneres</string>
|
||||
<string name="library_title_genre_see_all_button">Visualitza-ho tot</string>
|
||||
<string name="library_title_music_folder">Carpetes de música</string>
|
||||
<string name="library_title_playlist">Llistes de reproducció</string>
|
||||
<string name="library_title_playlist_see_all_button">Visualitza-ho tot</string>
|
||||
<string name="login_empty">No s\'ha afegit cap servidor</string>
|
||||
<string name="login_title">Servidors del Subsonic</string>
|
||||
<string name="login_title_expanded">Servidors del Subsonic</string>
|
||||
<string name="media_route_menu_title">Emissió</string>
|
||||
<string name="menu_add_button">Afegeix</string>
|
||||
<string name="menu_add_to_playlist_button">Afegeix a la llista de reproducció</string>
|
||||
<string name="menu_download_all_button">Baixa-ho tot</string>
|
||||
<string name="menu_rate_album">Valora l\'àlbum</string>
|
||||
<string name="menu_download_label">Baixa</string>
|
||||
<string name="menu_filter_all">Tot</string>
|
||||
<string name="menu_filter_download">Baixades</string>
|
||||
<string name="menu_group_by_album">Àlbum</string>
|
||||
<string name="menu_group_by_artist">Artista</string>
|
||||
<string name="menu_group_by_genre">Gènere</string>
|
||||
<string name="menu_group_by_track">Pista</string>
|
||||
<string name="menu_group_by_year">Any</string>
|
||||
<string name="menu_home_label">Inici</string>
|
||||
<string name="menu_last_week_name">La setmana passada</string>
|
||||
<string name="menu_last_month_name">El mes passat</string>
|
||||
<string name="menu_last_year_name">L\'any passat</string>
|
||||
<string name="menu_library_label">Biblioteca</string>
|
||||
<string name="menu_search_button">Cerca</string>
|
||||
<string name="menu_settings_button">Paràmetres</string>
|
||||
<string name="menu_sort_artist">Artista</string>
|
||||
<string name="menu_sort_name">Nom</string>
|
||||
<string name="menu_sort_random">Aleatori</string>
|
||||
<string name="menu_sort_album_count">Nombre d\'àlbums</string>
|
||||
<string name="menu_sort_recently_added">Addicions recents</string>
|
||||
<string name="menu_sort_recently_played">Reproduccions recents</string>
|
||||
<string name="menu_sort_most_played">Més reproduccions</string>
|
||||
<string name="menu_sort_most_recently_starred">Estrelles més recents</string>
|
||||
<string name="menu_sort_least_recently_starred">Estrelles menys recents</string>
|
||||
<string name="menu_pin_button">Afegeix a la pantalla d\'inici</string>
|
||||
<string name="menu_unpin_button">Suprimeix de la pantalla d\'inici</string>
|
||||
<string name="menu_sort_year">Any</string>
|
||||
<string name="player_playback_speed">%1$.2fx</string>
|
||||
<string name="player_queue_clean_all_button">Esborra la cua de reproducció</string>
|
||||
<string name="player_queue_save_queue_success">S\'ha desat la cua de reproducció</string>
|
||||
<string name="player_lyrics_download_content_description">Baixa les lletres per a la reproducció fora de línia</string>
|
||||
<string name="player_lyrics_downloaded_content_description">Lletres baixades per a la reproducció fora de línia</string>
|
||||
<string name="player_lyrics_download_success">S\'han desat les lletres per a la reproducció fora de línia.</string>
|
||||
<string name="player_lyrics_download_failure">No hi ha lletres disponibles que es puguin baixar.</string>
|
||||
<string name="player_server_priority">Prioritat dels servidors</string>
|
||||
<string name="player_unknown_format">Format desconegut</string>
|
||||
<string name="player_transcoding">Transcodificació</string>
|
||||
<string name="player_transcoding_requested">sol·licitat</string>
|
||||
<string name="playlist_catalogue_title">Catàleg de llistes de reproducció</string>
|
||||
<string name="playlist_catalogue_title_expanded">Exploració de llistes de reproducció</string>
|
||||
<string name="playlist_chooser_dialog_empty">No s\'ha creat cap llista de reproducció</string>
|
||||
<string name="playlist_chooser_dialog_negative_button">Cancel·la</string>
|
||||
<string name="playlist_chooser_dialog_neutral_button">Crea</string>
|
||||
<string name="playlist_chooser_dialog_title">Addició a una llista de reproducció</string>
|
||||
<string name="playlist_chooser_dialog_toast_add_success">S\'han afegit les cançons a la llista de reproducció</string>
|
||||
<string name="playlist_chooser_dialog_toast_add_failure">No s\'han pogut afegir les cançons a la llista de reproducció</string>
|
||||
<string name="playlist_chooser_dialog_toast_all_skipped">S\'han omès totes les cançons com a duplicades</string>
|
||||
<string name="playlist_counted_tracks">%1$d pistes • %2$s</string>
|
||||
<string name="playlist_duration">Durada • %1$s</string>
|
||||
<string name="playlist_editor_dialog_action_delete_toast">Manteniu-ho premut per a suprimir-ho</string>
|
||||
<string name="playlist_editor_dialog_hint_name">Nom de la llista de reproducció</string>
|
||||
<string name="playlist_editor_dialog_negative_button">Cancel·la</string>
|
||||
<string name="playlist_editor_dialog_neutral_button">Suprimeix</string>
|
||||
<string name="playlist_editor_dialog_positive_button">Desa</string>
|
||||
<string name="playlist_editor_dialog_title">Edició de la llista de reproducció</string>
|
||||
<string name="playlist_page_play_button">Reprodueix</string>
|
||||
<string name="playlist_page_shuffle_button">Reprodueix aleatòriament</string>
|
||||
<string name="playlist_song_count">Llista de reproducció • %1$d cançons</string>
|
||||
<string name="podcast_bottom_sheet_add_to_queue">Afegeix a la cua</string>
|
||||
<string name="podcast_bottom_sheet_delete">Suprimeix</string>
|
||||
<string name="podcast_bottom_sheet_download">Baixa</string>
|
||||
<string name="podcast_bottom_sheet_go_to_channel">Ves al canal</string>
|
||||
<string name="podcast_bottom_sheet_play_next">Reprodueix a continuació</string>
|
||||
<string name="podcast_bottom_sheet_remove">Suprimeix</string>
|
||||
<string name="podcast_channel_catalogue_title">Canals</string>
|
||||
<string name="podcast_channel_catalogue_title_expanded">Exploració de canals</string>
|
||||
<string name="podcast_channel_editor_dialog_hint_rss_url">URL de l\'RSS</string>
|
||||
<string name="podcast_channel_editor_dialog_title">Canal de pòdcast</string>
|
||||
<string name="podcast_channel_page_title_description_section">Descripció</string>
|
||||
<string name="podcast_channel_page_title_episode_section">Episodis</string>
|
||||
<string name="podcast_channel_page_title_no_episode_available">No hi ha cap episodi disponible</string>
|
||||
<string name="podcast_episode_download_request_snackbar">S\'ha enviat la sol·licitud al servidor</string>
|
||||
<string name="podcast_info_empty_button">Feu clic per a ocultar la secció.\nEls efectes seran visibles quan reinicieu l\'aplicació.</string>
|
||||
<string name="podcast_info_empty_subtitle">Quan afegiu un canal, el trobareu aquí.</string>
|
||||
<string name="podcast_info_empty_title">No s\'ha trobat cap pòdcast.</string>
|
||||
<string name="podcast_release_date_duration_formatter">%1$s • %2$s</string>
|
||||
<string name="radio_editor_dialog_hint_homepage_url">URL de la pàgina d\'inici de la ràdio</string>
|
||||
<string name="radio_editor_dialog_hint_name">Nom de la ràdio</string>
|
||||
<string name="radio_editor_dialog_hint_stream_url">URL de la transmissió de la ràdio</string>
|
||||
<string name="radio_editor_dialog_negative_button">Cancel·la</string>
|
||||
<string name="radio_editor_dialog_neutral_button">Suprimeix</string>
|
||||
<string name="radio_editor_dialog_positive_button">Desa</string>
|
||||
<string name="radio_editor_dialog_title">Emissora de ràdio per Internet</string>
|
||||
<string name="radio_station_info_empty_button">Feu clic per a ocultar la secció.\nEls efectes seran visibles quan reinicieu l\'aplicació.</string>
|
||||
<string name="radio_station_info_empty_subtitle">Quan afegiu una emissora de ràdio, la trobareu aquí.</string>
|
||||
<string name="radio_station_info_empty_title">No s\'ha trobat cap emissora.</string>
|
||||
<string name="rating_dialog_negative_button">Cancel·la</string>
|
||||
<string name="rating_dialog_positive_button">Desa</string>
|
||||
<string name="rating_dialog_title">Valoració</string>
|
||||
<string name="search_hint">Cerqueu títols, artistes o àlbums</string>
|
||||
<string name="search_info_minimum_characters">Introduïu com a mínim tres caràcters</string>
|
||||
<string name="search_title_album">Àlbums</string>
|
||||
<string name="search_title_artist">Artistes</string>
|
||||
<string name="search_title_song">Cançons</string>
|
||||
<string name="server_signup_dialog_action_low_security">Seguretat baixa</string>
|
||||
<string name="server_signup_dialog_action_delete_toast">Manteniu-ho premut per a suprimir-ho</string>
|
||||
<string name="server_signup_dialog_hint_local_address">URL local</string>
|
||||
<string name="server_signup_dialog_hint_name">Nom del servidor</string>
|
||||
<string name="server_signup_dialog_hint_password">Contrasenya</string>
|
||||
<string name="server_signup_dialog_hint_url">URL del servidor</string>
|
||||
<string name="server_signup_dialog_hint_username">Nom d\'usuari</string>
|
||||
<string name="server_signup_dialog_negative_button">Cancel·la</string>
|
||||
<string name="server_signup_dialog_neutral_button">Suprimeix</string>
|
||||
<string name="server_signup_dialog_positive_button">Desa</string>
|
||||
<string name="server_signup_dialog_title">Addició d\'un servidor</string>
|
||||
<string name="server_unreachable_dialog_negative_button">Cancel·la</string>
|
||||
<string name="server_unreachable_dialog_neutral_button">Ves a l\'inici de sessió</string>
|
||||
<string name="server_unreachable_dialog_positive_button">Continua igualment</string>
|
||||
<string name="server_unreachable_dialog_summary">El servidor sol·licitat no està disponible. Si trieu continuar, aquest quadre de diàleg no apareixerà durant una hora.</string>
|
||||
<string name="server_unreachable_dialog_title">Servidor no disponible</string>
|
||||
<string name="settings_about_summary">Tempus és un client de música lliure i lleuger per al Subsonic, dissenyat i creat nativament per a l\'Android.</string>
|
||||
<string name="settings_about_title">Quant a</string>
|
||||
<string name="settings_always_on_display">Pantalla sempre encesa</string>
|
||||
<string name="settings_allow_playlist_duplicates">Permet afegir duplicats a una llista de reproducció</string>
|
||||
<string name="settings_allow_playlist_duplicates_summary">Si s\'habilita, no es comprovarà si hi ha elements duplicats en afegir-los a una llista de reproducció.</string>
|
||||
<string name="settings_audio_transcode_download_format">Format de transcodificació</string>
|
||||
<string name="settings_audio_transcode_download_priority_summary">Si s\'habilita, Tempus no forçarà la baixada de la pista amb els paràmetres de transcodificació següents.</string>
|
||||
<string name="settings_audio_transcode_download_priority_title">Prioritza els paràmetres del servidor per a la transmissió a les baixades</string>
|
||||
<string name="settings_audio_transcode_download_summary">Si s\'habilita, Tempus baixarà les pistes transcodificades.</string>
|
||||
<string name="settings_audio_transcode_download_title">Baixa les pistes transcodificades</string>
|
||||
<string name="settings_audio_transcode_estimate_content_length_summary">Si s\'habilita, es demanarà al servidor la durada estimada de la pista.</string>
|
||||
<string name="settings_audio_transcode_estimate_content_length_title">Estima la durada del contingut</string>
|
||||
<string name="settings_audio_transcode_format_download">Format de transcodificació per a les baixades</string>
|
||||
<string name="settings_audio_transcode_format_mobile">Format de transcodificació amb dades mòbils</string>
|
||||
<string name="settings_audio_transcode_format_wifi">Format de transcodificació amb wifi</string>
|
||||
<string name="settings_audio_transcode_priority_summary">Si s\'habilita, Tempus no forçarà la transmissió de la pista amb els paràmetres de transcodificació següents.</string>
|
||||
<string name="settings_audio_transcode_priority_title">Prioritza els paràmetres de transcodificació del servidor</string>
|
||||
<string name="settings_audio_transcode_priority_toast">Prioritat de transcodificació de la pista proporcionada al servidor</string>
|
||||
<string name="settings_buffering_strategy">Estratègia de memòria intermèdia</string>
|
||||
<string name="settings_buffering_strategy_summary">Perquè el canvi tingui efecte, heu de reiniciar l\'aplicació manualment.</string>
|
||||
<string name="settings_choose_download_folder">Trieu una carpeta per als fitxers de música baixats.</string>
|
||||
<string name="settings_clear_download_folder">Esborra la carpeta de baixades</string>
|
||||
<string name="settings_continuous_play_summary">Permet que segueixi sonant música quan acabi una llista de reproducció; es reproduiran cançons similars.</string>
|
||||
<string name="settings_continuous_play_title">Reproducció contínua</string>
|
||||
<string name="settings_covers_cache">Mida de la memòria cau de les caràtules</string>
|
||||
<string name="settings_data_saving_mode_summary">Per a reduir el consum de dades, evita la baixada de les caràtules.</string>
|
||||
<string name="settings_data_saving_mode_title">Limita l\'ús de les dades mòbils</string>
|
||||
<string name="settings_delete_download_storage_summary">Si continueu, se suprimiran permanentment tots els elements desats.</string>
|
||||
<string name="settings_delete_download_storage_title">Suprimeix els elements desats</string>
|
||||
<string name="settings_download_storage_title">Emmagatzematge per a les baixades.</string>
|
||||
<string name="settings_download_folder_cleared">S\'ha esborrat la carpeta de baixades.</string>
|
||||
<string name="settings_download_folder_set">S\'ha definit la carpeta de baixades.</string>
|
||||
<string name="settings_set_download_folder">Defineix la carpeta de baixades</string>
|
||||
<string name="settings_system_equalizer_summary">Ajusteu els paràmetres d\'àudio</string>
|
||||
<string name="settings_system_equalizer_title">Equalitzador del sistema</string>
|
||||
<string name="settings_github_link">https://github.com/eddyizm/tempus</string>
|
||||
<string name="settings_github_summary">Seguiu el desenvolupament</string>
|
||||
<string name="settings_github_title">Github</string>
|
||||
<string name="settings_support_discussion_link">https://github.com/eddyizm/tempus/discussions</string>
|
||||
<string name="settings_github_update">Actualitzacions</string>
|
||||
<string name="settings_github_update_title">Comprova si hi ha actualitzacions a GitHub</string>
|
||||
<string name="settings_github_update_summary">Si feu servir la versió de GitHub, per defecte l\'aplicació comprovarà si hi ha noves versions en format APK. Canvieu-ho per a inhabilitar les comprovacions automàtiques de GitHub.</string>
|
||||
<string name="settings_support_summary">Uniu-vos a discussions de la comunitat i obteniu ajuda</string>
|
||||
<string name="settings_support_title">Ajuda als usuaris</string>
|
||||
<string name="settings_scan_result">S\'està analitzant: s\'han comptat %1$d pistes</string>
|
||||
<string name="settings_image_size">Resolució de les imatges</string>
|
||||
<string name="settings_language">Llengua</string>
|
||||
<string name="settings_logout_title">Tanca la sessió</string>
|
||||
<string name="settings_max_bitrate_download">Taxa de bits de les baixades</string>
|
||||
<string name="settings_max_bitrate_mobile">Taxa de bits amb dades mòbils</string>
|
||||
<string name="settings_max_bitrate_wifi">Taxa de bits amb wifi</string>
|
||||
<string name="settings_media_cache">Mida de la memòria cau de fitxers multimèdia</string>
|
||||
<string name="settings_music_directory">Mostra les carpetes de música</string>
|
||||
<string name="settings_music_directory_summary">Si s\'habilita, es mostra la secció de carpetes de música. Tingueu en compte que, perquè la navegació amb carpetes funcioni correctament, el servidor ha de ser compatible amb aquesta característica.</string>
|
||||
<string name="settings_podcast">Mostra els pòdcasts</string>
|
||||
<string name="settings_podcast_summary">Si s\'habilita, es mostra la secció de pòdcasts. Reinicieu l\'aplicació perquè tingui efecte completament.</string>
|
||||
<string name="settings_audio_quality">Mostra la qualitat de l\'àudio</string>
|
||||
<string name="settings_audio_quality_summary">Es mostraran la taxa de bits i el format d\'àudio per a cada pista d\'àudio.</string>
|
||||
<string name="settings_song_rating">Mostra la valoració amb estrelles de les cançons</string>
|
||||
<string name="settings_song_rating_summary">Si s\'habilita, es mostra la valoració de 5 estrelles d\'una pista a la pàgina de la cançó.\n\n*Cal reiniciar l\'aplicació</string>
|
||||
<string name="settings_item_rating">Mostra la valoració dels elements</string>
|
||||
<string name="settings_item_rating_summary">Si s\'habilita, es mostrarà la valoració dels elements i si s\'han marcat com a preferits.</string>
|
||||
<string name="settings_queue_syncing_countdown">Temporitzador de sincronització</string>
|
||||
<string name="settings_queue_syncing_summary">Si s\'habilita, l\'usuari tindrà la possibilitat de desar la cua de reproducció i carregar l\'estat en obrir l\'aplicació.</string>
|
||||
<string name="settings_queue_syncing_title">Sincronitza la cua de reproducció d\'aquest usuari (implantat parcialment)</string>
|
||||
<string name="settings_show_mini_shuffle_button">Mostra el botó de reproducció aleatòria</string>
|
||||
<string name="settings_show_mini_shuffle_button_summary">Si s\'habilita, es mostra el botó de reproducció aleatòria i se suprimeix el cor del minireproductor.</string>
|
||||
<string name="settings_radio">Mostra la ràdio</string>
|
||||
<string name="settings_radio_summary">Si s\'habilita, es mostra la secció de ràdio. Reinicieu l\'aplicació perquè tingui efecte completament.</string>
|
||||
<string name="settings_auto_download_lyrics">Baixa les lletres automàticament</string>
|
||||
<string name="settings_auto_download_lyrics_summary">Desa automàticament les lletres quan estiguin disponibles perquè es puguin mostrar fora de línia.</string>
|
||||
<string name="settings_replay_gain">Mode de ReplayGain</string>
|
||||
<string name="settings_rounded_corner">Cantonades arrodonides</string>
|
||||
<string name="settings_rounded_corner_size">Mida de les cantonades</string>
|
||||
<string name="settings_rounded_corner_size_summary">Defineix la magnitud de l\'angle de curvatura.</string>
|
||||
<string name="settings_rounded_corner_summary">Si s\'habilita, defineix un angle de curvatura per a totes les caràtules representades. Els canvis tindran efecte quan reinicieu l\'aplicació.</string>
|
||||
<string name="settings_scan_title">Analitza la biblioteca</string>
|
||||
<string name="settings_scrobble_title">Habilita l\'anàlisi musical</string>
|
||||
<string name="settings_system_language">Llengua del sistema</string>
|
||||
<string name="settings_share_title">Habilita l\'ús compartit de música</string>
|
||||
<string name="settings_streaming_cache_size">Mida de la memòria cau de transmissió</string>
|
||||
<string name="settings_streaming_cache_storage_title">Emmagatzematge de la memòria cau de transmissió</string>
|
||||
<string name="settings_sub_summary_scrobble">Tingueu en compte que l\'anàlisi musical també depèn que el servidor tingui habilitada la recepció d\'aquestes dades.</string>
|
||||
<string name="settings_summary_skip_min_star_rating">En escoltar la ràdio d\'un artista, una mescla instantània o totes les cançons aleatòriament, les pistes per sota d\'un llindar de l\'usuari s\'ignoraran.</string>
|
||||
<string name="settings_summary_replay_gain">ReplayGain és una característica que us permet ajustar el nivell de volum de les pistes d\'àudio perquè l\'escolta sigui coherent. Aquest paràmetre només és efectiu si la pista conté les metadades necessàries.</string>
|
||||
<string name="settings_summary_scrobble">L\'anàlisi musical és una característica que permet al vostre dispositiu enviar informació sobre les cançons que escolteu al servidor de música. Aquesta informació ajuda a crear recomanacions personalitzades a partir de les vostres preferències musicals.</string>
|
||||
<string name="settings_summary_share">Permet que l\'usuari comparteixi música mitjançant un enllaç. El servidor ha de ser compatible i tenir habilitada aquesta característica i es limita a pistes individuals, àlbums i llistes de reproducció.</string>
|
||||
<string name="settings_summary_syncing">Retorna l\'estat de la cua de reproducció per a aquest usuari. Això inclou les pistes a la cua de reproducció, la pista en reproducció actualment i la posició de la pista. El servidor ha de ser compatible amb aquesta característica.\n*Aquest paràmetre no funciona al 100% en tots els servidors/dispositius.</string>
|
||||
<string name="settings_summary_streaming_cache_size">%1$s \nEn ús actualment: %2$s MiB</string>
|
||||
<string name="settings_summary_transcoding">Es dona prioritat al mode de transcodificació. Si s\'estableix en «Reproducció directa», la taxa de bits del fitxer no canviarà.</string>
|
||||
<string name="settings_summary_transcoding_download">Baixa el contingut multimèdia transcodificat. Si s\'habilita, no es farà servir l\'extrem de baixada, sinó els paràmetres següents. \n\nSi s\'estableix «Format de transcodificació per a les baixades» en «Baixada directa», la taxa de bits del fitxer no canviarà.</string>
|
||||
<string name="settings_summary_transcoding_estimate_content_length">Quan el fitxer es transcodifica en temps real, el client normalment no mostra la durada de la pista. És possible sol·licitar una estimació de la durada de la pista en reproducció als servidors compatibles, però els temps de resposta poden ser més llargs.</string>
|
||||
<string name="settings_sync_starred_artists_for_offline_use_summary">Si s\'habilita, els artistes amb estrelles es baixaran per a l\'ús fora de línia.</string>
|
||||
<string name="settings_sync_starred_artists_for_offline_use_title">Sincronitza els artistes amb estrelles per a l\'ús fora de línia</string>
|
||||
<string name="settings_sync_starred_albums_for_offline_use_summary">Si s\'habilita, els àlbums amb estrelles es baixaran per a l\'ús fora de línia.</string>
|
||||
<string name="settings_sync_starred_albums_for_offline_use_title">Sincronitza els àlbums amb estrelles per a l\'ús fora de línia</string>
|
||||
<string name="settings_sync_starred_tracks_for_offline_use_summary">Si s\'habilita, les pistes amb estrelles es baixaran per a l\'ús fora de línia.</string>
|
||||
<string name="settings_sync_starred_tracks_for_offline_use_title">Sincronitza les pistes amb estrelles per a l\'ús fora de línia</string>
|
||||
<string name="settings_theme">Tema</string>
|
||||
<string name="settings_title_data">Dades</string>
|
||||
<string name="settings_title_general">General</string>
|
||||
<string name="settings_title_playlist">Llista de reproducció</string>
|
||||
<string name="settings_title_rating">Valoració</string>
|
||||
<string name="settings_title_replay_gain">ReplayGain</string>
|
||||
<string name="settings_title_scrobble">Anàlisi musical</string>
|
||||
<string name="settings_title_skip_min_star_rating">Ignora les pistes segons la valoració</string>
|
||||
<string name="settings_title_skip_min_star_rating_dialog">Cançons amb una valoració de:</string>
|
||||
<string name="settings_title_share">Comparteix</string>
|
||||
<string name="settings_title_syncing">Sincronització</string>
|
||||
<string name="settings_title_transcoding">Transcodificació</string>
|
||||
<string name="settings_title_transcoding_download">Baixada transcodificada</string>
|
||||
<string name="settings_title_ui">Interfície d\'usuari</string>
|
||||
<string name="settings_transcoded_download">Baixada transcodificada</string>
|
||||
<string name="settings_version_summary" translatable="false">3.1.0</string>
|
||||
<string name="settings_version_title">Versió</string>
|
||||
<string name="settings_wifi_only_summary">Demana la confirmació de l\'usuari abans d\'iniciar la transmissió per la xarxa mòbil.</string>
|
||||
<string name="settings_wifi_only_title">Alerta de transmissió només per wifi</string>
|
||||
<string name="share_bottom_sheet_copy_link">Copia l\'enllaç</string>
|
||||
<string name="share_bottom_sheet_delete">Suprimeix l\'element compartit</string>
|
||||
<string name="share_bottom_sheet_update">Actualitza l\'element compartit</string>
|
||||
<string name="share_subtitle_item">Data de venciment: %1$s</string>
|
||||
<string name="share_no_expiration">Mai</string>
|
||||
<string name="share_unsupported_error">L\'ús compartit no s\'admet o no està habilitat</string>
|
||||
<string name="asset_link_clipboard_label">Enllaç a recurs de Tempus</string>
|
||||
<string name="asset_link_label_song">UID de la cançó</string>
|
||||
<string name="asset_link_label_album">UID de l\'àlbum</string>
|
||||
<string name="asset_link_label_artist">UID de l\'artista</string>
|
||||
<string name="asset_link_label_playlist">UID de la llista de reproducció</string>
|
||||
<string name="asset_link_label_genre">UID del gènere</string>
|
||||
<string name="asset_link_label_year">UID de l\'any</string>
|
||||
<string name="asset_link_label_unknown">UID del recurs</string>
|
||||
<string name="asset_link_error_unsupported">Enllaç a recurs no admès</string>
|
||||
<string name="asset_link_error_song">No s\'ha pogut obrir la cançó</string>
|
||||
<string name="asset_link_error_album">No s\'ha pogut obrir l\'àlbum</string>
|
||||
<string name="asset_link_error_artist">No s\'ha pogut obrir l\'artista</string>
|
||||
<string name="asset_link_error_playlist">No s\'ha pogut obrir la llista de reproducció</string>
|
||||
<string name="asset_link_chip_text">%1$s • %2$s</string>
|
||||
<string name="asset_link_copied_toast">S\'ha copiat %1$s al porta-retalls</string>
|
||||
<string name="asset_link_debug_toast">Enllaç al recurs: %1$s</string>
|
||||
<string name="share_update_dialog_hint_description">Descripció</string>
|
||||
<string name="share_update_dialog_hint_expiration_date">Data de venciment</string>
|
||||
<string name="share_update_dialog_negative_button">Cancel·la</string>
|
||||
<string name="share_update_dialog_positive_button">Desa</string>
|
||||
<string name="share_update_dialog_title">Comparteix</string>
|
||||
<string name="song_bottom_sheet_add_to_playlist">Afegeix a la llista de reproducció</string>
|
||||
<string name="song_bottom_sheet_add_to_queue">Afegeix a la cua</string>
|
||||
<string name="song_bottom_sheet_download">Baixa</string>
|
||||
<string name="song_bottom_sheet_error_retrieving_album">S\'ha produït un error en recuperar l\'àlbum</string>
|
||||
<string name="song_bottom_sheet_error_retrieving_artist">S\'ha produït un error en recuperar l\'artista</string>
|
||||
<string name="song_bottom_sheet_go_to_album">Ves a l\'àlbum</string>
|
||||
<string name="song_bottom_sheet_go_to_artist">Ves a l\'artista</string>
|
||||
<string name="song_bottom_sheet_instant_mix">Mescla instantània</string>
|
||||
<string name="song_bottom_sheet_play_next">Reprodueix a continuació</string>
|
||||
<string name="song_bottom_sheet_rate">Valoració</string>
|
||||
<string name="song_bottom_sheet_remove">Suprimeix</string>
|
||||
<string name="song_bottom_sheet_share">Comparteix</string>
|
||||
<string name="song_list_page_downloaded">Baixades</string>
|
||||
<string name="song_list_page_most_played">Pistes més reproduïdes</string>
|
||||
<string name="song_list_page_recently_added">Pistes afegides recentment</string>
|
||||
<string name="song_list_page_recently_played">Pistes reproduïdes recentment</string>
|
||||
<string name="song_list_page_starred">Pistes amb estrelles</string>
|
||||
<string name="song_list_page_top">Les millors cançons de %1$s</string>
|
||||
<string name="song_list_page_year">Any %1$d</string>
|
||||
<string name="song_subtitle_formatter">%1$s • %2$s %3$s</string>
|
||||
<string name="starred_sync_dialog_negative_button">Cancel·la</string>
|
||||
<string name="starred_sync_dialog_neutral_button">Continua</string>
|
||||
<string name="starred_sync_dialog_positive_button">Continua i baixa</string>
|
||||
<string name="starred_sync_dialog_summary">La baixada de les pistes amb estrelles pot requerir un ús de dades important.</string>
|
||||
<string name="starred_sync_dialog_title">Sincronitza les pistes amb estrelles</string>
|
||||
<string name="starred_artist_sync_dialog_summary">La baixada dels artistes amb estrelles pot requerir un ús de dades important.</string>
|
||||
<string name="starred_artist_sync_dialog_title">Sincronitza els artistes amb estrelles</string>
|
||||
<string name="starred_album_sync_dialog_summary">La baixada dels àlbums amb estrelles pot requerir un ús de dades important.</string>
|
||||
<string name="starred_album_sync_dialog_title">Sincronitza els àlbums amb estrelles</string>
|
||||
<string name="streaming_cache_storage_dialog_sub_summary">Perquè els canvis tinguin efecte, reinicieu l\'aplicació.</string>
|
||||
<string name="streaming_cache_storage_dialog_summary">Si canvieu la destinació dels fitxers emmagatzemats a la memòria cau d\'un emmagatzematge a un altre, se suprimiran immediatament tots els fitxers emmagatzemats anteriorment a la memòria cau de l\'altre emmagatzematge.</string>
|
||||
<string name="streaming_cache_storage_dialog_title">Selecció de l\'opció d\'emmagatzematge</string>
|
||||
<string name="streaming_cache_storage_external_dialog_positive_button">Extern</string>
|
||||
<string name="streaming_cache_storage_internal_dialog_negative_button">Intern</string>
|
||||
<string name="support_url">https://ko-fi.com/eddyizm</string>
|
||||
<string name="track_info_album">Àlbum</string>
|
||||
<string name="track_info_artist">Artista</string>
|
||||
<string name="track_info_bit_depth">Profunditat de bits</string>
|
||||
<string name="track_info_bitrate">Taxa de bits</string>
|
||||
<string name="track_info_content_type">Tipus de contingut</string>
|
||||
<string name="track_info_dialog_positive_button">D\'acord</string>
|
||||
<string name="track_info_dialog_title">Informació de la pista</string>
|
||||
<string name="track_info_disc_number">Número de disc</string>
|
||||
<string name="track_info_duration">Durada</string>
|
||||
<string name="track_info_genre">Gènere</string>
|
||||
<string name="track_info_path">Camí</string>
|
||||
<string name="track_info_sampling_rate">Freqüència de mostreig</string>
|
||||
<string name="track_info_size">Mida</string>
|
||||
<string name="track_info_suffix">Sufix</string>
|
||||
<string name="track_info_summary_downloaded_file">El fitxer s\'ha baixat amb les API del Subsonic. El còdec i la taxa de bits del fitxer són els mateixos que els del fitxer d\'origen.</string>
|
||||
<string name="track_info_summary_full_transcode">L\'aplicació sol·licitarà al servidor la transcodificació del fitxer i la modificació de la taxa de bits. El còdec sol·licitat per l\'usuari és %1$s, amb una taxa de bits de %2$s. Qualsevol canvi potencial del còdec i la taxa de bits del fitxer en el format triat el gestionarà el servidor, que pot admetre l\'operació o no.</string>
|
||||
<string name="track_info_summary_original_file">L\'aplicació només llegirà el fitxer original proporcionat pel servidor. L\'aplicació sol·licitarà explícitament al servidor el fitxer transcodificat amb la taxa de bits del fitxer original.</string>
|
||||
<string name="track_info_summary_server_prioritized">La qualitat del fitxer reproduït dependrà de la decisió del servidor. L\'aplicació no forçarà la tria de còdec i taxa de bits per a cap transcodificació potencial.</string>
|
||||
<string name="track_info_summary_transcoding_bitrate">L\'aplicació sol·licitarà al servidor la modificació de la taxa de bits del fitxer. L\'usuari ha sol·licitat una taxa de bits de %1$s, mentre que el còdec del fitxer d\'origen seguirà sent el mateix. Qualsevol canvi de la taxa de bits del fitxer en el format triat el gestionarà el servidor, que pot admetre l\'operació o no.</string>
|
||||
<string name="track_info_summary_transcoding_codec">L\'aplicació sol·licitarà al servidor la transcodificació del fitxer. El còdec sol·licitat per l\'usuari és %1$s, mentre que la taxa de bits serà la mateixa que la del fitxer d\'origen. La transcodificació potencial del fitxer en el format triat depèn del servidor, que pot admetre l\'operació o no.</string>
|
||||
<string name="track_info_title">Títol</string>
|
||||
<string name="track_info_track_number">Número de pista</string>
|
||||
<string name="track_info_transcoded_content_type">Tipus de contingut transcodificat</string>
|
||||
<string name="track_info_transcoded_suffix">Sufix de la transcodificació</string>
|
||||
<string name="track_info_year">Any</string>
|
||||
<string name="undraw_page">unDraw</string>
|
||||
<string name="undraw_thanks">Volem donar un agraïment especial a UnDraw; aquesta aplicació no seria tan bonica sense les seves il·lustracions.</string>
|
||||
<string name="undraw_url">https://undraw.co/</string>
|
||||
<string name="widget_label">Giny de Tempus</string>
|
||||
<string name="widget_not_playing">No hi ha res en reproducció</string>
|
||||
<string name="widget_placeholder_subtitle">Obre Tempus</string>
|
||||
<string name="widget_time_elapsed_placeholder">0:00</string>
|
||||
<string name="widget_time_duration_placeholder">0:00</string>
|
||||
<string name="widget_content_desc_album_art">Caràtula de l\'àlbum</string>
|
||||
<string name="widget_content_desc_play_pause">Reprodueix o posa en pausa</string>
|
||||
<string name="widget_content_desc_next">Pista següent</string>
|
||||
<string name="widget_content_desc_prev">Pista anterior</string>
|
||||
<string name="widget_content_desc_shuffle">Reprodueix aleatòriament</string>
|
||||
<string name="widget_content_desc_repeat">Canvia el mode de repetició</string>
|
||||
<plurals name="home_sync_starred_albums_count">
|
||||
<item quantity="one">Se sincronitzarà %d àlbum</item>
|
||||
<item quantity="other">Se sincronitzaran %d àlbums</item>
|
||||
</plurals>
|
||||
<plurals name="home_sync_starred_artists_count">
|
||||
<item quantity="one">Se sincronitzarà %d artista</item>
|
||||
<item quantity="other">Se sincronitzaran %d artistes</item>
|
||||
</plurals>
|
||||
<plurals name="songs_download_started">
|
||||
<item quantity="one">S\'està baixant %d cançó</item>
|
||||
<item quantity="other">S\'estan baixant %d cançons</item>
|
||||
</plurals>
|
||||
<string name="equalizer_fragment_title">Equalitzador</string>
|
||||
<string name="equalizer_reset">Reinicialitza</string>
|
||||
<string name="equalizer_enable">Habilita</string>
|
||||
<string name="equalizer_not_supported">No és compatible amb aquest dispositiu</string>
|
||||
<string name="settings_app_equalizer">Equalitzador</string>
|
||||
<string name="settings_app_equalizer_summary">Obre l\'equalitzador integrat</string>
|
||||
|
||||
<string name="settings_album_detail">Mostra els detalls de l\'àlbum</string>
|
||||
<string name="settings_album_detail_summary">Si s\'habilita, es mostren els detalls de l\'àlbum, com el gènere i el nombre de cançons, a la pàgina de l\'àlbum.</string>
|
||||
<string name="settings_artist_sort_by_album_count">Ordena els artistes per nombre d\'àlbums</string>
|
||||
<string name="settings_artist_sort_by_album_count_summary">Si s\'habilita, ordena els artistes per nombre d\'àlbums. Si no, s\'ordenen per nom.</string>
|
||||
</resources>
|
||||
@@ -177,6 +177,7 @@
|
||||
<string name="menu_filter_download">Descargado</string>
|
||||
<string name="menu_group_by_album">Álbum</string>
|
||||
<string name="menu_group_by_artist">Artista</string>
|
||||
<string name="settings_github_update_title">Comprobar actualizaciones en GitHub</string>
|
||||
<string name="settings_scan_result">Escaneo: hay %1$d pistas</string>
|
||||
<string name="settings_support_title">Soporte al usuario</string>
|
||||
<string name="settings_image_size">Resolución de la imagen</string>
|
||||
@@ -185,7 +186,7 @@
|
||||
<string name="settings_logout_title">Cerrar sesión</string>
|
||||
<string name="settings_github_link">https://github.com/eddyizm/tempus</string>
|
||||
<string name="settings_github_summary">Siga el desarrollo</string>
|
||||
<string name="settings_github_title">Github</string>
|
||||
<string name="settings_github_title">GitHub</string>
|
||||
<string name="menu_group_by_genre">Género</string>
|
||||
<string name="menu_group_by_track">Pista</string>
|
||||
<string name="menu_group_by_year">Año</string>
|
||||
@@ -199,6 +200,7 @@
|
||||
<string name="menu_sort_artist">Artista</string>
|
||||
<string name="menu_sort_name">Nombre</string>
|
||||
<string name="menu_sort_random">Aleatorio</string>
|
||||
<string name="menu_sort_album_count">Número de álbumes</string>
|
||||
<string name="menu_sort_recently_added">Añadido recientemente</string>
|
||||
<string name="menu_sort_recently_played">Reproducido recientemente</string>
|
||||
<string name="menu_sort_most_played">Lo más reproducido</string>
|
||||
@@ -413,7 +415,9 @@
|
||||
<string name="share_bottom_sheet_delete">Eliminar compartición</string>
|
||||
<string name="share_bottom_sheet_update">Actualizar compartición</string>
|
||||
<string name="share_subtitle_item">Fecha de caducidad: %1$s</string>
|
||||
<string name="share_no_expiration">Nunca</string>
|
||||
<string name="share_unsupported_error">El uso compartido no está soportado o no está habilitado</string>
|
||||
<string name="asset_link_debug_toast">Enlace de recurso: %1$s</string>
|
||||
<string name="share_update_dialog_hint_description">Descripción</string>
|
||||
<string name="share_update_dialog_hint_expiration_date">Fecha de caducidad</string>
|
||||
<string name="song_bottom_sheet_add_to_queue">Añadir a la cola</string>
|
||||
@@ -479,6 +483,7 @@
|
||||
<string name="player_lyrics_downloaded_content_description">Letras descargadas para uso sin conexión</string>
|
||||
<string name="player_lyrics_download_success">Letra guardada para uso sin conexión</string>
|
||||
<string name="settings_allow_playlist_duplicates">Permitir añadir pistas repetidas a la lista</string>
|
||||
<string name="settings_github_update_summary">Si se usa la versión de GitHub, la app comprobará nuevas actualizaciones del APK.</string>
|
||||
<string name="settings_support_summary">Participa en las discusiones y el soporte de la comunidad</string>
|
||||
<string name="settings_show_mini_shuffle_button">Mostrar el botón «Aleatorio»</string>
|
||||
<string name="settings_auto_download_lyrics">Descargar automáticamente las letras</string>
|
||||
@@ -486,4 +491,24 @@
|
||||
<string name="settings_sync_starred_artists_for_offline_use_summary">Si está habilitada, los artistas destacados se descargarán para uso sin conexión.</string>
|
||||
<string name="widget_time_elapsed_placeholder">0:00</string>
|
||||
<string name="exo_controls_heart_off_description">Eliminar de favoritos</string>
|
||||
<string name="asset_link_chip_text">%1$s • %2$s</string>
|
||||
<string name="asset_link_copied_toast">Copiado %1$s al portapapeles</string>
|
||||
<string name="settings_album_detail">Mostrar los detalles del álbum</string>
|
||||
<string name="settings_album_detail_summary">Si está habilitada, muestra los detalles del álbum, como el género, el número de pistas, etc. en la página de álbum</string>
|
||||
<string name="asset_link_clipboard_label">Enlace de recurso de Tempus</string>
|
||||
<string name="asset_link_label_song">UID de la pista</string>
|
||||
<string name="asset_link_label_album">UID del álbum</string>
|
||||
<string name="asset_link_label_artist">UID del artista</string>
|
||||
<string name="asset_link_label_playlist">UID de la lista de reproducción</string>
|
||||
<string name="asset_link_label_genre">UID del género</string>
|
||||
<string name="asset_link_label_year">UID del año</string>
|
||||
<string name="asset_link_label_unknown">UID del recurso</string>
|
||||
<string name="asset_link_error_unsupported">Enlace de recurso no válido</string>
|
||||
<string name="asset_link_error_song">No se ha podido abrir la pista</string>
|
||||
<string name="asset_link_error_album">No se ha podido abrir el álbum</string>
|
||||
<string name="asset_link_error_artist">No se ha podido abrir el artista</string>
|
||||
<string name="asset_link_error_playlist">No se ha podido abrir la lista de reproducción</string>
|
||||
<string name="settings_github_update">Actualizaciones</string>
|
||||
<string name="settings_artist_sort_by_album_count">Ordenar artistas por número de álbumes</string>
|
||||
<string name="settings_artist_sort_by_album_count_summary">Ordena los artistas por número de álbumes si la opción está habilitada. Si no, los ordena por nombre.</string>
|
||||
</resources>
|
||||
@@ -254,4 +254,4 @@
|
||||
<item>3</item>
|
||||
<item>4</item>
|
||||
</string-array>
|
||||
</resources>
|
||||
</resources>
|
||||
@@ -1,417 +1,536 @@
|
||||
<resources>
|
||||
<string name="activity_battery_optimizations_conclusion">Se hai problemi, visita https://dontkillmyapp.com. Qui trovi istruzioni dettagliate su come disabilitare le funzionalità di risparmio energetico che potrebbero influire sulle prestazioni dell\'app.</string>
|
||||
<string name="activity_battery_optimizations_summary">Per favore, disabilita le ottimizzazioni della batteria per la riproduzione multimediale quando lo schermo è spento.</string>
|
||||
<string name="activity_battery_optimizations_title">Ottimizzazioni della Batteria</string>
|
||||
<string name="activity_info_offline_mode">Modalità offline</string>
|
||||
<string name="activity_battery_optimizations_conclusion">Se hai problemi, visita https://dontkillmyapp.com. Qui trovi istruzioni dettagliate su come disabilitare le funzionalità di risparmio energetico che potrebbero influire sulle prestazioni dell\'app.</string>
|
||||
<string name="activity_battery_optimizations_summary">Disattiva le ottimizzazioni della batteria per la riproduzione multimediale quando lo schermo è spento.</string>
|
||||
<string name="activity_battery_optimizations_title">Ottimizzazioni della Batteria</string>
|
||||
<string name="activity_info_offline_mode">Modalità offline</string>
|
||||
<string name="album_bottom_sheet_add_to_playlist">Aggiungi alla playlist</string>
|
||||
<string name="album_bottom_sheet_add_to_queue">Aggiungi alla coda</string>
|
||||
<string name="album_bottom_sheet_download_all">Scarica tutto</string>
|
||||
<string name="album_bottom_sheet_go_to_artist">Vai all\'artista</string>
|
||||
<string name="album_bottom_sheet_instant_mix">Mix istantaneo</string>
|
||||
<string name="album_bottom_sheet_play_next">Riproduci successivo</string>
|
||||
<string name="album_bottom_sheet_remove_all">Rimuovi tutto</string>
|
||||
<string name="album_bottom_sheet_share">Condividi</string>
|
||||
<string name="album_bottom_sheet_shuffle">Riproduzione casuale</string>
|
||||
<string name="album_catalogue_title">Album</string>
|
||||
<string name="album_catalogue_title_expanded">Sfoglia Album</string>
|
||||
<string name="album_error_retrieving_artist">Errore nel recupero dell\'artista</string>
|
||||
<string name="album_list_page_downloaded">Album scaricati</string>
|
||||
<string name="album_list_page_most_played">Album più riprodotti</string>
|
||||
<string name="album_list_page_new_releases">Nuove uscite</string>
|
||||
<string name="album_list_page_recently_added">Album aggiunti di recente</string>
|
||||
<string name="album_list_page_recently_played">Album riprodotti di recente</string>
|
||||
<string name="album_list_page_starred">Album preferiti</string>
|
||||
<string name="album_list_page_title">Album</string>
|
||||
<string name="album_page_extra_info_button">Simili a questo</string>
|
||||
<string name="album_page_play_button">Riproduci</string>
|
||||
<string name="album_page_release_date_label">Rilasciato il %1$s</string>
|
||||
<string name="album_page_release_dates_label">Rilasciato il %1$s, originariamente il %2$s</string>
|
||||
<string name="album_page_shuffle_button">Riproduzione casuale</string>
|
||||
<string name="album_page_tracks_count_and_duration">%1$d brani • %2$d minuti</string>
|
||||
<string name="app_name">Tempus</string>
|
||||
<string name="artist_adapter_radio_station_starting">Ricerca in corso…</string>
|
||||
<string name="artist_bottom_sheet_instant_mix">Mix istantaneo</string>
|
||||
<string name="artist_bottom_sheet_shuffle">Riproduzione casuale</string>
|
||||
<string name="artist_catalogue_title">Artisti</string>
|
||||
<string name="artist_catalogue_title_expanded">Sfoglia Artisti</string>
|
||||
<string name="artist_error_retrieving_radio">Errore nel recupero della radio dell\'artista</string>
|
||||
<string name="artist_error_retrieving_tracks">Errore nel recupero dei brani dell\'artista</string>
|
||||
<string name="artist_list_page_downloaded">Artisti scaricati</string>
|
||||
<string name="artist_list_page_starred">Artisti preferiti</string>
|
||||
<string name="artist_list_page_title">Artisti</string>
|
||||
<string name="artist_page_radio_button">Radio</string>
|
||||
<string name="artist_page_shuffle_button">Riproduzione casuale</string>
|
||||
<string name="artist_page_switch_layout_button">Cambia layout</string>
|
||||
<string name="artist_page_title_album_more_like_this_button">Simili a questo</string>
|
||||
<string name="artist_page_title_album_section">Album</string>
|
||||
<string name="artist_page_title_biography_more_button">Altro</string>
|
||||
<string name="artist_page_title_biography_section">Biografia</string>
|
||||
<string name="artist_page_title_most_streamed_song_section">Brani più ascoltati</string>
|
||||
<string name="artist_page_title_most_streamed_song_see_all_button">Vedi tutto</string>
|
||||
<string name="battery_optimization_negative_button">Ignora</string>
|
||||
<string name="battery_optimization_neutral_button">Non chiedere di nuovo</string>
|
||||
<string name="battery_optimization_positive_button">Disabilita</string>
|
||||
<string name="connection_alert_dialog_negative_button">Annulla</string>
|
||||
<string name="connection_alert_dialog_neutral_button">Attiva risparmio dati</string>
|
||||
<string name="connection_alert_dialog_positive_button">OK</string>
|
||||
<string name="connection_alert_dialog_summary">L\'accesso al server Subsonic è stato limitato alle connessioni Wi-Fi. Per evitare che questo avviso riappaia, disabilita il controllo connessione nelle impostazioni dell\'app.</string>
|
||||
<string name="connection_alert_dialog_title">Wi-Fi non connesso</string>
|
||||
<string name="content_description_shuffle_button">Riproduzione casuale</string>
|
||||
<string name="delete_download_storage_dialog_negative_button">Annulla</string>
|
||||
<string name="delete_download_storage_dialog_positive_button">Continua</string>
|
||||
<string name="delete_download_storage_dialog_summary">Attenzione, procedendo questa azione eliminerà definitivamente tutti gli elementi scaricati da tutti i server.</string>
|
||||
<string name="delete_download_storage_dialog_title">Elimina elementi salvati</string>
|
||||
<string name="description_empty_title">Descrizione non disponibile</string>
|
||||
<string name="disc_titlefull">Disco %1$s - %2$s</string>
|
||||
<string name="disc_titleless">Disco %1$s</string>
|
||||
<string name="download_directory_dialog_negative_button">Annulla</string>
|
||||
<string name="download_directory_dialog_positive_button">Scarica</string>
|
||||
<string name="download_directory_dialog_summary">Tutti i brani in questa cartella verranno scaricati. I brani nelle sottocartelle non verranno scaricati.</string>
|
||||
<string name="download_directory_dialog_title">Scarica i brani</string>
|
||||
<string name="download_info_empty_subtitle">Una volta scaricato un brano, lo troverai qui</string>
|
||||
<string name="download_info_empty_title">Nessun download ancora!</string>
|
||||
<string name="download_item_multiple_subtitle_formatter">%1$s • %2$s elementi</string>
|
||||
<string name="download_item_single_subtitle_formatter">%1$s elementi</string>
|
||||
<string name="download_shuffle_all_subtitle">Riproduzione casuale di tutto</string>
|
||||
<string name="download_storage_dialog_sub_summary">Per rendere effettive le modifiche, riavvia l\'app.</string>
|
||||
<string name="download_storage_dialog_summary">Cambiare la destinazione dei file scaricati da una memoria all\'altra eliminerà immediatamente tutti i file scaricati precedentemente nella vecchia memoria.</string>
|
||||
<string name="download_storage_dialog_title">Seleziona opzione di memoria</string>
|
||||
<string name="download_storage_external_dialog_positive_button">Esterna</string>
|
||||
<string name="download_storage_internal_dialog_negative_button">Interna</string>
|
||||
<string name="download_title_section">Download</string>
|
||||
<string name="downloaded_bottom_sheet_add_to_queue">Aggiungi alla coda</string>
|
||||
<string name="downloaded_bottom_sheet_play_next">Riproduci successivo</string>
|
||||
<string name="downloaded_bottom_sheet_remove">Rimuovi</string>
|
||||
<string name="downloaded_bottom_sheet_remove_all">Rimuovi tutto</string>
|
||||
<string name="downloaded_bottom_sheet_shuffle">Riproduzione casuale</string>
|
||||
<string name="empty_string" />
|
||||
<string name="error_required">Obbligatorio</string>
|
||||
<string name="error_server_prefix">Prefisso http o https richiesto</string>
|
||||
<string name="exo_download_notification_channel_name">Download</string>
|
||||
<string name="filter_info_selection">Seleziona due o più filtri</string>
|
||||
<string name="filter_title">Filtro</string>
|
||||
<string name="filter_title_expanded">Filtra Generi</string>
|
||||
<string name="genre_catalogue_title">Catalogo dei Generi</string>
|
||||
<string name="genre_catalogue_title_expanded">Sfoglia Generi</string>
|
||||
<string name="github_update_dialog_negative_button">Ricordamelo più tardi</string>
|
||||
<string name="github_update_dialog_neutral_button">Supportami</string>
|
||||
<string name="github_update_dialog_positive_button">Scarica ora</string>
|
||||
<string name="github_update_dialog_summary">È disponibile una nuova versione dell\'app su Github.</string>
|
||||
<string name="github_update_dialog_title">Aggiornamento disponibile</string>
|
||||
<string name="home_rearrangement_dialog_negative_button">Annulla</string>
|
||||
<string name="home_rearrangement_dialog_neutral_button">Reimposta</string>
|
||||
<string name="home_rearrangement_dialog_positive_button">Salva</string>
|
||||
<string name="home_rearrangement_dialog_title">Riorganizza home</string>
|
||||
<string name="home_rearrangement_dialog_subtitle">Si prega di notare che per rendere effettive le modifiche è necessario riavviare l\'applicazione.</string>
|
||||
<string name="home_subtitle_best_of">Le migliori canzoni dei tuoi artisti preferiti</string>
|
||||
<string name="home_subtitle_made_for_you">Inizia un mix da una canzone che ti è piaciuta</string>
|
||||
<string name="home_subtitle_new_internet_radio_station">Aggiungi una nuova radio</string>
|
||||
<string name="home_subtitle_new_podcast_channel">Aggiungi un nuovo canale podcast</string>
|
||||
<string name="home_sync_starred_cancel">Annulla</string>
|
||||
<string name="home_sync_starred_download">Scarica</string>
|
||||
<string name="home_sync_starred_subtitle">Scaricare questi brani potrebbe comportare un uso significativo di dati</string>
|
||||
<string name="home_sync_starred_title">Sembra che ci siano brani da sincronizzare con una stella</string>
|
||||
<string name="home_title_best_of">Il meglio di</string>
|
||||
<string name="home_title_discovery">Scoperta</string>
|
||||
<string name="home_title_discovery_shuffle_all_button">Mescola tutto</string>
|
||||
<string name="home_title_flashback">Flashback</string>
|
||||
<string name="home_title_internet_radio_station">Stazioni radio internet</string>
|
||||
<string name="home_title_last_played">Ultimi ascolti</string>
|
||||
<string name="home_title_last_played_see_all_button">Vedi tutto</string>
|
||||
<string name="home_title_last_week">La scorsa settimana</string>
|
||||
<string name="home_title_last_month">Il mese scorso</string>
|
||||
<string name="home_title_last_year">L\'anno scorso</string>
|
||||
<string name="home_title_made_for_you">Fatto per te</string>
|
||||
<string name="home_title_most_played">Più ascoltati</string>
|
||||
<string name="home_title_most_played_see_all_button">Vedi tutto</string>
|
||||
<string name="home_title_new_releases">Nuove uscite</string>
|
||||
<string name="home_title_newest_podcasts">Podcast più recenti</string>
|
||||
<string name="home_title_pinned_playlists">Playlist</string>
|
||||
<string name="home_title_podcast_channels">Canali</string>
|
||||
<string name="home_title_podcast_channels_see_all_button">Vedi tutto</string>
|
||||
<string name="home_title_radio_station">Stazioni radio</string>
|
||||
<string name="home_title_recently_added">Aggiunti di recente</string>
|
||||
<string name="home_title_recently_added_see_all_button">Vedi tutto</string>
|
||||
<string name="home_title_shares">Condivisioni</string>
|
||||
<string name="home_title_starred_albums">★ Album con stella</string>
|
||||
<string name="home_title_starred_albums_see_all_button">Vedi tutto</string>
|
||||
<string name="home_title_starred_artists">★ Artisti con stella</string>
|
||||
<string name="home_title_starred_artists_see_all_button">Vedi tutto</string>
|
||||
<string name="home_title_starred_tracks">★ Brani con stella</string>
|
||||
<string name="home_title_starred_tracks_see_all_button">Vedi tutto</string>
|
||||
<string name="home_title_top_songs">I tuoi migliori brani</string>
|
||||
<string name="home_option_reorganize">Riorganizza</string>
|
||||
<string name="label_dot_separator" translatable="false">•</string>
|
||||
<string name="label_placeholder" translatable="false">--</string>
|
||||
<string name="library_title_album">Album</string>
|
||||
<string name="library_title_album_see_all_button">Vedi tutto</string>
|
||||
<string name="library_title_artist">Artisti</string>
|
||||
<string name="library_title_artist_see_all_button">Vedi tutto</string>
|
||||
<string name="library_title_genre">Generi</string>
|
||||
<string name="library_title_genre_see_all_button">Vedi tutto</string>
|
||||
<string name="library_title_music_folder">Cartelle musicali</string>
|
||||
<string name="library_title_playlist">Playlist</string>
|
||||
<string name="library_title_playlist_see_all_button">Vedi tutto</string>
|
||||
<string name="login_empty">Nessun server aggiunto</string>
|
||||
<string name="login_title">Server Subsonic</string>
|
||||
<string name="login_title_expanded">Server Subsonic</string>
|
||||
<string name="media_route_menu_title">Trasmetti</string>
|
||||
<string name="menu_add_button">Aggiungi</string>
|
||||
<string name="album_bottom_sheet_download_all">Scarica tutto</string>
|
||||
<string name="album_bottom_sheet_go_to_artist">Vai all\'artista</string>
|
||||
<string name="album_bottom_sheet_instant_mix">Mix istantaneo</string>
|
||||
<string name="album_bottom_sheet_play_next">Riproduci dopo</string>
|
||||
<string name="album_bottom_sheet_remove_all">Rimuovi tutto</string>
|
||||
<string name="album_bottom_sheet_share">Condividi</string>
|
||||
<string name="album_bottom_sheet_shuffle">Riproduzione casuale</string>
|
||||
<string name="album_catalogue_title">Album</string>
|
||||
<string name="album_catalogue_title_expanded">Sfoglia Album</string>
|
||||
<string name="album_error_retrieving_artist">Errore nel recupero dell\'artista</string>
|
||||
<string name="album_list_page_downloaded">Album scaricati</string>
|
||||
<string name="album_list_page_most_played">Album più riprodotti</string>
|
||||
<string name="album_list_page_new_releases">Nuove uscite</string>
|
||||
<string name="album_list_page_recently_added">Album aggiunti di recente</string>
|
||||
<string name="album_list_page_recently_played">Album riprodotti di recente</string>
|
||||
<string name="album_list_page_starred">Album preferiti</string>
|
||||
<string name="album_list_page_title">Album</string>
|
||||
<string name="album_page_extra_info_button">Altri simili</string>
|
||||
<string name="album_page_play_button">Riproduci</string>
|
||||
<string name="album_page_release_date_label">Rilasciato il %1$s</string>
|
||||
<string name="album_page_release_dates_label">Rilasciato il %1$s, originariamente il %2$s</string>
|
||||
<string name="album_page_shuffle_button">Riproduzione casuale</string>
|
||||
<string name="album_page_tracks_count_and_duration">%1$d brani • %2$d minuti</string>
|
||||
<string name="app_name">Tempus</string>
|
||||
<string name="artist_adapter_radio_station_starting">Cercando…</string>
|
||||
<string name="artist_bottom_sheet_instant_mix">Mix istantaneo</string>
|
||||
<string name="artist_bottom_sheet_shuffle">Riproduzione casuale</string>
|
||||
<string name="artist_catalogue_title">Artisti</string>
|
||||
<string name="artist_catalogue_title_expanded">Sfoglia Artisti</string>
|
||||
<string name="artist_error_retrieving_radio">Errore nel recupero della radio dell\'artista</string>
|
||||
<string name="artist_error_retrieving_tracks">Errore nel recupero dei brani dell\'artista</string>
|
||||
<string name="artist_list_page_downloaded">Artisti scaricati</string>
|
||||
<string name="artist_list_page_starred">Artisti preferiti</string>
|
||||
<string name="artist_list_page_title">Artisti</string>
|
||||
<string name="artist_page_radio_button">Radio</string>
|
||||
<string name="artist_page_shuffle_button">Riproduzione casuale</string>
|
||||
<string name="artist_page_switch_layout_button">Cambia layout</string>
|
||||
<string name="artist_page_title_album_more_like_this_button">Altri simili</string>
|
||||
<string name="artist_page_title_album_section">Album</string>
|
||||
<string name="artist_page_title_biography_more_button">Altro</string>
|
||||
<string name="artist_page_title_biography_section">Biografia</string>
|
||||
<string name="artist_page_title_most_streamed_song_section">Brani più ascoltati</string>
|
||||
<string name="artist_page_title_most_streamed_song_see_all_button">Vedi tutto</string>
|
||||
<string name="battery_optimization_negative_button">Ignora</string>
|
||||
<string name="battery_optimization_neutral_button">Non chiedere di nuovo</string>
|
||||
<string name="battery_optimization_positive_button">Disabilita</string>
|
||||
<string name="connection_alert_dialog_negative_button">Annulla</string>
|
||||
<string name="connection_alert_dialog_neutral_button">Attiva risparmio dati</string>
|
||||
<string name="connection_alert_dialog_positive_button">OK</string>
|
||||
<string name="connection_alert_dialog_summary">L\'accesso al server Subsonic è stato limitato alle connessioni Wi-Fi. Per evitare che questo avviso riappaia, disabilita il controllo connessione nelle impostazioni dell\'app.</string>
|
||||
<string name="connection_alert_dialog_title">Wi-Fi non connesso</string>
|
||||
<string name="content_description_shuffle_button">Riproduzione casuale</string>
|
||||
<string name="delete_download_storage_dialog_negative_button">Annulla</string>
|
||||
<string name="delete_download_storage_dialog_positive_button">Continua</string>
|
||||
<string name="delete_download_storage_dialog_summary">Attenzione, procedendo questa azione eliminerà definitivamente tutti gli elementi scaricati da tutti i server.</string>
|
||||
<string name="delete_download_storage_dialog_title">Elimina elementi salvati</string>
|
||||
<string name="description_empty_title">Descrizione non disponibile</string>
|
||||
<string name="disc_titlefull">Disco %1$s - %2$s</string>
|
||||
<string name="disc_titleless">Disco %1$s</string>
|
||||
<string name="download_directory_dialog_negative_button">Annulla</string>
|
||||
<string name="download_directory_dialog_positive_button">Scarica</string>
|
||||
<string name="download_directory_dialog_summary">Tutti i brani in questa cartella verranno scaricati. I brani nelle sottocartelle non verranno scaricati.</string>
|
||||
<string name="download_directory_dialog_title">Scarica i brani</string>
|
||||
<string name="download_directory_set">Imposta dove scaricare la musica</string>
|
||||
<string name="download_info_empty_subtitle">Una volta scaricato un brano, lo troverai qui</string>
|
||||
<string name="download_info_empty_title">Ancora nessun download!</string>
|
||||
<string name="download_item_multiple_subtitle_formatter">%1$s • %2$s elementi</string>
|
||||
<string name="download_item_single_subtitle_formatter">%1$s elementi</string>
|
||||
<string name="download_shuffle_all_subtitle">Riproduzione casuale di tutto</string>
|
||||
<string name="download_storage_dialog_sub_summary">Per rendere effettive le modifiche, riavvia l\'app.</string>
|
||||
<string name="download_storage_dialog_summary">Cambiare la destinazione dei file scaricati da una memoria all\'altra eliminerà immediatamente tutti i file scaricati precedentemente nella vecchia memoria.</string>
|
||||
<string name="download_storage_dialog_title">Seleziona opzione di memoria</string>
|
||||
<string name="download_storage_external_dialog_positive_button">Esterna</string>
|
||||
<string name="download_storage_internal_dialog_negative_button">Interna</string>
|
||||
<string name="download_storage_directory_dialog_neutral_button">Cartella</string>
|
||||
<string name="download_title_section">Scarica</string>
|
||||
<string name="download_refresh_no_directory">Imposta una cartella di download per aggiornare i tuoi download.</string>
|
||||
<string name="download_refresh_no_changes">Nessun download mancante trovato.</string>
|
||||
<plurals name="download_refresh_removed">
|
||||
<item quantity="one">Rimosso %d download mancante.</item>
|
||||
<item quantity="other">Rimossi %d download mancanti.</item>
|
||||
</plurals>
|
||||
<string name="download_refresh_button_content_description">Aggiorna gli elementi scaricati</string>
|
||||
<string name="downloaded_bottom_sheet_add_to_queue">Aggiungi alla coda</string>
|
||||
<string name="downloaded_bottom_sheet_play_next">Riproduci dopo</string>
|
||||
<string name="downloaded_bottom_sheet_remove">Rimuovi</string>
|
||||
<string name="downloaded_bottom_sheet_remove_all">Rimuovi tutto</string>
|
||||
<string name="downloaded_bottom_sheet_shuffle">Riproduzione casuale</string>
|
||||
<string name="empty_string" />
|
||||
<string name="error_required">Obbligatorio</string>
|
||||
<string name="error_server_prefix">Prefisso http o https richiesto</string>
|
||||
<string name="exo_download_notification_channel_name">Download</string>
|
||||
<string name="exo_controls_heart_off_description">Aggiungi ai preferiti</string>
|
||||
<string name="exo_controls_heart_on_description">Rimuovi dai preferiti</string>
|
||||
<string name="cast_expanded_controller_loading">Caricamento…</string>
|
||||
<string name="filter_info_selection">Seleziona due o più filtri</string>
|
||||
<string name="filter_title">Filtro</string>
|
||||
<string name="filter_artist">Filtra artisti</string>
|
||||
<string name="filter_title_expanded">Filtra Generi</string>
|
||||
<string name="generic_list_page_count">(%1$d)</string>
|
||||
<string name="generic_list_page_count_unknown">(+%1$d)</string>
|
||||
<string name="genre_catalogue_title">Catalogo dei Generi</string>
|
||||
<string name="genre_catalogue_title_expanded">Sfoglia Generi</string>
|
||||
<string name="github_update_dialog_negative_button">Ricordamelo più tardi</string>
|
||||
<string name="github_update_dialog_neutral_button">Supportami</string>
|
||||
<string name="github_update_dialog_positive_button">Scarica ora</string>
|
||||
<string name="github_update_dialog_summary">È disponibile una nuova versione dell\'app su Github.</string>
|
||||
<string name="github_update_dialog_title">Aggiornamento disponibile</string>
|
||||
<string name="home_rearrangement_dialog_negative_button">Annulla</string>
|
||||
<string name="home_rearrangement_dialog_neutral_button">Ripristina</string>
|
||||
<string name="home_rearrangement_dialog_positive_button">Salva</string>
|
||||
<string name="home_rearrangement_dialog_title">Riorganizza home</string>
|
||||
<string name="home_rearrangement_dialog_subtitle">Per rendere effettive le modifiche è necessario riavviare l\'applicazione.</string>
|
||||
<string name="home_section_music">Musica</string>
|
||||
<string name="home_section_podcast">Podcast</string>
|
||||
<string name="home_section_radio">Radio</string>
|
||||
<string name="home_subtitle_best_of">Le migliori canzoni dei tuoi artisti preferiti</string>
|
||||
<string name="home_subtitle_made_for_you">Inizia un mix da una canzone che ti è piaciuta</string>
|
||||
<string name="home_subtitle_new_internet_radio_station">Aggiungi una nuova radio</string>
|
||||
<string name="home_subtitle_new_podcast_channel">Aggiungi un nuovo canale podcast</string>
|
||||
<string name="home_sync_starred_cancel">Annulla</string>
|
||||
<string name="home_sync_starred_download">Scarica</string>
|
||||
<string name="home_sync_starred_subtitle">Scaricare questi brani potrebbe comportare un uso significativo di dati</string>
|
||||
<string name="home_sync_starred_title">Sembra che ci siano alcuni brani preferiti da sincronizzare</string>
|
||||
<string name="home_sync_starred_albums_title">Sincronizza Album Preferiti</string>
|
||||
<string name="home_sync_starred_albums_subtitle">Gli album preferiti saranno disponibili offline</string>
|
||||
<string name="home_sync_starred_artists_title">Sincronizza Artisti Preferiti</string>
|
||||
<string name="home_sync_starred_artists_subtitle">Hai artisti preferiti con musica non scaricata</string>
|
||||
<string name="home_title_best_of">Il meglio di</string>
|
||||
<string name="home_title_discovery">Scopri</string>
|
||||
<string name="home_title_discovery_shuffle_all_button">Mescola tutto</string>
|
||||
<string name="home_title_flashback">Flashback</string>
|
||||
<string name="home_title_internet_radio_station">Stazioni internet-radio</string>
|
||||
<string name="home_title_last_played">Ultimi ascolti</string>
|
||||
<string name="home_title_last_played_see_all_button">Vedi tutto</string>
|
||||
<string name="home_title_last_week">La scorsa settimana</string>
|
||||
<string name="home_title_last_month">Il mese scorso</string>
|
||||
<string name="home_title_last_year">L\'anno scorso</string>
|
||||
<string name="home_title_made_for_you">Fatto per te</string>
|
||||
<string name="home_title_most_played">Più ascoltati</string>
|
||||
<string name="home_title_most_played_see_all_button">Vedi tutto</string>
|
||||
<string name="home_title_new_releases">Nuove uscite</string>
|
||||
<string name="home_title_newest_podcasts">Podcast più recenti</string>
|
||||
<string name="home_title_pinned_playlists">Playlist</string>
|
||||
<string name="home_title_podcast_channels">Canali</string>
|
||||
<string name="home_title_podcast_channels_see_all_button">Vedi tutto</string>
|
||||
<string name="home_title_radio_station">Stazioni radio</string>
|
||||
<string name="home_title_recently_added">Aggiunti di recente</string>
|
||||
<string name="home_title_recently_added_see_all_button">Vedi tutto</string>
|
||||
<string name="home_title_shares">Condivisioni</string>
|
||||
<string name="home_title_starred_albums">★ Album preferiti</string>
|
||||
<string name="home_title_starred_albums_see_all_button">Vedi tutto</string>
|
||||
<string name="home_title_starred_artists">★ Artisti preferiti</string>
|
||||
<string name="home_title_starred_artists_see_all_button">Vedi tutto</string>
|
||||
<string name="home_title_starred_tracks">★ Brani preferiti</string>
|
||||
<string name="home_title_starred_tracks_see_all_button">Vedi tutto</string>
|
||||
<string name="home_title_top_songs">I tuoi migliori brani</string>
|
||||
<string name="home_option_reorganize">Riorganizza</string>
|
||||
<string name="label_dot_separator" translatable="false">•</string>
|
||||
<string name="label_placeholder" translatable="false">--</string>
|
||||
<string name="library_title_album">Album</string>
|
||||
<string name="library_title_album_see_all_button">Vedi tutto</string>
|
||||
<string name="library_title_artist">Artisti</string>
|
||||
<string name="library_title_artist_see_all_button">Vedi tutto</string>
|
||||
<string name="library_title_genre">Generi</string>
|
||||
<string name="library_title_genre_see_all_button">Vedi tutto</string>
|
||||
<string name="library_title_music_folder">Cartelle della musica</string>
|
||||
<string name="library_title_playlist">Playlist</string>
|
||||
<string name="library_title_playlist_see_all_button">Vedi tutto</string>
|
||||
<string name="login_empty">Nessun server aggiunto</string>
|
||||
<string name="login_title">Server Subsonic</string>
|
||||
<string name="login_title_expanded">Server Subsonic</string>
|
||||
<string name="media_route_menu_title">Trasmetti</string>
|
||||
<string name="menu_add_button">Aggiungi</string>
|
||||
<string name="menu_add_to_playlist_button">Aggiungi alla playlist</string>
|
||||
<string name="menu_download_all_button">Scarica tutto</string>
|
||||
<string name="menu_download_label">Scarica</string>
|
||||
<string name="menu_filter_all">Tutti</string>
|
||||
<string name="menu_filter_download">Scaricati</string>
|
||||
<string name="menu_group_by_album">Album</string>
|
||||
<string name="menu_group_by_artist">Artista</string>
|
||||
<string name="menu_group_by_genre">Genere</string>
|
||||
<string name="menu_group_by_track">Brano</string>
|
||||
<string name="menu_group_by_year">Anno</string>
|
||||
<string name="menu_home_label">Home</string>
|
||||
<string name="menu_last_week_name">La scorsa settimana</string>
|
||||
<string name="menu_last_month_name">Il mese scorso</string>
|
||||
<string name="menu_last_year_name">L\'anno scorso</string>
|
||||
<string name="menu_library_label">Libreria</string>
|
||||
<string name="menu_search_button">Cerca</string>
|
||||
<string name="menu_settings_button">Impostazioni</string>
|
||||
<string name="menu_sort_artist">Artista</string>
|
||||
<string name="menu_sort_name">Nome</string>
|
||||
<string name="menu_sort_random">Casuale</string>
|
||||
<string name="menu_sort_recently_added">Aggiunti di recente</string>
|
||||
<string name="menu_pin_button">Aggiungi alla schermata home</string>
|
||||
<string name="menu_unpin_button">Rimuovi dalla schermata home</string>
|
||||
<string name="menu_sort_year">Anno</string>
|
||||
<string name="player_playback_speed">%1$.2fx</string>
|
||||
<string name="player_queue_clean_all_button">Svuota coda di riproduzione</string>
|
||||
<string name="menu_rate_album">Valuta l\'album</string>
|
||||
<string name="menu_download_label">Scarica</string>
|
||||
<string name="menu_filter_all">Tutti</string>
|
||||
<string name="menu_filter_download">Scaricati</string>
|
||||
<string name="menu_group_by_album">Album</string>
|
||||
<string name="menu_group_by_artist">Artista</string>
|
||||
<string name="menu_group_by_genre">Genere</string>
|
||||
<string name="menu_group_by_track">Brano</string>
|
||||
<string name="menu_group_by_year">Anno</string>
|
||||
<string name="menu_home_label">Home</string>
|
||||
<string name="menu_last_week_name">La scorsa settimana</string>
|
||||
<string name="menu_last_month_name">Il mese scorso</string>
|
||||
<string name="menu_last_year_name">L\'anno scorso</string>
|
||||
<string name="menu_library_label">Libreria</string>
|
||||
<string name="menu_search_button">Cerca</string>
|
||||
<string name="menu_settings_button">Impostazioni</string>
|
||||
<string name="menu_sort_artist">Artista</string>
|
||||
<string name="menu_sort_name">Nome</string>
|
||||
<string name="menu_sort_random">Casuale</string>
|
||||
<string name="menu_sort_album_count">Numero di Album</string>
|
||||
<string name="menu_sort_recently_added">Aggiunti di recente</string>
|
||||
<string name="menu_sort_recently_played">Riprodotti di recente</string>
|
||||
<string name="menu_sort_most_played">Più riprodotti</string>
|
||||
<string name="menu_sort_most_recently_starred">Preferiti più recentemente</string>
|
||||
<string name="menu_sort_least_recently_starred">Preferiti meno recentemente</string>
|
||||
<string name="menu_pin_button">Aggiungi alla schermata home</string>
|
||||
<string name="menu_unpin_button">Rimuovi dalla schermata home</string>
|
||||
<string name="menu_sort_year">Anno</string>
|
||||
<string name="player_playback_speed">%1$.2fx</string>
|
||||
<string name="player_queue_clean_all_button">Svuota coda di riproduzione</string>
|
||||
<string name="player_queue_save_queue_success">Salvato</string>
|
||||
<string name="player_server_priority">Priorità server</string>
|
||||
<string name="playlist_catalogue_title">Catalogo playlist</string>
|
||||
<string name="playlist_catalogue_title_expanded">Sfoglia le playlist</string>
|
||||
<string name="playlist_chooser_dialog_empty">Nessuna playlist creata</string>
|
||||
<string name="playlist_chooser_dialog_negative_button">Annulla</string>
|
||||
<string name="playlist_chooser_dialog_neutral_button">Crea</string>
|
||||
<string name="playlist_chooser_dialog_title">Aggiungi a una playlist</string>
|
||||
<string name="playlist_chooser_dialog_toast_add_success">Aggiunta di un brano alla playlist</string>
|
||||
<string name="playlist_chooser_dialog_toast_add_failure">Impossibile aggiungere un brano alla playlist</string>
|
||||
<string name="playlist_counted_tracks">%1$d brani • %2$s</string>
|
||||
<string name="playlist_duration">Durata • %1$s</string>
|
||||
<string name="playlist_editor_dialog_action_delete_toast">Premi a lungo per eliminare</string>
|
||||
<string name="playlist_editor_dialog_hint_name">Nome della playlist</string>
|
||||
<string name="playlist_editor_dialog_negative_button">Annulla</string>
|
||||
<string name="playlist_editor_dialog_neutral_button">Elimina</string>
|
||||
<string name="playlist_editor_dialog_positive_button">Salva</string>
|
||||
<string name="playlist_editor_dialog_title">Modifica playlist</string>
|
||||
<string name="playlist_page_play_button">Riproduci</string>
|
||||
<string name="playlist_page_shuffle_button">Mescola</string>
|
||||
<string name="playlist_song_count">Playlist • %1$d brani</string>
|
||||
<string name="podcast_bottom_sheet_add_to_queue">Aggiungi alla coda</string>
|
||||
<string name="podcast_bottom_sheet_delete">Elimina</string>
|
||||
<string name="podcast_bottom_sheet_download">Scarica</string>
|
||||
<string name="podcast_bottom_sheet_go_to_channel">Vai al canale</string>
|
||||
<string name="podcast_bottom_sheet_play_next">Riproduci dopo</string>
|
||||
<string name="podcast_bottom_sheet_remove">Rimuovi</string>
|
||||
<string name="podcast_channel_catalogue_title">Canali</string>
|
||||
<string name="podcast_channel_catalogue_title_expanded">Sfoglia Canali</string>
|
||||
<string name="podcast_channel_editor_dialog_hint_rss_url">URL RSS</string>
|
||||
<string name="podcast_channel_editor_dialog_title">Canale Podcast</string>
|
||||
<string name="podcast_channel_page_title_description_section">Descrizione</string>
|
||||
<string name="podcast_channel_page_title_episode_section">Episodi</string>
|
||||
<string name="podcast_channel_page_title_no_episode_available">Nessun episodio disponibile</string>
|
||||
<string name="podcast_episode_download_request_snackbar">La tua richiesta è stata inviata al server</string>
|
||||
<string name="podcast_info_empty_button">Clicca per nascondere la sezione\nGli effetti saranno visibili al riavvio</string>
|
||||
<string name="podcast_info_empty_subtitle">Una volta aggiunto un canale, lo troverai qui</string>
|
||||
<string name="podcast_info_empty_title">Nessun podcast trovato!</string>
|
||||
<string name="podcast_release_date_duration_formatter">%1$s • %2$s</string>
|
||||
<string name="radio_editor_dialog_hint_homepage_url">URL Homepage Radio</string>
|
||||
<string name="radio_editor_dialog_hint_name">Nome Radio</string>
|
||||
<string name="radio_editor_dialog_hint_stream_url">URL Stream Radio</string>
|
||||
<string name="radio_editor_dialog_negative_button">Annulla</string>
|
||||
<string name="radio_editor_dialog_neutral_button">Elimina</string>
|
||||
<string name="radio_editor_dialog_positive_button">Salva</string>
|
||||
<string name="radio_editor_dialog_title">Stazione Radio Internet</string>
|
||||
<string name="radio_station_info_empty_button">Clicca per nascondere la sezione\nGli effetti saranno visibili al riavvio</string>
|
||||
<string name="radio_station_info_empty_subtitle">Una volta aggiunta una stazione radio, la troverai qui</string>
|
||||
<string name="radio_station_info_empty_title">Nessuna stazione trovata!</string>
|
||||
<string name="rating_dialog_negative_button">Annulla</string>
|
||||
<string name="rating_dialog_positive_button">Salva</string>
|
||||
<string name="rating_dialog_title">Valuta</string>
|
||||
<string name="search_hint">Cerca titolo, artisti o album</string>
|
||||
<string name="search_info_minimum_characters">Inserisci almeno tre caratteri</string>
|
||||
<string name="search_title_album">Album</string>
|
||||
<string name="search_title_artist">Artisti</string>
|
||||
<string name="search_title_song">Brani</string>
|
||||
<string name="server_signup_dialog_action_low_security">Bassa sicurezza</string>
|
||||
<string name="server_signup_dialog_action_delete_toast">Premi a lungo per eliminare</string>
|
||||
<string name="server_signup_dialog_hint_local_address">URL locale</string>
|
||||
<string name="server_signup_dialog_hint_name">Nome Server</string>
|
||||
<string name="server_signup_dialog_hint_password">Password</string>
|
||||
<string name="server_signup_dialog_hint_url">URL Server</string>
|
||||
<string name="server_signup_dialog_hint_username">Nome utente</string>
|
||||
<string name="server_signup_dialog_negative_button">Annulla</string>
|
||||
<string name="server_signup_dialog_neutral_button">Elimina</string>
|
||||
<string name="server_signup_dialog_positive_button">Salva</string>
|
||||
<string name="server_signup_dialog_title">Aggiungi server</string>
|
||||
<string name="server_unreachable_dialog_negative_button">Annulla</string>
|
||||
<string name="server_unreachable_dialog_neutral_button">Vai al login</string>
|
||||
<string name="server_unreachable_dialog_positive_button">Continua comunque</string>
|
||||
<string name="server_unreachable_dialog_summary">Il server richiesto non è disponibile. Se scegli di continuare, questo messaggio non apparirà per la prossima ora.</string>
|
||||
<string name="server_unreachable_dialog_title">Server irraggiungibile</string>
|
||||
<string name="settings_about_summary">Tempus è un client musicale open source e leggero per Subsonic, progettato e costruito nativamente per Android.</string>
|
||||
<string name="settings_about_title">Informazioni</string>
|
||||
<string name="settings_always_on_display">Sempre attivo</string>
|
||||
<string name="settings_audio_transcode_download_format">Formato transcodifica</string>
|
||||
<string name="settings_audio_transcode_download_priority_summary">Se abilitato, Tempus non forzerà il download del brano con le impostazioni di transcodifica sottostanti.</string>
|
||||
<string name="settings_audio_transcode_download_priority_title">Dare priorità alle impostazioni del server per lo streaming nei download</string>
|
||||
<string name="settings_audio_transcode_download_summary">Se abilitato, Tempus scaricherà i brani transcodificati.</string>
|
||||
<string name="settings_audio_transcode_download_title">Scarica brani transcodificati</string>
|
||||
<string name="settings_audio_transcode_estimate_content_length_summary">Se abilitato, verrà richiesto al server di fornire la durata stimata del brano.</string>
|
||||
<string name="settings_audio_transcode_estimate_content_length_title">Stima della lunghezza del contenuto</string>
|
||||
<string name="settings_audio_transcode_format_download">Formato transcodifica per download</string>
|
||||
<string name="settings_audio_transcode_format_mobile">Formato transcodifica su mobile</string>
|
||||
<string name="settings_audio_transcode_format_wifi">Formato transcodifica su Wi-Fi</string>
|
||||
<string name="settings_audio_transcode_priority_summary">Se abilitato, Tempus non forzerà lo streaming del brano con le impostazioni di transcodifica sottostanti.</string>
|
||||
<string name="settings_audio_transcode_priority_title">Dare priorità alle impostazioni di transcodifica del server</string>
|
||||
<string name="settings_audio_transcode_priority_toast">Priorità di transcodifica del brano assegnata al server</string>
|
||||
<string name="settings_buffering_strategy">Strategia di buffering</string>
|
||||
<string name="settings_buffering_strategy_summary">Perché la modifica abbia effetto è necessario riavviare manualmente l\'app.</string>
|
||||
<string name="settings_continuous_play_summary">Consente alla musica di continuare a suonare dopo la fine di una playlist, riproducendo brani simili</string>
|
||||
<string name="settings_continuous_play_title">Riproduzione continua</string>
|
||||
<string name="settings_covers_cache">Dimensione della cache delle copertine</string>
|
||||
<string name="settings_data_saving_mode_summary">Per ridurre il consumo di dati, evita di scaricare le copertine.</string>
|
||||
<string name="settings_data_saving_mode_title">Limita utilizzo dei dati mobili</string>
|
||||
<string name="settings_delete_download_storage_summary">Continuando, tutti gli elementi salvati verranno eliminati in modo irreversibile.</string>
|
||||
<string name="settings_delete_download_storage_title">Elimina elementi salvati</string>
|
||||
<string name="settings_download_storage_title">Archivio download</string>
|
||||
<string name="settings_system_equalizer_summary">Regola le impostazioni audio</string>
|
||||
<string name="settings_system_equalizer_title">Equalizzatore di sistema</string>
|
||||
<string name="settings_github_link">https://github.com/eddyizm/tempus</string>
|
||||
<string name="settings_github_summary">Segui lo sviluppo</string>
|
||||
<string name="settings_github_title">Github</string>
|
||||
<string name="settings_image_size">Imposta risoluzione delle immagini</string>
|
||||
<string name="settings_language">Lingua</string>
|
||||
<string name="settings_logout_title">Esci</string>
|
||||
<string name="settings_max_bitrate_download">Bitrate per download</string>
|
||||
<string name="settings_max_bitrate_mobile">Bitrate su mobile</string>
|
||||
<string name="settings_max_bitrate_wifi">Bitrate su Wi-Fi</string>
|
||||
<string name="settings_media_cache">Dimensione della cache dei file multimediali</string>
|
||||
<string name="settings_music_directory">Mostra directory musicali</string>
|
||||
<string name="settings_music_directory_summary">Se abilitato, mostra la sezione delle directory musicali. Nota che per la navigazione nelle cartelle è necessario che il server supporti questa funzionalità.</string>
|
||||
<string name="settings_podcast">Mostra podcast</string>
|
||||
<string name="settings_podcast_summary">Se abilitato, mostra la sezione podcast. Riavvia l\'app per rendere effettive le modifiche.</string>
|
||||
<string name="settings_audio_quality">Mostra qualità audio</string>
|
||||
<string name="settings_audio_quality_summary">Il bitrate e il formato audio saranno mostrati per ogni traccia.</string>
|
||||
<string name="settings_item_rating">Mostra valutazione</string>
|
||||
<string name="settings_item_rating_summary">Se abilitato, verrà mostrata la valutazione dell\'elemento e se è contrassegnato come preferito.</string>
|
||||
<string name="settings_queue_syncing_countdown">Timer sincronizzazione</string>
|
||||
<string name="settings_queue_syncing_summary">Se abilitato, l\'utente avrà la possibilità di salvare la propria coda di riproduzione e potrà caricare lo stato all\'apertura dell\'applicazione.</string>
|
||||
<string name="settings_queue_syncing_title">Sincronizza coda di riproduzione per questo utente</string>
|
||||
<string name="settings_radio">Mostra radio</string>
|
||||
<string name="settings_radio_summary">Se abilitato, mostra la sezione radio. Riavvia l\'app per applicare completamente le modifiche.</string>
|
||||
<string name="settings_replay_gain">Imposta modalità di guadagno di riproduzione</string>
|
||||
<string name="settings_rounded_corner">Angoli arrotondati</string>
|
||||
<string name="settings_rounded_corner_size">Dimensione angoli</string>
|
||||
<string name="settings_rounded_corner_size_summary">Imposta la magnitudine dell\'angolo di curvatura.</string>
|
||||
<string name="settings_rounded_corner_summary">Se abilitato, imposta un angolo di curvatura per tutte le copertine visualizzate. Le modifiche avranno effetto al riavvio.</string>
|
||||
<string name="settings_scan_title">Scansiona libreria</string>
|
||||
<string name="settings_scrobble_title">Abilita scrobbling musicale</string>
|
||||
<string name="settings_share_title">Abilita condivisione musicale</string>
|
||||
<string name="settings_streaming_cache_size">Dimensione cache streaming</string>
|
||||
<string name="settings_streaming_cache_storage_title">Archiviazione cache streaming</string>
|
||||
<string name="settings_sub_summary_scrobble">È importante notare che lo scrobbling si basa anche sul fatto che il server sia abilitato a ricevere questi dati.</string>
|
||||
<string name="settings_summary_skip_min_star_rating">Quando si ascolta la radio di un artista, un mix istantaneo o quando si mescolano tutti i brani, i brani sotto una certa valutazione dell\'utente verranno ignorati.</string>
|
||||
<string name="settings_summary_replay_gain">Il guadagno di riproduzione è una funzionalità che consente di regolare il livello del volume delle tracce audio per un\'esperienza di ascolto coerente. Questa impostazione è efficace solo se la traccia contiene i metadati necessari.</string>
|
||||
<string name="settings_summary_scrobble">Lo scrobbling è una funzionalità che consente al tuo dispositivo di inviare informazioni sulle canzoni che ascolti al server musicale. Queste informazioni aiutano a creare raccomandazioni personalizzate in base alle tue preferenze musicali.</string>
|
||||
<string name="settings_summary_share">Permette all\'utente di condividere musica tramite un link. La funzionalità deve essere supportata e abilitata sul server ed è limitata a brani, album e playlist singoli.</string>
|
||||
<string name="settings_summary_syncing">Restituisce lo stato della coda di riproduzione per questo utente. Ciò include i brani nella coda di riproduzione, il brano attualmente in riproduzione e la posizione all\'interno di questo brano. Il server deve supportare questa funzionalità.</string>
|
||||
<string name="settings_summary_streaming_cache_size">%1$s \nAttualmente in uso: %2$s MiB</string>
|
||||
<string name="settings_summary_transcoding">Priorità data alla modalità di transcoding. Se impostato su "Riproduzione diretta", il bitrate del file non verrà modificato.</string>
|
||||
<string name="settings_summary_transcoding_download">Scarica media transcodificati. Se abilitato, l\'endpoint di download non verrà utilizzato, ma le impostazioni seguenti. \n\n Se "Formato di transcodifica per i download" è impostato su "Download diretto", il bitrate del file non verrà modificato.</string>
|
||||
<string name="settings_summary_transcoding_estimate_content_length">Quando il file viene transcodificato al volo, il client di solito non mostra la lunghezza della traccia. È possibile richiedere ai server che supportano la funzionalità di stimare la durata della traccia in riproduzione, ma i tempi di risposta possono essere più lunghi.</string>
|
||||
<string name="settings_sync_starred_tracks_for_offline_use_summary">Se abilitato, le tracce contrassegnate verranno scaricate per l\'uso offline.</string>
|
||||
<string name="settings_sync_starred_tracks_for_offline_use_title">Sincronizza tracce contrassegnate per uso offline</string>
|
||||
<string name="settings_theme">Tema</string>
|
||||
<string name="settings_title_data">Dati</string>
|
||||
<string name="settings_title_general">Generale</string>
|
||||
<string name="settings_title_rating">Valutazione</string>
|
||||
<string name="settings_title_replay_gain">Guadagno di riproduzione</string>
|
||||
<string name="settings_title_scrobble">Scrobble</string>
|
||||
<string name="settings_title_skip_min_star_rating">Ignora brani in base alla valutazione</string>
|
||||
<string name="settings_title_skip_min_star_rating_dialog">Brani con una valutazione di:</string>
|
||||
<string name="settings_title_share">Condividi</string>
|
||||
<string name="settings_title_syncing">Sincronizzazione</string>
|
||||
<string name="settings_title_transcoding">Transcoding</string>
|
||||
<string name="settings_title_transcoding_download">Download di Transcoding</string>
|
||||
<string name="settings_title_ui">Interfaccia utente</string>
|
||||
<string name="settings_transcoded_download">Download transcodificato</string>
|
||||
<string name="settings_version_summary" translatable="false">3.1.0</string>
|
||||
<string name="settings_version_title">Versione</string>
|
||||
<string name="settings_wifi_only_summary">Chiedi conferma all\'utente prima di effettuare streaming su rete mobile.</string>
|
||||
<string name="settings_wifi_only_title">Streaming solo tramite Wi-Fi avviso</string>
|
||||
<string name="share_bottom_sheet_copy_link">Copia link</string>
|
||||
<string name="share_bottom_sheet_delete">Elimina condivisione</string>
|
||||
<string name="share_bottom_sheet_update">Aggiorna condivisione</string>
|
||||
<string name="share_subtitle_item">Data di scadenza: %1$s</string>
|
||||
<string name="share_unsupported_error">La condivisione non è supportata o non è abilitata</string>
|
||||
<string name="share_update_dialog_hint_description">Descrizione</string>
|
||||
<string name="share_update_dialog_hint_expiration_date">Data di scadenza</string>
|
||||
<string name="share_update_dialog_negative_button">Annulla</string>
|
||||
<string name="share_update_dialog_positive_button">Salva</string>
|
||||
<string name="share_update_dialog_title">Condividi</string>
|
||||
<string name="song_bottom_sheet_add_to_playlist">Aggiungi alla playlist</string>
|
||||
<string name="song_bottom_sheet_add_to_queue">Aggiungi alla coda</string>
|
||||
<string name="song_bottom_sheet_download">Scarica</string>
|
||||
<string name="song_bottom_sheet_error_retrieving_album">Errore nel recupero dell\'album</string>
|
||||
<string name="song_bottom_sheet_error_retrieving_artist">Errore nel recupero dell\'artista</string>
|
||||
<string name="song_bottom_sheet_go_to_album">Vai all\'album</string>
|
||||
<string name="song_bottom_sheet_go_to_artist">Vai all\'artista</string>
|
||||
<string name="song_bottom_sheet_instant_mix">Mix istantaneo</string>
|
||||
<string name="song_bottom_sheet_play_next">Riproduci dopo</string>
|
||||
<string name="song_bottom_sheet_rate">Valuta</string>
|
||||
<string name="song_bottom_sheet_remove">Rimuovi</string>
|
||||
<string name="song_bottom_sheet_share">Condividi</string>
|
||||
<string name="song_list_page_downloaded">Scaricato</string>
|
||||
<string name="song_list_page_most_played">Tracce più riprodotte</string>
|
||||
<string name="song_list_page_recently_added">Tracce aggiunte di recente</string>
|
||||
<string name="song_list_page_recently_played">Tracce riprodotte di recente</string>
|
||||
<string name="song_list_page_starred">Tracce contrassegnate</string>
|
||||
<string name="song_list_page_top">Le migliori tracce di %1$s</string>
|
||||
<string name="song_list_page_year">Anno %1$d</string>
|
||||
<string name="song_subtitle_formatter">%1$s • %2$s %3$s</string>
|
||||
<string name="starred_sync_dialog_negative_button">Annulla</string>
|
||||
<string name="starred_sync_dialog_neutral_button">Continua</string>
|
||||
<string name="starred_sync_dialog_positive_button">Continua e scarica</string>
|
||||
<string name="starred_sync_dialog_summary">Il download delle tracce contrassegnate potrebbe richiedere una grande quantità di dati.</string>
|
||||
<string name="starred_sync_dialog_title">Sincronizza tracce contrassegnate</string>
|
||||
<string name="streaming_cache_storage_dialog_sub_summary">Per rendere effettive le modifiche, riavvia l\'app.</string>
|
||||
<string name="streaming_cache_storage_dialog_summary">Cambiare la destinazione dei file memorizzati nella cache da un\'unità di archiviazione a un\'altra può comportare la cancellazione di eventuali file memorizzati nella cache in precedenza nell\'altra unità di archiviazione.</string>
|
||||
<string name="streaming_cache_storage_dialog_title">Seleziona opzione di archiviazione</string>
|
||||
<string name="streaming_cache_storage_external_dialog_positive_button">Esterno</string>
|
||||
<string name="streaming_cache_storage_internal_dialog_negative_button">Interno</string>
|
||||
<string name="support_url">https://buymeacoffee.com/a.cappiello</string>
|
||||
<string name="track_info_album">Album</string>
|
||||
<string name="track_info_artist">Artista</string>
|
||||
<string name="track_info_bitrate">Bitrate</string>
|
||||
<string name="track_info_content_type">Tipo di contenuto</string>
|
||||
<string name="track_info_dialog_positive_button">OK</string>
|
||||
<string name="track_info_dialog_title">Info traccia</string>
|
||||
<string name="track_info_disc_number">Numero del disco</string>
|
||||
<string name="track_info_duration">Durata</string>
|
||||
<string name="track_info_genre">Genere</string>
|
||||
<string name="track_info_path">Percorso</string>
|
||||
<string name="track_info_size">Dimensione</string>
|
||||
<string name="track_info_suffix">Suffisso</string>
|
||||
<string name="track_info_summary_downloaded_file">Il file è stato scaricato utilizzando le API Subsonic. Il codec e il bitrate del file rimangono invariati rispetto al file sorgente.</string>
|
||||
<string name="track_info_summary_full_transcode">L\'applicazione richiederà al server di transcodedare il file e modificare il suo bitrate. Il codec richiesto dall\'utente è %1$s, con un bitrate di %2$s. Eventuali modifiche al codec e al bitrate del file nel formato scelto saranno gestite dal server, che potrebbe o meno supportare l\'operazione.</string>
|
||||
<string name="track_info_summary_original_file">L\'applicazione leggerà solo il file originale fornito dal server. L\'app richiederà esplicitamente al server il file non transcodedato con il bitrate della sorgente originale.</string>
|
||||
<string name="track_info_summary_server_prioritized">La qualità del file da riprodurre è lasciata alla decisione del server. L\'app non imporrà la scelta di codec e bitrate per eventuali transcoding.</string>
|
||||
<string name="track_info_summary_transcoding_bitrate">L\'applicazione richiederà al server di modificare il bitrate del file. L\'utente ha richiesto un bitrate di %1$s, mentre il codec del file sorgente rimarrà lo stesso. Eventuali modifiche al bitrate del file nel formato scelto saranno effettuate dal server, che potrebbe o meno supportare l\'operazione.</string>
|
||||
<string name="track_info_summary_transcoding_codec">L\'applicazione richiederà al server di transcodedare il file. Il codec richiesto dall\'utente è %1$s, mentre il bitrate sarà lo stesso del file sorgente. L\'eventuale transcoding del file nel formato scelto dipende dal server, in quanto potrebbe o meno supportare l\'operazione.</string>
|
||||
<string name="track_info_title">Titolo</string>
|
||||
<string name="track_info_track_number">Numero traccia</string>
|
||||
<string name="track_info_transcoded_content_type">Tipo di contenuto transcodedato</string>
|
||||
<string name="track_info_transcoded_suffix">Suffisso transcodedato</string>
|
||||
<string name="track_info_year">Anno</string>
|
||||
<string name="undraw_page">unDraw</string>
|
||||
<string name="undraw_thanks">Un ringraziamento speciale va a unDraw, senza le cui illustrazioni non avremmo potuto rendere questa applicazione più bella.</string>
|
||||
<string name="undraw_url">https://undraw.co/</string>
|
||||
<string name="player_lyrics_download_content_description">Scarica i testi delle canzoni per riprodurli offline</string>
|
||||
<string name="player_lyrics_downloaded_content_description">Testi scaricati per la riproduzione offline</string>
|
||||
<string name="player_lyrics_download_success">Testi salvati per la riproduzione offline.</string>
|
||||
<string name="player_lyrics_download_failure">I testi non sono disponibili per il download.</string>
|
||||
<string name="player_server_priority">Priorità server</string>
|
||||
<string name="player_unknown_format">Formato sconosciuto</string>
|
||||
<string name="player_transcoding">Transcodifica</string>
|
||||
<string name="player_transcoding_requested">richiesto</string>
|
||||
<string name="playlist_catalogue_title">Catalogo playlist</string>
|
||||
<string name="playlist_catalogue_title_expanded">Sfoglia le playlist</string>
|
||||
<string name="playlist_chooser_dialog_empty">Nessuna playlist creata</string>
|
||||
<string name="playlist_chooser_dialog_negative_button">Annulla</string>
|
||||
<string name="playlist_chooser_dialog_neutral_button">Crea</string>
|
||||
<string name="playlist_chooser_dialog_title">Aggiungi a una playlist</string>
|
||||
<string name="playlist_chooser_dialog_toast_add_success">Aggiunta di un brano alla playlist</string>
|
||||
<string name="playlist_chooser_dialog_toast_add_failure">Impossibile aggiungere un brano alla playlist</string>
|
||||
<string name="playlist_chooser_dialog_toast_all_skipped">Tutte le canzoni sono state saltate perché duplicate</string>
|
||||
<string name="playlist_counted_tracks">%1$d brani • %2$s</string>
|
||||
<string name="playlist_duration">Durata • %1$s</string>
|
||||
<string name="playlist_editor_dialog_action_delete_toast">Premi a lungo per eliminare</string>
|
||||
<string name="playlist_editor_dialog_hint_name">Nome della playlist</string>
|
||||
<string name="playlist_editor_dialog_negative_button">Annulla</string>
|
||||
<string name="playlist_editor_dialog_neutral_button">Elimina</string>
|
||||
<string name="playlist_editor_dialog_positive_button">Salva</string>
|
||||
<string name="playlist_editor_dialog_title">Modifica playlist</string>
|
||||
<string name="playlist_page_play_button">Riproduci</string>
|
||||
<string name="playlist_page_shuffle_button">Mescola</string>
|
||||
<string name="playlist_song_count">Playlist • %1$d brani</string>
|
||||
<string name="podcast_bottom_sheet_add_to_queue">Aggiungi alla coda</string>
|
||||
<string name="podcast_bottom_sheet_delete">Elimina</string>
|
||||
<string name="podcast_bottom_sheet_download">Scarica</string>
|
||||
<string name="podcast_bottom_sheet_go_to_channel">Vai al canale</string>
|
||||
<string name="podcast_bottom_sheet_play_next">Riproduci dopo</string>
|
||||
<string name="podcast_bottom_sheet_remove">Rimuovi</string>
|
||||
<string name="podcast_channel_catalogue_title">Canali</string>
|
||||
<string name="podcast_channel_catalogue_title_expanded">Sfoglia Canali</string>
|
||||
<string name="podcast_channel_editor_dialog_hint_rss_url">URL RSS</string>
|
||||
<string name="podcast_channel_editor_dialog_title">Canale Podcast</string>
|
||||
<string name="podcast_channel_page_title_description_section">Descrizione</string>
|
||||
<string name="podcast_channel_page_title_episode_section">Episodi</string>
|
||||
<string name="podcast_channel_page_title_no_episode_available">Nessun episodio disponibile</string>
|
||||
<string name="podcast_episode_download_request_snackbar">La tua richiesta è stata inviata al server</string>
|
||||
<string name="podcast_info_empty_button">Clicca per nascondere la sezione\nGli effetti saranno visibili al riavvio</string>
|
||||
<string name="podcast_info_empty_subtitle">Una volta aggiunto un canale, lo troverai qui</string>
|
||||
<string name="podcast_info_empty_title">Nessun podcast trovato!</string>
|
||||
<string name="podcast_release_date_duration_formatter">%1$s • %2$s</string>
|
||||
<string name="radio_editor_dialog_hint_homepage_url">URL Homepage Radio</string>
|
||||
<string name="radio_editor_dialog_hint_name">Nome Radio</string>
|
||||
<string name="radio_editor_dialog_hint_stream_url">URL Stream Radio</string>
|
||||
<string name="radio_editor_dialog_negative_button">Annulla</string>
|
||||
<string name="radio_editor_dialog_neutral_button">Elimina</string>
|
||||
<string name="radio_editor_dialog_positive_button">Salva</string>
|
||||
<string name="radio_editor_dialog_title">Stazione Internet-Radio</string>
|
||||
<string name="radio_station_info_empty_button">Clicca per nascondere la sezione\nGli effetti saranno visibili al riavvio</string>
|
||||
<string name="radio_station_info_empty_subtitle">Una volta aggiunta una stazione radio, la troverai qui</string>
|
||||
<string name="radio_station_info_empty_title">Nessuna stazione trovata!</string>
|
||||
<string name="rating_dialog_negative_button">Annulla</string>
|
||||
<string name="rating_dialog_positive_button">Salva</string>
|
||||
<string name="rating_dialog_title">Valuta</string>
|
||||
<string name="search_hint">Cerca titolo, artisti o album</string>
|
||||
<string name="search_info_minimum_characters">Inserisci almeno tre caratteri</string>
|
||||
<string name="search_title_album">Album</string>
|
||||
<string name="search_title_artist">Artisti</string>
|
||||
<string name="search_title_song">Brani</string>
|
||||
<string name="server_signup_dialog_action_low_security">Bassa sicurezza</string>
|
||||
<string name="server_signup_dialog_action_delete_toast">Premi a lungo per eliminare</string>
|
||||
<string name="server_signup_dialog_hint_local_address">URL locale</string>
|
||||
<string name="server_signup_dialog_hint_name">Nome Server</string>
|
||||
<string name="server_signup_dialog_hint_password">Password</string>
|
||||
<string name="server_signup_dialog_hint_url">URL Server</string>
|
||||
<string name="server_signup_dialog_hint_username">Nome utente</string>
|
||||
<string name="server_signup_dialog_negative_button">Annulla</string>
|
||||
<string name="server_signup_dialog_neutral_button">Elimina</string>
|
||||
<string name="server_signup_dialog_positive_button">Salva</string>
|
||||
<string name="server_signup_dialog_title">Aggiungi server</string>
|
||||
<string name="server_unreachable_dialog_negative_button">Annulla</string>
|
||||
<string name="server_unreachable_dialog_neutral_button">Vai al login</string>
|
||||
<string name="server_unreachable_dialog_positive_button">Continua comunque</string>
|
||||
<string name="server_unreachable_dialog_summary">Il server richiesto non è disponibile. Se scegli di continuare, questo messaggio non apparirà per la prossima ora.</string>
|
||||
<string name="server_unreachable_dialog_title">Server irraggiungibile</string>
|
||||
<string name="settings_about_summary">Tempus è un client musicale open source e leggero per Subsonic, progettato e costruito nativamente per Android.</string>
|
||||
<string name="settings_about_title">Informazioni</string>
|
||||
<string name="settings_always_on_display">Sempre attivo</string>
|
||||
<string name="settings_allow_playlist_duplicates">Allow adding duplicates to playlist</string>
|
||||
<string name="settings_allow_playlist_duplicates_summary">If enabled, duplicates won\'t be checked while adding to a playlist.</string>
|
||||
<string name="settings_audio_transcode_download_format">Formato transcodifica</string>
|
||||
<string name="settings_audio_transcode_download_priority_summary">Se abilitato, Tempus non forzerà il download del brano con le impostazioni di transcodifica sottostanti.</string>
|
||||
<string name="settings_audio_transcode_download_priority_title">Dare priorità alle impostazioni del server per lo streaming nei download</string>
|
||||
<string name="settings_audio_transcode_download_summary">Se abilitato, Tempus scaricherà i brani transcodificati.</string>
|
||||
<string name="settings_audio_transcode_download_title">Scarica brani transcodificati</string>
|
||||
<string name="settings_audio_transcode_estimate_content_length_summary">Se abilitato, verrà richiesto al server di fornire la durata stimata del brano.</string>
|
||||
<string name="settings_audio_transcode_estimate_content_length_title">Stima della lunghezza del contenuto</string>
|
||||
<string name="settings_audio_transcode_format_download">Formato transcodifica per download</string>
|
||||
<string name="settings_audio_transcode_format_mobile">Formato transcodifica su mobile</string>
|
||||
<string name="settings_audio_transcode_format_wifi">Formato transcodifica su Wi-Fi</string>
|
||||
<string name="settings_audio_transcode_priority_summary">Se abilitato, Tempus non forzerà lo streaming del brano con le impostazioni di transcodifica sottostanti.</string>
|
||||
<string name="settings_audio_transcode_priority_title">Dare priorità alle impostazioni di transcodifica del server</string>
|
||||
<string name="settings_audio_transcode_priority_toast">Priorità di transcodifica del brano assegnata al server</string>
|
||||
<string name="settings_buffering_strategy">Strategia di buffering</string>
|
||||
<string name="settings_buffering_strategy_summary">Perché la modifica abbia effetto è necessario riavviare manualmente l\'app.</string>
|
||||
<string name="settings_choose_download_folder">Scegli una cartella dove scaricare la musica</string>
|
||||
<string name="settings_clear_download_folder">Svuota la cartella di download</string>
|
||||
<string name="settings_continuous_play_summary">Consente alla musica di continuare a suonare dopo la fine di una playlist, riproducendo brani simili</string>
|
||||
<string name="settings_continuous_play_title">Riproduzione continua</string>
|
||||
<string name="settings_covers_cache">Dimensione della cache delle copertine</string>
|
||||
<string name="settings_data_saving_mode_summary">Per ridurre il consumo di dati, evita di scaricare le copertine.</string>
|
||||
<string name="settings_data_saving_mode_title">Limita utilizzo dei dati mobili</string>
|
||||
<string name="settings_delete_download_storage_summary">Continuando, tutti gli elementi salvati verranno eliminati in modo irreversibile.</string>
|
||||
<string name="settings_delete_download_storage_title">Elimina elementi salvati</string>
|
||||
<string name="settings_download_storage_title">Archivio download</string>
|
||||
<string name="settings_download_folder_cleared">Cartella di download svuotata.</string>
|
||||
<string name="settings_download_folder_set">Cartella di download impostata</string>
|
||||
<string name="settings_set_download_folder">Imposta cartella di download</string>
|
||||
<string name="settings_system_equalizer_summary">Regola le impostazioni audio</string>
|
||||
<string name="settings_system_equalizer_title">Equalizzatore di sistema</string>
|
||||
<string name="settings_github_link">https://github.com/eddyizm/tempus</string>
|
||||
<string name="settings_github_summary">Segui lo sviluppo</string>
|
||||
<string name="settings_github_title">Github</string>
|
||||
<string name="settings_support_discussion_link">https://github.com/eddyizm/tempus/discussions</string>
|
||||
<string name="settings_github_update">Aggiornamenti</string>
|
||||
<string name="settings_github_update_title">Controlla GitHub per aggiornamenti</string>
|
||||
<string name="settings_github_update_summary">Se si utilizza la versione GitHub, per impostazione predefinita l\'app controllerà la presenza di nuove versioni. Disattiva per disabilitare i controlli automatici su GitHub</string>
|
||||
<string name="settings_support_summary">Partecipa alle discussioni della community e al supporto</string>
|
||||
<string name="settings_support_title">Supporto utenti</string>
|
||||
<string name="settings_scan_result">Scansione: conteggio di %1$d brani</string>
|
||||
<string name="settings_image_size">Imposta risoluzione delle immagini</string>
|
||||
<string name="settings_language">Lingua</string>
|
||||
<string name="settings_logout_title">Esci</string>
|
||||
<string name="settings_max_bitrate_download">Bitrate per download</string>
|
||||
<string name="settings_max_bitrate_mobile">Bitrate su mobile</string>
|
||||
<string name="settings_max_bitrate_wifi">Bitrate su Wi-Fi</string>
|
||||
<string name="settings_media_cache">Dimensione della cache dei file multimediali</string>
|
||||
<string name="settings_music_directory">Mostra directory musicali</string>
|
||||
<string name="settings_music_directory_summary">Se abilitato, mostra la sezione delle directory musicali. Nota che per la navigazione nelle cartelle è necessario che il server supporti questa funzionalità.</string>
|
||||
<string name="settings_podcast">Mostra podcast</string>
|
||||
<string name="settings_podcast_summary">Se abilitato, mostra la sezione podcast. Riavvia l\'app per rendere effettive le modifiche.</string>
|
||||
<string name="settings_audio_quality">Mostra qualità audio</string>
|
||||
<string name="settings_audio_quality_summary">Il bitrate e il formato audio saranno mostrati per ogni traccia.</string>
|
||||
<string name="settings_song_rating">Mostra valutazione della canzone</string>
|
||||
<string name="settings_song_rating_summary">Se abilitato, mostra la valutazione a 5 stelle per la traccia nella pagina della canzone\n\n*Richiede il riavvio dell\'app</string>
|
||||
<string name="settings_item_rating">Mostra valutazione</string>
|
||||
<string name="settings_item_rating_summary">Se abilitato, verrà mostrata la valutazione dell\'elemento e se è contrassegnato come preferito.</string>
|
||||
<string name="settings_queue_syncing_countdown">Timer sincronizzazione</string>
|
||||
<string name="settings_queue_syncing_summary">Se abilitato, l\'utente avrà la possibilità di salvare la propria coda di riproduzione e potrà caricare lo stato all\'apertura dell\'applicazione.</string>
|
||||
<string name="settings_queue_syncing_title">Sincronizza coda di riproduzione per questo utente [Not Fully Baked]</string>
|
||||
<string name="settings_show_mini_shuffle_button">Mostra il pulsante di riproduzione casuale</string>
|
||||
<string name="settings_show_mini_shuffle_button_summary">Se abilitato, mostra il pulsante di riproduzione casuale, rimuove il cuore nel mini player</string>
|
||||
<string name="settings_radio">Mostra radio</string>
|
||||
<string name="settings_radio_summary">Se abilitato, mostra la sezione radio. Riavvia l\'app per applicare completamente le modifiche.</string>
|
||||
<string name="settings_auto_download_lyrics">Scarica automaticamente i testi</string>
|
||||
<string name="settings_auto_download_lyrics_summary">Salva automaticamente i testi quando sono disponibili in modo che possano essere mostrati offline.</string>
|
||||
<string name="settings_replay_gain">Imposta modalità di guadagno di riproduzione</string>
|
||||
<string name="settings_rounded_corner">Angoli arrotondati</string>
|
||||
<string name="settings_rounded_corner_size">Dimensione angoli</string>
|
||||
<string name="settings_rounded_corner_size_summary">Imposta la grandezza dell\'angolo di curvatura.</string>
|
||||
<string name="settings_rounded_corner_summary">Se abilitato, imposta un angolo di curvatura per tutte le copertine visualizzate. Le modifiche avranno effetto al riavvio.</string>
|
||||
<string name="settings_scan_title">Scansiona libreria</string>
|
||||
<string name="settings_scrobble_title">Abilita scrobbling musicale</string>
|
||||
<string name="settings_system_language">Lingua di sistema</string>
|
||||
<string name="settings_share_title">Abilita condivisione musicale</string>
|
||||
<string name="settings_streaming_cache_size">Dimensione cache streaming</string>
|
||||
<string name="settings_streaming_cache_storage_title">Archiviazione cache streaming</string>
|
||||
<string name="settings_sub_summary_scrobble">È importante notare che lo scrobbling si basa anche sul fatto che il server sia abilitato a ricevere questi dati.</string>
|
||||
<string name="settings_summary_skip_min_star_rating">Quando si ascolta la radio di un artista, un mix istantaneo o quando si mescolano tutti i brani, i brani sotto una certa valutazione dell\'utente verranno ignorati.</string>
|
||||
<string name="settings_summary_replay_gain">Il guadagno di riproduzione è una funzionalità che consente di regolare il livello del volume delle tracce audio per un\'esperienza di ascolto coerente. Questa impostazione è efficace solo se la traccia contiene i metadati necessari.</string>
|
||||
<string name="settings_summary_scrobble">Lo scrobbling è una funzionalità che consente al tuo dispositivo di inviare informazioni sulle canzoni che ascolti al server musicale. Queste informazioni aiutano a creare raccomandazioni personalizzate in base alle tue preferenze musicali.</string>
|
||||
<string name="settings_summary_share">Permette all\'utente di condividere musica tramite un link. La funzionalità deve essere supportata e abilitata sul server ed è limitata a brani, album e playlist singoli.</string>
|
||||
<string name="settings_summary_syncing">Restituisce lo stato della coda di riproduzione per questo utente. Ciò include i brani nella coda di riproduzione, il brano attualmente in riproduzione e la posizione all\'interno di questo brano. Il server deve supportare questa funzionalità.\n*This setting is not 100% working on all servers/devices.</string>
|
||||
<string name="settings_summary_streaming_cache_size">%1$s \nAttualmente in uso: %2$s MiB</string>
|
||||
<string name="settings_summary_transcoding">Priorità data alla modalità di transcoding. Se impostato su "Riproduzione diretta", il bitrate del file non verrà modificato.</string>
|
||||
<string name="settings_summary_transcoding_download">Scarica media transcodificati. Se abilitato, l\'endpoint di download non verrà utilizzato, ma le impostazioni seguenti. \n\n Se "Formato di transcodifica per i download" è impostato su "Download diretto", il bitrate del file non verrà modificato.</string>
|
||||
<string name="settings_summary_transcoding_estimate_content_length">Quando il file viene transcodificato al volo, il client di solito non mostra la lunghezza della traccia. È possibile richiedere ai server che supportano la funzionalità di stimare la durata della traccia in riproduzione, ma i tempi di risposta possono essere più lunghi.</string>
|
||||
<string name="settings_sync_starred_artists_for_offline_use_summary">Se abilitato, gli artisti preferiti verranno scaricati per l\'uso offline.</string>
|
||||
<string name="settings_sync_starred_artists_for_offline_use_title">Sincronizza artisti preferiti per uso offline</string>
|
||||
<string name="settings_sync_starred_albums_for_offline_use_summary">Se abilitato, gli album preferiti verranno scaricati per l\'uso offline.</string>
|
||||
<string name="settings_sync_starred_albums_for_offline_use_title">Sincronizza album preferiti per uso offline</string>
|
||||
<string name="settings_sync_starred_tracks_for_offline_use_summary">Se abilitato, le tracce preferite verranno scaricate per l\'uso offline.</string>
|
||||
<string name="settings_sync_starred_tracks_for_offline_use_title">Sincronizza tracce preferite per uso offline</string>
|
||||
<string name="settings_theme">Tema</string>
|
||||
<string name="settings_title_data">Dati</string>
|
||||
<string name="settings_title_general">Generale</string>
|
||||
<string name="settings_title_playlist">Playlist</string>
|
||||
<string name="settings_title_rating">Valutazione</string>
|
||||
<string name="settings_title_replay_gain">Guadagno di riproduzione</string>
|
||||
<string name="settings_title_scrobble">Scrobble</string>
|
||||
<string name="settings_title_skip_min_star_rating">Ignora brani in base alla valutazione</string>
|
||||
<string name="settings_title_skip_min_star_rating_dialog">Brani con una valutazione di:</string>
|
||||
<string name="settings_title_share">Condividi</string>
|
||||
<string name="settings_title_syncing">Sincronizzazione</string>
|
||||
<string name="settings_title_transcoding">Transcodifica</string>
|
||||
<string name="settings_title_transcoding_download">Transcodifica dei Download</string>
|
||||
<string name="settings_title_ui">Interfaccia utente</string>
|
||||
<string name="settings_transcoded_download">Download transcodificato</string>
|
||||
<string name="settings_version_summary" translatable="false">3.1.0</string>
|
||||
<string name="settings_version_title">Versione</string>
|
||||
<string name="settings_wifi_only_summary">Chiedi conferma all\'utente prima di effettuare streaming su rete mobile.</string>
|
||||
<string name="settings_wifi_only_title">Streaming solo tramite Wi-Fi avviso</string>
|
||||
<string name="share_bottom_sheet_copy_link">Copia link</string>
|
||||
<string name="share_bottom_sheet_delete">Elimina condivisione</string>
|
||||
<string name="share_bottom_sheet_update">Aggiorna condivisione</string>
|
||||
<string name="share_subtitle_item">Data di scadenza: %1$s</string>
|
||||
<string name="share_no_expiration">Mai</string>
|
||||
<string name="share_unsupported_error">La condivisione non è supportata o non è abilitata</string>
|
||||
<string name="asset_link_clipboard_label">Link asset Tempus</string>
|
||||
<string name="asset_link_label_song">UID Canzone</string>
|
||||
<string name="asset_link_label_album">UID Album</string>
|
||||
<string name="asset_link_label_artist">UID Artista</string>
|
||||
<string name="asset_link_label_playlist">UID Playlist</string>
|
||||
<string name="asset_link_label_genre">UID Genere</string>
|
||||
<string name="asset_link_label_year">UID Anno</string>
|
||||
<string name="asset_link_label_unknown">UID Asset</string>
|
||||
<string name="asset_link_error_unsupported">Link asset non supportato</string>
|
||||
<string name="asset_link_error_song">Impossibile aprire la canzone</string>
|
||||
<string name="asset_link_error_album">Impossibile aprire l\'album</string>
|
||||
<string name="asset_link_error_artist">Impossibile aprire l\'artista</string>
|
||||
<string name="asset_link_error_playlist">Impossibile aprire la playlist</string>
|
||||
<string name="asset_link_chip_text">%1$s • %2$s</string>
|
||||
<string name="asset_link_copied_toast">Copiato %1$s negli appunti</string>
|
||||
<string name="asset_link_debug_toast">Link asset: %1$s</string>
|
||||
<string name="share_update_dialog_hint_description">Descrizione</string>
|
||||
<string name="share_update_dialog_hint_expiration_date">Data di scadenza</string>
|
||||
<string name="share_update_dialog_negative_button">Annulla</string>
|
||||
<string name="share_update_dialog_positive_button">Salva</string>
|
||||
<string name="share_update_dialog_title">Condividi</string>
|
||||
<string name="song_bottom_sheet_add_to_playlist">Aggiungi alla playlist</string>
|
||||
<string name="song_bottom_sheet_add_to_queue">Aggiungi alla coda</string>
|
||||
<string name="song_bottom_sheet_download">Scarica</string>
|
||||
<string name="song_bottom_sheet_error_retrieving_album">Errore nel recupero dell\'album</string>
|
||||
<string name="song_bottom_sheet_error_retrieving_artist">Errore nel recupero dell\'artista</string>
|
||||
<string name="song_bottom_sheet_go_to_album">Vai all\'album</string>
|
||||
<string name="song_bottom_sheet_go_to_artist">Vai all\'artista</string>
|
||||
<string name="song_bottom_sheet_instant_mix">Mix istantaneo</string>
|
||||
<string name="song_bottom_sheet_play_next">Riproduci dopo</string>
|
||||
<string name="song_bottom_sheet_rate">Valuta</string>
|
||||
<string name="song_bottom_sheet_remove">Rimuovi</string>
|
||||
<string name="song_bottom_sheet_share">Condividi</string>
|
||||
<string name="song_list_page_downloaded">Scaricato</string>
|
||||
<string name="song_list_page_most_played">Tracce più riprodotte</string>
|
||||
<string name="song_list_page_recently_added">Tracce aggiunte di recente</string>
|
||||
<string name="song_list_page_recently_played">Tracce riprodotte di recente</string>
|
||||
<string name="song_list_page_starred">Tracce contrassegnate</string>
|
||||
<string name="song_list_page_top">Le migliori tracce di %1$s</string>
|
||||
<string name="song_list_page_year">Anno %1$d</string>
|
||||
<string name="song_subtitle_formatter">%1$s • %2$s %3$s</string>
|
||||
<string name="starred_sync_dialog_negative_button">Annulla</string>
|
||||
<string name="starred_sync_dialog_neutral_button">Continua</string>
|
||||
<string name="starred_sync_dialog_positive_button">Continua e scarica</string>
|
||||
<string name="starred_sync_dialog_summary">Il download delle tracce contrassegnate potrebbe richiedere una grande quantità di dati.</string>
|
||||
<string name="starred_sync_dialog_title">Sincronizza tracce contrassegnate</string>
|
||||
<string name="starred_artist_sync_dialog_summary">Scaricare gli artisti preferiti potrebbe richiedere una grande quantità di dati.</string>
|
||||
<string name="starred_artist_sync_dialog_title">Sincronizza artisti preferiti</string>
|
||||
<string name="starred_album_sync_dialog_summary">Scaricare gli album preferiti potrebbe richiedere una grande quantità di dati.</string>
|
||||
<string name="starred_album_sync_dialog_title">Sincronizza album preferiti</string>
|
||||
<string name="streaming_cache_storage_dialog_sub_summary">Per rendere effettive le modifiche, riavvia l\'app.</string>
|
||||
<string name="streaming_cache_storage_dialog_summary">Cambiare la destinazione dei file memorizzati nella cache da un\'unità di archiviazione a un\'altra può comportare la cancellazione di eventuali file memorizzati nella cache in precedenza nell\'altra unità di archiviazione.</string>
|
||||
<string name="streaming_cache_storage_dialog_title">Seleziona opzione di archiviazione</string>
|
||||
<string name="streaming_cache_storage_external_dialog_positive_button">Esterno</string>
|
||||
<string name="streaming_cache_storage_internal_dialog_negative_button">Interno</string>
|
||||
<string name="support_url">https://ko-fi.com/eddyizm</string>
|
||||
<string name="track_info_album">Album</string>
|
||||
<string name="track_info_artist">Artista</string>
|
||||
<string name="track_info_bit_depth">Profondità bit</string>
|
||||
<string name="track_info_bitrate">Bitrate</string>
|
||||
<string name="track_info_content_type">Tipo di contenuto</string>
|
||||
<string name="track_info_dialog_positive_button">OK</string>
|
||||
<string name="track_info_dialog_title">Info traccia</string>
|
||||
<string name="track_info_disc_number">Numero del disco</string>
|
||||
<string name="track_info_duration">Durata</string>
|
||||
<string name="track_info_genre">Genere</string>
|
||||
<string name="track_info_path">Percorso</string>
|
||||
<string name="track_info_sampling_rate">Frequenza di campionamento</string>
|
||||
<string name="track_info_size">Dimensione</string>
|
||||
<string name="track_info_suffix">Suffisso</string>
|
||||
<string name="track_info_summary_downloaded_file">Il file è stato scaricato utilizzando le API Subsonic. Il codec e il bitrate del file rimangono invariati rispetto al file sorgente.</string>
|
||||
<string name="track_info_summary_full_transcode">L\'applicazione richiederà al server di transcodedare il file e modificare il suo bitrate. Il codec richiesto dall\'utente è %1$s, con un bitrate di %2$s. Eventuali modifiche al codec e al bitrate del file nel formato scelto saranno gestite dal server, che potrebbe o meno supportare l\'operazione.</string>
|
||||
<string name="track_info_summary_original_file">L\'applicazione leggerà solo il file originale fornito dal server. L\'app richiederà esplicitamente al server il file non transcodedato con il bitrate della sorgente originale.</string>
|
||||
<string name="track_info_summary_server_prioritized">La qualità del file da riprodurre è lasciata alla decisione del server. L\'app non imporrà la scelta di codec e bitrate per eventuali transcoding.</string>
|
||||
<string name="track_info_summary_transcoding_bitrate">L\'applicazione richiederà al server di modificare il bitrate del file. L\'utente ha richiesto un bitrate di %1$s, mentre il codec del file sorgente rimarrà lo stesso. Eventuali modifiche al bitrate del file nel formato scelto saranno effettuate dal server, che potrebbe o meno supportare l\'operazione.</string>
|
||||
<string name="track_info_summary_transcoding_codec">L\'applicazione richiederà al server di transcodedare il file. Il codec richiesto dall\'utente è %1$s, mentre il bitrate sarà lo stesso del file sorgente. L\'eventuale transcoding del file nel formato scelto dipende dal server, in quanto potrebbe o meno supportare l\'operazione.</string>
|
||||
<string name="track_info_title">Titolo</string>
|
||||
<string name="track_info_track_number">Numero traccia</string>
|
||||
<string name="track_info_transcoded_content_type">Tipo di contenuto transcodificato</string>
|
||||
<string name="track_info_transcoded_suffix">Suffisso transcodificato</string>
|
||||
<string name="track_info_year">Anno</string>
|
||||
<string name="undraw_page">unDraw</string>
|
||||
<string name="undraw_thanks">Un ringraziamento speciale va a unDraw, senza le cui illustrazioni non avremmo potuto rendere questa applicazione più bella.</string>
|
||||
<string name="undraw_url">https://undraw.co/</string>
|
||||
<string name="widget_label">Widget Tempus</string>
|
||||
<string name="widget_not_playing">Non in riproduzione</string>
|
||||
<string name="widget_placeholder_subtitle">Apri Tempus</string>
|
||||
<string name="widget_time_elapsed_placeholder">0:00</string>
|
||||
<string name="widget_time_duration_placeholder">0:00</string>
|
||||
<string name="widget_content_desc_album_art">Immagine dell\'album</string>
|
||||
<string name="widget_content_desc_play_pause">Riproduci o metti in pausa</string>
|
||||
<string name="widget_content_desc_next">Traccia successiva</string>
|
||||
<string name="widget_content_desc_prev">Traccia precedente</string>
|
||||
<string name="widget_content_desc_shuffle">Attiva/disattiva riproduzione casuale</string>
|
||||
<string name="widget_content_desc_repeat">Cambia modalità di ripetizione</string>
|
||||
<plurals name="home_sync_starred_albums_count">
|
||||
<item quantity="one">%d album da sincronizzare</item>
|
||||
<item quantity="other">%d album da sincronizzare</item>
|
||||
</plurals>
|
||||
<plurals name="home_sync_starred_artists_count">
|
||||
<item quantity="one">%d artista da sincronizzare</item>
|
||||
<item quantity="other">%d artisti da sincronizzare</item>
|
||||
</plurals>
|
||||
<plurals name="songs_download_started">
|
||||
<item quantity="one">Scaricando %d canzone</item>
|
||||
<item quantity="other">Scaricando %d canzoni</item>
|
||||
</plurals>
|
||||
<string name="equalizer_fragment_title">Equalizzatore</string>
|
||||
<string name="equalizer_reset">Reimposta</string>
|
||||
<string name="equalizer_enable">Abilita</string>
|
||||
<string name="equalizer_not_supported">Non supportato su questo dispositivo</string>
|
||||
<string name="settings_app_equalizer">Equalizzatore</string>
|
||||
<string name="settings_app_equalizer_summary">Apri l\'equalizzatore integrato</string>
|
||||
|
||||
<string name="settings_album_detail">Mostra dettagli album</string>
|
||||
<string name="settings_album_detail_summary">Se abilitato, mostra i dettagli dell\'album come genere, numero di canzoni, ecc. nella pagina dell\'album</string>
|
||||
<string name="settings_artist_sort_by_album_count">Ordina artisti per numero di album</string>
|
||||
<string name="settings_artist_sort_by_album_count_summary">Se abilitato, ordina gli artisti per numero di album. Ordina per nome se disabilitato.</string>
|
||||
</resources>
|
||||
|
||||
@@ -321,6 +321,9 @@
|
||||
<string name="settings_github_summary">Śledź tworzenie aplikacji</string>
|
||||
<string name="settings_github_title">GitHub</string>
|
||||
<string name="settings_support_discussion_link">https://github.com/eddyizm/tempus/discussions</string>
|
||||
<string name="settings_github_update">Aktualizacje</string>
|
||||
<string name="settings_github_update_title">Sprawdzaj dostępność nowych aktualizacji na githubie</string>
|
||||
<string name="settings_github_update_summary">Jeżeli używana jest wersja z githuba, domyślnie aplikacja będzie sprawdzać czy są dostępne nowe wydania apk. Kliknij przełącznik aby, wyłączyć automatyczne sprawdzanie</string>
|
||||
<string name="settings_support_summary">Dołącz do dyskusji i wsparcia społeczności</string>
|
||||
<string name="settings_support_title">Wsparcie użytkowników</string>
|
||||
<string name="settings_scan_result">Skanowanie: naliczono %1$d utworów</string>
|
||||
@@ -400,6 +403,7 @@
|
||||
<string name="share_bottom_sheet_delete">Usuń udostępnianie</string>
|
||||
<string name="share_bottom_sheet_update">Zaktualizuj udostępnianie</string>
|
||||
<string name="share_subtitle_item">Data wygaśnięcia: %1$s</string>
|
||||
<string name="share_no_expiration">Nigdy</string>
|
||||
<string name="share_unsupported_error">Udostępnianie nie jest wspierane lub włączone</string>
|
||||
<string name="asset_link_clipboard_label">Link zasobu Tempus</string>
|
||||
<string name="asset_link_label_song">UID piosenki</string>
|
||||
@@ -516,4 +520,6 @@
|
||||
<string name="settings_app_equalizer_summary">Otwórz wbudowany korektor dźwięku</string>
|
||||
<string name="settings_album_detail">Pokaż szczegóły albumu</string>
|
||||
<string name="settings_album_detail_summary">Jeżeli włączone, pokaż szczegóły albumu takie jak gatunek, ilość piosenek itp. na stronie albumu</string>
|
||||
<string name="settings_artist_sort_by_album_count">Sortuj wykonawców po ilości albumów</string>
|
||||
<string name="settings_artist_sort_by_album_count_summary">Jeżeli włączone, sortuje wykonawców po ilości albumów. Jeżeli wyłączone, sortuje albumy po nazwach.</string>
|
||||
</resources>
|
||||
|
||||
@@ -270,7 +270,13 @@
|
||||
<string name="server_unreachable_dialog_title">Сервер недоступен</string>
|
||||
<string name="settings_about_summary">Tempus — это легкий музыкальный клиент с открытым исходным кодом для Subsonic, разработанный и созданный специально для Android.</string>
|
||||
<string name="settings_about_title">О нас</string>
|
||||
<string name="settings_album_detail">Показать детали альбома</string>
|
||||
<string name="settings_album_detail_summary">Если включено, отображать информацию об альбоме, например жанр, количество песен и т. д., на странице альбома.</string>
|
||||
<string name="settings_allow_playlist_duplicates">Разрешить добавление дубликатов в плейлист</string>
|
||||
<string name="settings_allow_playlist_duplicates_summary">Если включено, дубликаты не будут проверяться при добавлении в плейлист..</string>
|
||||
<string name="settings_always_on_display">Всегда на дисплее</string>
|
||||
<string name="settings_artist_sort_by_album_count">Сортировать исполнителей по количеству альбомов</string>
|
||||
<string name="settings_artist_sort_by_album_count_summary">Если включено, сортировать исполнителей по количеству альбомов. Если отключено, сортировать по имени.</string>
|
||||
<string name="settings_audio_transcode_download_format">Формат перекодирования</string>
|
||||
<string name="settings_audio_transcode_download_priority_summary">Если этот параметр включен, Tempus не будет принудительно загружать трек с настройками перекодирования, указанными ниже.</string>
|
||||
<string name="settings_audio_transcode_download_priority_title">Установите приоритет настроек сервера, используемых для потоковой передачи при загрузке</string>
|
||||
@@ -319,8 +325,12 @@
|
||||
<string name="settings_queue_syncing_countdown">Таймер синхронизации</string>
|
||||
<string name="settings_queue_syncing_summary">Если этот параметр включен, пользователь будет иметь возможность сохранять свою очередь воспроизведения и загружать состояние при открытии приложения.</string>
|
||||
<string name="settings_queue_syncing_title">Синхронизировать очередь воспроизведения для этого пользователя</string>
|
||||
<string name="settings_show_mini_shuffle_button">Показать кнопку Shuffle</string>
|
||||
<string name="settings_show_mini_shuffle_button_summary">Если включено, показывать кнопку перемешивания, убрать сердечко в мини-плеере</string>
|
||||
<string name="settings_radio">Показать радио</string>
|
||||
<string name="settings_radio_summary">Если включено, показывать раздел радио. Перезапустите приложение, чтобы оно вступило в силу.</string>
|
||||
<string name="settings_auto_download_lyrics">Автоматическая загрузка текстов песен</string>
|
||||
<string name="settings_auto_download_lyrics_summary">Автоматически сохранять тексты песен, когда они доступны, чтобы их можно было просматривать в автономном режиме.</string>
|
||||
<string name="settings_replay_gain">Установите режим усиления воспроизведения</string>
|
||||
<string name="settings_rounded_corner">Закругленные углы</string>
|
||||
<string name="settings_rounded_corner_size">Размер углов</string>
|
||||
@@ -346,6 +356,9 @@
|
||||
<string name="settings_sync_starred_albums_for_offline_use_title">Синхронизировать помеченные альбомы для использования в автономном режиме.</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_sync_starred_artists_for_offline_use_title">Синхронизировать избранных исполнителей для использования офлайн</string>
|
||||
<string name="settings_support_summary">Присоединяйтесь к обсуждениям в сообществе и оказывайте поддержку</string>
|
||||
<string name="settings_support_title">Поддержка пользователей</string>
|
||||
<string name="settings_theme">Тема</string>
|
||||
<string name="settings_title_data">Данные</string>
|
||||
<string name="settings_title_general">Общий</string>
|
||||
|
||||
@@ -200,6 +200,7 @@
|
||||
<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_album_count">Album Count</string>
|
||||
<string name="menu_sort_recently_added">Recently added</string>
|
||||
<string name="menu_sort_recently_played">Recently played</string>
|
||||
<string name="menu_sort_most_played">Most played</string>
|
||||
@@ -330,6 +331,9 @@
|
||||
<string name="settings_github_summary">Follow the development</string>
|
||||
<string name="settings_github_title">Github</string>
|
||||
<string name="settings_support_discussion_link">https://github.com/eddyizm/tempus/discussions</string>
|
||||
<string name="settings_github_update">Updates</string>
|
||||
<string name="settings_github_update_title">Check github for release updates</string>
|
||||
<string name="settings_github_update_summary">If using the github version, by default app will check for new apk release. Toggle to disable automatic github checks</string>
|
||||
<string name="settings_support_summary">Join community discussions and support</string>
|
||||
<string name="settings_support_title">User support</string>
|
||||
<string name="settings_scan_result">Scanning: counting %1$d tracks</string>
|
||||
@@ -378,7 +382,7 @@
|
||||
<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.\n*This setting is not 100% working on all servers/devices.</string>
|
||||
<string name="settings_summary_streaming_cache_size">%1$s \nCurrently in use: %2$s MiB</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_download">Download transcoded media. If enabled, the download endpoint will not be used, but the following settings. \n\n If \"Transcode format for downloads\" 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_sync_starred_artists_for_offline_use_summary">If enabled, starred artists will be downloaded for offline use.</string>
|
||||
<string name="settings_sync_starred_artists_for_offline_use_title">Sync starred artists for offline use</string>
|
||||
@@ -409,6 +413,7 @@
|
||||
<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_no_expiration">Never</string>
|
||||
<string name="share_unsupported_error">Sharing is not supported or not enabled</string>
|
||||
<string name="asset_link_clipboard_label">Tempus asset link</string>
|
||||
<string name="asset_link_label_song">Song UID</string>
|
||||
@@ -526,4 +531,6 @@
|
||||
|
||||
<string name="settings_album_detail">Show album detail</string>
|
||||
<string name="settings_album_detail_summary">If enabled, show the album details like genre, song count etc. on the album page</string>
|
||||
<string name="settings_artist_sort_by_album_count">Sort artists by album count</string>
|
||||
<string name="settings_artist_sort_by_album_count_summary">If enabled, sort the artists by album count. Sort by name if disabled.</string>
|
||||
</resources>
|
||||
|
||||
@@ -116,6 +116,12 @@
|
||||
android:summary="@string/settings_album_detail_summary"
|
||||
android:key="album_detail" />
|
||||
|
||||
<SwitchPreference
|
||||
android:title="@string/settings_artist_sort_by_album_count"
|
||||
android:defaultValue="false"
|
||||
android:summary="@string/settings_artist_sort_by_album_count_summary"
|
||||
android:key="artist_sort_by_album_count" />
|
||||
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory app:title="@string/settings_title_playlist">
|
||||
@@ -394,6 +400,18 @@
|
||||
app:useSimpleSummaryProvider="true" />
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory
|
||||
android:key="settings_github_update_category_key"
|
||||
app:title="@string/settings_github_update">
|
||||
<Preference
|
||||
app:selectable="false"
|
||||
app:summary="@string/settings_github_update_summary" />
|
||||
<SwitchPreference
|
||||
android:title="@string/settings_github_update_title"
|
||||
android:defaultValue="true"
|
||||
android:key="github_update_check" />
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory app:title="@string/settings_about_title">
|
||||
<Preference
|
||||
app:selectable="false"
|
||||
|
||||
@@ -11,4 +11,5 @@
|
||||
<locale android:name="es-ES"/> <!-- Spanish (Spain) -->
|
||||
<locale android:name="pl-PL"/> <!-- Polish -->
|
||||
<locale android:name="tr-TR"/> <!-- Turkish -->
|
||||
<locale android:name="ca"/> <!-- Catalan -->
|
||||
</locale-config>
|
||||
|
||||
@@ -1,151 +1,18 @@
|
||||
package com.cappielloantonio.tempo.service
|
||||
|
||||
import android.app.PendingIntent.FLAG_IMMUTABLE
|
||||
import android.app.PendingIntent.FLAG_UPDATE_CURRENT
|
||||
import android.app.TaskStackBuilder
|
||||
import android.content.Intent
|
||||
import android.os.Binder
|
||||
import android.os.IBinder
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.media3.cast.CastPlayer
|
||||
import androidx.media3.cast.SessionAvailabilityListener
|
||||
import androidx.media3.common.AudioAttributes
|
||||
import androidx.media3.common.C
|
||||
import androidx.media3.common.MediaItem
|
||||
import androidx.media3.common.Player
|
||||
import androidx.media3.common.Tracks
|
||||
import androidx.media3.common.util.UnstableApi
|
||||
import androidx.media3.exoplayer.DefaultLoadControl
|
||||
import androidx.media3.exoplayer.ExoPlayer
|
||||
import androidx.media3.session.MediaLibraryService
|
||||
import androidx.media3.session.MediaSession.ControllerInfo
|
||||
import com.cappielloantonio.tempo.repository.AutomotiveRepository
|
||||
import com.cappielloantonio.tempo.repository.QueueRepository
|
||||
import com.cappielloantonio.tempo.ui.activity.MainActivity
|
||||
import com.cappielloantonio.tempo.util.AssetLinkUtil
|
||||
import com.cappielloantonio.tempo.util.Constants
|
||||
import com.cappielloantonio.tempo.util.DownloadUtil
|
||||
import com.cappielloantonio.tempo.util.DynamicMediaSourceFactory
|
||||
import com.cappielloantonio.tempo.util.MappingUtil
|
||||
import com.cappielloantonio.tempo.util.Preferences
|
||||
import com.cappielloantonio.tempo.util.ReplayGainUtil
|
||||
import com.cappielloantonio.tempo.widget.WidgetUpdateManager
|
||||
import com.google.android.gms.cast.framework.CastContext
|
||||
import com.google.android.gms.common.ConnectionResult
|
||||
import com.google.android.gms.common.GoogleApiAvailability
|
||||
|
||||
@UnstableApi
|
||||
class MediaService : MediaLibraryService(), SessionAvailabilityListener {
|
||||
private lateinit var automotiveRepository: AutomotiveRepository
|
||||
private lateinit var player: ExoPlayer
|
||||
class MediaService : BaseMediaService(), SessionAvailabilityListener {
|
||||
private val automotiveRepository = AutomotiveRepository()
|
||||
private lateinit var castPlayer: CastPlayer
|
||||
private lateinit var mediaLibrarySession: MediaLibrarySession
|
||||
private lateinit var librarySessionCallback: MediaLibrarySessionCallback
|
||||
lateinit var equalizerManager: EqualizerManager
|
||||
|
||||
inner class LocalBinder : Binder() {
|
||||
fun getEqualizerManager(): EqualizerManager {
|
||||
return this@MediaService.equalizerManager
|
||||
}
|
||||
}
|
||||
|
||||
private val binder = LocalBinder()
|
||||
|
||||
companion object {
|
||||
const val ACTION_BIND_EQUALIZER = "com.cappielloantonio.tempo.service.BIND_EQUALIZER"
|
||||
}
|
||||
private val widgetUpdateHandler = Handler(Looper.getMainLooper())
|
||||
private var widgetUpdateScheduled = false
|
||||
private val widgetUpdateRunnable = object : Runnable {
|
||||
override fun run() {
|
||||
if (!player.isPlaying) {
|
||||
widgetUpdateScheduled = false
|
||||
return
|
||||
}
|
||||
updateWidget()
|
||||
widgetUpdateHandler.postDelayed(this, WIDGET_UPDATE_INTERVAL_MS)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
|
||||
initializeRepository()
|
||||
initializePlayer()
|
||||
initializeMediaLibrarySession()
|
||||
restorePlayerFromQueue()
|
||||
initializePlayerListener()
|
||||
initializeCastPlayer()
|
||||
initializeEqualizerManager()
|
||||
|
||||
setPlayer(
|
||||
null,
|
||||
if (this::castPlayer.isInitialized && castPlayer.isCastSessionAvailable) castPlayer else player
|
||||
)
|
||||
}
|
||||
|
||||
override fun onGetSession(controllerInfo: ControllerInfo): MediaLibrarySession {
|
||||
return mediaLibrarySession
|
||||
}
|
||||
|
||||
override fun onTaskRemoved(rootIntent: Intent?) {
|
||||
val player = mediaLibrarySession.player
|
||||
|
||||
if (!player.playWhenReady || player.mediaItemCount == 0) {
|
||||
stopSelf()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
equalizerManager.release()
|
||||
stopWidgetUpdates()
|
||||
releasePlayer()
|
||||
super.onDestroy()
|
||||
}
|
||||
|
||||
override fun onBind(intent: Intent?): IBinder? {
|
||||
// Check if the intent is for our custom equalizer binder
|
||||
if (intent?.action == ACTION_BIND_EQUALIZER) {
|
||||
return binder
|
||||
}
|
||||
// Otherwise, handle it as a normal MediaLibraryService connection
|
||||
return super.onBind(intent)
|
||||
}
|
||||
|
||||
private fun initializeRepository() {
|
||||
automotiveRepository = AutomotiveRepository()
|
||||
}
|
||||
|
||||
private fun initializeEqualizerManager() {
|
||||
equalizerManager = EqualizerManager()
|
||||
val audioSessionId = player.audioSessionId
|
||||
if (equalizerManager.attachToSession(audioSessionId)) {
|
||||
val enabled = Preferences.isEqualizerEnabled()
|
||||
equalizerManager.setEnabled(enabled)
|
||||
|
||||
val bands = equalizerManager.getNumberOfBands()
|
||||
val savedLevels = Preferences.getEqualizerBandLevels(bands)
|
||||
for (i in 0 until bands) {
|
||||
equalizerManager.setBandLevel(i.toShort(), savedLevels[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun initializePlayer() {
|
||||
player = ExoPlayer.Builder(this)
|
||||
.setRenderersFactory(getRenderersFactory())
|
||||
.setMediaSourceFactory(DynamicMediaSourceFactory(this))
|
||||
.setAudioAttributes(AudioAttributes.DEFAULT, true)
|
||||
.setHandleAudioBecomingNoisy(true)
|
||||
.setWakeMode(C.WAKE_MODE_NETWORK)
|
||||
.setLoadControl(initializeLoadControl())
|
||||
.build()
|
||||
|
||||
player.shuffleModeEnabled = Preferences.isShuffleModeEnabled()
|
||||
player.repeatMode = Preferences.getRepeatMode()
|
||||
}
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
private fun initializeCastPlayer() {
|
||||
@@ -153,254 +20,41 @@ class MediaService : MediaLibraryService(), SessionAvailabilityListener {
|
||||
.isGooglePlayServicesAvailable(this) == ConnectionResult.SUCCESS
|
||||
) {
|
||||
CastContext.getSharedInstance(this, ContextCompat.getMainExecutor(this))
|
||||
.addOnSuccessListener { castContext ->
|
||||
castPlayer = CastPlayer(castContext)
|
||||
castPlayer.setSessionAvailabilityListener(this@MediaService)
|
||||
|
||||
if (castPlayer.isCastSessionAvailable && this::mediaLibrarySession.isInitialized) {
|
||||
setPlayer(player, castPlayer)
|
||||
}
|
||||
}
|
||||
.addOnSuccessListener { castContext ->
|
||||
castPlayer = CastPlayer(castContext)
|
||||
castPlayer.setSessionAvailabilityListener(this@MediaService)
|
||||
initializePlayerListener(castPlayer)
|
||||
if (castPlayer.isCastSessionAvailable)
|
||||
setPlayer(mediaLibrarySession.player, castPlayer)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun initializeMediaLibrarySession() {
|
||||
val sessionActivityPendingIntent =
|
||||
TaskStackBuilder.create(this).run {
|
||||
addNextIntent(Intent(this@MediaService, MainActivity::class.java))
|
||||
getPendingIntent(0, FLAG_IMMUTABLE or FLAG_UPDATE_CURRENT)
|
||||
}
|
||||
|
||||
librarySessionCallback = createLibrarySessionCallback()
|
||||
mediaLibrarySession =
|
||||
MediaLibrarySession.Builder(this, player, librarySessionCallback)
|
||||
.setSessionActivity(sessionActivityPendingIntent)
|
||||
.build()
|
||||
}
|
||||
|
||||
private fun restorePlayerFromQueue() {
|
||||
if (player.mediaItemCount > 0) return
|
||||
|
||||
val queueRepository = QueueRepository()
|
||||
val storedQueue = queueRepository.media
|
||||
if (storedQueue.isNullOrEmpty()) return
|
||||
|
||||
val mediaItems = MappingUtil.mapMediaItems(storedQueue)
|
||||
if (mediaItems.isEmpty()) return
|
||||
|
||||
val lastIndex = try {
|
||||
queueRepository.lastPlayedMediaIndex
|
||||
} catch (_: Exception) {
|
||||
0
|
||||
}.coerceIn(0, mediaItems.size - 1)
|
||||
|
||||
val lastPosition = try {
|
||||
queueRepository.lastPlayedMediaTimestamp
|
||||
} catch (_: Exception) {
|
||||
0L
|
||||
}.let { if (it < 0L) 0L else it }
|
||||
|
||||
player.setMediaItems(mediaItems, lastIndex, lastPosition)
|
||||
player.prepare()
|
||||
updateWidget()
|
||||
}
|
||||
|
||||
private fun createLibrarySessionCallback(): MediaLibrarySessionCallback {
|
||||
override fun getMediaLibrarySessionCallback(): MediaLibrarySession.Callback {
|
||||
return MediaLibrarySessionCallback(this, automotiveRepository)
|
||||
}
|
||||
|
||||
private fun initializePlayerListener() {
|
||||
player.addListener(object : Player.Listener {
|
||||
override fun onMediaItemTransition(mediaItem: MediaItem?, reason: Int) {
|
||||
if (mediaItem == null) return
|
||||
override fun playerInitHook() {
|
||||
super.playerInitHook()
|
||||
initializeCastPlayer()
|
||||
if (this::castPlayer.isInitialized && castPlayer.isCastSessionAvailable)
|
||||
setPlayer(null, castPlayer)
|
||||
}
|
||||
|
||||
if (reason == Player.MEDIA_ITEM_TRANSITION_REASON_SEEK || reason == Player.MEDIA_ITEM_TRANSITION_REASON_AUTO) {
|
||||
MediaManager.setLastPlayedTimestamp(mediaItem)
|
||||
}
|
||||
updateWidget()
|
||||
}
|
||||
|
||||
override fun onTracksChanged(tracks: Tracks) {
|
||||
ReplayGainUtil.setReplayGain(player, tracks)
|
||||
|
||||
val currentMediaItem = player.currentMediaItem
|
||||
if (currentMediaItem != null && currentMediaItem.mediaMetadata.extras != null) {
|
||||
MediaManager.scrobble(currentMediaItem, false)
|
||||
}
|
||||
|
||||
if (player.currentMediaItemIndex + 1 == player.mediaItemCount)
|
||||
MediaManager.continuousPlay(player.currentMediaItem)
|
||||
}
|
||||
|
||||
override fun onIsPlayingChanged(isPlaying: Boolean) {
|
||||
if (!isPlaying) {
|
||||
MediaManager.setPlayingPausedTimestamp(
|
||||
player.currentMediaItem,
|
||||
player.currentPosition
|
||||
)
|
||||
} else {
|
||||
MediaManager.scrobble(player.currentMediaItem, false)
|
||||
}
|
||||
if (isPlaying) {
|
||||
scheduleWidgetUpdates()
|
||||
} else {
|
||||
stopWidgetUpdates()
|
||||
}
|
||||
updateWidget()
|
||||
}
|
||||
|
||||
override fun onPlaybackStateChanged(playbackState: Int) {
|
||||
super.onPlaybackStateChanged(playbackState)
|
||||
|
||||
if (!player.hasNextMediaItem() &&
|
||||
playbackState == Player.STATE_ENDED &&
|
||||
player.mediaMetadata.extras?.getString("type") == Constants.MEDIA_TYPE_MUSIC
|
||||
) {
|
||||
MediaManager.scrobble(player.currentMediaItem, true)
|
||||
MediaManager.saveChronology(player.currentMediaItem)
|
||||
}
|
||||
updateWidget()
|
||||
}
|
||||
|
||||
override fun onPositionDiscontinuity(
|
||||
oldPosition: Player.PositionInfo,
|
||||
newPosition: Player.PositionInfo,
|
||||
reason: Int
|
||||
) {
|
||||
super.onPositionDiscontinuity(oldPosition, newPosition, reason)
|
||||
|
||||
if (reason == Player.DISCONTINUITY_REASON_AUTO_TRANSITION) {
|
||||
if (oldPosition.mediaItem?.mediaMetadata?.extras?.getString("type") == Constants.MEDIA_TYPE_MUSIC) {
|
||||
MediaManager.scrobble(oldPosition.mediaItem, true)
|
||||
MediaManager.saveChronology(oldPosition.mediaItem)
|
||||
}
|
||||
|
||||
if (newPosition.mediaItem?.mediaMetadata?.extras?.getString("type") == Constants.MEDIA_TYPE_MUSIC) {
|
||||
MediaManager.setLastPlayedTimestamp(newPosition.mediaItem)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onShuffleModeEnabledChanged(shuffleModeEnabled: Boolean) {
|
||||
Preferences.setShuffleModeEnabled(shuffleModeEnabled)
|
||||
}
|
||||
|
||||
override fun onRepeatModeChanged(repeatMode: Int) {
|
||||
Preferences.setRepeatMode(repeatMode)
|
||||
}
|
||||
})
|
||||
if (player.isPlaying) {
|
||||
scheduleWidgetUpdates()
|
||||
override fun releasePlayers() {
|
||||
if (this::castPlayer.isInitialized) {
|
||||
castPlayer.setSessionAvailabilityListener(null)
|
||||
castPlayer.release()
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateWidget() {
|
||||
val mi = player.currentMediaItem
|
||||
val title = mi?.mediaMetadata?.title?.toString()
|
||||
?: mi?.mediaMetadata?.extras?.getString("title")
|
||||
val artist = mi?.mediaMetadata?.artist?.toString()
|
||||
?: mi?.mediaMetadata?.extras?.getString("artist")
|
||||
val album = mi?.mediaMetadata?.albumTitle?.toString()
|
||||
?: mi?.mediaMetadata?.extras?.getString("album")
|
||||
val extras = mi?.mediaMetadata?.extras
|
||||
val coverId = extras?.getString("coverArtId")
|
||||
val songLink = extras?.getString("assetLinkSong")
|
||||
?: AssetLinkUtil.buildLink(AssetLinkUtil.TYPE_SONG, extras?.getString("id"))
|
||||
val albumLink = extras?.getString("assetLinkAlbum")
|
||||
?: AssetLinkUtil.buildLink(AssetLinkUtil.TYPE_ALBUM, extras?.getString("albumId"))
|
||||
val artistLink = extras?.getString("assetLinkArtist")
|
||||
?: AssetLinkUtil.buildLink(AssetLinkUtil.TYPE_ARTIST, extras?.getString("artistId"))
|
||||
val position = player.currentPosition.takeIf { it != C.TIME_UNSET } ?: 0L
|
||||
val duration = player.duration.takeIf { it != C.TIME_UNSET } ?: 0L
|
||||
WidgetUpdateManager.updateFromState(
|
||||
this,
|
||||
title ?: "",
|
||||
artist ?: "",
|
||||
album ?: "",
|
||||
coverId,
|
||||
player.isPlaying,
|
||||
player.shuffleModeEnabled,
|
||||
player.repeatMode,
|
||||
position,
|
||||
duration,
|
||||
songLink,
|
||||
albumLink,
|
||||
artistLink
|
||||
)
|
||||
}
|
||||
|
||||
private fun scheduleWidgetUpdates() {
|
||||
if (widgetUpdateScheduled) return
|
||||
widgetUpdateHandler.postDelayed(widgetUpdateRunnable, WIDGET_UPDATE_INTERVAL_MS)
|
||||
widgetUpdateScheduled = true
|
||||
}
|
||||
|
||||
private fun stopWidgetUpdates() {
|
||||
if (!widgetUpdateScheduled) return
|
||||
widgetUpdateHandler.removeCallbacks(widgetUpdateRunnable)
|
||||
widgetUpdateScheduled = false
|
||||
}
|
||||
|
||||
private fun initializeLoadControl(): DefaultLoadControl {
|
||||
return DefaultLoadControl.Builder()
|
||||
.setBufferDurationsMs(
|
||||
(DefaultLoadControl.DEFAULT_MIN_BUFFER_MS * Preferences.getBufferingStrategy()).toInt(),
|
||||
(DefaultLoadControl.DEFAULT_MAX_BUFFER_MS * Preferences.getBufferingStrategy()).toInt(),
|
||||
DefaultLoadControl.DEFAULT_BUFFER_FOR_PLAYBACK_MS,
|
||||
DefaultLoadControl.DEFAULT_BUFFER_FOR_PLAYBACK_AFTER_REBUFFER_MS
|
||||
)
|
||||
.build()
|
||||
}
|
||||
|
||||
private fun getQueueFromPlayer(player: Player): List<MediaItem> {
|
||||
val queue = mutableListOf<MediaItem>()
|
||||
for (i in 0 until player.mediaItemCount) {
|
||||
queue.add(player.getMediaItemAt(i))
|
||||
}
|
||||
return queue
|
||||
}
|
||||
|
||||
private fun setPlayer(oldPlayer: Player?, newPlayer: Player) {
|
||||
if (oldPlayer === newPlayer) return
|
||||
oldPlayer?.stop()
|
||||
mediaLibrarySession.player = newPlayer
|
||||
}
|
||||
|
||||
private fun releasePlayer() {
|
||||
if (this::castPlayer.isInitialized) castPlayer.setSessionAvailabilityListener(null)
|
||||
if (this::castPlayer.isInitialized) castPlayer.release()
|
||||
player.release()
|
||||
mediaLibrarySession.release()
|
||||
automotiveRepository.deleteMetadata()
|
||||
super.releasePlayers()
|
||||
}
|
||||
|
||||
private fun getRenderersFactory() = DownloadUtil.buildRenderersFactory(this, false)
|
||||
|
||||
override fun onCastSessionAvailable() {
|
||||
val currentQueue = getQueueFromPlayer(player)
|
||||
val currentIndex = player.currentMediaItemIndex
|
||||
val currentPosition = player.currentPosition
|
||||
val isPlaying = player.playWhenReady
|
||||
|
||||
setPlayer(player, castPlayer)
|
||||
|
||||
castPlayer.setMediaItems(currentQueue, currentIndex, currentPosition)
|
||||
castPlayer.playWhenReady = isPlaying
|
||||
castPlayer.prepare()
|
||||
setPlayer(exoplayer, castPlayer)
|
||||
}
|
||||
|
||||
override fun onCastSessionUnavailable() {
|
||||
val currentQueue = getQueueFromPlayer(castPlayer)
|
||||
val currentIndex = castPlayer.currentMediaItemIndex
|
||||
val currentPosition = castPlayer.currentPosition
|
||||
val isPlaying = castPlayer.playWhenReady
|
||||
|
||||
setPlayer(castPlayer, player)
|
||||
|
||||
player.setMediaItems(currentQueue, currentIndex, currentPosition)
|
||||
player.playWhenReady = isPlaying
|
||||
player.prepare()
|
||||
setPlayer(castPlayer, exoplayer)
|
||||
}
|
||||
}
|
||||
|
||||
private const val WIDGET_UPDATE_INTERVAL_MS = 1000L
|
||||
}
|
||||
2
fastlane/metadata/android/en-US/changelogs/2.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
chore: updated tempo references to tempus
|
||||
fix: Crash on share no expiration date or field returned from api
|
||||
10
fastlane/metadata/android/en-US/changelogs/3.txt
Normal file
@@ -0,0 +1,10 @@
|
||||
Update Spanish (es-ES) translation
|
||||
Shuffle for artists without using `getTopSongs`
|
||||
Update USAGE.md with instant mix detils
|
||||
Sort artists by album count
|
||||
Fix downloaded tab performance
|
||||
Remove NestedScrollViews for fragment_album_page
|
||||
Playlist page should not snap
|
||||
Do not override getItemViewType and getItemId
|
||||
Update media3 dependencies
|
||||
Update MediaItems after network change
|
||||
2
fastlane/metadata/android/en-US/changelogs/4.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
reverts change causing album disc/track list to get out of order
|
||||
Add listener to enable equalizer when audioSessionId
|
||||
5
fastlane/metadata/android/en-US/changelogs/5.txt
Normal file
@@ -0,0 +1,5 @@
|
||||
Equalizer fix in main build variant
|
||||
fix Images not filling holder in discovery
|
||||
Make artist and album clickable on home discovery
|
||||
Implement scroll to currently playing feature
|
||||
Shuffling genres now queuing 500 tracks vs 25
|
||||
5
fastlane/metadata/android/en-US/changelogs/6.txt
Normal file
@@ -0,0 +1,5 @@
|
||||
Update russian strings.xml
|
||||
Disallow duplicate songs in queue
|
||||
Fixed crash when viewing share
|
||||
Update Polish translation
|
||||
Add podcast channel visible when empty podcasts
|
||||
5
fastlane/metadata/android/en-US/changelogs/7.txt
Normal file
@@ -0,0 +1,5 @@
|
||||
* fix: Fix player queue soft-lock
|
||||
* chore: Add Catalan i18n
|
||||
* chore: Refactor MediaService
|
||||
* chore(i18n): Update Spanish translation
|
||||
* chore(i18n): Update Italian translation
|
||||
@@ -8,11 +8,12 @@ Features
|
||||
- Streaming and Offline Mode: Stream music directly from your Subsonic server. Offline mode is currently under active development and may have limitations when using multiple servers.
|
||||
- Playlist Management: Create, edit, and manage playlists to curate your perfect music collection.
|
||||
- Gapless Playback: Experience uninterrupted playback with gapless listening mode.
|
||||
- Chromecast Support: Stream your music to Chromecast devices. The support is currently in a rudimentary state.
|
||||
- Scrobbling Integration: Optionally integrate Tempus with Last.fm to scrobble your played tracks, gather music insights, and further personalize your music recommendations, if supported by your Subsonic server.
|
||||
- Scrobbling Integration: Optionally integrate Tempus with Last.fm or Listenbrainz.org to scrobble your played tracks, gather music insights, and further personalize your music recommendations, if supported by your Subsonic server.
|
||||
- Podcasts and Radio: If your Subsonic server supports it, listen to podcasts and radio shows directly within Tempus, expanding your audio entertainment options.
|
||||
- Transcoding Support: Activate transcoding of tracks on your Subsonic server, allowing you to set a transcoding profile for optimized streaming directly from the app. This feature requires support from your Subsonic server.
|
||||
- Multiple Libraries: Tempus handles multi-library setups gracefully. They are displayed as Library folders.
|
||||
- Equalizer: Option to use in app equalizer.
|
||||
- Widget: New widget to keeping the basic controls on your screen at all times.
|
||||
- Available in 11 languages: Currently in Chinese, French, German, Italian, Korean, Polish, Portuguese, Russion, Spanish and Turkish
|
||||
|
||||
*Chromecast/Android Auto Not Supported: These features require google libraries and are only available in the github apk.*
|
||||
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 1.3 MiB |
|
Before Width: | Height: | Size: 327 KiB |
|
Before Width: | Height: | Size: 703 KiB |
|
Before Width: | Height: | Size: 246 KiB |
|
Before Width: | Height: | Size: 200 KiB |
|
Before Width: | Height: | Size: 320 KiB |
|
Before Width: | Height: | Size: 151 KiB |
|
Before Width: | Height: | Size: 136 KiB |
|
Before Width: | Height: | Size: 327 KiB |
|
Before Width: | Height: | Size: 1.2 MiB |
|
Before Width: | Height: | Size: 319 KiB |
|
Before Width: | Height: | Size: 693 KiB |
|
Before Width: | Height: | Size: 247 KiB |
|
Before Width: | Height: | Size: 196 KiB |
|
Before Width: | Height: | Size: 314 KiB |
|
Before Width: | Height: | Size: 144 KiB |
|
Before Width: | Height: | Size: 133 KiB |
@@ -1,34 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg id="Livello_2" data-name="Livello 2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 958.83 227.02">
|
||||
<defs>
|
||||
<style>
|
||||
.cls-1 {
|
||||
fill: #f24b6a;
|
||||
}
|
||||
</style>
|
||||
</defs>
|
||||
<g id="Livello_1-2" data-name="Livello 1">
|
||||
<g>
|
||||
<g>
|
||||
<path d="m127.84,23.28v27.94h-47.22v129.87h-33.19V51.22H0v-27.94h127.84Z"/>
|
||||
<path d="m207.46,146.83c-.79,6.92-4.39,13.96-10.81,21.09-9.99,11.35-23.98,17.02-41.97,17.02-14.85,0-27.94-4.78-39.29-14.35-11.35-9.56-17.02-25.12-17.02-46.68,0-20.2,5.12-35.69,15.36-46.47,10.24-10.78,23.54-16.17,39.88-16.17,9.71,0,18.45,1.82,26.23,5.46,7.78,3.64,14.2,9.39,19.27,17.24,4.57,6.92,7.53,14.95,8.89,24.09.78,5.35,1.11,13.06.96,23.13h-79.87c.43,11.71,4.1,19.91,11.03,24.62,4.21,2.93,9.28,4.39,15.2,4.39,6.28,0,11.38-1.78,15.31-5.35,2.14-1.93,4.03-4.6,5.67-8.03h31.16Zm-30.19-35.76c-.5-8.06-2.94-14.19-7.33-18.36-4.39-4.18-9.83-6.26-16.33-6.26-7.07,0-12.55,2.21-16.43,6.64-3.89,4.43-6.34,10.42-7.33,17.99h47.43Z"/>
|
||||
<path d="m289.12,96.51c-2.57-5.64-7.6-8.46-15.1-8.46-8.71,0-14.56,2.82-17.56,8.46-1.64,3.21-2.46,8-2.46,14.35v70.23h-30.94v-116.49h29.66v17.02c3.78-6.07,7.35-10.39,10.71-12.96,5.92-4.57,13.6-6.85,23.02-6.85,8.92,0,16.13,1.96,21.63,5.89,4.42,3.64,7.78,8.32,10.06,14.03,4-6.85,8.96-11.88,14.88-15.1,6.28-3.21,13.28-4.82,20.98-4.82,5.14,0,10.21,1,15.2,3,5,2,9.53,5.5,13.6,10.49,3.28,4.07,5.5,9.07,6.64,14.99.71,3.93,1.07,9.67,1.07,17.24l-.21,73.55h-31.26v-74.3c0-4.42-.71-8.06-2.14-10.92-2.71-5.42-7.71-8.14-14.99-8.14-8.42,0-14.24,3.5-17.45,10.49-1.64,3.71-2.46,8.17-2.46,13.38v69.49h-30.73v-69.49c0-6.92-.71-11.95-2.14-15.1Z"/>
|
||||
<path d="m504.99,76.92c9.42,10.06,14.13,24.84,14.13,44.33,0,20.56-4.62,36.22-13.86,47-9.24,10.78-21.15,16.17-35.71,16.17-9.28,0-16.99-2.32-23.13-6.96-3.36-2.57-6.64-6.32-9.85-11.24v60.81h-30.19V64.39h29.23v17.24c3.28-5.07,6.78-9.06,10.49-11.99,6.78-5.21,14.85-7.82,24.2-7.82,13.63,0,25.2,5.03,34.69,15.1Zm-17.34,45.82c0-8.99-2.05-16.95-6.16-23.88-4.11-6.92-10.76-10.39-19.97-10.39-11.06,0-18.67,5.25-22.8,15.74-2.14,5.57-3.21,12.63-3.21,21.2,0,13.56,3.6,23.09,10.81,28.59,4.28,3.21,9.35,4.82,15.2,4.82,8.49,0,14.97-3.28,19.43-9.85,4.46-6.57,6.69-15.31,6.69-26.23Z"/>
|
||||
<path d="m634.28,79.17c9.85,12.35,14.78,26.95,14.78,43.79s-4.93,31.78-14.78,43.95c-9.85,12.17-24.8,18.25-44.86,18.25s-35.01-6.08-44.86-18.25c-9.85-12.17-14.77-26.82-14.77-43.95s4.92-31.44,14.77-43.79c9.85-12.35,24.8-18.52,44.86-18.52s35.01,6.17,44.86,18.52Zm-44.97,7.28c-8.92,0-15.79,3.16-20.61,9.48-4.82,6.32-7.23,15.33-7.23,27.03s2.41,20.74,7.23,27.09c4.82,6.35,11.69,9.53,20.61,9.53s15.77-3.18,20.56-9.53c4.78-6.35,7.17-15.38,7.17-27.09s-2.39-20.72-7.17-27.03c-4.78-6.32-11.63-9.48-20.56-9.48Z"/>
|
||||
</g>
|
||||
<g>
|
||||
<rect class="cls-1" x="827.72" width="11.92" height="227.02"/>
|
||||
<rect class="cls-1" x="803.88" y="17.88" width="11.92" height="167.43"/>
|
||||
<rect class="cls-1" x="780.05" y="35.76" width="11.92" height="53.64"/>
|
||||
<rect class="cls-1" x="708.53" y="35.76" width="11.92" height="53.64"/>
|
||||
<rect class="cls-1" x="946.92" y="35.76" width="11.92" height="53.64"/>
|
||||
<rect class="cls-1" x="756.21" y="17.88" width="11.92" height="89.39"/>
|
||||
<rect class="cls-1" x="899.24" y="17.88" width="11.92" height="89.39"/>
|
||||
<rect class="cls-1" x="732.37" width="11.92" height="125.15"/>
|
||||
<rect class="cls-1" x="923.08" width="11.92" height="125.15"/>
|
||||
<rect class="cls-1" x="875.4" y="35.76" width="11.92" height="53.64"/>
|
||||
<rect class="cls-1" x="851.56" y="17.88" width="11.92" height="167.43"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 3.6 KiB |
BIN
mockup/usage/add_podcast_feed.png
Normal file
|
After Width: | Height: | Size: 16 KiB |
BIN
mockup/usage/add_radio_station.png
Normal file
|
After Width: | Height: | Size: 6.4 KiB |